Back / forward navigation behaves inconsistently around pushState |
||||||
Issue descriptionChrome Version: 58.0.3029.110 (64-bit) OS: OSX 10.12.14, Ubuntu 14.04.5 LTS, and Android 7.1 What steps will reproduce the problem? 1. Open a new tab 2. Visit http://danyao.github.io/nav_tester/nav_tester.html 3. Click on "history.pushState()" 4. Type in any URL in the URL bar to navigate away from the page (e.g. "www.google.com") 5. Click on Back arrow to navigate back 6. Click on Back arrow again 7. Click on Back arrow a 3rd time (you should be on NTP now) 8. Click on Forward arrow to navigate forward 9. Click on Forward arrow again 10. Click on Forward arrow a 3rd time (you should be on www.google.com now) What is the expected result? You should see the same page at step 5 and step 9 due to symmetry of navigation. The visible URL should be 2.html?q=history-pushState. Similarly, you should see the same page at step 6 and step 8. The visible URL should be nav_tester.html. What happens instead? In step 5, visible URL is 2.html?q=history-pushState, and the web content is 2.html. In step 9, visible URL is 2.html?q=history-pushState, but the web content is nav_tester.html (the page with the buttons). In step 6, visible URL is nav_tester.html, but the web content is 2.html. In step 8, visible URL is nav_tester.html, and the web content is nav_tester.html (the page with the buttons). Safari and Firefox behave differently from one another, but both are internally consistent: - Firefox renders nav_tester.html for steps 5, 6, 8, 9 regardless of the visible URL. - Safari renders 2.html for steps 5, 6, 8, 9 regardless of the visible URL if the URL entered in step 4 is from a different origin. - Safari renders nav_tester.html for steps 5, 6, 8, 9 regardless of the visible URL if the URL entered in step 4 is from the same origin (e.g. danyao.github.io/nav_tester/1.html). Video for bug repo in Chrome: https://drive.google.com/open?id=0BzYTRLFDi_IYSUVGcmkxVTZXdk0 Video for Firefox: https://drive.google.com/open?id=0BzYTRLFDi_IYZjByNkpER21Vek0 Video for Safari (different origin in step 4): https://drive.google.com/open?id=0BzYTRLFDi_IYS3BtUmVnSzJmcms Video for Safari (same origin in step 4): https://drive.google.com/open?id=0BzYTRLFDi_IYaWV5djJrUjlHOUk
,
May 18 2017
,
May 19 2017
,
May 19 2017
Step 5 does what I would expect-- it loads 2.html since that's what the URL says to load, rather than where the user was at the time. Step 6 is very surprising to me, since we should end up on nav_tester.html and not stay on 2.html. Is there some spec language describing what the intended behavior here is? I'm not sure I agree with the expectations mentioned in the bug, but I could be wrong.
,
May 23 2017
Thanks for looking into this. I agree that the observed behavior for step 5 is not unreasonable. However, it seems unintuitive to me that step 5 and step 9 render different pages, given that the displayed URLs in these two steps are the same. As best as I can tell, the spec is ambiguous about what should be rendered in this case. But I'm fairly new to HTML standard so I could be missing things. Here are the relevant language from HTML Standard 7.7.2 The History Interface (https://html.spec.whatwg.org/multipage/browsers.html#the-history-interface): 8. If the method invoked was the pushState() method: 8a.1. Remove all the entries in the browsing context's session history after the current entry. If the current entry is the last entry in the session history, then no entries are removed. NOTE: This doesn't necessarily have to affect the user agent's user interface. 8a.2. Remove any tasks queued by the history traversal task source that are associated with any Document objects in the top-level browsing context's document family. 8a.3. If appropriate, update the current entry to reflect any state that the user agent wishes to persist. The entry is then said to be an entry with persisted user state. 8a.4. Add a session history entry entry to the session history, after the current entry, with serializedData as the serialized state, the given title as the title, new URL as the URL of the entry, and the scroll restoration mode of the current entry in the session history as the scroll restoration mode. 8a.5. Update the current entry to be this newly added entry. Otherwise, if the method invoked was the replaceState() method: 8b.1. Update the current entry in the session history so that serializedData is the entry's new serialized state, the given title is the new title, and new URL is the entry's new URL. 9. If the current entry in the session history represents a non-GET request (e.g. it was the result of a POST submission) then update it to instead represent a GET request. 10. Set document's URL to new URL. NOTE: Since this is neither a navigation of the browsing context nor a history traversal, it does not cause a hashchange event to be fired. ===== It's not clear to me whether "update the current entry to be this newly added entry" in step 8a.5 is limited to just updating the history object or also updating the page being displayed. Step 10 seems similarly ambiguous to me. It seems that Firefox has taken the first interpretation and Safari has taken the latter. It's a little bit weird that for the same session, Chrome behaves like Safari when traversing history backwards, but when traversing history forward, behaves like Firefox. I think we should at least be consistent. The final NOTE seems to support the interpretation of not loading the page since "this is *neither* a navigation ... nor a history traversal". However, this leaves unclear what should happen when user uses history traversal to go back to this page later, which is the scenario described in this bug.
,
May 23 2017
+Domenic, the spec owner I know.
,
May 24 2017
Scoping down to just one component to avoid falling into the trap of responsibility diffusion. Based on the comments, it seems that UI>Browser>Navigation is the better fit for now. Please, bounce back if this is incorrect.
,
Sep 20
I've tested https://play.stillmuchtoponder.com/nav_tester/nav_tester.html (origin URL redirects) in Edge 18.17741 Insider Preview, made a screencast: https://www.youtube.com/watch?v=brzs-bgiihw The behavior of URLs and visible content aligns with Chrome. In the forward direction, the web contents is nav_tester.html even after the URL changes to 2.html?q=history-pushState (per spec, I'm virtually certain) and in the backwards direction 2.html is loaded and its contents shown even as the URL changes back to nav_tester.html. Looks like Safari also matches if behavior in the "Video for Safari (different origin in step 4)" case. It seems likely to me that this comes down to differences due to page cache, a.k.a. bfcache. Firefox is probably keeping the page in memory and just switching it back in. Saving some state in JS and making the rendering depend on it would confirm this. For Safari, the difference between same-origin and cross-origin navigation makes it seem likely that it's also a difference between continuing to use the same WebKit instance (same-origin) or starting a new one (cross-origin), and thus whether the original page needs to be reloaded or is already available. This test case is one where the page made no attempt to make sure the state of the page matches the URL. When this happens on real pages, as I'm sure it does, it's not just an interop problem but also a user can't reload the page or bookmark it and get back to what they are looking at. To fix the inteorp problem here I think would require aligning on bfcache behavior, which would be a very large investment. domenic@, do you know of other problems caused by bfcache differences, and is there anything that could be done about it? |
||||||
►
Sign in to add a comment |
||||||
Comment 1 by danyao@chromium.org
, May 18 2017