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

Sync navigation to a fragment identifier is probably not compatible with out-of-process iframes #3497

Closed
bzbarsky opened this issue Feb 22, 2018 · 10 comments · Fixed by #6315
Labels
interop Implementations are not interoperable with each other normative change topic: agent The interaction with JavaScript's agent and agent cluster concepts topic: navigation

Comments

@bzbarsky
Copy link
Contributor

Consider code like this:

frames[0].location.hash = "foo";
postMessage("parent", "*");

where the child has an event listener for one of the many events that https://html.spec.whatwg.org/multipage/browsing-the-web.html#traverse-the-history fires synchronously (focus, blur, popstate, hashchange, at the very least) and the event listener calls parent.postMessage("child", "*"). I believe current spec requires the "child" message to be delivered before the "parent" message in this situation. This is rather complicated if the parent and child are in different processes.

A possible fix would be to keep the sync behavior only when the thing doing the navigation and the thing being navigated are in the same unit of similar-origin related browsing contexts or something.

@domenic how does Chrome handle this with out-of-process iframes enabled?

/cc @annevk @jgraham @mystor @smaug----

@bzbarsky
Copy link
Contributor Author

Looks like https://www.w3.org/Bugs/Public/show_bug.cgi?id=17155 said the event fire async in browsers but the spec has them firing sync?

@domenic
Copy link
Member

domenic commented Feb 22, 2018

So first, the OP example doesn't quite work because hash is not exposed cross origin. You'd need to use e.g. frames[0].location.href = frames[0].location.href + "#foo" or similar, I guess.

Anyway, asking some Chrome folks, it sounds like navigation is async when it's cross-origin-domain: code here. So at least to align with Chrome, it sounds like the fix is very similar to what you propose in the OP:

A possible fix would be to keep the sync behavior only when the thing doing the navigation and the thing being navigated are in the same unit of similar-origin related browsing contexts or something.

modulo the difference between same-origin-domain and URSOBC. (URSOBC = everything that could become same-origin-domain via potential document.domain manipulations, basically.)

@bzbarsky
Copy link
Contributor Author

You'd need to use e.g. frames[0].location.href = frames[0].location.href + "#foo" or similar, I guess.

Right.

So the comments in the Chrome code are making this sound like an attempted security mitigation, which is why it's presumably stricter than URSOBC...

I suspect we could do something like that. Hopefully the "origin document" is identically defined across implementations...

@bzbarsky
Copy link
Contributor Author

One issue with Chrome's approach: if the other frame has already set document.domain but we have not yet, and we set href, then set document.domain, then set href again without returning to the event loop in between, the first set ends up async, the second ends up sync, and then we effectively "lose" that second set when the first one finally happens.

@csreis
Copy link

csreis commented Feb 22, 2018

I might be missing something here, but I don't see how this example can happen with out-of-process iframes, even with Domenic's modification. frames[0].location.href is not visible cross-origin either, and generates a DOMException if the child frame is in a different origin. It seems like any way that the parent frame could trigger a same-document navigation in the child frame would require them to be same-origin with each other, which would mean Chrome would never use an out-of-process iframe for the child. Is there an example that works with a cross-origin frame?

As for the document.domain case, Chrome uses "sites" (eTLD+1, plus scheme) as the unit of granularity for deciding when to isolate pages in different processes, such that any two origins which could become same-origin by modifying document.domain would also end up in the same process. We're looking into exceptions for this (e.g., an OriginPolicy saying that an origin won't ever change its document.domain and thus we can use a finer granularity for isolating it), but for the moment I don't think we're likely to hit the aforementioned problem with document.domain.

Sorry if I've misunderstood-- feel free to follow up if so!

@zetafunction
Copy link

zetafunction commented Feb 22, 2018

It doesn't require reading window.location. A (cross-origin) page could do something like this:

window[0].location = 'https://example.com/doc.html';
document.querySelector('iframe').addEventListener('load', () => {
  window[0].location = 'https://example.com/doc.html#hash';
});

However, even this is OK and doesn't result in a behavior difference between cross-origin but in-process frames vs out-of-process iframes.

As for the document.domain example highlighted, it feels a bit odd to do a check based on URSOBC (though this would be compatible with OOPIF still). Personally, I don't really see losing the second navigation as that bad: don't we have this issue with fragment navigations in general, if there are async navigations in progress?

@bzbarsky
Copy link
Contributor Author

As @zetafunction said, you can trigger a same-document navigation cross-origin easily. You just have to know what the URL already loaded there is. For example, because you loaded it to start with. Cross-origin sets of location.href work fine.

As for the document.domain thing, the issue is that Chrome currently does the navigation either sync or async based on document.domain bits, not just processes. Please see the code @domenic linked to.

don't we have this issue with fragment navigations in general, if there are async navigations in progress?

Yes. But usually you have some idea whether your navigation is going to be sync or not....

@rniwa
Copy link

rniwa commented Feb 22, 2018

We know for a fact not doing same-origin fragment navigation breaks major sites such as Gmail because we accidentally broke it recently.

I think making all fragment navigation asynchronous is a non-starter.

@zetafunction
Copy link

Presumably you do know at least somewhat, since you're mucking around with document.domain, which is going to change what browsing contexts you're same origin to. To me, it doesn't seem worth the complexity of using URSOBC.

@bzbarsky
Copy link
Contributor Author

@rniwa Same-origin bits definitely have to be sync, I agree

@zetafunction I agree that a same-origin-domain happens to be simpler to implement in this case, at least in Gecko, largely because we don't have a "source document" available there. If we did, it's all about the same, since every document knows its URSOBC in Gecko.

@annevk annevk added normative change interop Implementations are not interoperable with each other labels Feb 23, 2018
@annevk annevk added the topic: agent The interaction with JavaScript's agent and agent cluster concepts label Feb 26, 2019
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 normative change topic: agent The interaction with JavaScript's agent and agent cluster concepts topic: navigation
Development

Successfully merging a pull request may close this issue.

6 participants