Re: [whatwg] Navigation and history traversal issues
On Thu, 22 Aug 2013, Andrew Oakley wrote: On 19/09/12 01:18, Ian Hickson wrote: I've changed the spec so that traversing the history by a delta always cancels any pending navigations unless you're in the middle of an unload, in which case it just aborts the algorithm entirely. I've also made back()/forward()/go() not work during the document's unload handler, since that could be used for griefing. I'm tempted to disable it entirely for all docs a la alert(), but I've no idea if that's Web- compatible and I suspect not. I assume this is where steps 3 and 4 of the traverse the history by a delta algorithm came from. Step 3. Step 4 is about canceling an existing navigation if you hit back. It's not clear from the spec which browsing context and document these steps refer to. Is it the specified browsing context and the active document of that context (I think that makes most sense)? Hm. I meant it to be the Document of the History object, but you raise an interesting point. This whole thing is rather poorly defined. I've tried to clear it up. Additionally it isn't clear which event loop the task should be associated with. I've tried to clear this up too. It turns out this wasn't taking into account per-frame process isolation, anyway. I had to introduce yet another kind of event loop. This does introduce some race conditions in UAs with more than one event loop per tab, but I don't know what we can do about that without adding in a lot of blocking during traversal, which I am guessing wouldn't be popular. Aah, ok. The spec already says that's not allowed. You can't get to the History object of a cross-origin Window: http://www.whatwg.org/specs/web-apps/current-work/#security-window (I forget what the story is if you get a History object from a same-origin Window, then have the browsing context navigated, then use the History object you kept around... I expect it is supposed to work much as if you were to call it on the new, cross-origin, History object, though.) The implication here as that you should never be able to do a history traversal of a browsing context that is not same origin (and so there is only one event loop to choose from). The story about keeping history objects around seems does not seem to be specified anywhere (so the assumption was that it should work as normal). It looks like some browsers don't let you use history objects you kept around (they should probably throw an InvalidStateError), others let you use them if the current document of the relevant browsing context is same-origin (and should probably throw a SecurityError). It's rather awkward to test this, but can we have something in the spec to prevent cross-origin history traversal? If this is not in the same section as the traverse the history by a delta algorithm can we have a note to say that this can never happen cross-origin? Well, no, not really, because it can. For example, if you have an iframe that's cross-origin, and it's navigated, and then you call your own History object's back() method, it'll actually traverse that iframe. But a History object of a non-active Document is a different matter... Looks like this will need help from WebIDL. I've filed bug 23359 and marked it as blocked on bug 23358: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23359 https://www.w3.org/Bugs/Public/show_bug.cgi?id=23358 -- Ian Hickson U+1047E)\._.,--,'``.fL http://ln.hixie.ch/ U+263A/, _.. \ _\ ;`._ ,. Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Re: [whatwg] Navigation and history traversal issues
On 19/09/12 01:18, Ian Hickson wrote: I've changed the spec so that traversing the history by a delta always cancels any pending navigations unless you're in the middle of an unload, in which case it just aborts the algorithm entirely. I've also made back()/forward()/go() not work during the document's unload handler, since that could be used for griefing. I'm tempted to disable it entirely for all docs a la alert(), but I've no idea if that's Web- compatible and I suspect not. I assume this is where steps 3 and 4 of the traverse the history by a delta algorithm came from. It's not clear from the spec which browsing context and document these steps refer to. Is it the specified browsing context and the active document of that context (I think that makes most sense)? Additionally it isn't clear which event loop the task should be associated with. Aah, ok. The spec already says that's not allowed. You can't get to the History object of a cross-origin Window: http://www.whatwg.org/specs/web-apps/current-work/#security-window (I forget what the story is if you get a History object from a same-origin Window, then have the browsing context navigated, then use the History object you kept around... I expect it is supposed to work much as if you were to call it on the new, cross-origin, History object, though.) The implication here as that you should never be able to do a history traversal of a browsing context that is not same origin (and so there is only one event loop to choose from). The story about keeping history objects around seems does not seem to be specified anywhere (so the assumption was that it should work as normal). It looks like some browsers don't let you use history objects you kept around (they should probably throw an InvalidStateError), others let you use them if the current document of the relevant browsing context is same-origin (and should probably throw a SecurityError). It's rather awkward to test this, but can we have something in the spec to prevent cross-origin history traversal? If this is not in the same section as the traverse the history by a delta algorithm can we have a note to say that this can never happen cross-origin? Thanks -- Andrew Oakley
[whatwg] Navigation and history traversal issues
On Tue, 12 Jun 2012, James Graham wrote: In particular, what stops such navigations from re-triggering the unload handler, and thus starting yet another navigation? I've updated the spec to have guards in place for 'pagehide' and 'unload'. (Not yet 'beforeunload'. Should we do that too?) It looks like the spec tries to make a distinction between navigations that are cross-origin and those that are not (step 4 in the navigating across documents algorithm); I'm not sure why this inconsistency is desirable rather than using the cross-origin approach always. Based on some tests ([1]-[5]), it seems that WebKit seems to cancel the navigation in the unload handler always, Opera seems to always carry out the navigation in the unload handler, and Gecko seems to follow WebKit in the cross-origin case and Opera in the same-origin case. In all cases the unload handler is only called once. [1] http://hoppipolla.co.uk/tests/navigation/003.html [2] http://hoppipolla.co.uk/tests/navigation/004.html [3] http://hoppipolla.co.uk/tests/navigation/005.html [4] http://hoppipolla.co.uk/tests/navigation/006.html [5] http://hoppipolla.co.uk/tests/navigation/007.html On Tue, 12 Jun 2012, Boris Zbarsky wrote: For what it's worth, we initially tried to do what you say WebKit does but ran into web compat issues. See https://bugzilla.mozilla.org/show_bug.cgi?id=371360 for the original bug where we blocked all navigation during unload and https://bugzilla.mozilla.org/show_bug.cgi?id=409888 for the bug where we changed to the current behavior. I believe the spec says what it says based on our implementation experience here... Yeah, the spec's behaviour is intentional here. The error in the spec was just that it still fired unload again. I've fixed that. On Wed, 13 Jun 2012, James Graham wrote: That seems to be true. On the other hand it appears that gecko will still respect navigation from unload even if the unload was triggered by explicit user interaction (e.g. by editing the address bar), as long as all the origins match, so you can end up at a different page to the one you expected. That is very surprising behaviour (although I see that you can argue that it is possible in other ways). When it's same origin, you really have no way to know what's going on. The page could trivially pushState() a continuously changing URL, for example, and could serve random files from the server for any URL. On Thu, 14 Jun 2012, James Graham wrote: On 06/13/2012 11:18 PM, Ian Hickson wrote: On Fri, 20 Apr 2012, Henri Sivonen wrote: * Should window.stop() really not abort the parser like the spec seems to suggest? Looks like Opera is alone with the non-aborting behavior. The spec is wrong. Can you elaborate on this? How can you tell? I presume the TC is something like !doctype html Before stop script window.stop() /script After stop Only Opera displays after stop here. We are planning to change this behaviour, so that window.stop is much more like the abort the document (I haven't yet closely studied how this interacts with the readystate and other things that Henri has been looking at). The spec now clearly requires the parser-stopping behaviour. See also this bug where I'm tracking an issue with the word cancel: https://www.w3.org/Bugs/Public/show_bug.cgi?id=16801 On Fri, 15 Jun 2012, James Graham wrote: FWIW I think the conceptually simplest solution here is for aborting the document to go through The End, so that defer scripts are run, DOMContentLoaded and load events fire, and the readyState changes in the normal way. This isn't quite like the behaviour of Gecko or WebKit today, but is spec-wise easy to understand, and hopefully no one is relying too much on specific behaviour of window.stop(). Aborting a document happens for many reasons other than stop(). For example, document.open(), navigation, the user hitting STOP, going back() in history, etc. In particular, The End can block on network, so we definitely don't want to require that UAs do that when you close a tab, for example. On Wed, 15 Aug 2012, Glenn Maynard wrote: Should this alert on initial load? !doctype htmlbody onpopstate=alert('xxx') [1] says After creating the Document object, but before any script execution, certainly before the parser stops, the user agent must update the session history with the new page. That invokes [2] update the session history with the new page, which invokes [3] Traverse the history to the new entry, which fires popstate in step 14. However, After creating the Document object, but before any script execution seems like it could happen before or after the body element has been parsed, so the alert may or may not happen. Yeah, this is an oversight as specced. Fixed. On Sun, 16 Sep 2012, Justin Lebar wrote: Suppose an attack page evil.html controls a separate frame F (e.g.
Re: [whatwg] Navigation and history traversal issues
This is all great; thanks for the quick turnaround! I've also made back()/forward()/go() not work during the document's unload handler, since that could be used for griefing. I'm tempted to disable it entirely for all docs a la alert(), but I've no idea if that's Web- compatible and I suspect not. I don't know what you mean by the last sentence here. In my tests, IE and Opera do not support cross-origin back/forward/go, if that's what you mean. I don't see any good reason for us to support that in Firefox, either, if we could get away with removing it. -Justin On Tue, Sep 18, 2012 at 8:18 PM, Ian Hickson i...@hixie.ch wrote: On Tue, 12 Jun 2012, James Graham wrote: In particular, what stops such navigations from re-triggering the unload handler, and thus starting yet another navigation? I've updated the spec to have guards in place for 'pagehide' and 'unload'. (Not yet 'beforeunload'. Should we do that too?) It looks like the spec tries to make a distinction between navigations that are cross-origin and those that are not (step 4 in the navigating across documents algorithm); I'm not sure why this inconsistency is desirable rather than using the cross-origin approach always. Based on some tests ([1]-[5]), it seems that WebKit seems to cancel the navigation in the unload handler always, Opera seems to always carry out the navigation in the unload handler, and Gecko seems to follow WebKit in the cross-origin case and Opera in the same-origin case. In all cases the unload handler is only called once. [1] http://hoppipolla.co.uk/tests/navigation/003.html [2] http://hoppipolla.co.uk/tests/navigation/004.html [3] http://hoppipolla.co.uk/tests/navigation/005.html [4] http://hoppipolla.co.uk/tests/navigation/006.html [5] http://hoppipolla.co.uk/tests/navigation/007.html On Tue, 12 Jun 2012, Boris Zbarsky wrote: For what it's worth, we initially tried to do what you say WebKit does but ran into web compat issues. See https://bugzilla.mozilla.org/show_bug.cgi?id=371360 for the original bug where we blocked all navigation during unload and https://bugzilla.mozilla.org/show_bug.cgi?id=409888 for the bug where we changed to the current behavior. I believe the spec says what it says based on our implementation experience here... Yeah, the spec's behaviour is intentional here. The error in the spec was just that it still fired unload again. I've fixed that. On Wed, 13 Jun 2012, James Graham wrote: That seems to be true. On the other hand it appears that gecko will still respect navigation from unload even if the unload was triggered by explicit user interaction (e.g. by editing the address bar), as long as all the origins match, so you can end up at a different page to the one you expected. That is very surprising behaviour (although I see that you can argue that it is possible in other ways). When it's same origin, you really have no way to know what's going on. The page could trivially pushState() a continuously changing URL, for example, and could serve random files from the server for any URL. On Thu, 14 Jun 2012, James Graham wrote: On 06/13/2012 11:18 PM, Ian Hickson wrote: On Fri, 20 Apr 2012, Henri Sivonen wrote: * Should window.stop() really not abort the parser like the spec seems to suggest? Looks like Opera is alone with the non-aborting behavior. The spec is wrong. Can you elaborate on this? How can you tell? I presume the TC is something like !doctype html Before stop script window.stop() /script After stop Only Opera displays after stop here. We are planning to change this behaviour, so that window.stop is much more like the abort the document (I haven't yet closely studied how this interacts with the readystate and other things that Henri has been looking at). The spec now clearly requires the parser-stopping behaviour. See also this bug where I'm tracking an issue with the word cancel: https://www.w3.org/Bugs/Public/show_bug.cgi?id=16801 On Fri, 15 Jun 2012, James Graham wrote: FWIW I think the conceptually simplest solution here is for aborting the document to go through The End, so that defer scripts are run, DOMContentLoaded and load events fire, and the readyState changes in the normal way. This isn't quite like the behaviour of Gecko or WebKit today, but is spec-wise easy to understand, and hopefully no one is relying too much on specific behaviour of window.stop(). Aborting a document happens for many reasons other than stop(). For example, document.open(), navigation, the user hitting STOP, going back() in history, etc. In particular, The End can block on network, so we definitely don't want to require that UAs do that when you close a tab, for example. On Wed, 15 Aug 2012, Glenn Maynard wrote: Should this alert on initial load? !doctype htmlbody onpopstate=alert('xxx') [1] says After creating the Document object, but before any script execution,
Re: [whatwg] Navigation and history traversal issues
On Tue, 18 Sep 2012, Justin Lebar wrote: This is all great; thanks for the quick turnaround! I've also made back()/forward()/go() not work during the document's unload handler, since that could be used for griefing. I'm tempted to disable it entirely for all docs a la alert(), but I've no idea if that's Web- compatible and I suspect not. I don't know what you mean by the last sentence here. In my tests, IE and Opera do not support cross-origin back/forward/go, if that's what you mean. I don't see any good reason for us to support that in Firefox, either, if we could get away with removing it. I meant blocking all scripted back/forward session history traversal while any page is running the unload algorithms. As far as cross-origin back/forward, there are 404 pages on the Web that have javascript:history.back() links; these would break for cross-origin links if we blocked cross-origin history traversal. I don't really see much point. What's the security risk? -- Ian Hickson U+1047E)\._.,--,'``.fL http://ln.hixie.ch/ U+263A/, _.. \ _\ ;`._ ,. Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Re: [whatwg] Navigation and history traversal issues
I've also made back()/forward()/go() not work during the document's unload handler, since that could be used for griefing. I'm tempted to disable it entirely for all docs a la alert(), but I've no idea if that's Web- compatible and I suspect not. I don't know what you mean by the last sentence here. In my tests, IE and Opera do not support cross-origin back/forward/go, if that's what you mean. I don't see any good reason for us to support that in Firefox, either, if we could get away with removing it. I meant blocking all scripted back/forward session history traversal while any page is running the unload algorithms. Ah, I see. I don't have any idea if that's a good idea or not, so, okay. :) As far as cross-origin back/forward, there are 404 pages on the Web that have javascript:history.back() links; these would break for cross-origin links if we blocked cross-origin history traversal. I don't really see much point. What's the security risk? The issue isn't a history.back() which crosses origins -- that seems fine -- but rather calling history.back() on a cross-origin window. (Sorry that wasn't clear.) It's not clear that this poses a security risk (otherwise, I'm sure we'd have removed it by now), aside from making it easier to tickle Firefox into buggy states like this bug [1]. But it's also not clear to me what benefit there is to being able to call back() on an arbitrary window. I guess I can navigate a window, so I might as well be able to make it go back? But those aren't quite the same thing. -Justin [1] https://bugzilla.mozilla.org/show_bug.cgi?id=737307
Re: [whatwg] Navigation and history traversal issues
On Tue, 18 Sep 2012, Justin Lebar wrote: The issue isn't a history.back() which crosses origins -- that seems fine -- but rather calling history.back() on a cross-origin window. (Sorry that wasn't clear.) Aah, ok. The spec already says that's not allowed. You can't get to the History object of a cross-origin Window: http://www.whatwg.org/specs/web-apps/current-work/#security-window (I forget what the story is if you get a History object from a same-origin Window, then have the browsing context navigated, then use the History object you kept around... I expect it is supposed to work much as if you were to call it on the new, cross-origin, History object, though.) -- Ian Hickson U+1047E)\._.,--,'``.fL http://ln.hixie.ch/ U+263A/, _.. \ _\ ;`._ ,. Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'