Rationalize behavior of Window object reuse
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
Windowobject 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 theWindowobject is not reused). Firefox also appears to perform the navigation with replacement enabled, but also reuses theWindowobject.
I only looked at the tests so far, but I would recommend also taking a look at:
- Differences in behavior when the iframe is dynamically added before/after the load event. I think there are interop differences here.
- 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
See also #2545 and #2566.
#546 is also somewhat related.
In https://github.com/web-platform-tests/wpt/pull/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.
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:
- The synthetic (non-parser-created) initial
about:blankdocument is eagerly created when a browsing context is created. (I.e. for iframes when the iframe element is inserted into a document.) - Don't distinguish between no
src,src=""andsrc="about:blank"(all of these result in the URL being navigated to beingabout:blank). - 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:blankand the document in the browsing context is the synthetic initialabout:blankdocument, fire theloadevent 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.
Oh, and the navigation steps begin synchronously when the iframe is inserted into the document.
Thanks @hsivonen!
We will work to incorporate that model into https://github.com/whatwg/html/pull/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 https://github.com/whatwg/html/pull/6315 lands (getting close!!) the tests and spec will be aligned.