Skip to content

Commit 725373f

Browse files
annevkdomenic
authored andcommitted
Review Draft Publication: February 2019
1 parent 397ef13 commit 725373f

File tree

1 file changed

+384
-0
lines changed

1 file changed

+384
-0
lines changed

review-drafts/2019-02.bs

Lines changed: 384 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,384 @@
1+
<pre class=metadata>
2+
Group: WHATWG
3+
Date: 2019-02-18
4+
H1: Storage
5+
Shortname: storage
6+
Text Macro: TWITTER storagestandard
7+
Abstract: The Storage Standard defines an API for persistent storage and quota estimates, as well as the platform storage architecture.
8+
Translation: ja https://triple-underscore.github.io/storage-ja.html
9+
</pre>
10+
11+
12+
13+
<h2 id=introduction>Introduction</h2>
14+
15+
Over the years the web has grown various APIs that can be used for storage, e.g., IndexedDB,
16+
<code>localStorage</code>, and <code>showNotification()</code>. The Storage Standard consolidates
17+
these APIs by defining:
18+
19+
<ul class=brief>
20+
<li>A bucket, the primitive these APIs store their data in
21+
<li>A way of making that bucket persistent
22+
<li>A way of getting usage and quota estimates for an <a for=/>origin</a>
23+
</ul>
24+
25+
<p>Traditionally, as the user runs out of storage space on their device, the data stored with these
26+
APIs gets lost without the user being able to intervene. However, persistent buckets cannot be
27+
cleared without consent by the user. This thus brings data guarantees users have enjoyed on native
28+
platforms to the web.
29+
30+
<div class="example" id=example-3a7051a8>
31+
<p>A simple way to make storage persistent is through invoking the {{persist()}} method. It
32+
simultaneously requests the end user for permission and changes the storage to be persistent once
33+
granted:</p>
34+
35+
<pre><code class="lang-javascript">
36+
navigator.storage.persist().then(persisted => {
37+
if(persisted) {
38+
/* &hellip; */
39+
}
40+
})
41+
</code></pre>
42+
43+
<p>To not show user-agent-driven dialogs to the end user unannounced slightly more involved code
44+
can be written:</p>
45+
46+
<pre><code class="lang-javascript">
47+
Promise.all([
48+
navigator.storage.persisted(),
49+
navigator.permissions.query({name: "persistent-storage"})
50+
]).then(([persisted, permission]) => {
51+
if(!persisted &amp;&amp; permission.status == "granted") {
52+
navigator.storage.persist().then( /* &hellip; */ )
53+
} else if(!persistent &amp;&amp; permission.status == "prompt") {
54+
showPersistentStorageExplanation()
55+
}
56+
})
57+
</code></pre>
58+
59+
<p>The {{estimate()}} method can be used to determine whether there is enough space left to
60+
store content for an application:
61+
62+
<pre><code class="lang-javascript">
63+
function retrieveNextChunk(nextChunkInfo) {
64+
return navigator.storage.estimate().then(info => {
65+
if(info.quota - info.usage > nextChunkInfo.size)
66+
return fetch(nextChunkInfo.url)
67+
else throw new Error("insufficient space to store next chunk")
68+
}).then( /* &hellip; */ )
69+
}
70+
</code></pre>
71+
72+
</div>
73+
74+
75+
76+
<h2 id=terminology>Terminology</h2>
77+
78+
<p>This specification depends on the Infra Standard. [[!INFRA]]
79+
80+
<p>This specification uses terminology from the DOM, HTML, IDL, Permissions API, and URL Standards.
81+
[[DOM]] [[HTML]] [[WEBIDL]] [[PERMISSIONS]] [[URL]]
82+
83+
A <dfn>schemeless origin group</dfn> is a group of one of the following:
84+
85+
<ul>
86+
<li>Identical <a lt="opaque origin">opaque origins</a>.
87+
<li><a lt="tuple origin">Tuple origins</a> whose <a for=origin>host</a> is identical and not a
88+
<a for=/>domain</a>.
89+
<li><a lt="tuple origin">Tuple origins</a> whose <a for=origin>host</a> is a <a for=/>domain</a> of
90+
which the <a href="https://publicsuffix.org/list/">registrable domain</a> is identical.</li>
91+
</ul>
92+
93+
<p class="note">This definition will move to a more suitable location eventually.
94+
95+
96+
97+
<h2 id=infrastructure>Infrastructure</h2>
98+
99+
A user agent has various kinds of storage:
100+
101+
<dl>
102+
<dt>Credentials
103+
<dd><p>End-user credentials, such as username and passwords submitted through HTML forms
104+
<dt>Permissions
105+
<dd><p>Permissions for various features, such as geolocation
106+
<dt>Network
107+
<dd><p>HTTP cache, cookies, authentication entries, TLS client certificates
108+
<dt>Site
109+
<dd>Indexed DB, Cache API, service worker registrations, <code>localStorage</code>,
110+
<code>history.pushState()</code>, application caches, notifications, etc.
111+
</dl>
112+
113+
This specification primarily concerns itself with <dfn export id=site-storage>site storage</dfn>.
114+
115+
<a>Site storage</a> consists of zero or more
116+
<dfn export id=site-storage-unit>site storage units</dfn>.
117+
118+
Each <a for=/>origin</a> has an associated <a>site storage unit</a>. A <a>site storage unit</a>
119+
contains a single <dfn export id=bucket oldids=box>bucket</dfn>. [[HTML]]
120+
121+
122+
<h3 id=buckets oldids=boxes>Buckets</h3>
123+
124+
A <a>bucket</a> has <dfn export for=bucket oldids=box-mode>mode</dfn> which is either
125+
"<code title>best-effort</code>" or "<code title>persistent</code>". A
126+
<dfn export oldids=persistent-box>persistent bucket</dfn> is a <a>bucket</a> whose
127+
<a for=bucket>mode</a> is "<code title>persistent</code>". A
128+
<dfn export oldids=non-persistent-box>non-persistent bucket</dfn> is a <a>bucket</a> whose
129+
<a for=bucket>mode</a> is <em>not</em> "<code title>persistent</code>".
130+
131+
A bucket is considered to be an atomic unit. Whenever a <a>bucket</a> is cleared by the user agent,
132+
it must be cleared in its entirety.
133+
134+
135+
136+
<h2 id=persistence>Persistence permission</h2>
137+
138+
A <a>bucket</a> can only be turned into a <a>persistent bucket</a> if the user (or user agent
139+
on behalf of the user) has granted permission to use the {{"persistent-storage"}} feature.
140+
141+
<p class="note">When granted to an <a for=/>origin</a>, the persistence permission can be used to
142+
protect storage from the user agent's clearing policies. The user agent cannot clear storage marked
143+
as persistent without involvement from the <a for=/>origin</a> or user. This makes it particularly
144+
useful for resources the user needs to have available while offline or resources the user creates
145+
locally.
146+
147+
The <dfn for="PermissionName" enum-value>"<code>persistent-storage</code>"</dfn>
148+
<a>powerful feature</a>'s permission-related flags, algorithms, and types are defaulted, except for:
149+
150+
<dl>
151+
<dt><a>permission state</a></dt>
152+
<dd>{{"persistent-storage"}}'s <a>permission state</a> must have the same value for all
153+
<a>environment settings objects</a> with a given <a for=/>origin</a>.</dd>
154+
155+
<dt><a>permission revocation algorithm</a></dt>
156+
<dd algorithm="permission-revocation">If {{"persistent-storage"}}'s <a>permission state</a> is not
157+
{{"granted"}}, then set the current <a for=/>origin</a>’s <a>site storage unit</a>'s
158+
<a>bucket</a>'s <a for=bucket>mode</a> to "<code>best-effort</code>".</dd>
159+
</dl>
160+
161+
162+
163+
<h2 id=usage-and-quota>Usage and quota</h2>
164+
165+
The <dfn export>site storage usage</dfn> of an <a for=/>origin</a> <var>origin</var> is a rough
166+
estimate of the amount of bytes used in <var>origin</var>'s <a>site storage unit</a>.
167+
168+
<p class=note>This cannot be an exact amount as user agents might, and are encouraged to, use
169+
deduplication, compression, and other techniques that obscure exactly how much bytes an
170+
<a for=/>origin</a> uses.
171+
172+
The <dfn export>site storage quota</dfn> of an <a for=/>origin</a> <var>origin</var> is a
173+
conservative estimate of the amount of bytes available to <var>origin</var>'s
174+
<a>site storage unit</a>. This amount should be less than the total available storage space on the
175+
device to give users some wiggle room.
176+
177+
<p class=note>User agents are strongly encouraged to provide "popular" <a for=/>origins</a> with a
178+
larger <a>site storage quota</a>. Factors such as navigation frequency, recency of visits,
179+
bookmarking, and <a href="#persistence">permission</a> for {{"persistent-storage"}} can be used as
180+
indications of "popularity".
181+
182+
183+
184+
<h2 id=ui-guidelines>User Interface Guidelines</h2>
185+
186+
User agents should not distinguish between network storage and <a>site storage</a> in their user
187+
interface. Instead user agents should offer users the ability to remove all storage for a given
188+
<a>schemeless origin group</a>. This ensures to some extent that network storage cannot be used to
189+
revive <a>site storage</a>. This also reduces the amount users need to know about the different ways
190+
in which a <a>schemeless origin group</a> can store data.
191+
<!-- To some extent, since HTTP ETag... And also, permissions/credentials, maybe? -->
192+
193+
Credentials storage should be separated as it might contain data the user might not be able to
194+
revive, such as an autogenerated password. Since permissions storage is mostly simple booleans it
195+
too can be separated to avoid inconveniencing the user. Credentials and permissions are also
196+
somewhat easier to understand and differentiate for users from network storage and
197+
<a>site storage</a>.
198+
199+
200+
<h3 id=storage-pressure>Storage Pressure</h3>
201+
202+
When the user agent notices it comes under storage pressure and it cannot free up sufficient space
203+
by clearing network storage and <a>non-persistent buckets</a> within <a>site storage</a>, then the
204+
user agent should alert the user and offer a way to clear <a>persistent buckets</a>.
205+
206+
207+
208+
<h2 id=api>API</h2>
209+
210+
<pre class=idl>
211+
[SecureContext]
212+
interface mixin NavigatorStorage {
213+
[SameObject] readonly attribute StorageManager storage;
214+
};
215+
Navigator includes NavigatorStorage;
216+
WorkerNavigator includes NavigatorStorage;
217+
</pre>
218+
219+
Each <a>environment settings object</a> has an associated {{StorageManager}} object.
220+
[[HTML]]
221+
222+
The <dfn attribute for=NavigatorStorage><code>storage</code></dfn> attribute's getter must return
223+
<a>context object</a>'s <a>relevant settings object</a>'s {{StorageManager}} object.
224+
225+
<pre class=idl>
226+
[SecureContext,
227+
Exposed=(Window,Worker)]
228+
interface StorageManager {
229+
Promise&lt;boolean> persisted();
230+
[Exposed=Window] Promise&lt;boolean> persist();
231+
232+
Promise&lt;StorageEstimate> estimate();
233+
};
234+
235+
dictionary StorageEstimate {
236+
unsigned long long usage;
237+
unsigned long long quota;
238+
};
239+
</pre>
240+
241+
The <dfn method for=StorageManager><code>persisted()</code></dfn> method, when invoked, must run
242+
these steps:
243+
244+
<ol>
245+
<li><p>Let <var>promise</var> be a new promise.
246+
247+
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
248+
<a for="environment settings object">origin</a>.
249+
250+
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
251+
{{TypeError}}.
252+
253+
<li>
254+
<p>Otherwise, run these steps <a>in parallel</a>:
255+
256+
<ol>
257+
<li>
258+
<p>Let <var>persisted</var> be true if <var>origin</var>'s <a>site storage unit</a>'s
259+
<a>bucket</a> is a <a>persistent bucket</a>, and false otherwise.
260+
261+
<p class=note>It will be false when there's an internal error.
262+
263+
<li><p><a>Queue a task</a> to resolve <var>promise</var> with <var>persisted</var>.
264+
</ol>
265+
266+
<li><p>Return <var>promise</var>.
267+
</ol>
268+
269+
The <dfn method for=StorageManager><code>persist()</code></dfn> method, when invoked, must run these
270+
steps:
271+
272+
<ol>
273+
<li><p>Let <var>promise</var> be a new promise.
274+
275+
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
276+
<a for="environment settings object">origin</a>.
277+
278+
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
279+
{{TypeError}}.
280+
281+
<li>
282+
<p>Otherwise, run these steps <a>in parallel</a>:
283+
284+
<ol>
285+
<li>
286+
<p>Let <var>permission</var> be the result of <a>requesting permission to use</a>
287+
{{"persistent-storage"}}.
288+
289+
<p class="note">User agents are encouraged to not let the user answer this question twice for
290+
the same <a for=/>origin</a> around the same time and this algorithm is not equipped to handle
291+
such a scenario.
292+
293+
<li>
294+
<p>Let <var>persisted</var> be true, if <var>origin</var>'s <a>site storage unit</a>'s
295+
<a>bucket</a> is a <a>persistent bucket</a>, and false otherwise.
296+
297+
<p class=note>It will be false when there's an internal error.
298+
299+
<li>
300+
<p>If <var>persisted</var> is false and <var>permission</var> is {{"granted"}}, then:
301+
302+
<ol>
303+
<li><p>Set <var>origin</var>'s <a>site storage unit</a>'s <a>bucket</a>'s <a>mode</a> to
304+
"<code>persistent</code>".
305+
306+
<li><p>If there was no internal error, then set <var>persisted</var> to true.
307+
</ol>
308+
309+
<li><p><a>Queue a task</a> to resolve <var>promise</var> with <var>persisted</var>.
310+
</ol>
311+
312+
<li><p>Return <var>promise</var>.
313+
</ol>
314+
315+
The <dfn method for=StorageManager><code>estimate()</code></dfn> method, when invoked,
316+
must run these steps:
317+
318+
<ol>
319+
<li><p>Let <var>promise</var> be a new promise.
320+
321+
<li><p>Let <var>origin</var> be <a>context object</a>'s <a>relevant settings object</a>'s
322+
<a for="environment settings object">origin</a>.
323+
324+
<li><p>If <var>origin</var> is an <a>opaque origin</a>, then reject <var>promise</var> with a
325+
{{TypeError}}.
326+
327+
<li>
328+
<p>Otherwise, run these steps <a>in parallel</a>:
329+
330+
<ol>
331+
<li><p>Let <var>usage</var> be <a>site storage usage</a> for <var>origin</var>.
332+
333+
<li><p>Let <var>quota</var> be <a>site storage quota</a> for <var>origin</var>.
334+
335+
<li><p>Let <var>dictionary</var> be a new {{StorageEstimate}} dictionary whose {{usage}} member
336+
is <var>usage</var> and {{quota}} member is <var>quota</var>.
337+
338+
<li>
339+
<p>If there was an internal error while obtaining <var>usage</var> and <var>quota</var>, then
340+
<a>queue a task</a> to reject <var>promise</var> with a {{TypeError}}.
341+
342+
<p class=note>Internal errors are supposed to be extremely rare and indicate some kind of
343+
low-level platform or hardware fault. However, at the scale of the web with the diversity of
344+
implementation and platforms, the unexpected does occur.
345+
346+
<li><p>Otherwise, <a>queue a task</a> to resolve <var>promise</var> with <var>dictionary</var>.
347+
</ol>
348+
349+
<li><p>Return <var>promise</var>.
350+
</ol>
351+
352+
353+
354+
<h2 class=no-num id="acks">Acknowledgments</h2>
355+
356+
With that, many thanks to
357+
Adrian Bateman,
358+
Alex Russell,
359+
Aislinn Grigas,
360+
Ali Alabbas,
361+
Ben Kelly,
362+
Ben Turner,
363+
Dale Harvey,
364+
David Grogan,
365+
fantasai,
366+
Jake Archibald<!-- technically B.J. Archibald -->,
367+
Jeffrey Yasskin,
368+
Jinho Bang,
369+
Jonas Sicking,
370+
Joshua Bell,
371+
Kenji Baheux,
372+
Kinuko Yasuda,
373+
Luke Wagner,
374+
Michael Nordman,
375+
Mounir Lamouri,
376+
Shachar Zohar,
377+
黃強 (Shawn Huang), and
378+
簡冠庭 (Timothy Guan-tin Chien)
379+
for being awesome!
380+
381+
This standard is written by
382+
<a lang=nl href=https://annevankesteren.nl/>Anne van Kesteren</a>
383+
(<a href=https://www.mozilla.org/>Mozilla</a>,
384+

0 commit comments

Comments
 (0)