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

Rationalize behavior of Window object reuse #3267

Open
zetafunction opened this issue Dec 2, 2017 · 7 comments
Open

Rationalize behavior of Window object reuse #3267

zetafunction opened this issue Dec 2, 2017 · 7 comments

Comments

@zetafunction
Copy link

I've been looking into some bugs in Chrome where the Window object is not reused even though it should be. I wrote several tests to try to document this behavior, and I got some interesting results.

Test Edge Firefox Chrome (pre-patch) Chrome (post-patch)
same-origin.html Fail Pass Fail Pass
same-origin-initial.html Pass Pass Pass Pass
explicit-about-blank.html Pass Fail Pass Pass
srcdoc.html Fail Pass Fail Fail

I think these differences are because it's hard to interpret how the different areas of the spec apply. Here are several of the different sections that are relevant:

https://html.spec.whatwg.org/multipage/browsers.html#windows notes that the relationship of Window and Document is 1:1 except in the special case of navigating a browser context from the initial about:blank Document to another Document with replacement enabled (but doesn't mention that this is same-origin only).

https://html.spec.whatwg.org/multipage/browsing-the-web.html#initialise-the-document-object mentions that a new Window object is created unless the only entry in session history is the initial about:blank Document and the navigation is occurring with replacement enabled and the new Document is same-origin.

https://html.spec.whatwg.org/multipage/history.html#location-object-navigate talks about the forced navigation with replacement enabled when navigating from the initial about:blank Document when it is the only Document in session history. This is the only language I could find that specifies replacement enabled is true for navigations from the initial about:blank Document. However, since this is associated with Location, it's unclear if the same replacement enabled behavior applies to setting the src attribute of HTMLIFrameElement. Presumably it does apply. Perhaps this language should be centralized in https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate instead.

Finally, there's https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes to describe how the src attribute is handled. It seems to imply that different behavior should happen if the src attribute is omitted vs intentionally set to "about:blank".

  • If src is unset, there will be no effect other than firing the load event at the frame.
  • If explicitly set to "about:blank", follow the steps in https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate . Presumably this will navigate to about:blank with replacement enabled (and now the iframe element is no longer on the initial about:blank Document, even though session history doesn't appear to have changed). This would imply that the Window object should not be reused and the next navigation should not occur with replacement enabled. Note that explicit-about-blank.html seems to contradict this: in Edge and Chrome, there is no session history for the explicit navigation to "about:blank", since the next navigation still occurs with replacement enabled (though the Window object is not reused). Firefox also appears to perform the navigation with replacement enabled, but also reuses the Window object.
@wanderview
Copy link
Member

I only looked at the tests so far, but I would recommend also taking a look at:

  1. Differences in behavior when the iframe is dynamically added before/after the load event. I think there are interop differences here.
  2. Declarative iframe with a script that modified it synchronously on evaluation. I believe this has the best interop of any of the cases right now.

I did add some tests recently for the declarative thing and how it interacts with service workers clients API here:

https://github.com/w3c/web-platform-tests/blob/master/service-workers/service-worker/about-blank-replacement.https.html

@annevk
Copy link
Member

annevk commented Dec 2, 2017

See also #2545 and #2566.

@zetafunction
Copy link
Author

#546 is also somewhat related.

@domenic
Copy link
Member

domenic commented Mar 13, 2019

In web-platform-tests/wpt#15809 @annevk tests that at least in the same-origin case, Chrome and Safari do not use "replacement enabled" for iframe navigation from the initial about:blank.

In general I think if we can get rid of the 1-Window-2-Documents cases in the platform, we should do so, so maybe we can remove

Similarly, if the nested browsing context's session history contained only one Document when the process the iframe attributes algorithm was invoked, and that was the about:blank Document created when the nested browsing context was created, then any navigation required of the user agent in that algorithm must be completed with replacement enabled.

and get Firefox to align? We'd need to test the cross-origin case first though. And even then, just briefly skimming through all the related issues, maybe it's not that simple.

domenic added a commit that referenced this issue Aug 10, 2021
For iframes: previously, we did a confusing thing where we would navigate to a non-initial about:blank. 2/3 engines instead just fire a synchronous load event at the iframe element, with no navigation. This is a bit simpler, and matches the popup case a bit better (after the below modifications).

For popups: previously, we fired the load event (but not the pageshow event) when the popup stays on the initial about:blank. 2/3 engines instead fire no events in this case, and the remaining one only fires it in the explicit "about:blank" case but not in the empty string case.

In both cases, it wasn't quite clear what to do when navigating to something like about:blank#foo or about:blank?foo. The spec now makes it clear that such cases cause URL updates of the new browsing context but not full navigations. In particular, for now at least the the initial about:blank-ness of the Document is retained. (In browsers, it seems like it's always retained for replacement vs. push purposes, but is only sometimes retained for Window object reuse purposes. #3267 tracks sorting that out.)

This also refactors the window open steps so that they have two primary branches, depending on whether to create a new window or not. Previously the steps were sorta unified, but there were a lot of consultations of the "new" variable throughout, which made it hard to understand the differences between the cases.

Closes #6863.
mfreed7 pushed a commit to mfreed7/html that referenced this issue Jun 3, 2022
For iframes: previously, we did a confusing thing where we would navigate to a non-initial about:blank. 2/3 engines instead just fire a synchronous load event at the iframe element, with no navigation. This is a bit simpler, and matches the popup case a bit better (after the below modifications).

For popups: previously, we fired the load event (but not the pageshow event) when the popup stays on the initial about:blank. 2/3 engines instead fire no events in this case, and the remaining one only fires it in the explicit "about:blank" case but not in the empty string case.

In both cases, it wasn't quite clear what to do when navigating to something like about:blank#foo or about:blank?foo. The spec now makes it clear that such cases cause URL updates of the new browsing context but not full navigations. In particular, for now at least the the initial about:blank-ness of the Document is retained. (In browsers, it seems like it's always retained for replacement vs. push purposes, but is only sometimes retained for Window object reuse purposes. whatwg#3267 tracks sorting that out.)

This also refactors the window open steps so that they have two primary branches, depending on whether to create a new window or not. Previously the steps were sorta unified, but there were a lot of consultations of the "new" variable throughout, which made it hard to understand the differences between the cases.

Closes whatwg#6863.
@hsivonen
Copy link
Member

window-reuse-in-nested-browsing-contexts.tentative.html appears to be testing the topic of this issue.

I'm trying to make Gecko's about:blank behavior more Web-compatible. The major breakthrough so far has been no longer pursuing for onload purposes what I believe to have been Presto-style initial about:blank (async onload with single about:blank document as opposed to Gecko's two about:blank documents) that I've advocated for for the past 11 years and instead pursuing WebKit/Blink-style onload behavior (single about:blank document with sync onload).

Most of window-reuse-in-nested-browsing-contexts.tentative.html is explicitly written to assume async onload for the initial about:blank. I now (tentatively) believe that we shouldn't pursue that.

What I'm currently pursuing is:

  1. The synthetic (non-parser-created) initial about:blank document is eagerly created when a browsing context is created. (I.e. for iframes when the iframe element is inserted into a document.)
  2. Don't distinguish between no src, src="" and src="about:blank" (all of these result in the URL being navigated to being about:blank).
  3. When the navigation steps are about to reach the point of starting the fetch for the URL being navigated to, if the URL is about:blank and the document in the browsing context is the synthetic initial about:blank document, fire the load event for the document that's already in the browsing context and return early without starting a fetch.

Notably, if the browsing context has been navigated away from the synthetic about:blank and is subsequently navigated to about:blank, the navigation happens as with any other URL with the parser parsing the empty stream and the load event firing async. This appears to be Chrome-compatible.

I may still find problems with the above approach.

@hsivonen
Copy link
Member

Oh, and the navigation steps begin synchronously when the iframe is inserted into the document.

@domenic
Copy link
Member

domenic commented Aug 31, 2022

Thanks @hsivonen!

We will work to incorporate that model into #6315 . In the meantime, please feel free to update any WPTs, and/or incorporate the tests that @zetafunction mentioned in the OP (from https://chromium-review.googlesource.com/c/chromium/src/+/804797), perhaps with flipped expectations. We can add a link to your comment to justify merging them despite the spec mismatch. I will happily approve such changes, and then once #6315 lands (getting close!!) the tests and spec will be aligned.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

6 participants