html
html copied to clipboard
Define when navigator and location objects are replaced
The document
attribute returns the associated Document and the history
attribute is defined in terms of that as well.
For navigator
and location
, there isn't anything to suggest that these objects are replaced when navigating an iframe.
https://jsbin.com/jagiruh/edit?html,output is a test that passes in Chrome, Firefox and Safari, showing that a bunch of objects are replaced when navigating an iframe. It fails with "Permission denied" in Edge.
Is this all entangled with the reasons that WindowProxy exist, perhaps?
This came up in https://codereview.chromium.org/2805763004 where I wanted to propose something like an "is active" check for the navigator object.
Yeah, WindowProxy typically persists, but its underlying Window object definitely changes upon navigation (unless initial about:blank).
The spec here is pretty bad for Navigator:
The navigator attribute of the Window interface must return an instance of the Navigator interface, which represents...
It could be any instance! The entire browser could share a single instance!! Ugh.
For Location it's much better:
Each Window object is associated with a unique instance of a Location object, allocated when the Window object is created.
and Window objects are created explicitly during navigation and other scenarios.
I wrote this test:
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe></iframe>
<script>
async_test(t => {
const frameLocation = self[0].location,
frameNavigator = self[0].navigator,
frameHistory = self[0].history
self[0].frameElement.onload = t.step_func_done(() => {
assert_equals(self[0].location, frameLocation)
assert_equals(self[0].navigator, frameNavigator)
assert_equals(self[0].history, frameHistory)
})
self[0].location.href = "/common/blank.html"
})
</script>
Note that History objects are technically associated with documents, so it should change here, but in Firefox it does not and it passes the test. However, in Chrome and Safari I get
assert_equals: expected [stringifying object threw TypeError: Cannot convert object to primitive value with type object] but got object "http://web-platform.test:8000/common/blank.html"
which I'm not sure what to do with.
@foolip @cdumez any clues why the above test throws a TypeError? It looks perfectly reasonable to me.
It looks like the exception is thrown when test harness tries to construct a string for the error message from frameLocation. All those assertions fail in WebKit, we do not return the same objects after the navigation. Therefore, frameLocation is in a state where it throws when trying to do whatever the test harness does.
That seems like a bug in itself as everything is same-origin and nothing would end up throwing like that (it seems to simply try to ToString it). And not returning the same object is a bug too. I guess all this is even less interoperable than I thought.
I believe our location object relies on having a frame to be useful. However, after the navigation, it seems we create a new Location object and the old one ends up being frameless.
If I reduce my above test to just Navigator
objects I get different objects in Chrome and Safari, I get "Permission denied" in Edge, and I get a pass in Firefox.
I think what Firefox does makes the most sense, unless we really want to tie all these objects to Document
objects while they are accessed from the Window
object...
Created a test for window.location
with @domenic's excellent per-global framework: https://github.com/w3c/web-platform-tests/pull/5778. All browsers are different. 🎉
- https://bugzilla.mozilla.org/show_bug.cgi?id=1361975
- https://bugs.chromium.org/p/chromium/issues/detail?id=718363
- https://bugs.webkit.org/show_bug.cgi?id=171648
- https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11895829/
Once we make progress there I'd propose we make History and Navigator per-global as well.
Wow, thanks for the excellent research! I've merged https://github.com/w3c/web-platform-tests/pull/5778, do you think we should wait for that to be sorted out implementation-side before doing anything further here?
Yeah, I'm wondering whether Chromium and WebKit have architectural reasons for having almost everything associated with Document
objects. If they're okay with moving some things to the global object I think we're good, but if not, we probably want to reconsider a couple of things before pushing this further.
I'm not sure either. A change like this might end up with @tkent-google to ponder, WDYT?
I guess fixing this is not easy due to out-of-process iframe.
I'm actually now confused how it can work securely to associate things with the global object. If you grab a reference to this associated thing and then navigate the initial about:blank frame cross-origin, what happens? @bzbarsky how does Firefox handle that?
If you grab a reference to this associated thing and then navigate the initial about:blank frame cross-origin, what happens?
The same thing that happens when you navigate any other page cross-origin. You get a new global object, with a different origin from the old global object. The things you were holding on to are still referencing the old global object. Do you have a specific scenario or API you're concerned about?
Thanks, I had just missed that replacement only happens same-origin. @tkent-google so what exactly is the issue with out-of-process child documents then?
I don't have an idea of exact technical issues at this moment. It's just a guess, and I don't think fixing it is impossible.