html
html copied to clipboard
Iframe session history for JS-created iframes
A detail in https://github.com/whatwg/html/pull/6315 doesn't match what browsers currently do. Specifically the step 12 details in https://whatpr.org/html/6315/document-sequences.html#create-a-new-nested-navigable, but it will also impact other areas if we want to match browsers.
Let's say the following navigations take place:
- Top level
/a
- Navigate to
/a#foo
- Navigate to
/a#bar
- Add an iframe
/i-1
The model in https://github.com/whatwg/html/pull/6315 creates:

Whereas it seems like browsers create:

This difference is observable if:
- Go -1 (to step 2)
- Navigate iframe to
/i-2
The model in https://github.com/whatwg/html/pull/6315 would result in:

This is observable, because after your 'push' navigation, going back will take you to the previous page in the iframe.
However, it seems like browsers actually do this:

Meaning the /i-1
entry is gone.
It gets worse
Let's say the following navigations take place:
- Top level
//
, contains iframe-1//1-a
- Navigate iframe-1 to
//1-b
- Add iframe-2
//2-a
- Navigate iframe-2 to
//2-b
(I'm avoiding top-level navigations here to avoid 'the bug' where children don't navigate if the top-level navigates)
Following the theory above, browsers end up with this:

If you go back, iframe-2 goes back to //2-a
, then if you go forward, iframe-2 goes to //2-b
. All browsers do that. So far so good.
But what if we go -2, from step 3 to step 1?
Chrome and Firefox: iframe-2 stays on //2-b
.
Safari: Reloads the page from the server.
Seems like Safari is having a panic. Chrome and Firefox seem to go "there's no history entry for the iframe at this step, so do nothing".
But if we go from step 3 to step 1, then to step 2?
Chrome: iframe-2 navigates to //2-a
.
Firefox: iframe-2 stays on //2-b
.
Firefox's behaviour here is mad. Whether step 2 navigates the iframe to //2-a
depends on the direction you approach it.
I don't think we should (or even could?) spec what Firefox or Safari do here.
We could spec what Chrome does. It would mean get the target history entry could return null, eg getting the history for iframe-2 and step 1. In that case, we wouldn't attempt to change the current or active history entry for that navigable, and just let it get out of sync with the timeline.
Or, we could stick with what we've got, and expect browsers to align with it. I think our model makes more sense. It prevents things getting out of sync, and it ensures that if you 'push' navigate an iframe forward, then its previous entry is 'back'.
What does Add an iframe /i-1
mean? Is i
here some index or is /i-1
just an id or what?
And last time checked, all the browsers have list of trees model for entries. They may implement it in a bit different ways. List of trees is the model I'd expect in the spec eventually.
Firefox model there is to compare trees and it needs to find differing entries to load something.
Sorry, by "add an iframe" I mean "create an iframe with JavaScript and connect it to the document". /i-1
is the URL it's displaying.
The reproduction steps for
- Top level
//
, contains iframe-1//1-a
- Navigate iframe-1 to
//1-b
- Add iframe-2
//2-a
- Navigate iframe-2 to
//2-b
are:
- Go to https://static-misc-3.glitch.me/iframe-history-weirdness/
- Navigate the iframe to "page 2"
- Press "add another iframe"
- Navigate the new iframe to "page 2"
Now you can test moving between steps. Notably, go(-2)
puts things in a new state (first iframe on page 1, second iframe on page 2).
The reason I'd rather stick with the model in https://github.com/whatwg/html/pull/6315 rather than what Chrome (and Firefox-ish) does here, is the model in https://github.com/whatwg/html/pull/6315 tries to ensure that going back twice will put you in the same history state as go(-2)
. But that isn't what browsers are doing here.
In case it isn't clear, I did a little video to explain the issue https://www.youtube.com/watch?v=F-E6GmuQxOg
the model in #6315 tries to ensure that going back twice will put you in the same history state as
go(-2)
. But that isn't what browsers are doing here.
I would really, really like to preserve the invariant you're gesturing at. I hope we can get browsers to agree on that, even if everyone has to change!
To be clear, there are actually two invariants that all browsers are violating in this scenario, and which I'd really like preserve:
-
go(n)
followed bygo(m)
ends up likego(n + m)
- Traversals must never create a state which the user has never seen before. They must instead traverse to some already-seen state.
#6315 does guarantee these, so we know it's possible!
While we do use a list of trees in Chrome, we've started sharing the frame-specific session history items (which we call FrameNavigationEntries) across the joint session history items (which we call NavigationEntries), in cases where a given frame doesn't change between NavigationEntries. That might make it relatively easier for us to make the change @jakearchibald is proposing here, compared to before.
The description is basically correct-- the earlier NavigationEntries had a tree that didn't include the dynamically added iframe (since it didn't exist at the time), and Chrome simply doesn't navigate a frame if it can't find a history item corresponding to it.
I wouldn't necessarily be opposed to retroactively updating the previous NavigationEntries (trees) to know about dynamically added iframes, even if it might be a bit tricky to find all the entries that need updating.
Traversals must never create a state which the user has never seen before. They must instead traverse to some already-seen state.
204 / Content-Disposition
can create that situation, but I think that's 'ok' because it's the server's choice to have done that.