whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.03k stars 2.63k forks source link

Should navigating to the current URL preserve history.state? #6213

Open domenic opened 3 years ago

domenic commented 3 years ago

Spinning off from https://github.com/whatwg/html/issues/6197#issuecomment-742743387.

Test page: https://boom-bath.glitch.me/session-history-2.html

  1. Press "Set history.state to 'foo'"
  2. Then press any of the other three four buttons/links.

Results:

Also of note: both browsers agree that overall document scroll position is retained on location.reload() and cleared otherwise. Chrome, however, clears the textbox scroll position and its value (if updated) in all cases, including location.reload(); Firefox retains those pieces of data in location.reload().

At a spec level, discussed a bit in https://github.com/whatwg/html/issues/6197#issuecomment-742711556, we have several distinguishable actions:

and we have the following potential behaviors:

So from I can see:

I'm not sure what the best behavior is here. At a spec level, the simplest thing would be (B) for all cases (X), (Y), and (Z), but that matches no browsers. This is all complicated by the fact that the other pieces of a session history entry are harder to test and in some cases not totally specified.

/cc @natechapin @smaug----

domenic commented 3 years ago

In another venue @rakina notes that Chrome's comparison is between does not compare the URL that started the navigation with the browsing context's active document's URL, but instead compares the final URL of the navigation to the browsing context's active document's URL. Also, Chrome ignores fragments.

Fortunately, after https://github.com/whatwg/html/pull/6476, the red XXX box talking about sometimes copying over the state also compares the final URL (i.e., the URL of newDocument) to the browsing context's active document's URL (i.e., the URL of sessionHistory's current entry that is about to be replaced). So the XXX box is getting closer to at least one browser, which is nice.

rakina commented 3 years ago

I tested this out with a modified version of Domenic's test above. I checked history.state, history.scrollRestoration, and the scroll offset of the viewport/tab and textarea

No fragment (sameurl.html -> sameurl.html) Same-URL nav type Creates new document Keeps history.state, history.scrollRestoration Keeps viewport scroll offset Keeps textarea scroll offset
location.reload() Chrome, Firefox, Safari Chrome, Firefox, Safari Chrome, Firefox, Safari Firefox
location.href = location.href Chrome, Firefox, Safari Chrome, Safari
location.replace(location.href) Chrome, Firefox, Safari Chrome, Safari
URL bar enter Chrome, Firefox, Safari Chrome, Safari Chrome
Browser UI reload Chrome, Firefox, Safari Chrome, Firefox, Safari Chrome, Firefox, Safari Firefox

With fragment (sameurl.html#bottom -> sameurl.html#bottom)

Same-URL nav type Creates new document Keeps history.state & history.scrollRestoration Keeps viewport scroll offset Scrolls to fragment Keeps textarea scroll offset
location.reload() Chrome, Firefox, Safari Chrome, Firefox, Safari Chrome, Firefox, Safari Firefox
location.href = location.href Chrome, Safari Chrome, Firefox, Safari
location.replace(location.href) Chrome, Safari Chrome, Firefox, Safari
URL bar enter Chrome, Safari Chrome, Safari Chrome Firefox, Safari
Browser UI reload Chrome, Firefox, Safari Chrome, Firefox, Safari Chrome, Firefox, Safari Firefox

Reloads

Same-URL navigations

General

I think since Chrome & Safari behaves the same way, maybe it makes sense to copy history.state & history.scrollRestoration on same-URL navigations. Or maybe it's weird to do that but not restore the scroll offsets too?

domenic commented 3 years ago

Thanks for continuing the investigation Rakina!

Not sure how to check the other session history attributes. I guess browsing context name can be checked via WPT, but that seems to have its own set of problems.

I think we should spend a small amount of time on this. If writing tests is easy, and it aligns well with other pieces of state (e.g. is always copied over in the same way as history.state), then let's try to make sure it's specced and tested well. If it's too hard to write tests for, or gives weird results due to how it interacts with other parts of the system, then we can set it aside.

I think since Chrome & Safari behaves the same way, maybe it makes sense to copy history.state & history.scrollRestoration on same-URL navigations.

Agreed.

Or maybe it's weird to do that but not restore the scroll offsets too?

Could we restore the scroll offsets too? Everyone seems to restore the viewport scroll offset according to your table above. And we can say that the lack of consistency for textarea scroll offset restoration is down to the "may" clause in https://html.spec.whatwg.org/multipage/browsing-the-web.html#restore-persisted-state .


Note that once we get this more solid, especially if we copy over most (all?) of the fields, the next natural question will be whether we should maintain the distinction between the "entry update"/"reload" case and the "replace" case in https://html.spec.whatwg.org/#update-the-session-history-with-the-new-page . Maybe we can merge them. But figuring out the full consequences of that will be tricky, so my instinct is to wait and tackle it after we improve the spec for the "replace" case, like we're doing here.

domenic commented 3 years ago

@rakina, do you think you'd be able to help with web platform tests for this? I'd love to get this spec issue resolved.

jakearchibald commented 3 years ago

Here's another fun one:

  1. Navigate to /a.
  2. pushState({}, '', '').
  3. Browser UI reload, but server redirects to /b.

Chrome, Safari, Firefox: history.state is null.

  1. Navigate to /a.
  2. pushState({}, '', '').
  3. Browser UI reload, but server redirects to /a.

Firefox: history.state is null. Chrome, Safari: history.state is retained.

  1. Navigate to /a#foo.
  2. pushState({}, '', '').
  3. Browser UI reload, but server redirects to /a (the hash is still retained in the browser).

Firefox, Safari: history.state is null. Chrome: history.state is retained.

The correlates with the findings in https://github.com/whatwg/html/issues/6680, including "I have no idea what Safari is doing".

samthor commented 3 years ago

Chiming in with another oddity with Firefox around events. (I'm not sure this needs a whole new bug although happy to file one.)

Demo here, for the "With fragment" case above (re: @rakina's demo). When a fragment is loaded, the two internal navigations (location.href = location.href & location.replace(location.href))—plus user-driven self navigation via clicking a link—have different outcomes:

smaug---- commented 3 years ago

I do see popstate in Firefox when clicking a link.