forked from whatwg/storage
-
Notifications
You must be signed in to change notification settings - Fork 0
/
storage.bs
788 lines (568 loc) · 29.3 KB
/
storage.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
<pre class=metadata>
Group: WHATWG
H1: Storage
Shortname: storage
Text Macro: TWITTER storagestandard
Text Macro: LATESTRD 2023-02
Abstract: The Storage Standard defines an API for persistent storage and quota estimates, as well as the platform storage architecture.
Translation: ja https://triple-underscore.github.io/storage-ja.html
</pre>
<pre class=anchors>
urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: agent; url: #sec-agents; type: dfn
text: agent cluster; url: #sec-agent-clusters; type: dfn
</pre>
<pre class=link-defaults>
spec:infra; type:dfn; text:implementation-defined
spec:infra; type:dfn; text:user agent
</pre>
<h2 id=introduction>Introduction</h2>
<p>Over the years the web has grown various APIs that can be used for storage, e.g., IndexedDB,
<code>localStorage</code>, and <code>showNotification()</code>. The Storage Standard consolidates
these APIs by defining:
<ul class=brief>
<li>A bucket, the primitive these APIs store their data in
<li>A way of making that bucket persistent
<li>A way of getting usage and quota estimates for an <a for=/>origin</a>
</ul>
<p>Traditionally, as the user runs out of storage space on their device, the data stored with these
APIs gets lost without the user being able to intervene. However, persistent buckets cannot be
cleared without consent by the user. This thus brings data guarantees users have enjoyed on native
platforms to the web.
<div class="example" id=example-3a7051a8>
<p>A simple way to make storage persistent is through invoking the {{persist()}} method. It
simultaneously requests the end user for permission and changes the storage to be persistent once
granted:</p>
<pre><code class="lang-javascript">
navigator.storage.persist().then(persisted => {
if (persisted) {
/* … */
}
});
</code></pre>
<p>To not show user-agent-driven dialogs to the end user unannounced slightly more involved code
can be written:</p>
<pre><code class="lang-javascript">
Promise.all([
navigator.storage.persisted(),
navigator.permissions.query({name: "persistent-storage"})
]).then(([persisted, permission]) => {
if (!persisted && permission.state == "granted") {
navigator.storage.persist().then( /* … */ );
} else if (!persisted && permission.state == "prompt") {
showPersistentStorageExplanation();
}
});
</code></pre>
<p>The {{estimate()}} method can be used to determine whether there is enough space left to
store content for an application:
<pre><code class="lang-javascript">
function retrieveNextChunk(nextChunkInfo) {
return navigator.storage.estimate().then(info => {
if (info.quota - info.usage > nextChunkInfo.size) {
return fetch(nextChunkInfo.url);
} else {
throw new Error("insufficient space to store next chunk");
}
}).then( /* … */ );
}
</code></pre>
</div>
<h2 id=terminology>Terminology</h2>
<p>This specification depends on the Infra Standard. [[!INFRA]]
<p>This specification uses terminology from the HTML, IDL, and Permissions Standards.
[[!HTML]] [[!WEBIDL]] [[!PERMISSIONS]]
<h2 id=infrastructure>Lay of the land</h2>
<p>A <a for=/>user agent</a> has various kinds of semi-persistent state:
<dl>
<dt>Credentials
<dd><p>End-user credentials, such as username and passwords submitted through HTML forms
<dt>Permissions
<dd><p>Permissions for various features, such as geolocation
<dt>Network
<dd><p>HTTP cache, cookies, authentication entries, TLS client certificates
<dt id=site-storage>Storage
<dd>Indexed DB, Cache API, service worker registrations, <code>localStorage</code>,
<code>sessionStorage</code>, application caches, notifications, etc.
</dl>
<p>This standard primarily concerns itself with storage.
<h2 id=model>Model</h2>
<p>Standards defining local or session storage APIs will define a <a>storage endpoint</a> and
<a lt="registered storage endpoints">register</a> it by changing this standard. They will invoke
either the <a>obtain a local storage bottle map</a> or the
<a>obtain a session storage bottle map</a> algorithm, which will give them:
<ul>
<li><p>Failure, which might mean the API has to throw or otherwise indicate there is no storage
available for that <a for=/>environment settings object</a>.
<li><p>A <a>storage proxy map</a> that operates analogously to a <a for=/>map</a>, which can be
used to store data in a manner that suits the API. This standard takes care of isolating that data
from other APIs, <a>storage keys</a>, and <a>storage types</a>.
</ul>
<p class=note>If you are defining a standard for such an API, consider filing an issue against this
standard for assistance and review.
<p><img src=assets/model-diagram.svg alt="Storage model diagram (described in the next paragraph)." width=434 height=815>
<p>To isolate this data this standard defines a <a for=/>storage shed</a> which segments
<a>storage shelves</a> by a <a>storage key</a>. A <a>storage shelf</a> in turn consists of a
<a>storage bucket</a> and will likely consist of multiple <a>storage buckets</a> in the future to
allow for different storage policies. And lastly, a <a>storage bucket</a> consists of
<a>storage bottles</a>, one for each <a>storage endpoint</a>.
<h3 id=storage-endpoints>Storage endpoints</h3>
<p>A <dfn export>storage endpoint</dfn> is a <a lt="local storage">local</a> or
<a>session storage</a> API that uses the infrastructure defined by this standard, most notably
<a for=/>storage bottles</a>, to keep track of its storage needs.
<p>A <a>storage endpoint</a> has an <dfn for="storage endpoint">identifier</dfn>, which is a
<a>storage identifier</a>.
<p>A <a>storage endpoint</a> also has <dfn for="storage endpoint">types</dfn>, which is a
<a for=/>set</a> of <a>storage types</a>.
<p>A <a>storage endpoint</a> also has a <dfn for="storage endpoint">quota</dfn>, which is null or a
number representing a recommended <a for="storage bottle">quota</a> (in bytes) for each
<a for=/>storage bottle</a> corresponding to this <a>storage endpoint</a>.
<p>A <dfn>storage identifier</dfn> is an <a for=/>ASCII string</a>.
<p>A <dfn>storage type</dfn> is "<code>local</code>" or "<code>session</code>".
<hr>
<p>The <dfn>registered storage endpoints</dfn> are a <a for=/>set</a> of <a>storage endpoints</a>
defined by the following table:
<!-- Note, these will be exposed more generally through https://github.com/whatwg/storage/pull/69 so
please keep https://w3ctag.github.io/design-principles/#casing-rules in mind. -->
<table>
<tr>
<th><a for="storage endpoint">Identifier</a>
<th><a for="storage endpoint">Type</a>
<th><a for="storage endpoint">Quota</a>
<tr>
<td>"<code>caches</code>"
<td>« "<code>local</code>" »
<td>null
<tr>
<td>"<code>indexedDB</code>"
<td>« "<code>local</code>" »
<td>null
<tr>
<td>"<code>localStorage</code>"
<td>« "<code>local</code>" »
<td>5 × 2<sup>20</sup> (i.e., 5 mebibytes)
<tr>
<td>"<code>serviceWorkerRegistrations</code>"
<td>« "<code>local</code>" »
<td>null
<tr>
<td>"<code>sessionStorage</code>"
<td>« "<code>session</code>" »
<td>5 × 2<sup>20</sup> (i.e., 5 mebibytes)
</table>
<p class=note>As mentioned, standards can use these <a>storage identifiers</a> with
<a>obtain a local storage bottle map</a> and <a>obtain a session storage bottle map</a>. It is
anticipated that some APIs will be applicable to both <a>storage types</a> going forward.
<!-- If that does not happen by 2024 we probably ought to admit defeat and simplify this a bit. -->
<h3 id=storage-keys>Storage keys</h3>
<p>A <dfn>storage key</dfn> is a <a>tuple</a> consisting of an <dfn for="storage key">origin</dfn>
(an <a for=/>origin</a>). [[!HTML]]
<p class=XXX>This is expected to change; see
<a href="https://privacycg.github.io/storage-partitioning/">Client-Side Storage Partitioning</a>.
<p>To <dfn export>obtain a storage key</dfn>, given an <a for=/>environment</a>
<var>environment</var>, run these steps:
<ol>
<li><p>Let <var>key</var> be the result of running
<a>obtain a storage key for non-storage purposes</a> with <var>environment</var>.
<li><p>If <var>key</var>'s <a for="storage key">origin</a> is an <a>opaque origin</a>, then return
failure.
<li><p>If the user has disabled storage, then return failure.
<li><p>Return <var>key</var>.
</ol>
<p>To <dfn export>obtain a storage key for non-storage purposes</dfn>, given an
<a for=/>environment</a> <var>environment</var>, run these steps:
<ol>
<li><p>Let <var>origin</var> be <var>environment</var>'s
<a for="environment settings object">origin</a> if <var>environment</var> is an
<a>environment settings object</a>; otherwise <var>environment</var>'s
<a for=environment>creation URL</a>'s <a for=url>origin</a>.
<li><p>Return a <a>tuple</a> consisting of <var>origin</var>.
</ol>
<p>To determine whether a <a>storage key</a> <var>A</var>
<dfn export for="storage key" lt=equal>equals</dfn> <a>storage key</a> <var>B</var>, run these
steps:
<ol>
<li><p>If <var>A</var>'s <a for="storage key">origin</a> is not <a>same origin</a> with
<var>B</var>'s <a for="storage key">origin</a>, then return false.
<li><p>Return true.
</ol>
<h3 id=storage-sheds>Storage sheds</h3>
<p>A <dfn>storage shed</dfn> is a <a for=/>map</a> of <a>storage keys</a> to <a>storage shelves</a>.
It is initially empty.
<hr>
<p>A <a for=/>user agent</a> holds a <dfn for="user agent">storage shed</dfn>, which is a
<a for=/>storage shed</a>. A user agent's <a for="user agent">storage shed</a> holds all
<dfn>local storage</dfn> data.
<p>A <a for=/>traversable navigable</a> holds a
<dfn oldids="browsing-session-storage-shed" for="traversable navigable">storage shed</dfn>, which is
a <a for=/>storage shed</a>. A <a for=/>traversable navigable</a>'s
<a for="traversable navigable">storage shed</a> holds all <dfn>session storage</dfn> data.
<p>To
<dfn export oldids="legacy-clone-a-browsing-session-storage-shed">legacy-clone a traversable storage shed</dfn>,
given a <a for=/>traversable navigable</a> <var>A</var> and a <a for=/>traversable navigable</a>
<var>B</var>, run these steps:
<ol>
<li>
<p><a for=map>For each</a> <var>key</var> → <var>shelf</var> of <var>A</var>'s
<a for="traversable navigable">storage shed</a>:
<ol>
<li><p>Let <var>newShelf</var> be the result of running <a>create a storage shelf</a> with
"<code>session</code>".
<li><p>Set <var>newShelf</var>'s <a>bucket map</a>["<code>default</code>"]'s
<a>bottle map</a>["<code>sessionStorage</code>"]'s <a for="storage bottle">map</a> to a
<a for=map>clone</a> of <var>shelf</var>'s <a>bucket map</a>["<code>default</code>"]'s
<a>bottle map</a>["<code>sessionStorage</code>"]'s <a for="storage bottle">map</a>.
<li><p>Set <var>B</var>'s <a for="traversable navigable">storage shed</a>[<var>key</var>] to
<var>newShelf</var>.
</ol>
</ol>
<p class="note">This is considered legacy as the benefits, if any, do not outweigh the
implementation complexity. And therefore it will not be expanded or used outside of
<cite>HTML</cite>. [[HTML]]
<h3 id=storage-shelves>Storage shelves</h3>
<p>A <dfn oldids=site-storage-unit lt="storage shelf|storage shelves">storage shelf</dfn> exists for
each <a>storage key</a> within a <a for=/>storage shed</a>. It holds a <dfn>bucket map</dfn>,
which is a <a for=/>map</a> of <a for=/>strings</a> to <a>storage buckets</a>.
<p class=note>For now "<code>default</code>" is the only <a for=map>key</a> that exists in a
<a>bucket map</a>. See <a href="https://github.com/whatwg/storage/issues/2">issue #2</a>. It is
given a <a for=map>value</a> when a <a>storage shelf</a> is
<a lt="obtain a storage shelf">obtained</a> for the first time.
<p>To <dfn>obtain a storage shelf</dfn>, given a <a for=/>storage shed</a> <var>shed</var>, an
<a>environment settings object</a> <var>environment</var>, and a <a>storage type</a>
<var>type</var>, run these steps:
<ol>
<li><p>Let <var>key</var> be the result of running <a>obtain a storage key</a> with
<var>environment</var>.
<li><p>If <var>key</var> is failure, then return failure.
<li><p>If <var>shed</var>[<var>key</var>] does not <a for=map>exist</a>, then set
<var>shed</var>[<var>key</var>] to the result of running <a>create a storage shelf</a> with
<var>type</var>.
<li><p>Return <var>shed</var>[<var>key</var>].
</ol>
<p>To <dfn>obtain a local storage shelf</dfn>, given an <a>environment settings object</a>
<var>environment</var>, return the result of running <a>obtain a storage shelf</a> with the user
agent's <a for="user agent">storage shed</a>, <var>environment</var>, and "<code>local</code>".
<p>To <dfn>create a storage shelf</dfn>, given a <a>storage type</a> <var>type</var>, run these
steps:
<ol>
<li><p>Let <var>shelf</var> be a new <a>storage shelf</a>.
<li><p>Set <var>shelf</var>'s <a>bucket map</a>["<code>default</code>"] to the result of running
<a>create a storage bucket</a> with <var>type</var>.
<li><p>Return <var>shelf</var>.
</ol>
<h3 id=buckets oldids=boxes>Storage buckets</h3>
<p>A <dfn>storage bucket</dfn> is a place for <a>storage endpoints</a> to store data.
<p>A <a>storage bucket</a> has a <dfn>bottle map</dfn> of <a>storage identifiers</a> to
<a>storage bottles</a>.
<hr>
<p>A <dfn id=bucket oldids=box>local storage bucket</dfn> is a <a>storage bucket</a> for
<a>local storage</a> APIs.
<p>A <a>local storage bucket</a> has a
<dfn for="local storage bucket" id=bucket-mode oldids=box-mode>mode</dfn>, which is
"<code>best-effort</code>" or "<code>persistent</code>". It is initially "<code>best-effort</code>".
<hr>
<p>A <dfn>session storage bucket</dfn> is a <a>storage bucket</a> for <a>session storage</a> APIs.
<hr>
<p>To <dfn>create a storage bucket</dfn>, given a <a>storage type</a> <var>type</var>, run these
steps:
<ol>
<li><p>Let <var>bucket</var> be null.
<li><p>If <var>type</var> is "<code>local</code>", then set <var>bucket</var> to a new
<a>local storage bucket</a>.
<li>
<p>Otherwise:
<ol>
<li><p>Assert: <var>type</var> is "<code>session</code>".
<li><p>Set <var>bucket</var> to a new <a>session storage bucket</a>.
</ol>
<li><p><a for=set>For each</a> <var>endpoint</var> of <a>registered storage endpoints</a> whose
<a for="storage endpoint">types</a> <a for=set>contain</a> <var>type</var>, set <var>bucket</var>'s
<a>bottle map</a>[<var>endpoint</var>'s <a for="storage endpoint">identifier</a>] to a new
<a>storage bottle</a> whose <a for="storage bottle">quota</a> is <var>endpoint</var>'s
<a for="storage endpoint">quota</a>.
<li><p>Return <var>bucket</var>.
</ol>
<h3 id=storage-bottles>Storage bottles</h3>
<p>A <dfn>storage bottle</dfn> is a part of a <a>storage bucket</a> carved out for a single
<a>storage endpoint</a>. A <a>storage bottle</a> has a <dfn for="storage bottle">map</dfn>, which is
initially an empty <a for=/>map</a>. A <a>storage bottle</a> also has a
<dfn for="storage bottle">proxy map reference set</dfn>, which is initially an empty
<a for=/>set</a>. A <a>storage bottle</a> also has a <dfn for="storage bottle">quota</dfn>, which is
null or a number representing a conservative estimate of the total amount of bytes it can hold. Null
indicates the lack of a limit. <span class=note>It is still bound by the <a>storage quota</a> of its
encompassing <a for=/>storage shelf</a>.
<p>A <a>storage bottle</a>'s <a for="storage bottle">map</a> is where the actual data meant to be
stored lives. User agents are expected to store this data, and make it available across <a>agent</a>
and even <a>agent cluster</a> boundaries, in an <a>implementation-defined</a> manner, so that this
standard and standards using this standard can access the contents.
<hr>
<p>To <dfn>obtain a storage bottle map</dfn>, given a <a>storage type</a> <var>type</var>,
<a for=/>environment settings object</a> <var>environment</var>, and <a>storage identifier</a>
<var>identifier</var>, run these steps:</p>
<ol>
<li><p>Let <var>shed</var> be null.
<li><p>If <var>type</var> is "<code>local</code>", then set <var>shed</var> to the user agent's
<a for="user agent">storage shed</a>.
<li>
<p>Otherwise:
<ol>
<li><p>Assert: <var>type</var> is "<code>session</code>".
<li><p>Set <var>shed</var> to <var>environment</var>'s
<a for=/>global object</a>'s
<a>associated <code>Document</code></a>'s
<a>node navigable</a>'s
<a for=navigable>traversable navigable</a>'s
<a for="traversable navigable">storage shed</a>.
</ol>
<li><p>Let <var>shelf</var> be the result of running <a>obtain a storage shelf</a>, with
<var>shed</var>, <var>environment</var>, and <var>type</var>.
<li><p>If <var>shelf</var> is failure, then return failure.
<li><p>Let <var>bucket</var> be <var>shelf</var>'s <a>bucket map</a>["<code>default</code>"].
<li><p>Let <var>bottle</var> be <var>bucket</var>'s <a>bottle map</a>[<var>identifier</var>].
<li><p>Let <var>proxyMap</var> be a new <a>storage proxy map</a> whose
<a for="storage proxy map">backing map</a> is <var>bottle</var>'s <a for="storage bottle">map</a>.
<li><p><a for=set>Append</a> <var>proxyMap</var> to <var>bottle</var>'s
<a for="storage bottle">proxy map reference set</a>.
<li><p>Return <var>proxyMap</var>.
</ol>
<p>To <dfn export>obtain a local storage bottle map</dfn>, given an
<a for=/>environment settings object</a> <var>environment</var> and <a>storage identifier</a>
<var>identifier</var>, return the result of running <a>obtain a storage bottle map</a> with
"<code>local</code>", <var>environment</var>, and <var>identifier</var>.
<p>To <dfn export>obtain a session storage bottle map</dfn>, given an
<a for=/>environment settings object</a> <var>environment</var> and <a>storage identifier</a>
<var>identifier</var>, return the result of running <a>obtain a storage bottle map</a> with
"<code>session</code>", <var>environment</var>, and <var>identifier</var>.
<h3 id=storage-proxy-maps>Storage proxy maps</h3>
<p>A <dfn>storage proxy map</dfn> is equivalent to a <a for=/>map</a>, except that all operations
are instead performed on its <dfn for="storage proxy map">backing map</dfn>.
<p class="XXX">This allows for the <a for="storage proxy map">backing map</a> to be replaced. This
is needed for <a href="https://github.com/whatwg/storage/issues/4">issue #4</a> and potentially the
<a href="https://privacycg.github.io/storage-access/">Storage Access API</a>.
<h3 id=storage-task-source>Storage task source</h2>
<p>The <dfn export id=task-source>storage task source</dfn> is a <a for=/>task source</a> to be used
for all <a for=/>tasks</a> related to a <a>storage endpoint</a>. In particular those that relate to
a <a>storage endpoint</a>'s <a for="storage endpoint">quota</a>.
<div algorithm>
<p>To <dfn export>queue a storage task</dfn> given a <a for=/>global object</a> <var>global</var>
and a series of steps <var>steps</var>, <a>queue a global task</a> on the <a>storage task source</a>
with <var>global</var> and <var>steps</var>.
</div>
<h2 id=persistence>Persistence permission</h2>
<p>A <a>local storage bucket</a> can only have its <a for="local storage bucket">mode</a> change to
"<code>persistent</code>" if the user (or user agent on behalf of the user) has granted permission
to use the "<code>persistent-storage</code>" <a>powerful feature</a>.
<p class="note">When granted to an <a for=/>origin</a>, the persistence permission can be used to
protect storage from the user agent's clearing policies. The user agent cannot clear storage marked
as persistent without involvement from the <a for=/>origin</a> or user. This makes it particularly
useful for resources the user needs to have available while offline or resources the user creates
locally.
<p>The "<code>persistent-storage</code>" <a>powerful feature</a>'s permission-related algorithms,
and types are defaulted, except for:
<dl>
<dt><a>permission state</a>
<dd><p>"<code>persistent-storage</code>"'s <a>permission state</a> must have the same value for all
<a>environment settings objects</a> with a given <a for="environment settings object">origin</a>.
<dt><a>permission revocation algorithm</a>
<dd algorithm="permission-revocation">
<ol>
<li><p>If the result of <a>getting the current permission state</a> with
"<code>persistent-storage</code>" is "{{PermissionState/granted}}", then return.
<li><p>Let <var>shelf</var> be the result of running <a>obtain a local storage shelf</a> with
<a>current settings object</a>.
<li><p>Set <var>shelf</var>'s <a>bucket map</a>["<code>default</code>"]'s
<a for="local storage bucket">mode</a> to "<code>best-effort</code>".
</ol>
</dl>
<h2 id=usage-and-quota>Usage and quota</h2>
<p>The <dfn export>storage usage</dfn> of a <a>storage shelf</a> is an <a>implementation-defined</a>
rough estimate of the amount of bytes used by it.
<p class=note>This cannot be an exact amount as user agents might, and are encouraged to, use
deduplication, compression, and other techniques that obscure exactly how much bytes a
<a>storage shelf</a> uses.
<p tracking-vector>The <dfn export>storage quota</dfn> of a <a>storage shelf</a> is an
<a>implementation-defined</a> conservative estimate of the total amount of bytes it can hold. This
amount should be less than the total storage space on the device. It must not be a function of the
available storage space on the device.
<div class=note>
<p>User agents are strongly encouraged to consider navigation frequency, recency of visits,
bookmarking, and <a href="#persistence">permission</a> for "<code>persistent-storage</code>" when
determining quotas.
<p>Directly or indirectly revealing available storage space can lead to fingerprinting and leaking
information outside the scope of the <a for=/>origin</a> involved.
</div>
<h2 id=management>Management</h2>
<p>Whenever a <a>storage bucket</a> is cleared by the user agent, it must be cleared in its
entirety. User agents should avoid clearing <a>storage buckets</a> while script that is able to
access them is running, unless instructed otherwise by the user.
<p>If removal of <a>storage buckets</a> leaves the encompassing <a>storage shelf</a>'s
<a>bucket map</a> <a for=map lt="is empty">empty</a>, then <a for=map>remove</a> that
<a>storage shelf</a> and corresponding <a>storage key</a> from the encompassing
<a for=/>storage shed</a>.
<h3 id=storage-pressure>Storage pressure</h3>
<p>A user agent that comes under storage pressure should clear network state and
<a>local storage buckets</a> whose <a for="local storage bucket">mode</a> is
"<code>best-effort</code>", ideally prioritizing removal in a manner that least impacts the user.
<p>If a user agent continues to be under storage pressure, then the user agent should inform the
user and offer a way to clear the remaining <a>local storage buckets</a>, i.e., those whose
<a for="local storage bucket">mode</a> is "<code>persistent</code>".
<p><a>Session storage buckets</a> must be cleared as <a for=/>traversable navigables</a> are closed.
<p class="note">If the user agent allows for revival of <a for=/>traversable navigables</a>, e.g.,
through reopening <a for=/>traversable navigables</a> or continued use of them after restarting the
user agent, then clearing necessarily involves a more complex set of heuristics.
<h3 id=ui-guidelines>User interface guidelines</h3>
<p>User agents should offer users the ability to clear network state and storage for individual
websites. User agents should not distinguish between network state and storage in their user
interface. This ensures network state cannot be used to revive storage and reduces the number of
concepts users need to be mindful of.
<!-- This intentionally does not define individual website as the specific UI is still in flux. -->
<p>Credentials should be separated as they contain data the user might not be able to revive, such
as an autogenerated password. Permissions are best separated too to avoid inconveniencing the user.
<h2 id=api>API</h2>
<pre class=idl>
[SecureContext]
interface mixin NavigatorStorage {
[SameObject] readonly attribute StorageManager storage;
};
Navigator includes NavigatorStorage;
WorkerNavigator includes NavigatorStorage;
</pre>
<p>Each <a>environment settings object</a> has an associated {{StorageManager}} object. [[!HTML]]
<p>The <dfn attribute for=NavigatorStorage><code>storage</code></dfn> getter steps are to return
<a>this</a>'s <a>relevant settings object</a>'s {{StorageManager}} object.
<pre class=idl>
[SecureContext,
Exposed=(Window,Worker)]
interface StorageManager {
Promise<boolean> persisted();
[Exposed=Window] Promise<boolean> persist();
Promise<StorageEstimate> estimate();
};
dictionary StorageEstimate {
unsigned long long usage;
unsigned long long quota;
};
</pre>
<div algorithm>
<p>The <dfn method for=StorageManager><code>persisted()</code></dfn> method steps are:
<ol>
<li><p>Let <var>promise</var> be <a>a new promise</a>.
<li><p>Let <var>global</var> be <a>this</a>'s <a>relevant global object</a>.
<li><p>Let <var>shelf</var> be the result of running <a>obtain a local storage shelf</a> with
<a>this</a>'s <a>relevant settings object</a>.
<li><p>If <var>shelf</var> is failure, then <a for=/>reject</a> <var>promise</var> with a
{{TypeError}}.
<li>
<p>Otherwise, run these steps <a>in parallel</a>:
<ol>
<li>
<p>Let <var>persisted</var> be true if <var>shelf</var>'s
<a>bucket map</a>["<code>default</code>"]'s <a for="local storage bucket">mode</a> is
"<code>persistent</code>"; otherwise false.
<p class=note>It will be false when there's an internal error.
<li><p><a>Queue a storage task</a> with <var>global</var> to <a for=/>resolve</a>
<var>promise</var> with <var>persisted</var>.
</ol>
<li><p>Return <var>promise</var>.
</ol>
</div>
<div algorithm>
<p>The <dfn method for=StorageManager><code>persist()</code></dfn> method steps are:
<ol>
<li><p>Let <var>promise</var> be <a>a new promise</a>.
<li><p>Let <var>global</var> be <a>this</a>'s <a>relevant global object</a>.
<li><p>Let <var>shelf</var> be the result of running <a>obtain a local storage shelf</a> with
<a>this</a>'s <a>relevant settings object</a>.
<li><p>If <var>shelf</var> is failure, then <a for=/>reject</a> <var>promise</var> with a
{{TypeError}}.
<li>
<p>Otherwise, run these steps <a>in parallel</a>:
<ol>
<li>
<p>Let <var>permission</var> be the result of <a>requesting permission to use</a>
"<code>persistent-storage</code>".
<p class="note">User agents are encouraged to not let the user answer this question twice for
the same <a for=/>origin</a> around the same time and this algorithm is not equipped to handle
such a scenario.
<li><p>Let <var>bucket</var> be <var>shelf</var>'s <a>bucket map</a>["<code>default</code>"].
<li>
<p>Let <var>persisted</var> be true if <var>bucket</var>'s
<a for="local storage bucket">mode</a> is "<code>persistent</code>"; otherwise false.
<p class=note>It will be false when there's an internal error.
<li>
<p>If <var>persisted</var> is false and <var>permission</var> is "{{PermissionState/granted}}",
then:
<ol>
<li><p>Set <var>bucket</var>'s <a for="local storage bucket">mode</a> to
"<code>persistent</code>".
<li><p>If there was no internal error, then set <var>persisted</var> to true.
</ol>
<li><p><a>Queue a storage task</a> with <var>global</var> to <a for=/>resolve</a>
<var>promise</var> with <var>persisted</var>.
</ol>
<li><p>Return <var>promise</var>.
</ol>
</div>
<div algorithm>
<p>The <dfn method for=StorageManager><code>estimate()</code></dfn> method steps are:
<ol>
<li><p>Let <var>promise</var> be <a>a new promise</a>.
<li><p>Let <var>global</var> be <a>this</a>'s <a>relevant global object</a>.
<li><p>Let <var>shelf</var> be the result of running <a>obtain a local storage shelf</a> with
<a>this</a>'s <a>relevant settings object</a>.
<li><p>If <var>shelf</var> is failure, then <a for=/>reject</a> <var>promise</var> with a
{{TypeError}}.
<li>
<p>Otherwise, run these steps <a>in parallel</a>:
<ol>
<li><p>Let <var>usage</var> be <a>storage usage</a> for <var>shelf</var>.
<li><p>Let <var>quota</var> be <a>storage quota</a> for <var>shelf</var>.
<li><p>Let <var>dictionary</var> be a new {{StorageEstimate}} dictionary whose {{usage}} member
is <var>usage</var> and {{quota}} member is <var>quota</var>.
<li>
<p>If there was an internal error while obtaining <var>usage</var> and <var>quota</var>, then
<a>queue a storage task</a> with <var>global</var> to <a for=/>reject</a> <var>promise</var>
with a {{TypeError}}.
<p class=note>Internal errors are supposed to be extremely rare and indicate some kind of
low-level platform or hardware fault. However, at the scale of the web with the diversity of
implementation and platforms, the unexpected does occur.
<li><p>Otherwise, <a>queue a storage task</a> with <var>global</var> to <a for=/>resolve</a>
<var>promise</var> with <var>dictionary</var>.
</ol>
<li><p>Return <var>promise</var>.
</ol>
</div>
<h2 class=no-num id="acks">Acknowledgments</h2>
<p>With that, many thanks to
Adrian Bateman,
Aislinn Grigas,
Alex Russell,
Ali Alabbas,
Andrew Sutherland,
Andrew Williams,
Austin Sullivan,
Ben Kelly,
Ben Turner,
Dale Harvey,
David Grogan,
Domenic Denicola,
fantasai,
Jake Archibald<!-- technically B.J. Archibald -->,
Jeffrey Yasskin,
Jesse Mykolyn,
Jinho Bang,
Jonas Sicking,
Joshua Bell,
Kenji Baheux,
Kinuko Yasuda,
Luke Wagner,
Michael Nordman,
Mike Taylor,
Mounir Lamouri,
Shachar Zohar,
黃強 (Shawn Huang),
簡冠庭 (Timothy Guan-tin Chien), and
Victor Costan
for being awesome!
<p>This standard is written by <a lang=nl href=https://annevankesteren.nl/>Anne van Kesteren</a>
(<a href=https://www.apple.com/>Apple</a>, <a href=mailto:[email protected]>[email protected]</a>).