From 853158dd46a408fe4da32bd432055ae8c9387d8b Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Thu, 30 Apr 2020 17:20:52 +0200 Subject: [PATCH] Finally define storage infrastructure TODO Closes #18. --- storage.bs | 294 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 244 insertions(+), 50 deletions(-) diff --git a/storage.bs b/storage.bs index bafbdc9..6850d13 100644 --- a/storage.bs +++ b/storage.bs @@ -85,7 +85,7 @@ function retrieveNextChunk(nextChunkInfo) {

Infrastructure

-

A user agent has various kinds of semi-persistent state: +

A user agent has various kinds of semi-persistent state:

Credentials @@ -94,37 +94,228 @@ function retrieveNextChunk(nextChunkInfo) {

Permissions for various features, such as geolocation

Network

HTTP cache, cookies, authentication entries, TLS client certificates -

Storage +
Storage
Indexed DB, Cache API, service worker registrations, localStorage, history.pushState(), application caches, notifications, etc.
-

This standard primarily concerns itself with storage. +

This standard primarily concerns itself with storage. -

Storage consists of zero or more storage units. -

Each origin has an associated storage unit. A storage unit contains a -single bucket. [[HTML]] +

Storage units

+

A storage type is "storage" or "session-storage". -

Buckets

+

A storage key is an origin. [[HTML]] -

A bucket has mode which is either -"best-effort" or "persistent". A -persistent bucket is a bucket whose -mode is "persistent". A -non-persistent bucket is a bucket whose -mode is not "persistent". +

This is expected to change, see +Client-Side Storage Partitioning. -

A bucket is considered to be an atomic unit. Whenever a bucket is cleared by the -user agent, it must be cleared in its entirety. +

A storage unit is the least granular unit of storage, other than +the user agent. It holds a map, which is a map of +ASCII strings to abstract storage buckets. + + +

A storage map is a map of storage keys to +storage units. It is initially empty. + +

To obtain a storage unit, given a storage map map, an +environment settings object environment, and a storage type +type, run these steps: + +

    +
  1. Let key be environment's + origin. + +

  2. If key is an opaque origin, then return failure. + +

  3. If the user has disabled storage, then return failure. + +

  4. +

    If map[key] does not exist, then: + +

      +
    1. Let unit be a new storage unit. + +

    2. +

      Set unit's map["default"] to the result of + create a storage bucket with type. + +

      For now "default" is all that exists. See + issue #2. + +

    3. Set map[key] to unit. +

    + +
  5. Return map[key]. +

+ +
+ +

A user agent holds a storage map, which is a +storage map. + +

A browsing session holds a storage map, which is a +storage map. + +

Browsing session is yet to be formally defined. For all intents and purposes it is a +top-level browsing context, except that it survives the top-level browsing context +being replaced due to a cross-origin opener policy. + + +

Storage identifiers

+ +

An abstract storage identifier is an ASCII string. + +

Each storage API has a storage identifier, which is an +abstract storage identifier. The registered storage identifiers are: + +

    +
  1. "caches" +
  2. "indexedDB" +
  3. "localStorage" +
  4. "serviceWorkerRegistrations" +
+ +

Each session storage API has a session storage identifier, which is an +abstract storage identifier. The registered session storage identifiers are +"sessionStorage". + + +

Storage buckets

+ +

An abstract storage bucket is a place for storage and session storage APIs to store +data. Whenever an abstract storage bucket is cleared by the user agent, it must be cleared in +its entirety. + + +

An area is a part +of an abstract storage bucket carved out for a particular storage API or session storage API. +An area has a +map, +which is an initially empty map. An area also has +a +proxy map reference set, +which is an initially empty set. + + +

An abstract storage bucket has a +map of +abstract storage identifiers to areas. + +


+ +

A storage bucket is an abstract storage bucket for storage +APIs. + +

A storage bucket has a +mode, which is +"best-effort" or "persistent". It is initially "best-effort". + +


+ +

A session storage bucket is an abstract storage bucket for session storage +APIs. + +


+ +

To create a storage bucket, given a storage type type, run these +steps: + +

    +
  1. Let bucket be null. + +

  2. +

    If type is "storage", then: + +

      +
    1. Set bucket to a new storage bucket. + +

    2. For each identifier of registered storage identifiers, set + bucket's map[identifier] to a new + area. +

    + +
  3. +

    Otherwise, if type is "session-storage": + +

      +
    1. Set bucket to a new session storage bucket. + +

    2. For each identifier of registered session storage identifiers, set + bucket's map[identifier] to a new + area. +

    + +
  4. Assert: bucket is an abstract storage bucket. + +

  5. Return bucket. +

+ + +

Storage proxy maps

+ +

A storage proxy map is equivalent to a map, except that all operations +are instead performed on its backing map. + +

This allows for the backing map to be replaced. + +


+ +

To obtain a storage bucket area map, given an environment settings object +environment, storage type type, and abstract storage identifier +identifier, run these steps:

+ +
    +
  1. Let map be null. + +

  2. If type is "storage", then set map to the user agent's + storage map. + +

  3. +

    Otherwise, if type is "session-storage", then set map to + environment's browsing session's + storage map. + +

    See + whatwg/html issue #4782 and + whatwg/html issue #5350 for defining + browsing session. It is roughly analogous to top-level browsing context except that it + cannot be replaced due to Cross-Origin-Opener-Policy or navigation. + +

  4. Assert: map is a storage map. + +

  5. Let unit be the result of running obtain a storage unit, with + map, environment, and type. + +

  6. If unit is failure, then return failure. + +

  7. Let bucket be unit's + map["default"]. + +

  8. Let area be bucket's + map[identifier]. + +

  9. Let proxyMap be a new storage proxy map whose + backing map is area's + map. + +

  10. Append a reference to proxyMap to area's + proxy map reference set. + +

  11. Return proxyMap. +

Persistence permission

-

A bucket can only be turned into a persistent bucket if the user (or user agent -on behalf of the user) has granted permission to use the {{"persistent-storage"}} feature. +

A storage bucket can only have its mode change to +"persistent" if the user (or user agent on behalf of the user) has granted permission +to use the {{"persistent-storage"}} feature.

When granted to an origin, the persistence permission can be used to protect storage from the user agent's clearing policies. The user agent cannot clear storage marked @@ -142,29 +333,29 @@ locally.

permission revocation algorithm
If {{"persistent-storage"}}'s permission state is not - {{"granted"}}, then set the current origin’s storage unit's bucket's - mode to "best-effort".
+ {{"granted"}}, then set the current origin’s storage unit's + storage bucket's mode to "best-effort". +

Usage and quota

-

The storage usage of an origin origin is a rough -estimate of the amount of bytes used in origin's storage unit. +

The storage usage of a storage unit is a rough estimate of the amount +of bytes used by it.

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 an -origin uses. +deduplication, compression, and other techniques that obscure exactly how much bytes a +storage unit uses. -

The storage quota of an origin origin is a conservative -estimate of the amount of bytes available to origin's storage unit. This amount -should be less than the total available storage space on the device to give users some wiggle room. +

The storage quota of a storage unit is a conservative estimate of the +amount of bytes available to it. This amount should be less than the total available storage space +on the device to give users some wiggle room. -

User agents are strongly encouraged to provide "popular" origins with a -larger storage quota. Factors such as navigation frequency, recency of visits, bookmarking, -and permission for {{"persistent-storage"}} can be used as indications of -"popularity". +

User agents are strongly encouraged to consider navigation frequency, recency of +visits, bookmarking, and permission for {{"persistent-storage"}} when +evaluating quotas. @@ -229,19 +420,20 @@ these steps:

  1. Let promise be a new promise. -

  2. Let origin be context object's relevant settings object's - origin. +

  3. Let unit be the result of running obtain a storage unit with the user + agent's storage map, this's relevant settings object, and + "storage". -

  4. If origin is an opaque origin, then reject promise with a - {{TypeError}}. +

  5. If unit is a failure, then reject promise with a {{TypeError}}.

  6. Otherwise, run these steps in parallel:

    1. -

      Let persisted be true if origin's storage unit's bucket - is a persistent bucket, and false otherwise. +

      Let persisted be true if unit's + map["default"]'s mode is + "persistent"; otherwise false.

      It will be false when there's an internal error. @@ -257,11 +449,11 @@ these steps:

      1. Let promise be a new promise. -

      2. Let origin be context object's relevant settings object's - origin. +

      3. Let unit be the result of running obtain a storage unit with the user + agent's storage map, this's relevant settings object, and + "storage". -

      4. If origin is an opaque origin, then reject promise with a - {{TypeError}}. +

      5. If unit is a failure, then reject promise with a {{TypeError}}.

      6. Otherwise, run these steps in parallel: @@ -275,9 +467,12 @@ these steps: the same origin around the same time and this algorithm is not equipped to handle such a scenario. +

      7. Let bucket be unit's + map["default"]. +

      8. -

        Let persisted be true, if origin's storage unit's bucket - is a persistent bucket, and false otherwise. +

        Let persisted be true if bucket's mode is + "persistent"; otherwise false.

        It will be false when there's an internal error. @@ -285,8 +480,7 @@ these steps:

        If persisted is false and permission is {{"granted"}}, then:

          -
        1. Set origin's storage unit's bucket's mode to - "persistent". +

        2. Set bucket's mode to "persistent".

        3. If there was no internal error, then set persisted to true.

        @@ -303,19 +497,19 @@ these steps:
        1. Let promise be a new promise. -

        2. Let origin be context object's relevant settings object's - origin. +

        3. Let unit be the result of running obtain a storage unit with the user + agent's storage map, this's relevant settings object, and + "storage". -

        4. If origin is an opaque origin, then reject promise with a - {{TypeError}}. +

        5. If unit is a failure, then reject promise with a {{TypeError}}.

        6. Otherwise, run these steps in parallel:

            -
          1. Let usage be storage usage for origin. +

          2. Let usage be storage usage for unit. -

          3. Let quota be storage quota for origin. +

          4. Let quota be storage quota for unit.

          5. Let dictionary be a new {{StorageEstimate}} dictionary whose {{usage}} member is usage and {{quota}} member is quota.