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

What browsers do for overlapping navigations/history traversals #6927

Open
domenic opened this issue Aug 2, 2021 · 6 comments
Open

What browsers do for overlapping navigations/history traversals #6927

domenic opened this issue Aug 2, 2021 · 6 comments
Labels
interop Implementations are not interoperable with each other topic: history topic: navigation

Comments

@domenic
Copy link
Member

domenic commented Aug 2, 2021

Consider sequences of two operations from the set { cross-document navigation, same-document navigation, cross-document history traversal, same-document history traversal }, both requested synchronously. (Although all traversals and cross-document navs do not actually finish synchronously.) E.g.

// cross-document nav, cross-document nav
location.search = "?1";
location.search = "?2";

// Assume our history list looks like [?1, ?1#2]
// same-document traversal, cross-document nav
history.back();
location.href = "?3";

What does the spec say should happen here? What do browsers do? I have written a bunch of web platform tests and found out the following things.

This is largely a superset of #6773 which has some previous discussion.

In what follows, XDN = cross-document navigation, XDT = cross-document traversal, and similarly for SDN and SDT. ✔ means "matches the spec", ? means "not yet tested"

First operation Second operation Spec result Firefox result Chrome result Safari result
XDN XDN Second nav cancels first nav
XDN XDT Traversal cancels nav
XDN SDN SDN happens synchronously first, then XDN
XDN SDT Traversal cancels nav Traversal ignored; nav wins Traversal ignored; nav wins
XDN window.stop() Nav is canceled
XDT XDN XDT and XDN race XDN gets ignored (even if the XDT is very slow) XDN gets ignored (even if the XDT is very slow) XDT gets ignored (even if the XDN is very slow)
XDT XDT Traversals queued, but what "back"/"forward" means is computed sync Ignore second XDT Coalesce into a single XDT (which could go nowhere or could go +/-2) Ignore first XDT
XDT SDN SDN un-queues all traversals and proceeds For fragment SDN: queue up traversal after SDN; for pushState SDN: XDT gets stopped SDN is ignored, XDT succeeds Queue up traversal after SDN
XDT SDT Traversals queued, but what "back"/"forward" means is computed sync Ignore second SDT Coalesce into a single XDT (going +/-2) Somehow the outer page gets reloaded??
XDT window.stop() Un-queue the traverals stop() ignored stop() ignored stop() ignored
XDT browser stop Un-queue the traverals ? ?
SDN XDN SDN completes sync, XDN then proceeds
SDN XDT SDN completes sync, XDT then proceeds Somehow the outer page gets reloaded??
SDN SDN SDN completes sync, second SDN completes sync
SDN SDT SDN completes sync, SDT then proceeds
SDN window.stop() SDN completes sync, stop() is too late so does nothing
SDN browser stop SDN completes sync, stop is too late so does nothing
SDT XDN XDN can cancel SDT if the SDT takes too long; otherwise it happens after the SDT ✔? SDT is too fast to be sure. ✔? SDT is too fast to be sure. ✔? SDT is too fast to be sure.
SDT XDT Traversals queued, but what "back"/"forward" means is computed sync Traverals queued, e.g. hashchange then load Traverals queued, e.g. hashchange then load XDT is ignored
SDT SDN SDN un-queues all traversals and proceeds For fragment SDN: queue up traversal after SDN; for pushState SDN: SDT gets stopped SDN is happens, but then SDT goes back to the entry computed when the back() call was made Queue up traversal after SDN
SDT SDT Traversals queued, but what "back"/"forward" means is computed sync Traverals are queued up and happen in order Second SDT is ignored First SDT is ignored
SDT window.stop() Un-queue the traveral stop() ignored stop() ignored stop() ignored
SDT browser stop Un-queue the traveral ? (hard to test for humans) ? (hard to test for humans; we may avoid showing the browser stop button entirely to avoid flicker) ? (hard to test for humans)

Presumably the next step, as with everything in the great history rewrite project, is for @jakearchibald to try to find a model that (a) makes some theoretical sense; (b) matches as many browsers in as many cases as we can.

Some tentative notes:

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 2, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 4, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 4, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 6, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 6, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Aug 6, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}
pull bot pushed a commit to jamlee-t/chromium that referenced this issue Aug 7, 2021
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Aug 14, 2021
…ns/traverals, a=testonly

Automatic update from web-platform-tests
Tentative WPTs for overlapping navigations/traverals

See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}

--

wpt-commits: 50e80a2e114e758bfaf652535ddbdbbde7bdb48f
wpt-pr: 29871
domenic added a commit to WICG/navigation-api that referenced this issue Aug 18, 2021
Follows https://chromium-review.googlesource.com/c/chromium/src/+/3102875, although it's not clear that the "inform app history about canceling navigation" / "inform app history about canceling traversals" is quite right since the implementation is a bit different from the spec and per whatwg/html#6927 browsers are quite messy.
@domenic
Copy link
Member Author

domenic commented Aug 18, 2021

Another case to consider is iframe removal. Presumably in that case all XDNs, SDTs, and XDTs are canceled.

jamienicol pushed a commit to jamienicol/gecko that referenced this issue Aug 20, 2021
…ns/traverals, a=testonly

Automatic update from web-platform-tests
Tentative WPTs for overlapping navigations/traverals

See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}

--

wpt-commits: 50e80a2e114e758bfaf652535ddbdbbde7bdb48f
wpt-pr: 29871
@domenic
Copy link
Member Author

domenic commented Sep 1, 2021

I filed

on a few behaviors that seem clearly wrong.

Here is a tentative proposal for what the spec should say:

First operation Second operation Proposed result Firefox matches? Chrome matches? Safari matches?
XDN XDN Second nav cancels first nav
XDN XDT Traversal cancels nav
XDN SDN SDN happens synchronously first, then XDN
XDN SDT Traversal cancels nav
XDN window.stop() Nav is canceled
XDT XDN XDN gets ignored (even if the XDT is very slow)
XDT XDT ??? ? ? ?
XDT SDN Queue up traversal after SDN ✔-ish
XDT SDT ??? ? ? ?
XDT window.stop() stop() ignored
XDT browser stop Un-queue the traverals ? ?
SDN XDN SDN completes sync, XDN then proceeds
SDN XDT SDN completes sync, XDT then proceeds ❌ (bug)
SDN SDN SDN completes sync, second SDN completes sync
SDN SDT SDN completes sync, SDT then proceeds
SDN window.stop() SDN completes sync, stop() is too late so does nothing
SDN browser stop SDN completes sync, stop is too late so does nothing
SDT XDN XDN can cancel SDT if the SDT takes too long; otherwise it happens after the SDT ✔? ✔? ✔?
SDT XDT ??? ? ? ?
SDT SDN Queue up traversal after SDN ✔-ish
SDT SDT ??? ? ? ?
SDT window.stop() stop() ignored
SDT browser stop Un-queue the traveral ✔? ✔? ✔?

The big ??? rows are what to do with multiple traversals. It appears at a very high level we have three behaviors to choose from:

  • Ignore first traversal (Safari)
  • Ignore second traversal (Firefox)
  • Coalesce the traversals, potentially to a no-op (Chrome)

I am going to ask my Chrome colleagues what they think about the rows where Chrome is the odd one out, as well as if they have any strong opinions about the correct behavior for multiple traversals. Thoughts from other browser vendors would be very welcome as well.

@annevk
Copy link
Member

annevk commented Sep 7, 2021

For SDT then XDT it seems there's a fourth traversals alternative? I.e., queue in order and do both? No strong opinions from me. @smaug---- @mystor @petervanderbeken might have some.

@domenic
Copy link
Member Author

domenic commented Sep 17, 2021

A few updates:


My colleagues have pretty much convinced me that Chrome's behavior for multiple traversals, of coalescing them, is a good idea. The argument is basically that it's almost certainly the right behavior for UI-triggered back button XDT+XDT or XDT+SDT case. That is, if the user presses back twice, then no matter how fast or slow those pages load, they should go back twice. Then, we should just extend that behavior to get consistency everywhere:

  • We should behave the same for UI-triggered SDT+XDT or SDT+SDT, even though the problem won't really crop up there since SDTs generally complete faster than users can click a second time.
  • We should behave the same between developer-triggered history.back() etc. and UI-triggered back button presses, since having those share the same underlying spec primitives and queues seems like a good idea.

I'd love to discuss this with others and hear if they agree. If I don't hear much I'll go ahead and update the above table proposal.


On the same thread, it was pointed out that my original assessment of what browsers do for XDN+SDT was not correct; it was Chrome that has the SDT cancel the XDN, whereas the other two browsers ignore the SDT. I think Chrome's behavior is more correct here because it is consistent with the XDN+XDT case, where all browsers agree that the XDT should cancel the XDN.

I have updated the above table accordingly. That leads to one of the few rows where the proposed spec result only matches 1/3 browsers, but I think it's still the right call.


On the same thread, I'm still pushing for Chrome to adopt the Safari/Firefox behavior for SDT+SDN and XDT+SDN. We'll see what my colleagues say.

The above table still reflects the Safari/Firefox behavior for those cases as the proposed spec behavior.


Finally, my plan is that once we settle (either by silence or through discussion 😄) the multi-traversal case, I'll update all the web platform tests to match the proposed spec. And then @jakearchibald can hopefully incorporate this into his session history rewrite spec PR.

@domenic
Copy link
Member Author

domenic commented Dec 7, 2021

Current proposal, using coalescing behavior for multiple traversals

First operation Second operation Proposed result Firefox matches? Chrome matches? Safari matches?
XDN XDN Second nav cancels first nav
XDN XDT Traversal cancels nav
XDN SDN SDN happens synchronously first, then XDN
XDN SDT Traversal cancels nav
XDN window.stop() Nav is canceled
XDT XDN XDN gets ignored (even if the XDT is very slow)
XDT XDT Coalesce
XDT SDN Queue up traversal after SDN ✔-ish
XDT SDT Coalesce
XDT window.stop() stop() ignored
XDT browser stop Un-queue the traverals ? ?
SDN XDN SDN completes sync, XDN then proceeds
SDN XDT SDN completes sync, XDT then proceeds ❌ (bug)
SDN SDN SDN completes sync, second SDN completes sync
SDN SDT SDN completes sync, SDT then proceeds
SDN window.stop() SDN completes sync, stop() is too late so does nothing
SDN browser stop SDN completes sync, stop is too late so does nothing
SDT XDN XDN can cancel SDT if the SDT takes too long; otherwise it happens after the SDT ✔? ✔? ✔?
SDT XDT Coalesce
SDT SDN Queue up traversal after SDN ✔-ish
SDT SDT Coalesce
SDT window.stop() stop() ignored
SDT browser stop Un-queue the traveral ✔? ✔? ✔?

Caveats:

  • This is all testing synchronous overlapping cases, e.g. history.back(); location.href = "...". Presumably there are cases where a navigation or traversal (especially an XDN/XDT) gets "far enough" that these results stop applying. I haven't tested those cases.
  • It's possible some of these behaviors differ based on whether the iframes involved are cross- or same-process. All tests were run on same-origin (thus same-process) iframes.

@domenic domenic added the interop Implementations are not interoperable with each other label Aug 8, 2022
@domenic
Copy link
Member Author

domenic commented Oct 10, 2022

Update: the navigation and session history rewrite has answers for this, and I think I've converted them all into updated web platform tests in web-platform-tests/wpt#36364.

The most complex case remains the overlapping traversals. I'm not sure I've 100% traced the implications of the rewrite for those cases through; I've asked for help from @jakearchibald and @domfarolino to confirm, as part of the review of that WPT PR. It appears the proposed spec there might not match any implementations in some cases. That's bad, and so we'll keep this issue open to track aligning that. But the spec is significantly better at matching implementations for all other cases.

mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}
NOKEYCHECK=True
GitOrigin-RevId: 279369534d529c705a4d4579fb93a1a7ae877622
domenic added a commit that referenced this issue Oct 31, 2022
This monster completely rewrites everything to do with navigation and traversal.

It introduces the "navigable" and "traversable navigable" concepts, which take on many of the roles that browsing contexts previously did, but better. A navigable can present a sequence of browsing contexts, which to the user seem to all be the same, but due to browsing context group switches, have different WindowProxys and are allocated in different agent clusters. A traversable navigable manages the session history for itself and all its descendant navigables, providing a synchronization point and source of truth.

The general flow of navigation and traversal is now geared toward creating a session history entry, populated with the appropriate document, before finally applying the history "step". The step concept for session history, managed by the traversable, replaces the previous idea of joint session history, which was a sort of deduplicated union of individual session histories for each browsing context within a top-level browsing context.

Notable things we won't tackle this round, but are much easier to tackle in the future:

- Iframe restoration on (non-bfcache) history traversal is not yet specified.
- Overlapping navigations and traversals (see #6927) are not perfect yet, although this makes them better.
- Browsing context names (see #313) are not perfect yet, although this makes them better.
- Base URL inheritance and storage in session history (see #421, #2883, and #3989) is not yet specified.
- Sandbox flag storage in session history (see #6809) is not yet specified.
- Task queuing when creating agents/realms/windows/documents (see #8443) remains sketchy.
- Window object reuse is not yet rationalized (see #3267).

Closes #854 by clarifying the javascript: URL origin and origin-checking setup.

Closes #1073 by properly resetting active-ness of documents when they are removed.

Closes #1130 by removing the source browsing context concept, using a sourceDocument argument instead, and taking source snapshot params at the appropriate early time.

Closes #1191 by properly sharing document state across documents, as well as overlapping same-document navigations plus cross-document traversals.

Closes #1336 by properly handling child browsing contexts.

Closes #1382 by only unloading after we are sure we have a new document (i.e., not a 204 or download).

Closes #1454 by rewriting session history closer to what implementations do, with the nested history concept in particular taking care of the issues discussed there.

Closes #1524 by introducing the POST data concept and storing it in the document state.

Closes #2436 by rewriting the spec for history.go() to be clear about the results. Tests: web-platform-tests/wpt#36366.

Closes #2566 by introducing an explicit "history object" definition. Tests: web-platform-tests/wpt#36367.

Closes #2649 through clear creation of srcdoc documents, including during history traversal.

Closes #3215 by preserving POST data and reusing it on reloads.

Closes #3447 by specifying a precise mechanism (the ongoing navigation) for canceling navigations, and the points at which that mechanism is consulted. It also stops queuing a task for hyperlink navigations.

Closes #3497 by posting appropriate tasks for cross-event-loop navigations.

Closes #3615 by rewriting traverse a history by a delta, which eventually calls into apply the history step, to navigate all relevant navigables.

Closes #3625 by storing information in the document state (not just the URL), so that future traversals can reconstruct the request appropriately.

Closes #3730 by doing proper task queuing for navigation, including one for javascript: URLs but not including one for normal same-frame navigations. Tests: web-platform-tests/wpt#36358.

Closes #3734 by rewriting the definition of script-closable to use well-defined concepts.

Closes #3812 by removing all uses of "active document" as a predicate instead of a property.

Closes #4054 by introducing the session history traversal queue and renaming the previous "history traversal task source" to "navigation and traversal task source".

Closes #4121 by doing the "allowed to navigate" check at the top of apply the history step.

Closes #4428 by keeping a strong reference from documents (including bfcached documents) to their containing browsing context.

Closes #4782 by introducing the top-level traversable and navigable concepts.

Closes #4838 by doing sandbox checking in a much more precise manner, in particular snapshotting the relevant flags early in any traversals.

Closes #4852 by using document state (in particular history policy container, request referrer, and request referrer policy) in reloads.

Closes #5103 by properly restoring scroll positions for everything that is traversed, as part of properly traversing more than one navigable.

Closes #5350 by properly restoring window names across browsing context group switches, and going back to the same browsing context as was previously there when traversing back across a BCG switch boundary. (Implementations could create new browsing contexts, as long as they restore the WindowProxy scripting relationships and other browsing context features; the result is observably equivalent.)

Closes #5597 by rewriting "allowed to download" to just take booleans, derived from the appropriate snapshotted or computed sandboxing flags.

Closes #5767, modulo bugs and oversights we made, by rewriting everything :).

Closes #5877 by re-specifying "fully active" in terms of navigables, instead of browsing contexts.

Closes #6446 by properly firing beforeunload to all descendant navigables, although whether or not they actually prompt still allows implementation leeway.

Closes #6483 by introducing the distinction between current session history entry and active session history entry.

Closes #6514 by settling on using a single origin for these checks.

Closes #6628 by storing window.name values in the document state, so even in strange splitting situations like described there, they remain.

Closes #6652 by no longer changing history.state when reactivating a document from bfcache ("restore the history object state" is called only when documentsEntryChanged is true). Tests: web-platform-tests/wpt#36368.

Closes #6773 by having careful handling of synchronous navigations during traversals. Test updates: web-platform-tests/wpt#36364.

Closes #6798 by treating javascript: URL navigations as replacements.

Works towards #6809 by storing srcdoc resources in the document state.

Closes #6813 by storing referrer in the document state. Tests for the repopulation case: web-platform-tests/wpt#36352. (No tests yet for the reload case.)

Closes #6947 by rolling its contents into this change: PDF documents are put in the same category as other inaccessible, no-DOM documents.

Closes #7107 by clearing history state on redirects and when origin changes by other means, such as CSP.

Closes #7441 by making window.blur() a no-op because that was simpler than updating it to operate on navigables.

Closes #7722 by incorporating its contents into the rewritten version.

Closes #8295 by refactoring the iframe/frame load event specs to avoid the bug.

Helps with #8395 by at least ensuring the javascript: case does not fire beforeunload. Tests: web-platform-tests/wpt#36488. (The other cases remain open for investigation and testing.)

Closes #8449 by exporting "create a fresh top-level traversable" which is designed for the use case in question.

Co-authored-by: Domenic Denicola <d@domenic.me>
Co-authored-by: Dominic Farolino <domfarolino@gmail.com>
i3roly pushed a commit to i3roly/firefox-dynasty that referenced this issue Jun 1, 2024
…ns/traverals, a=testonly

Automatic update from web-platform-tests
Tentative WPTs for overlapping navigations/traverals

See whatwg/html#6927.

This will help app history which needs its own versions of these tests.

Change-Id: I84c67c6f23149d9c106f011ec13807d68d749633
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3067883
Reviewed-by: Nate Chapin <japhet@chromium.org>
Commit-Queue: Domenic Denicola <domenic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#909433}

--

wpt-commits: 50e80a2e114e758bfaf652535ddbdbbde7bdb48f
wpt-pr: 29871
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interop Implementations are not interoperable with each other topic: history topic: navigation
Development

No branches or pull requests

3 participants
@domenic @annevk and others