Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward Declared API Addition #100

Closed
80 changes: 78 additions & 2 deletions storage-access.bs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ urlPrefix: https://w3c.github.io/webdriver/webdriver-spec.html#; spec: webdriver

<em>This section is non-normative.</em>

User Agents sometimes prevent content inside certain <{iframe}>s from accessing data stored in client-side storage mechanisms like cookies. This can break embedded content which relies on having access to client-side storage.
User Agents sometimes prevent content inside certain <{iframe}>s or requests from accessing data stored in client-side storage mechanisms like cookies. This can break embedded content which relies on having access to client-side storage.

The Storage Access API enables content inside <{iframe}>s to request and be granted access to their client-side storage, so that embedded content which relies on having access to client-side storage can work in such User Agents. [[STORAGE-ACCESS-INTRO]]
The Storage Access API enables Documents to request and be granted access to their unpartitioned client-side storage, so that embedded content which relies on having access to client-side storage can work in such User Agents. [[STORAGE-ACCESS-INTRO]]

</section>

Expand All @@ -98,6 +98,19 @@ Script in the <{iframe}> can call |doc|`.`{{Document/hasStorageAccess()}} to det

</div>

This specification defines two methods that must be called sequentially in top-level contexts to request access to its [=unpartitioned data=] without using iframes, {{Document/completeStorageAccessRequestFromSite()}} and {{Document/requestStorageAccessUnderSite()}}.

<div class=example>

Alex visits `https://site.example`. Alex clicks "Login with Identity", which Alex expects to log them in with their account on `https://identity.example`. The page, `https://site.example`, navigates to `https://identity.example` when the returned promise resolves.

Once navigated, Alex selects the account to log in with. In response, `https://identity.example` sets a cookie, which by virtue of its [=first-party-site context=] is in its [=unpartitioned data=] and calls `{{Document.requestStorageAccessUnderSite("https://site.example")}}`, which could ask Alex to give permission for the request.
Once the {{Promise}} returned by `{{Document/requestStorageAccessUnderSite()}}` resolves, the [=/browsing context=] navigates, returning Alex to `https://site.example`.

When Alex returns to `https://site.example`, the page calls `{{Document.completeStorageAccessRequestFromSite("https://identity.example")}}`. Once the returned promise resolves, requests to `https://identity.example` will bear the cookie set in the previous [=window=].

</div>

<dfn>Unpartitioned data</dfn> is client-side storage that would be available to a [=site=] were it loaded in a [=first-party-site context=].

A {{Document}} is in a <dfn>first-party-site context</dfn> if it is the [=active document=] of a [=top-level browsing context=]. Otherwise, it is in a [=first-party-site context=] if it is an [=active document=] and the [=environment settings object/origin=] and [=top-level origin=] of its [=relevant settings object=] are [=same site=] with one another.
Expand Down Expand Up @@ -144,6 +157,8 @@ A <dfn>storage access flag set</dfn> is a set of zero or more of the following f
:: When set, this flag indicates |embedded site| has access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |top-level site|.
: The <dfn for="storage access flag set" id=was-expressly-denied-storage-access-flag>was expressly denied storage access flag</dfn>
:: When set, this flag indicates that the user expressly denied |embedded site| access to its [=unpartitioned data=] when it's loaded in a [=third party context=] on |top-level site|.
: The <dfn for="has allow request storage access flag set" id=has-allow-request-storage-access-flag>has allow request storage access flag</dfn>
:: When set, this flag indicates |top-level site| is allowing |embedded site| to request access to its [=unpartitioned data=] on |top-level site| from the |embedded site| [=first-party-site context=].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how folks more familiar with spec syntax like @hober or @annevk feel about our usage of "site" in this PR and in SAA overall, and whether it's clear enough what's being referred to. What we really mean is "documents of embedded site" (in a first-party-site context), so maybe we need to introduce this definition separately.


To <dfn type="abstract-op">obtain a storage access flag set</dfn> for a [=partitioned storage key=] |key| from a [=/storage access map=] |map|, run the following steps:

Expand All @@ -162,6 +177,8 @@ To <dfn type="abstract-op">save the storage access flag set</dfn> for a [=partit
partial interface Document {
Promise&lt;boolean> hasStorageAccess();
Promise&lt;undefined> requestStorageAccess();
Promise&lt;undefined> requestStorageAccessUnderSite(DOMString topLevelURI);
Promise&lt;undefined> completeStorageAccessRequestFromSite(DOMString embeddedURI);
};
</pre>

Expand Down Expand Up @@ -223,6 +240,65 @@ When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>re

ISSUE: Shouldn't step 3.7 be [=same site=]?

When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>requestStorageAccessUnderSite(DOMString topLevelURI)</code></dfn> method must run these steps:

1. Let |p| be [=a new promise=].
1. If this algorithm was invoked when |doc|'s {{Window}} object did not have [=transient activation=], [=reject=] and return |p|.
1. Let |parsedURI| be the result of running the [=URL parser=] on <var ignore>topLevelURI</var>.
1. If |parsedURI| is failure, [=reject=] and return |p|.
1. If |doc|'s [=Document/browsing context=] is not a [=top-level browsing context=], [=reject=] and return |p|.
1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] and return |p|.
1. Let |parsedOrigin| be the |parsedURI|'s origin.
1. If |doc|'s [=Document/origin=] is same site to |parsedOrigin|, [=reject=] and return |p|.
1. Let |settings| be |doc|'s [=relevant settings object=].
1. Let |site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=environment settings object/origin=].
1. Let |topLevelSite| be the result of [=obtain a site|obtaining a site=] from |parsedOrigin|.
1. Let |key| be the [=partitioned storage key=] (|topLevelSite|, |site|).
1. Let |global| be |doc|'s [=relevant global object=].
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 [=was expressly denied storage access flag=] is set,
1. [=reject=] |p|
1. return |p|.
1. If |flag set|’s [=has storage access flag=] is set,
1. [=resolve=] |p|
1. 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 [=permission task source=] given |global| to
1. Set |flag set|'s [=has allow request storage access flag=].
1. Resolve |p|.
1. [=Save the storage access flag set=] for |key| in |map|.
1. Return |p|.


When invoked on {{Document}} |doc|, the <dfn export method for=Document><code>completeStorageAccessRequestFromSite(DOMString embeddedURI)</code></dfn> method must run these steps:

1. Let |p| be [=a new promise=].
1. Let |parsedURI| be the result of running the [=URL parser=] on <var ignore>embeddedURI</var>.
1. If |parsedURI| is failure, [=reject=] and return |p|.
1. If |doc|'s [=Document/browsing context=] is not a [=top-level browsing context=], [=reject=] and return |p|.
1. If |doc|'s [=Document/origin=] is an [=opaque origin=], [=reject=] and return |p|.
1. Let |parsedOrigin| be the |parsedURI|'s origin.
1. If |doc|'s [=Document/origin=] is same site to |parsedOrigin|, [=reject=] and return |p|.
1. Let |settings| be |doc|'s [=relevant settings object=].
1. Let |site| be the result of [=obtain a site|obtaining a site=] from |settings|' [=environment settings object/origin=].
1. Let |embeddedSite| be the result of [=obtain a site|obtaining a site=] from |parsedOrigin|.
1. Let |key| be the [=partitioned storage key=] (|site|, |embeddedSite|).
1. Let |global| be |doc|'s [=relevant global object=].
1. Run these steps [=in parallel=]:
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 allow request storage access flag=] is set,
1. Unset |flag set|'s [=has allow request storage access flag=].
1. [=Save the storage access flag set=] for |key| in |map|.
1. [=Resolve=] p.
1. Otherwise,
1. [=Reject=] p
1. Return |p|.


<h4 id="ua-policy">User Agent storage access policies</h4>

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.
Expand Down