From 22533caf002d353233675150bbfbf16aaff6f965 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Tue, 3 Nov 2020 15:42:16 -0500 Subject: [PATCH] First draft of a spec This ports over portions of https://wicg.github.io/portals/, attempting to generalize along the way. The novel thing it introduces is storing all prerendering browsing contexts in a map, and patching the navigation algorithm to allow activation in place of some navigations. It also does a better job of keeping the session history trivial. --- .editorconfig | 12 ++ .gitattributes | 2 + .github/workflows/build.yml | 22 ++++ .gitignore | 3 +- .pr-preview.json | 3 +- Makefile | 28 +++++ index.bs | 235 +++++++++++++++++++++++++++++++++--- 7 files changed, 287 insertions(+), 18 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/build.yml create mode 100644 Makefile diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8738948 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2f2e77e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +*.bs diff=html linguist-language=HTML diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9a665df --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,22 @@ +name: Build +on: + pull_request: + branches: + - main + push: + branches: + - main +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build + run: make ci + - name: Deploy + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./out diff --git a/.gitignore b/.gitignore index d5f19d8..dcaf716 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -node_modules -package-lock.json +index.html diff --git a/.pr-preview.json b/.pr-preview.json index e3e7155..d2206be 100644 --- a/.pr-preview.json +++ b/.pr-preview.json @@ -2,7 +2,6 @@ "src_file": "index.bs", "type": "bikeshed", "params": { - "force": 1 + "die-on": "warning" } } - diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..504e0b3 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +SHELL=/bin/bash + +.PHONY: ci clean local remote + +local: index.bs + bikeshed --die-on=warning spec index.bs index.html + +remote: index.html + +ci: index.bs index.html + mkdir -p out + cp index.html out/ + +clean: + rm index.html + +index.html: index.bs + @ (HTTP_STATUS=$$(curl https://api.csswg.org/bikeshed/ \ + --output index.html \ + --write-out "%{http_code}" \ + --header "Accept: text/plain, text/html" \ + -F die-on=warning \ + -F file=@index.bs) && \ + [[ "$$HTTP_STATUS" -eq "200" ]]) || ( \ + echo ""; cat index.html; echo ""; \ + rm -f index.html; \ + exit 22 \ + ); diff --git a/index.bs b/index.bs index 2495edd..47f5bcf 100644 --- a/index.bs +++ b/index.bs @@ -1,17 +1,224 @@ -
-Title: Alternate Loading Modes
-Shortname: alternate-loading-modes
-Level: 1
-Status: CG-DRAFT
-Group: WICG
-Repository: WICG/alternate-loading-modes
-URL: http://example.com/url-this-spec-will-live-at
-Editor: Jeremy Roman, Google https://www.google.com/, jbroman@chromium.org
-!Tests: web-platform-tests alternate-loading-modes/ (ongoing work)
-Abstract: A short description of your spec, one or two sentences.
+
+
+spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
+  type: dfn
+    urlPrefix: browsers.html
+      text: creating a new top-level browsing context; url: creating-a-new-top-level-browsing-context
+    urlPrefix: history.html
+      text: session history; url: session-history
+    urlPrefix: browsing-the-web.html
+      for: session history entry
+        text: document; url: she-document
+      for: navigate
+        text: mature; url: concept-navigate-mature
+      for: navigation params
+        text: request; url: navigation-params-request
+        text: reserved environment; url: navigation-params-reserved-environment
+      text: abort; for: Document; url: abort-a-document
+      text: create and initialize a Document object; url: initialise-the-document-object
+      text: history handling behavior; url: history-handling-behavior
+      text: prompt to unload; url: prompt-to-unload-a-document
+      text: refused to allow the document to be unloaded; url: refused-to-allow-the-document-to-be-unloaded
+      text: traverse the history; url: traverse-the-history
+
+ +

Prerendering browsing contexts

+ +The following section would be added as a new sub-section of [[HTML]]'s Browsing contexts section. + +Every [=browsing context=] has a loading mode, which is one of the following: + +: "`default`" +:: No special considerations are applied to content loaded in this browsing context +: "`prerender`" +:: This browsing context is displaying prerendered content +: "`uncredentialed-prerender`" +:: This browsing context is displaying prerendered content, and furthermore that content cannot make credentialed fetches + +By default, a [=browsing context=]'s [=browsing context/loading mode=] is "`default`". A browsing context whose [=browsing context/loading mode=] is either "`prerender`" or "`uncredentialed-prerender`" is known as a prerendering browsing context. + +

This specification enforces that [=prerendering browsing contexts=] are always [=top-level browsing contexts=], i.e., that a [=nested browsing context=]'s [=browsing context/loading mode=] is always "`default`". + +

Probably we will need more loading modes for handling [=nested browsing contexts=] inside of top-level prerendered ones. Definitely a to-do.

+ +A [=prerendering browsing context=] is empty if the only entry in its [=session history=] is the initial `about:blank` {{Document}}. + +Every {{Document}} has a prerendering browsing contexts map, which is an [=ordered map=] of ([=URL=], [=referrer policy=]) [=tuples=] to [=prerendering browsing contexts=]. This is used to fulfill [=navigate|navigations=] to a given URL by instead [=prerendering browsing context/activating=] the corresponding prerendering browsing context. + +

Should this map be scoped to the [=user agent=] instead? Or, allowed to be copied between documents? + +

+ To create a prerendering browsing context given a [=URL=] |startingURL|, a [=referrer policy=] |referrerPolicy|, and a {{Document}} |referrerDoc|: + + 1. Assert: |startingURL|'s [=url/scheme=] is a [=HTTP(S) scheme=]. + + 1. If |referrerDoc|'s [=Document/prerendering browsing contexts map=][(|startingURL|, |referrerPolicy|)] [=map/exists=], then return it. + + 1. Let |bc| be the result of [=creating a new top-level browsing context=]. + + 1. If |startingURL|'s [=url/origin=] is [=same origin=] with |referrerDoc|'s [=Document/origin=], then set |bc|'s [=browsing context/loading mode=] to "`prerender`"; otherwise, "`uncredentialed-prerender`". + + 1. Set |referrerDoc|'s [=Document/prerendering browsing contexts map=][|startingURL|] to |bc|. + + 1. Let |request| be a new [=request=] whose [=request/URL=] is |startingURL| and [=request/referrer policy=] is |referrerPolicy|. + + 1. [=Navigate=] |bc| to |request| with the [=source browsing context=] set to |referrerDoc|'s [=Document/browsing context=]. + + 1. Return |bc|. +
+ +
+ To activate a [=prerendering browsing context=] |successorBC| in place of a [=top-level browsing context=] |predecessorBC| given a [=history handling behavior=] |historyHandling|: + + 1. Assert: |historyHandling| is either "`default`" or "`replace`". + + 1. Assert: |successorBC| is not [=prerendering browsing context/empty=]. + + 1. Assert: |predecessorBC| is a [=top-level browsing context=]. + + + + 1. Cancel any preexisting but not yet [=navigate/mature=] attempts to navigate |predecessorBC|, including canceling any instances of the [=fetch=] algorithm started by those attempts. If one of those attempts has already created and initialized a new `Document` object, [=Document/abort=] that {{Document}} also. + + 1. [=Prompt to unload=] the [=active document=] of |predecessorBC|. If the user [=refused to allow the document to be unloaded=], then return. + + 1. [=Document/Abort=] the [=active document=] of |predecessorBC|. + + + + 1. TODO prepend the existing session history of |predecessorBC| into |successorBC|? Or, probably better, use the new "browsing session" concept to bridge them? Be sure to respect |historyHandling|. + + 1. [=In parallel=]: + + 1. Update the user agent's user interface to replace |predecessorBC| with |successorBC|, e.g., by updating the tab/window contents and the browser chrome. + + + 1. [=Queue a global task=] on the [=networking task source=] given |successorBC|'s [=browsing context/active window=] to perform the following steps + + 1. Set |successorBC|'s [=browsing context/loading mode=] to "`default`". +
+ + + + + +Patch the [=navigate=] algorithm to allow the [=prerendering browsing context/activate|activation=] of a [=prerendering browsing context=] in place of a normal navigation as follows: + +
+ In [=navigate=], append the following steps after the fragment navigation handling (currently step 6): + + 1. If all of the following are true: + + * |browsingContext| is a [=top-level browsing context=] + * |historyHandling| is "`default`" or "`replace`" + * navigationType is "`other`" + * |resource| is a [=request=] whose [=request/method=] is \``GET`\` + * |browsingContext|'s [=active document=]'s [=Document/prerendering browsing contexts map=][(|resource|'s [=request/URL=], |resource|'s [=request/referrer policy=])] [=map/exists=] + + then: + + 1. Let |successorBC| be |browsingContext|'s [=active document=]'s [=Document/prerendering browsing contexts map=][(|resource|'s [=request/URL=], |resource|'s [=request/referrer policy=])]. + + 1. If |successorBC| is not [=prerendering browsing context/empty=], then: + + 1. [=prerendering browsing context/Activate=] |successorBC| in place of |browsingContext| given |historyHandling|. + + 1. Return. +
+ +

Maintaining a trivial session history

+ +
+ Patch the [=navigate=] algorithm to ensure the [=session history=] of a [=prerendering browsing context=] stays trivial by prepending the following step before all others: + + 1. If browsingContext is a [=prerendering browsing context=], then: + + 1. Assert: |historyHandling| is not "`entry update`", since prerendering browsing contexts have trivial session histories and thus will never end up [=traverse the history|traversing=] back to an entry with null [=session history entry/document=]. + + 1. If |historyHandling| is "`default`", then set |historyHandling| to "`replace`". +
+ +
+ Patch the URL and history update steps by adding the following step after step 1: + + 1. If browsingContext is a [=prerendering browsing context=], then set isPush to false. +
+ +

Preventing non-HTTP(S) navigations

+ +Patch the [=navigate=] algorithm to prevent certain navigations in a [=prerendering browsing context=] as follows: + +

Portals might need an extra hook to close the portal in these cases. Or should we reconsider and just do nothing for portals too? That might be more elegant. I think it just requires portals to not be so zealous about clearing the host element/browsing context link, which isn't observable anyway? + +

+ In process a navigate response, append the following after the step which establishes the value of |failure|, but before the step which uses it to display an error page: + + 1. If browsingContext is a [=prerendering browsing context=], and any of the following hold: + + * |failure| is true; + * |navigationParams|'s [=navigation params/request=] is null; + * |navigationParams|'s [=navigation params/request=]'s [=request/current URL=]'s [=url/scheme=] is not a [=HTTP(S) scheme=]; + * |response| has a \``Content-Disposition`\` header specifying the `attachment` + disposition type; or + * |response|'s [=response/status=] is 204 or 205, + + then: + + 1. Run the [=environment discarding steps=] for |navigationParams|'s [=navigation params/reserved environment=]. + + 1. Return. +
+ +
+ In process a navigate URL scheme, insert the following step before the step + which displays inline content: + + 1. Otherwise, if browsingContext is a [=prerendering browsing context=], then return. +
+ +

Preventing nonsensical behaviors

+ +Some behaviors might make sense in most [=top-level browsing contexts=], but do not make sense in [=prerendering browsing contexts=]. This section enumerates specification patches to enforce such restrictions. + +

APIs for creating and navigating browsing contexts by name

+ +Modify the definition of script-closable to prevent window closing while in a [=prerendering browsing context=]: + +A [=browsing context=] is script-closable if either of the following is true: + +* it is an [=auxiliary browsing context=] that was created by script (as opposed to by an action of the user); or +* it is a [=top-level browsing context=] that is not a [=prerendering browsing context=] and whose [=session history=] contains only one {{Document}}. + +

Preventing intrusive behaviors

+ +Various behaviors are disallowed in [=prerendering browsing contexts=] because they would be intrusive to the user, since the prerendered content is not being actively interacted with. + +

Downloading resources

+ +Modify the allowed to download algorithm to ensure that prerendered content never performs downloads, by prepending the following steps: + +
+ 1. If initiator browsing context is a [=prerendering browsing context=], then return false. + + 1. If instantiator browsing context is a [=prerendering browsing context=], then return false. +
-Introduction {#intro} -===================== +

TODO

-See https://github.com/tabatkins/bikeshed to get started. +- Spec all the actual restrictions (storage, intrusive APIs, uncredentialed fetching) +- Ensure navigations get aborted, and the map emptied, on non-opt-in cases for uncredentialed. Potential race conditions here; maybe we shouldn't fill the map until navigation succeeds. +- CSP prefetch-src check (should this be done at create time?) +- CSP navigate-to check +- JS API