Specify back button history-entry skipping?
Per https://github.com/WICG/interventions/issues/21, some browsers have implemented a heuristic where pressing the back button skips certain entries in the joint session history. Generally, these are entries where there is no user interaction. The intent is to avoid "back trapping", e.g. if you arrive on a malicious site which does history.pushState() 10 times, this makes it hard to escape the site by pressing the back button.
Notably, this is different than ignoring the history.pushState() calls entirely (which is explicitly allowed). The joint session history entries still exist from the page's point of view: e.g., history.length still increases, and history.back() still goes back to them. But the user pressing the back button will do the equivalent of history.go(-11) or similar, instead of history.go(-1).
The current HTML Standard sort of allows this:
The History interface is not meant to place restrictions on how implementations represent the session history to the user.
For example, session history could be implemented in a tree-like manner, with each page having multiple "forward" pages. This specification doesn't define how the linear list of pages in the history object are derived from the actual session history as seen from the user's perspective.
... but sort of doesn't:
When the user navigates through a browsing context, e.g. using a browser's back and forward buttons, the user agent must traverse the history by a delta with a delta equivalent to the action specified by the user and the browsing context being operated on.
So at a minimum we should make it clearer that mapping the user expression to the delta might involve such considerations.
But we should probably go further than that. We should probably give more detail on which entries, exactly, will be skipped. Otherwise there are problematic interop problems, where e.g. pressing back will skip a history entry in one browser, and sites depend on it, but in other browsers that entry will not be skipped, leading to an inadvertent bad user experience.
@johannhof has mentioned that reverse-engineering Chromium's logic was a pain for Firefox (and still not shipped). And @miketaylr points out that there are many cases of "back button works in Chrome but not Firefox" bugs; my suspicion is that many of these are due to this issue (although others may be due to other history interop problems).
So even though this part of the web platform, dealing with how you translate user gestures on browser UI into web page actions, is traditionally outside the bounds of specs, in this case I think working on a specification for interoperable behavior would be appreciated. (As a compromise, I think the end result would be mostly "should"s, not "must"s.)
The bad news is that the heuristics here are still evolving. E.g. Chromium has some known bugs in our back-trapping prevention, which @shivanigithub and @creis have been looking at. So it's not even clear whether any browser has an implementation stable enough to spec. Maybe we can try to spec what we have, with the understanding that it will probably continue evolving? I just want to make sure we don't freeze this mapping in stone too early, by specifying it.
As part of this work we may also want to try coming up with more interoperable heuristics for ignoring pushState()/replaceState() calls, since that has similar user-facing effects. As introduced in https://github.com/whatwg/html/pull/999, the spec just says "Optionally, return" with no guidance.
Hi Domenic, we've run into an issue with history.pushState() in Safari on iOS, when calls are made approx 500ms after user interaction. This breaks back-button behaviour in our apps when page requests take a while to complete, and leads to some confusing behaviour for users. You seem to have some knowledge of which browsers have implemented interventions to the history API - would you mind sharing some details about which browsers are affected and any details you feel able to of their respective interventions? It's very hard to find this kind of information anywhere and it would be super useful in finding a solution that works universally. Many thanks in advance!
Not sure where to mention this, but I think it is also confusing that the context menu Back does not skip the skippable history entries the same way the toolbar back button does, which in my case causes my mouse button and backspace extension to behave differently as well - they apparently emulate the context menu back.
@domenic what do you think of a permissions API to allow an app to manipulate history entries in a 'non-skippable' way. The app would use the API and the user would be asked for permission right before the browser decides that some entry should be skipped and if the user provides the permission then the browser entries are not skipped anymore. I described other options as well here: https://issues.chromium.org/issues/330744614#comment20
I don't think this is something most browser UI teams would be interested in bothering the user about. The bar for a new permission is very high and needs to be something easily understood, like "microphone" or "camera" or "geolocation".