diff --git a/storage-access.bs b/storage-access.bs index ffa9102..ae06f08 100644 --- a/storage-access.bs +++ b/storage-access.bs @@ -51,6 +51,20 @@ urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver text: unsupported operation; url: dfn-unsupported-operation text: session; url: dfn-session text: success; url: dfn-success + +spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/ + type: dfn + text: source snapshot params; url: browsing-the-web.html#source-snapshot-params + text: snapshotting source snapshot params; url: browsing-the-web.html#snapshotting-source-snapshot-params + text: create navigation params by fetching; url: browsing-the-web.html#create-navigation-params-by-fetching + text: set up a window environment settings object; url: nav-history-apis.html#set-up-a-window-environment-settings-object + text: environment + text: DOM manipulation task source; url: webappapis.html#dom-manipulation-task-source + +spec: fetch; urlPrefix: https://fetch.spec.whatwg.org/ + type: dfn + for: response + text: has-cross-origin-redirects; url: #response-has-cross-origin-redirects
@@ -112,23 +126,14 @@ A {{Document}} is in a first-party-site context if it is the [=active
 
 A {{Document}} is in a third party context if it is not in a [=first-party-site context=].
 
-

User Agent state related to storage access

- -A storage access map is a [=map=] whose keys are [=partitioned storage keys=] and whose values are [=storage access flag sets=]. - -User Agents maintain a single global storage access map. +

Changes to user agent state related to storage access

-ISSUE(privacycg/storage-access#2): What is the lifecycle of the [=global storage access map=]? How long do we remember its contents? Firefox and Safari differ here. +Modify the definition of [=environment=] in the following manner: +1. Add a new member called has storage access of type [=boolean=]. -ISSUE(privacycg/storage-access#5): When do we age out entries in the [=global storage access map=]? See also [Scope of Storage Access](https://github.com/privacycg/storage-access#scope-of-storage-access). - -Each [=agent cluster=] has a storage access map. - -When an [=agent cluster=] is created, its [=agent cluster/storage access map=] is initialized with a [=map/clone=] of the [=global storage access map=]. - -To obtain the storage access map for a {{Document}} |doc|, run the following steps: - -1. Return the [=agent cluster/storage access map=] of |doc|'s [=relevant agent=]'s [=agent cluster=]. +Modify the definition of [=source snapshot params=] in the following manner: +1. Add a new member called has storage access of type [=boolean=]. +1. Add a new member called environment id of type opaque [=string=]. A partitioned storage key is a [=tuple=] consisting of a top-level site (a [=site=]) and an embedded origin (an [=/origin=]). @@ -146,18 +151,6 @@ To generate a partitioned storage key for a {{Docu 1. Let |top-level site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=top-level origin=]. 1. Return the [=partitioned storage key=] (|top-level site|, |site|). -A storage access flag set is a set of zero or more of the following flags, which are used to gate access to client-side storage for |embedded origin| when loaded in a [=third party context=] on |top-level site|: - -: The has storage access flag -:: When set, this flag indicates |embedded origin| has access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |top-level site|. - -To obtain a storage access flag set for a [=partitioned storage key=] |key| from a [=/storage access map=] |map|, run the following steps: - -1. If |map|[|key|] [=map/exists|does not exist=], run these steps: - 1. Let |flags| be a new [=storage access flag set=]. - 1. [=map/Set=] |map|[|key|] to |flags|. -1. Return |map|[|key|]. -

Changes to {{Document}}

@@ -174,17 +167,14 @@ When invoked on {{Document}} |doc|, the ha
 
 
 1. Let |p| be [=a new promise=].
-1. If |doc| is not [=Document/fully active=], then [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
+1. If |doc| is not [=Document/fully active=], then [=/reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
 1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=/resolve=] |p| with false and return |p|.
 1. Let |global| be |doc|'s [=relevant global object=].
-1. If |global| is not a [=secure context=], then [=resolve=] |p| with false and return |p|.
+1. If |global| is not a [=secure context=], then [=/resolve=] |p| with false and return |p|.
 1. If |doc|'s [=Document/browsing context=] is a [=top-level browsing context=], [=/resolve=] |p| with true and return |p|.
 1. If the [=top-level origin=] of |doc|'s [=relevant settings object=] is an [=opaque origin=], [=/resolve=] |p| with false and return |p|. 
 1. If |doc|'s [=Document/origin=] is [=same origin=] with the [=top-level origin=] of |doc|'s [=relevant settings object=], [=/resolve=] |p| with true and return |p|.
-1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|.
-1. If |key| is failure, [=resolve=] |p| with false and return |p|.
-1. Let |hasAccess| be the result of running [=determine if a site has storage access=] with |key| and |doc|.
-1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p| with |hasAccess|.
+1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p| with |global's| [=environment/has storage access=].
 1. Return |p|.
 
 ISSUE: Shouldn't step 8 be [=same site=]?
@@ -196,69 +186,70 @@ When invoked on {{Document}} |doc|, the re
 
 
 1. Let |p| be [=a new promise=].
-1. If |doc| is not [=Document/fully active=], then [=reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
+1. If |doc| is not [=Document/fully active=], then [=/reject=] |p| with an "{{InvalidStateError}}" {{DOMException}} and return |p|.
 1. Let |global| be |doc|'s [=relevant global object=].
-1. If |global| is not a [=secure context=], then [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
-1. If this algorithm was invoked when |doc|'s {{Window}} object did not have [=transient activation=], [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
+1. If |global| is not a [=secure context=], then [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
 1. If |doc|'s [=Document/browsing context=] is a [=top-level browsing context=], [=/resolve=] and return |p|.
-1. If |doc| is not [=allowed to use=] the `"storage-access"` permission, [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
-1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
-1. If the [=top-level origin=] of |doc|'s [=relevant settings object=] is an [=opaque origin=], [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|. 
+1. If |doc| is not [=allowed to use=] "`storage-access`", [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
+1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
+1. If the [=top-level origin=] of |doc|'s [=relevant settings object=] is an [=opaque origin=], [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|. 
 1. If |doc|'s [=Document/origin=] is [=same origin=] with the [=top-level origin=] of |doc|'s [=relevant settings object=], [=/resolve=] and return |p|.
-1. If |doc|'s [=active sandboxing flag set=] has its [=sandbox storage access by user activation flag=] set, [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
-1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|.
-1. If |key| is failure, [=reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
-1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|.
-1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|.
-1. If |flag set|'s [=has storage access flag=] is set, [=/resolve=] and return |p|.
-1. Otherwise, run these steps [=in parallel=]:
-    1. Let |hasAccess| be [=a new promise=].
-    1. [=Determine the storage access policy=] with |key|, |doc| and |hasAccess|.
-    1. [=Queue a global task=] on the [=permissions task source=] given |global| to
-        1. Set |flag set|'s [=has storage access flag=].
-        1. If |hasAccess| is true, resolve |p|.
-        1. Reject |p| with a "{{NotAllowedError}}" {{DOMException}}.
+1. If |doc|'s [=active sandboxing flag set=] has its [=sandbox storage access by user activation flag=] set, [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}} and return |p|.
+1. If |global|'s [=environment/has storage access=] is true, [=/resolve=] |p| with {{undefined}} and return.
+1. Let |has transient activation| be whether |doc|'s {{Window}} object has [=transient activation=].
+1. Run the following steps [=in parallel=]:
+    1. Let |process permission state| be an algorithm that, given a [=permission state=] |state|, runs the following steps:
+        1. [=Queue a global task=] on the [=permission task source=] given |global| to:
+            1. If |state| is [=permission/granted=]:
+                1. Set |global|'s [=environment/has storage access=] to true.
+                1. [=/Resolve=] |p| with {{undefined}}.
+            1. Else:
+                1. [=Consume user activation=] given |global|.
+                1. [=/Reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}.
+    1. Let |previous permission state| be the result of [=getting the current permission state=] given "storage-access" and |global|.
+    1. If |previous permission state| is not [=permission/prompt=]:
+        1. Run |process permission state| with |previous permission state|.
+        1. Abort these steps.
+    1. If |has transient activation| is false:
+        1. Run |process permission state| with [=permission/denied=].
+        1. Abort these steps.
+    1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|.
+    1. Let |implicitly granted| and |implicitly denied| (each a [=boolean=]) be the result of running an [=implementation-defined=] set of steps to determine if |key|'s [=partitioned storage key/embedded origin=]'s request for storage access on |key|'s [=partitioned storage key/top-level site=] should be granted or denied without prompting the user.
+    1. If |implicitly granted| is true:
+        1. Run |process permission state| with [=permission/granted=].
+        1. Abort these steps.
+    1. If |implicitly denied| is true:
+        1. Run |process permission state| with [=permission/denied=].
+        1. Abort these steps.
+    1. Let |permissionState| be the result of [=requesting permission to use=] "storage-access".
+    1. Run |process permission state| with |permissionState|.
 1. Return |p|.
 
+NOTE: The intent of this algorithm is to always require user activation before a storage-access permission will be set. Though it is within the means of user agents to set storage-access permissions based on custom heuristics without prior user activation, this specification strongly discourages such behavior, as it could lead to interoperability issues.
+
 ISSUE(privacycg/storage-access#144): We shouldn't use the permissions task source here.
 
 ISSUE: Shouldn't step 9 be [=same site=]?
 
-

User Agent storage access policies

- -Different User Agents have different policies around whether or not [=sites=] may access their [=unpartitioned data=] when they're in a [=third party context=]. User Agents check and/or modify these policies when client-side storage is accessed (see [[#storage]]) as well as when {{Document/hasStorageAccess()}} and {{Document/requestStorageAccess()}} are called. - -To determine if a site has storage access with [=partitioned storage key=] |key| and {{Document}} |doc|, run these steps: + -1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|. -1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. -1. If |flag set|'s [=has storage access flag=] is set, return true. -1. Return false. +When [=snapshotting source snapshot params=]: +1. Set [=source snapshot params/has storage access=] to |sourceDocument|'s [=source snapshot params/has storage access=]. +1. Set [=source snapshot params/environment id=] to |sourceDocument|'s [=relevant settings object=]'s [=environment/id=]. -To determine the storage access policy for [=partitioned storage key=] |key| with {{Document}} |doc| and {{Promise}} |p|, run these steps: +To the [=create navigation params by fetching=] algorithm, insert the following step as step 3: +1. Let |originalURL| be entry's URL. -1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|. -1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. -1. Let |implicitly granted| and |implicitly denied| (each a [=boolean=]) be the result of running an [=implementation-defined=] set of steps to determine if |key|'s [=partitioned storage key/embedded origin=]'s request for storage access on |key|'s [=partitioned storage key/top-level site=] should be granted or denied without prompting the user. -1. Let |global| be |doc|'s [=relevant global object=]. -1. If |implicitly granted| is true, [=queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p|, and return. -1. If |implicitly denied| is true, [=queue a global task=] on the [=permissions task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}, and return. -1. Let |permissionState| be the result of [=requesting permission to use=] "storage-access". -1. If |permissionState| is "granted", [=queue a global task=] on the [=permissions task source=] given |global| to [=/resolve=] |p|, and return. -1. Unset |flag set|'s [=has storage access flag=]. -1. If |doc|'s {{Window}} object has [=transient activation=], [=consume user activation=] with it. -1. [=Queue a global task=] on the [=permissions task source=] given |global| to [=/reject=] |p| with a "{{NotAllowedError}}" {{DOMException}}. - - +When creating |request|'s [=reserved client=] in [=create navigation params by fetching=]: +1. Set [=reserved client=]'s [=environment/has storage access=] to |sourceSnapshotParams|'s [=source snapshot params/has storage access=] if all of the following hold: + 1. |sourceSnapshotParams|'s [=source snapshot params/environment id=] equals navigable's [=active document=]'s [=relevant settings object=]'s [=environment/id=]. + 1. |originalURL|'s [=url/origin=] is [=same origin=] with currentURL's [=url/origin=]. + 1. |response| is null or |response|'s [=response/has-cross-origin-redirects=] is false. +1. Otherwise, set |request|'s [=reserved client=]'s [=environment/has storage access=] to false. -Before changing the current entry of a session history, run the following steps: +When [=set up a window environment settings object|setting up a window environment settings object=]: +1. Set settings object's [=environment/has storage access=] to reserved environment's [=environment/has storage access=]. -1. Let |doc| be current entry's {{Document}}. -1. Let |map| be the result of [=obtain the storage access map|obtaining the storage access map=] for |doc|'s [=Document/browsing context=]'s [=top-level browsing context=]. -1. Let |key| be the result of [=generate a partitioned storage key|generating a partitioned storage key=] from |doc|. -1. If |key| is failure, abort these steps. -1. Let |flag set| be the result of [=obtain a storage access flag set|obtaining the storage access flag set=] with |key| from |map|. -1. Unset |flag set|'s [=has storage access flag=]. ISSUE(privacycg/storage-access#3): What this section should look like ultimately hinges on @@ -270,9 +261,9 @@ This API only impacts HTTP cookies. A future revision of this API might impact o

Cookies

-This API is intended to be used with environments and user agent configurations that block access to unpartitioned cookies in a [=third party context=]. At the time of this writing, this concept has not yet been integrated into the [=HTTP-network-or-cache fetch=] and {{Document/cookie}} algorithms. To allow for such an integration, the [=cookie store=] will need to be modified to receive information about the top-level and embedded site of the request (to determine whether to attach cross-site, partitioned, or no cookies) as well as whether the request was made for a document that has storage access, through running the [=determine if a site has storage access=] algorithm that is defined in this specification. +This API is intended to be used with environments and user agent configurations that block access to unpartitioned cookies in a [=third party context=]. At the time of this writing, this concept has not yet been integrated into the [=HTTP-network-or-cache fetch=] and {{Document/cookie}} algorithms. To allow for such an integration, the [=cookie store=] will need to be modified to receive information about the top-level and embedded site of the request (to determine whether to attach cross-site, partitioned, or no cookies) as well as whether the request was made for a document that has storage access, through accessing the [=environment=]'s [=environment/has storage access=] that is defined in this specification. -Once the cookie store allows for receiving information about storage access, we would update [=HTTP-network-or-cache fetch=] and {{Document/cookie}} to run [=determine if a site has storage access=] and pass this information to the [=cookie store=] when retrieving cookies. +Once the cookie store allows for receiving information about storage access, we would update [=HTTP-network-or-cache fetch=] and {{Document/cookie}} to pass the [=environment=]'s [=environment/has storage access=] to the [=cookie store=] when retrieving cookies. When getting unpartitioned cookies from the [=cookie store=] with storage access, user agents will still follow applicable `SameSite` restrictions (i.e., not attach cookies marked `SameSite=Strict` or `SameSite=Lax` in [=third party contexts=]).