Skip to content

Commit fd84f61

Browse files
committed
Finally define storage infrastructure
TODO Closes #18.
1 parent 8e9ef53 commit fd84f61

File tree

1 file changed

+244
-50
lines changed

1 file changed

+244
-50
lines changed

storage.bs

Lines changed: 244 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function retrieveNextChunk(nextChunkInfo) {
8585

8686
<h2 id=infrastructure>Infrastructure</h2>
8787

88-
<p>A user agent has various kinds of semi-persistent state:
88+
<p>A <a for=/>user agent</a> has various kinds of semi-persistent state:
8989

9090
<dl>
9191
<dt>Credentials
@@ -94,37 +94,228 @@ function retrieveNextChunk(nextChunkInfo) {
9494
<dd><p>Permissions for various features, such as geolocation
9595
<dt>Network
9696
<dd><p>HTTP cache, cookies, authentication entries, TLS client certificates
97-
<dt>Storage
97+
<dt id=site-storage>Storage
9898
<dd>Indexed DB, Cache API, service worker registrations, <code>localStorage</code>,
9999
<code>history.pushState()</code>, application caches, notifications, etc.
100100
</dl>
101101

102-
<p>This standard primarily concerns itself with <dfn export id=site-storage>storage</dfn>.
102+
<p>This standard primarily concerns itself with storage.
103103

104-
<p><a>Storage</a> consists of zero or more <dfn export id=site-storage-unit>storage units</dfn>.
105104

106-
<p>Each <a for=/>origin</a> has an associated <a>storage unit</a>. A <a>storage unit</a> contains a
107-
single <dfn export id=bucket oldids=box>bucket</dfn>. [[HTML]]
105+
<h3 id=storage-units>Storage units</h3>
108106

107+
<p>A <dfn>storage type</dfn> is "<code>storage</code>" or "<code>session-storage</code>".
109108

110-
<h3 id=buckets oldids=boxes>Buckets</h3>
109+
<p>A <dfn>storage key</dfn> is an <a for=/>origin</a>. [[HTML]]
111110

112-
<p>A <a>bucket</a> has <dfn export for=bucket oldids=box-mode>mode</dfn> which is either
113-
"<code title>best-effort</code>" or "<code title>persistent</code>". A
114-
<dfn export oldids=persistent-box>persistent bucket</dfn> is a <a>bucket</a> whose
115-
<a for=bucket>mode</a> is "<code title>persistent</code>". A
116-
<dfn export oldids=non-persistent-box>non-persistent bucket</dfn> is a <a>bucket</a> whose
117-
<a for=bucket>mode</a> is <em>not</em> "<code title>persistent</code>".
111+
<p class=XXX>This is expected to change, see
112+
<a href="https://privacycg.github.io/storage-partitioning/">Client-Side Storage Partitioning</a>.
118113

119-
<p>A <a>bucket</a> is considered to be an atomic unit. Whenever a <a>bucket</a> is cleared by the
120-
user agent, it must be cleared in its entirety.
114+
<p>A <dfn id=site-storage-unit>storage unit</dfn> is the least granular unit of storage, other than
115+
the user agent. It holds a <dfn for="storage unit">map</dfn>, which is a <a for=/>map</a> of
116+
<a for=/>ASCII strings</a> to <a>abstract storage buckets</a>.
117+
<!-- Not sure if this needs to be exported, but it was already. -->
118+
119+
<p>A <dfn>storage map</dfn> is a <a for=/>map</a> of <a>storage keys</a> to <a>storage units</a>. It
120+
is initially empty.
121+
122+
<p>To <dfn>obtain a storage unit</dfn>, given a <a for=/>storage map</a> <var>map</var>, an
123+
<a>environment settings object</a> <var>environment</var>, and a <a>storage type</a>
124+
<var>type</var>, run these steps:
125+
126+
<ol>
127+
<li><p>Let <var>key</var> be <var>environment</var>'s
128+
<a for="environment settings object">origin</a>.
129+
130+
<li><p>If <var>key</var> is an <a>opaque origin</a>, then return failure.
131+
132+
<li><p>If the user has disabled storage, then return failure.
133+
134+
<li>
135+
<p>If <var>map</var>[<var>key</var>] does not <a for=map>exist</a>, then:
136+
137+
<ol>
138+
<li><p>Let <var>unit</var> be a new <a>storage unit</a>.
139+
140+
<li>
141+
<p>Set <var>unit</var>'s <a for="storage unit">map</a>["<code>default</code>"] to the result of
142+
<a>create a storage bucket</a> with <var>type</var>.
143+
144+
<p class="note">For now "<code>default</code>" is all that exists. See
145+
<a href="https://github.com/whatwg/storage/issues/2">issue #2</a>.
146+
147+
<li><p>Set <var>map</var>[<var>key</var>] to <var>unit</var>.
148+
</ol>
149+
150+
<li><p>Return <var>map</var>[<var>key</var>].
151+
</ol>
152+
153+
<hr>
154+
155+
<p>A <a for=/>user agent</a> holds a <dfn for="user agent">storage map</dfn>, which is a
156+
<a for=/>storage map</a>.
157+
158+
<p>A browsing session holds a <dfn for="browsing session">storage map</dfn>, which is a
159+
<a for=/>storage map</a>.
160+
161+
<p class=XXX>Browsing session is yet to be formally defined. For all intents and purposes it is a
162+
<a>top-level browsing context</a>, except that it survives the <a>top-level browsing context</a>
163+
being replaced due to a cross-origin opener policy.
164+
165+
166+
<h3 id=storage-identifiers>Storage identifiers</h3>
167+
168+
<p>An <dfn>abstract storage identifier</dfn> is an <a for=/>ASCII string</a>.
169+
170+
<p>Each storage API has a <dfn export>storage identifier</dfn>, which is an
171+
<a>abstract storage identifier</a>. The <dfn>registered storage identifiers</dfn> are:
172+
173+
<ol>
174+
<li>"<code>caches</code>"
175+
<li>"<code>indexedDB</code>"
176+
<li>"<code>localStorage</code>"
177+
<li>"<code>serviceWorkerRegistrations</code>"
178+
</ol>
179+
180+
<p>Each session storage API has a <dfn export>session storage identifier</dfn>, which is an
181+
<a>abstract storage identifier</a>. The <dfn>registered session storage identifiers</dfn> are
182+
"<code>sessionStorage</code>".
183+
184+
185+
<h3 id=buckets oldids=boxes>Storage buckets</h3>
186+
187+
<p>An <dfn>abstract storage bucket</dfn> is a place for storage and session storage APIs to store
188+
data. Whenever an <a>abstract storage bucket</a> is cleared by the user agent, it must be cleared in
189+
its entirety.
190+
<!-- This is probably better defined in some kind of storage lifetime section. -->
191+
192+
<p>An <dfn for="abstract storage bucket,storage bucket,session storage bucket">area</dfn> is a part
193+
of an <a>abstract storage bucket</a> carved out for a particular storage API or session storage API.
194+
An <a for="abstract storage bucket">area</a> has a
195+
<dfn for="abstract storage bucket/area,storage bucket/area,session storage bucket/area">map</dfn>,
196+
which is an initially empty <a for=/>map</a>. An <a for="abstract storage bucket">area</a> also has
197+
a
198+
<dfn for="abstract storage bucket/area,storage bucket/area,session storage bucket/area">proxy map reference set</dfn>,
199+
which is an initially empty <a for=/>set</a>.
200+
<!-- The idea is that storage area's map holds the actual storage. It's in a map because those are
201+
easy to work with. How the map is persisted is implementation-defined. How to make it
202+
available across process boundaries is implementation-defined. This note also applies to
203+
session buckets below. -->
204+
205+
<p>An <a>abstract storage bucket</a> has a
206+
<dfn for="abstract storage bucket,storage bucket,session storage bucket">map</dfn> of
207+
<a>abstract storage identifiers</a> to <a for="abstract storage bucket">areas</a>.
208+
209+
<hr>
210+
211+
<p>A <dfn id=bucket oldids=box>storage bucket</dfn> is an <a>abstract storage bucket</a> for storage
212+
APIs.
213+
214+
<p>A <a>storage bucket</a> has a
215+
<dfn for="storage bucket" id=bucket-mode oldids=box-mode>mode</dfn>, which is
216+
"<code>best-effort</code>" or "<code>persistent</code>". It is initially "<code>best-effort</code>".
217+
218+
<hr>
219+
220+
<p>A <dfn>session storage bucket</dfn> is an <a>abstract storage bucket</a> for session storage
221+
APIs.
222+
223+
<hr>
224+
225+
<p>To <dfn>create a storage bucket</dfn>, given a <a>storage type</a> <var>type</var>, run these
226+
steps:
227+
228+
<ol>
229+
<li><p>Let <var>bucket</var> be null.
230+
231+
<li>
232+
<p>If <var>type</var> is "<code>storage</code>", then:
233+
234+
<ol>
235+
<li><p>Set <var>bucket</var> to a new <a>storage bucket</a>.
236+
237+
<li><p>For each <var>identifier</var> of <a>registered storage identifiers</a>, set
238+
<var>bucket</var>'s <a for="storage bucket">map</a>[<var>identifier</var>] to a new
239+
<a for="storage bucket">area</a>.
240+
</ol>
241+
242+
<li>
243+
<p>Otherwise, if <var>type</var> is "<code>session-storage</code>":
244+
245+
<ol>
246+
<li><p>Set <var>bucket</var> to a new <a>session storage bucket</a>.
247+
248+
<li><p>For each <var>identifier</var> of <a>registered session storage identifiers</a>, set
249+
<var>bucket</var>'s <a for="session storage bucket">map</a>[<var>identifier</var>] to a new
250+
<a for="session storage bucket">area</a>.
251+
</ol>
252+
253+
<li><p>Assert: <var>bucket</var> is an <a>abstract storage bucket</a>.
254+
255+
<li><p>Return <var>bucket</var>.
256+
</ol>
257+
258+
259+
<h3 id=storage-proxy-maps>Storage proxy maps</h3>
260+
261+
<p>A <dfn>storage proxy map</dfn> is equivalent to a <a for=/>map</a>, except that all operations
262+
are instead performed on its <dfn for="storage proxy map">backing map</dfn>.
263+
264+
<p class="note">This allows for the <a for="storage proxy map">backing map</a> to be replaced.
265+
266+
<hr>
267+
268+
<p>To <dfn export>obtain a storage bucket area map</dfn>, given an
269+
<a for=/>environment settings object</a> <var>environment</var>, <a>storage type</a>
270+
<var>type</var>, and <a>abstract storage identifier</a> <var>identifier</var>, run these steps:</p>
271+
272+
<ol>
273+
<li><p>Let <var>map</var> be null.
274+
275+
<li><p>If <var>type</var> is "<code>storage</code>", then set <var>map</var> to the user agent's
276+
<a for="user agent">storage map</a>.
277+
278+
<li>
279+
<p>Otherwise, if <var>type</var> is "<code>session-storage</code>", then set <var>map</var> to
280+
<var>environment</var>'s <span class=XXX>browsing session</span>'s
281+
<a for="browsing session">storage map</a>.
282+
283+
<p class="XXX">See
284+
<a href="https://github.com/whatwg/html/issues/4782">whatwg/html issue #4782</a> and
285+
<a href="https://github.com/whatwg/html/issues/5350">whatwg/html issue #5350</a> for defining
286+
browsing session. It is roughly analogous to <a>top-level browsing context</a> except that it
287+
cannot be replaced due to <code>Cross-Origin-Opener-Policy</code> or navigation.
288+
289+
<li><p>Assert: <var>map</var> is a <a for=/>storage map</a>.
290+
291+
<li><p>Let <var>unit</var> be the result of running <a>obtain a storage unit</a>, with
292+
<var>map</var>, <var>environment</var>, and <var>type</var>.
293+
294+
<li><p>If <var>unit</var> is failure, then return failure.
295+
296+
<li><p>Let <var>bucket</var> be <var>unit</var>'s
297+
<a for="storage unit">map</a>["<code>default</code>"].
298+
299+
<li><p>Let <var>area</var> be <var>bucket</var>'s
300+
<a for="storage bucket">map</a>[<var>identifier</var>].
301+
302+
<li><p>Let <var>proxyMap</var> be a new <a>storage proxy map</a> whose
303+
<a for="storage proxy map">backing map</a> is <var>area</var>'s
304+
<a for="storage bucket/area">map</a>.
305+
306+
<li><p>Append a reference to <var>proxyMap</var> to <var>area</var>'s
307+
<a for="storage bucket/area">proxy map reference set</a>.
308+
309+
<li><p>Return <var>proxyMap</var>.
310+
</ol>
121311

122312

123313

124314
<h2 id=persistence>Persistence permission</h2>
125315

126-
<p>A <a>bucket</a> can only be turned into a <a>persistent bucket</a> if the user (or user agent
127-
on behalf of the user) has granted permission to use the {{"persistent-storage"}} feature.
316+
<p>A <a>storage bucket</a> can only have its <a for="storage bucket">mode</a> change to
317+
"<code>persistent</code>" if the user (or user agent on behalf of the user) has granted permission
318+
to use the {{"persistent-storage"}} feature.
128319

129320
<p class="note">When granted to an <a for=/>origin</a>, the persistence permission can be used to
130321
protect storage from the user agent's clearing policies. The user agent cannot clear storage marked
@@ -142,29 +333,29 @@ locally.
142333

143334
<dt><a>permission revocation algorithm</a></dt>
144335
<dd algorithm="permission-revocation">If {{"persistent-storage"}}'s <a>permission state</a> is not
145-
{{"granted"}}, then set the current <a for=/>origin</a>’s <a>storage unit</a>'s <a>bucket</a>'s
146-
<a for=bucket>mode</a> to "<code>best-effort</code>".</dd>
336+
{{"granted"}}, then set the current <a for=/>origin</a>’s <a>storage unit</a>'s
337+
<a>storage bucket</a>'s <a for="storage bucket">mode</a> to "<code>best-effort</code>".</dd>
338+
<!-- XXX -->
147339
</dl>
148340

149341

150342

151343
<h2 id=usage-and-quota>Usage and quota</h2>
152344

153-
<p>The <dfn export>storage usage</dfn> of an <a for=/>origin</a> <var>origin</var> is a rough
154-
estimate of the amount of bytes used in <var>origin</var>'s <a>storage unit</a>.
345+
<p>The <dfn export>storage usage</dfn> of a <a>storage unit</a> is a rough estimate of the amount
346+
of bytes used by it.
155347

156348
<p class=note>This cannot be an exact amount as user agents might, and are encouraged to, use
157-
deduplication, compression, and other techniques that obscure exactly how much bytes an
158-
<a for=/>origin</a> uses.
349+
deduplication, compression, and other techniques that obscure exactly how much bytes a
350+
<a>storage unit</a> uses.
159351

160-
<p>The <dfn export>storage quota</dfn> of an <a for=/>origin</a> <var>origin</var> is a conservative
161-
estimate of the amount of bytes available to <var>origin</var>'s <a>storage unit</a>. This amount
162-
should be less than the total available storage space on the device to give users some wiggle room.
352+
<p>The <dfn export>storage quota</dfn> of a <a>storage unit</a> is a conservative estimate of the
353+
amount of bytes available to it. This amount should be less than the total available storage space
354+
on the device to give users some wiggle room.
163355

164-
<p class=note>User agents are strongly encouraged to provide "popular" <a for=/>origins</a> with a
165-
larger <a>storage quota</a>. Factors such as navigation frequency, recency of visits, bookmarking,
166-
and <a href="#persistence">permission</a> for {{"persistent-storage"}} can be used as indications of
167-
"popularity".
356+
<p class=note>User agents are strongly encouraged to consider navigation frequency, recency of
357+
visits, bookmarking, and <a href="#persistence">permission</a> for {{"persistent-storage"}} when
358+
evaluating quotas.
168359

169360

170361

@@ -229,19 +420,20 @@ these steps:
229420
<ol>
230421
<li><p>Let <var>promise</var> be a new promise.
231422

232-
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
233-
<a for="environment settings object">origin</a>.
423+
<li><p>Let <var>unit</var> be the result of running <a>obtain a storage unit</a> with the user
424+
agent's <a for="user agent">storage map</a>, <a>this</a>'s <a>relevant settings object</a>, and
425+
"<code>storage</code>".
234426

235-
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
236-
{{TypeError}}.
427+
<li><p>If <var>unit</var> is a failure, then reject <var>promise</var> with a {{TypeError}}.
237428

238429
<li>
239430
<p>Otherwise, run these steps <a>in parallel</a>:
240431

241432
<ol>
242433
<li>
243-
<p>Let <var>persisted</var> be true if <var>origin</var>'s <a>storage unit</a>'s <a>bucket</a>
244-
is a <a>persistent bucket</a>, and false otherwise.
434+
<p>Let <var>persisted</var> be true if <var>unit</var>'s
435+
<a for="storage unit">map</a>["<code>default</code>"]'s <a for="storage bucket">mode</a> is
436+
"<code>persistent</code>"; otherwise false.
245437

246438
<p class=note>It will be false when there's an internal error.
247439

@@ -257,11 +449,11 @@ these steps:
257449
<ol>
258450
<li><p>Let <var>promise</var> be a new promise.
259451

260-
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
261-
<a for="environment settings object">origin</a>.
452+
<li><p>Let <var>unit</var> be the result of running <a>obtain a storage unit</a> with the user
453+
agent's <a for="user agent">storage map</a>, <a>this</a>'s <a>relevant settings object</a>, and
454+
"<code>storage</code>".
262455

263-
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
264-
{{TypeError}}.
456+
<li><p>If <var>unit</var> is a failure, then reject <var>promise</var> with a {{TypeError}}.
265457

266458
<li>
267459
<p>Otherwise, run these steps <a>in parallel</a>:
@@ -275,18 +467,20 @@ these steps:
275467
the same <a for=/>origin</a> around the same time and this algorithm is not equipped to handle
276468
such a scenario.
277469

470+
<li><p>Let <var>bucket</var> be <var>unit</var>'s
471+
<a for="storage unit">map</a>["<code>default</code>"].
472+
278473
<li>
279-
<p>Let <var>persisted</var> be true, if <var>origin</var>'s <a>storage unit</a>'s <a>bucket</a>
280-
is a <a>persistent bucket</a>, and false otherwise.
474+
<p>Let <var>persisted</var> be true if <var>bucket</var>'s <a for="storage bucket">mode</a> is
475+
"<code>persistent</code>"; otherwise false.
281476

282477
<p class=note>It will be false when there's an internal error.
283478

284479
<li>
285480
<p>If <var>persisted</var> is false and <var>permission</var> is {{"granted"}}, then:
286481

287482
<ol>
288-
<li><p>Set <var>origin</var>'s <a>storage unit</a>'s <a>bucket</a>'s <a>mode</a> to
289-
"<code>persistent</code>".
483+
<li><p>Set <var>bucket</var>'s <a for="storage bucket">mode</a> to "<code>persistent</code>".
290484

291485
<li><p>If there was no internal error, then set <var>persisted</var> to true.
292486
</ol>
@@ -303,19 +497,19 @@ these steps:
303497
<ol>
304498
<li><p>Let <var>promise</var> be a new promise.
305499

306-
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
307-
<a for="environment settings object">origin</a>.
500+
<li><p>Let <var>unit</var> be the result of running <a>obtain a storage unit</a> with the user
501+
agent's <a for="user agent">storage map</a>, <a>this</a>'s <a>relevant settings object</a>, and
502+
"<code>storage</code>".
308503

309-
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
310-
{{TypeError}}.
504+
<li><p>If <var>unit</var> is a failure, then reject <var>promise</var> with a {{TypeError}}.
311505

312506
<li>
313507
<p>Otherwise, run these steps <a>in parallel</a>:
314508

315509
<ol>
316-
<li><p>Let <var>usage</var> be <a>storage usage</a> for <var>origin</var>.
510+
<li><p>Let <var>usage</var> be <a>storage usage</a> for <var>unit</var>.
317511

318-
<li><p>Let <var>quota</var> be <a>storage quota</a> for <var>origin</var>.
512+
<li><p>Let <var>quota</var> be <a>storage quota</a> for <var>unit</var>.
319513

320514
<li><p>Let <var>dictionary</var> be a new {{StorageEstimate}} dictionary whose {{usage}} member
321515
is <var>usage</var> and {{quota}} member is <var>quota</var>.

0 commit comments

Comments
 (0)