Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new mode for iframes where they don't add to the joint session history? #6501

Open
domenic opened this issue Mar 16, 2021 · 22 comments
Open

Comments

@domenic
Copy link
Member

domenic commented Mar 16, 2021

This has come up several times in discussions with @annevk, @csreis, @natechapin, @smaug----, @jakearchibald, and others.

Basically, iframes participating in the joint session history can be problematic for web developers and for users. It means iframes can change the meaning of history.back() and the back button. Although this is sometimes a good thing, in terms of allowing the user to transition between multiple states, in other cases it's not desired, such as when iframing ads or other untrusted third-party code.

Even the app history proposal cannot tame this fully, because it wants to stay based on top of joint session history so as to avoid creating a whole parallel incompatible model. It tries to make things better, by saying that appHistory.back() is guaranteed to move your own frame back (and not just some subframe or parent frame), but that leads to a problem (WICG/navigation-api#73) where appHistory.back(); appHistory.forward() can take you to a different joint session history entry than the one you started, in a way that is visible in terms of what subframes are showing.

So I'm wondering if we can approach this from a different angle, of allowing web developers to say that a given frame cannot impact joint session history. In particular, I'd propose that this makes all navigations within the frame "replace" navigations. (This is what is currently planned for prerendering and portals, although those are early-stage.) We could also contemplate removing the history.back/forward/go() APIs in such frames since they would only be able to mess with the parent, and maybe we don't want to allow such parent-messing.

If people think this is a good idea, the biggest question is whether the embedded content would need to opt-in (e.g., in the style that document policy requires) or whether we could allow the embedder page to impose this on the embeddee without consent (like sandbox="" does).

@jakearchibald
Copy link
Collaborator

jakearchibald commented Mar 17, 2021

I figured we might need to solve this problem so I've been designing for it in #6315, but it's a little different to what you're proposing here.

I have:

  • Navigable - A thing that can be navigated, such as a top-level page or an iframe.
  • Traversable - A navigable that hold session history and manages the traversal of itself and its nested navigables.

Currently, browsers only treat the top-level as traversable, but the model will allow for traversables to be nested.

However, in this model, nested traversables would have their own session history of multiple entries, rather than have everything turned into "replace" navigations. These history entries wouldn't be traversable via the back/forward buttons, but could be traversed using history.back() and the new appHistory APIs.

Since the top-level doesn't store the session history of nested traversables, their session history wouldn't be repopulated after going back to the page (unless of course the previous page was kept alive in bfcache).

The benefit of this is you wouldn't need to remove history.back() from these contexts, since it'd only impact the session history of the nearest traversable, not the top level. Also, we wouldn't need to make it opt-in, since the behaviour would be normal, just sandboxed.

The downside is it might be more complicated to implement, but I'm not sure.

@annevk
Copy link
Member

annevk commented Mar 17, 2021

I think what @jakearchibald outlines matches what we wanted for #763 (see also the very long discussion at WICG/webcomponents#184). A good initial test for this feature might be deploying it for shadow iframes.

cc @rniwa

@domenic
Copy link
Member Author

domenic commented Jul 23, 2021

What is the appropriate model for how to impose this? I can think of a few options:

  • Global page-wide <meta>, applies to all descendant iframes
    • Descendant iframes cannot opt out; this is imposed on them
  • Document policy, applies to all descendant iframes
    • Descendant iframes need to agree to this via the Sec-Required-Document-Polic / Document-Policy dance
    • Seems not very useful as you'd have to convince all your descendants to agree
  • Permissions policy for "contribute to history", which by default is allowed for all iframes
    • You can disable per-iframe using allow="nested-history 'none'"
    • You can disable it for everything on your page using the HTTP header Permissions-Policy: nested-history=()
    • You can disable it for non-same-origin iframes using the HTTP header Permissions-Policy: nested-history=(self)
    • But there's no in-markup way of disabling it for all descendant iframes which is a bit sad
  • A new sandboxing behavior
    • By default sandboxed iframes do not contribute to history
    • They can be allowed to by sandbox="allow-nested-history"

Of these permissions policy seems pretty good...

@annevk
Copy link
Member

annevk commented Jul 26, 2021

Yeah, although I'm not a big fan of opt-in for third parties, I agree it fits here. (And perhaps we can switch the default over time.)

cc @clelland

@domenic
Copy link
Member Author

domenic commented Sep 8, 2021

Some implementation discussions among the Chrome team revealed that implementing the variant of this that @jakearchibald proposes, with independent session histories for the iframes, would be quite difficult. Also, it could be confusing for users in cases involving iframe restoration.

The proposal we're favoring now is to have this make every navigation in the iframe a "replace" navigation. This still meets the use cases I'm most concerned with, which is letting pages ensure that iframes don't add entries to the joint session history.

Does that still sound reasonable to Mozilla folks? /cc @smaug----

@annevk
Copy link
Member

annevk commented Sep 8, 2021

What happens with a nested history.back()? In scenarios where this is forced upon nested documents that still functioning could end up breaking things in unexpected ways.

I'm also still wondering if we could make this the default for shadow tree nested documents to solve the leakage problem there.

@domenic
Copy link
Member Author

domenic commented Sep 8, 2021

Yeah, it could indeed have that impact. I think that's OK?

I remain doubtful that it would be web-compatible to change how shadow trees work, but we'd welcome anyone running an experiment to prove us wrong.

@annevk
Copy link
Member

annevk commented Sep 8, 2021

In the case where this is a policy you force upon descendants you probably would not want any descendant to be able to navigate away from you again. It seems quite easy for a descendant to have some pushState() + back() logic that would break the top once run.

@domenic
Copy link
Member Author

domenic commented Sep 8, 2021

Oh, I see, because back() would still operate on the joint session history. Yeah, I agree that would be bad.

Were you thinking we would just make back() etc. a no-op in these frames?

@annevk
Copy link
Member

annevk commented Sep 9, 2021

I was wondering what you all were thinking since @jakearchibald hinted at this being a problem in #6501 (comment), but no-op would work I think.

@jakearchibald
Copy link
Collaborator

@domenic

Also, it could be confusing for users in cases involving iframe restoration.

In what way?

@domenic
Copy link
Member Author

domenic commented Sep 9, 2021

I think I was making a few logic jumps there that are not, in retrospect, justified. In particular I was thinking "having to save/restore/sync for these independent session history lists would be a lot of work to spec/implement, so, we should probably just not do so and restore from the src="" attribute. Which would lead to a confusing user experience." But yes, if we went to the trouble of implementing and speccing not only independent session history lists, but also saving/restoring them, then I think there are no user experience problems.

So this is mostly a question as to whether the web developer experience gains of allowing non-replace navigations/nontrivial session history in these scenarios is worth the implementation cost. I think the Chrome history engineers' position is that it is not worth it, and I tend to agree.

@domfarolino
Copy link
Member

So this is mostly a question as to whether the web developer experience gains of allowing non-replace navigations/nontrivial session history in these scenarios is worth the implementation cost. I think the Chrome history engineers' position is that it is not worth it, and I tend to agree.

Expanding on the Chrome implementation position, I think what's really hard to implement is actually full separation of top-level and iframe session history in general. So even if we had replacement-only session history model in iframes, I think what would be really challenging is (a) the back button restoration stuff mentioned earlier, (b) Divorcing the history entirely (i.e., not sharing history.length, having history.back() in an iframe not influence the top-level page). I think that's the tricky part. That is, the implementation complexity is agnostic to the triviality of the session history model in the iframe, but is related to the completeness of the separation in general. I'd like @csreis to correct me though if I am wrong.

@domenic
Copy link
Member Author

domenic commented Sep 9, 2021

So even if we had replacement-only session history model in iframes, I think what would be really challenging is (a) the back button restoration stuff mentioned earlier, (b) Divorcing the history entirely (i.e., not sharing history.length, having history.back() in an iframe not influence the top-level page).

Hmm, my impression was different. (a) restoration gets pretty easy if we only have to serialize "current URL" for the iframe, and not an entire history list? Maybe? (b) history.length never changes due to replacement, only due to push. And we can just add a check (e.g. in the renderer process) to bail out for history.back(), etc. in these iframes.

@domfarolino
Copy link
Member

history.length never changes due to replacement, only due to push

But it is still shared. If A.com embeds B.com and C.com where both B and C are sibling iframes, and C.com navigates a million times, I think all of these are observable by A.com and B.com via history.length.

Maybe this is too implementation-specific or "deep" but the question seems to be which one do we want:

  • Iframes with a fully separated history from the top-level page, and their history model just happens to be replace-only for simplicity. history.length always naturally returns 1
  • Normal joint session history between the top-level page and the iframe, but just patch all of the ways that the iframe could interact with the joint session history that are observable to the top-level page and vice-versa (i.e., replacement-only, manually patching history.length, more?)

The distinction between the two might be overly-pedantic from a spec perspective; if that's the case then sorry.

Hmm, my impression was different. (a) restoration gets pretty easy if we only have to serialize "current URL" for the iframe, and not an entire history list? Maybe?

That's quite possible! I'm not totally sure so I delegate to Charlie. From an impl POV it does seem like if we don't actually require fully separated iframe history but still have it operate on the same underlying objects (just in a replacement-only manner and with manually patched i.e., history.length), then yeah we'd get today's iframe restoration logic for free I think. Anyways if this comment is getting us off-track, my apologies.

@domenic
Copy link
Member Author

domenic commented Sep 9, 2021

But it is still shared. If A.com embeds B.com and C.com where both B and C are sibling iframes, and C.com navigates a million times, I think all of these are observable by A.com and B.com via history.length.

What I was getting at was if the million C.com navigations are with replacement, then history.length will not change.

But I agree it provides a communications channel. E.g. if B is in replace-only mode and C is not, then B and C can communicate by B observing history.length and C modifying it.

We could manually patch this out by making history.length return 1 in such replace-only iframes. Probably that's a good idea anyway since we're saying history.back() will no-op; it seems like those two should stay in sync, generally.

If we do such patching, then I hope the two are equivalent. But I agree they're conceptually different, and there might be things we miss patching which could make them diverge :-/.

@domfarolino
Copy link
Member

Yeah, that is the only thing I am worried about, basically a slightly incomplete audit and therefore incomplete manual patches.

Side-question:

Yeah, although I'm not a big fan of opt-in for third parties, I agree it fits here. (And perhaps we can switch the default over time.)

Do we think that we could ever actually make this the default behavior without requiring all content to opt-in to being framed? Right now content can opt-out of being framed and we're discussing the possibility of making all framed content have this replacement-only model in the future. This is a big pretty big behavior difference between a given site operating in a top-level context and the same site operating in a framed context, so I feel like this would just require turning the opt-in for this new replacement-only model into an opt-in for "You're being placed in an iframe".

@csreis
Copy link

csreis commented Sep 9, 2021

Hmm, my impression was different. (a) restoration gets pretty easy if we only have to serialize "current URL" for the iframe, and not an entire history list? Maybe?

That's quite possible! I'm not totally sure so I delegate to Charlie. From an impl POV it does seem like if we don't actually require fully separated iframe history but still have it operate on the same underlying objects (just in a replacement-only manner and with manually patched i.e., history.length), then yeah we'd get today's iframe restoration logic for free I think. Anyways if this comment is getting us off-track, my apologies.

To clarify, it's impractical in Chrome to restore a nested joint session history (e.g., if "nested traversables would have their own session history of multiple entries"), even if that nested history is limited to a single item. In the single item case, we would still need to store more than just the URL: also scroll position, form state, and many other things that get serialized.

However, I agree with @domfarolino that the replace-only + manual-patch suggestion seems to avoid the need for a nested joint session history. If an iframe in this mode is limited to a single session history item and can't otherwise affect the top-level joint session history, then it seems reasonable to store it as a single session history item within the top-level joint session history. That does seem like it would make restoration possible.

@rniwa
Copy link
Collaborator

rniwa commented Sep 10, 2021

Surely this should be a property of a frame, and not of a specific document / navigation? At least that's what we were discussing for the shadow DOM.

@domenic
Copy link
Member Author

domenic commented Sep 13, 2021

Yep, this would be a property of the iframe (browsing context); that's how permissions policy works.

blueboxd pushed a commit to blueboxd/chromium-legacy that referenced this issue Nov 5, 2021
Background:
Fenced frames can navigate themselves but their history is not part of
the browser back/forward list as that could be a communication channel
from the fenced frame to the embedding page. This aligns with MPArch's
disjoint back/forward list for nested frame trees. (For shadowDOM, this
would be achieved with additional API level restrictions like
history.length always returning 1 etc.)

Current Change:
This CL focuses on fenced frames to always have a replacement-only
navigation which was decided due to being a simpler model since it
doesn't imply that there's a hidden list of back/forward entries for
the nested page, only accessible via history APIs and not via the
back/forward buttons. This is also consistent with the iframes new
opt-in mode for disjoint session history as discussed in
whatwg/html#6501.
This change affects both shadowDOM and MPArch versions.

Design:
https://docs.google.com/document/d/17rtX55WkxMcfh6ipuhP4mNULIVxUApvYt4ZYXfX2x-s/edit#heading=h.af2cik2j1rbs

This CL includes browser tests to check the NavigationController's entry
count and
https://chromium-review.googlesource.com/c/chromium/src/+/3227344
added WPTs for all the history API surface.


Bug: 1242533
Change-Id: Ic574ee1bf87ce3a53dde7d280abaa46233d85b0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216452
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
Cr-Commit-Position: refs/heads/main@{#938761}
blueboxd pushed a commit to blueboxd/chromium-legacy that referenced this issue Nov 5, 2021
This reverts commit 5e37873.

Reason for revert: Appears to be breaking the tree https://ci.chromium.org/ui/p/chromium/builders/ci/Mac%20Builder%20(dbg)/202959/blamelist

Original change's description:
> Fenced Frames: Navigations always replace the current entry
>
> Background:
> Fenced frames can navigate themselves but their history is not part of
> the browser back/forward list as that could be a communication channel
> from the fenced frame to the embedding page. This aligns with MPArch's
> disjoint back/forward list for nested frame trees. (For shadowDOM, this
> would be achieved with additional API level restrictions like
> history.length always returning 1 etc.)
>
> Current Change:
> This CL focuses on fenced frames to always have a replacement-only
> navigation which was decided due to being a simpler model since it
> doesn't imply that there's a hidden list of back/forward entries for
> the nested page, only accessible via history APIs and not via the
> back/forward buttons. This is also consistent with the iframes new
> opt-in mode for disjoint session history as discussed in
> whatwg/html#6501.
> This change affects both shadowDOM and MPArch versions.
>
> Design:
> https://docs.google.com/document/d/17rtX55WkxMcfh6ipuhP4mNULIVxUApvYt4ZYXfX2x-s/edit#heading=h.af2cik2j1rbs
>
> This CL includes browser tests to check the NavigationController's entry
> count and
> https://chromium-review.googlesource.com/c/chromium/src/+/3227344
> added WPTs for all the history API surface.
>
>
> Bug: 1242533
> Change-Id: Ic574ee1bf87ce3a53dde7d280abaa46233d85b0d
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216452
> Reviewed-by: Reilly Grant <reillyg@chromium.org>
> Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
> Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
> Reviewed-by: Jeremy Roman <jbroman@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
> Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
> Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#938761}

Bug: 1242533
Change-Id: Ib946f629447de53164e1d335cf4b983e1fbdaa35
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3260236
Auto-Submit: anthonyvd <anthonyvd@chromium.org>
Owners-Override: anthonyvd <anthonyvd@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#938770}
@clelland
Copy link
Contributor

[Catching up]
Using permissions policy for this seems reasonable, with a default allowlist of *, as long as we're not considering the initial option of simply removing back() and friends from the history object. I suspect that doing that would require explicit opt-in from the frame.

Breaking back() gets close to that point, but it's arguably more acceptable to make it a no-op than to cause it to throw.

mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
Background:
Fenced frames can navigate themselves but their history is not part of
the browser back/forward list as that could be a communication channel
from the fenced frame to the embedding page. This aligns with MPArch's
disjoint back/forward list for nested frame trees. (For shadowDOM, this
would be achieved with additional API level restrictions like
history.length always returning 1 etc.)

Current Change:
This CL focuses on fenced frames to always have a replacement-only
navigation which was decided due to being a simpler model since it
doesn't imply that there's a hidden list of back/forward entries for
the nested page, only accessible via history APIs and not via the
back/forward buttons. This is also consistent with the iframes new
opt-in mode for disjoint session history as discussed in
whatwg/html#6501.
This change affects both shadowDOM and MPArch versions.

Design:
https://docs.google.com/document/d/17rtX55WkxMcfh6ipuhP4mNULIVxUApvYt4ZYXfX2x-s/edit#heading=h.af2cik2j1rbs

This CL includes browser tests to check the NavigationController's entry
count and
https://chromium-review.googlesource.com/c/chromium/src/+/3227344
added WPTs for all the history API surface.

Bug: 1242533
Change-Id: Ic574ee1bf87ce3a53dde7d280abaa46233d85b0d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216452
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
Cr-Commit-Position: refs/heads/main@{#938761}
NOKEYCHECK=True
GitOrigin-RevId: 5e37873045b5e4c8cd4244480476a2b0c23b8114
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
This reverts commit 5e37873045b5e4c8cd4244480476a2b0c23b8114.

Reason for revert: Appears to be breaking the tree https://ci.chromium.org/ui/p/chromium/builders/ci/Mac%20Builder%20(dbg)/202959/blamelist

Original change's description:
> Fenced Frames: Navigations always replace the current entry
>
> Background:
> Fenced frames can navigate themselves but their history is not part of
> the browser back/forward list as that could be a communication channel
> from the fenced frame to the embedding page. This aligns with MPArch's
> disjoint back/forward list for nested frame trees. (For shadowDOM, this
> would be achieved with additional API level restrictions like
> history.length always returning 1 etc.)
>
> Current Change:
> This CL focuses on fenced frames to always have a replacement-only
> navigation which was decided due to being a simpler model since it
> doesn't imply that there's a hidden list of back/forward entries for
> the nested page, only accessible via history APIs and not via the
> back/forward buttons. This is also consistent with the iframes new
> opt-in mode for disjoint session history as discussed in
> whatwg/html#6501.
> This change affects both shadowDOM and MPArch versions.
>
> Design:
> https://docs.google.com/document/d/17rtX55WkxMcfh6ipuhP4mNULIVxUApvYt4ZYXfX2x-s/edit#heading=h.af2cik2j1rbs
>
> This CL includes browser tests to check the NavigationController's entry
> count and
> https://chromium-review.googlesource.com/c/chromium/src/+/3227344
> added WPTs for all the history API surface.
>
>
> Bug: 1242533
> Change-Id: Ic574ee1bf87ce3a53dde7d280abaa46233d85b0d
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216452
> Reviewed-by: Reilly Grant <reillyg@chromium.org>
> Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
> Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
> Reviewed-by: Jeremy Roman <jbroman@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
> Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
> Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#938761}

Bug: 1242533
Change-Id: Ib946f629447de53164e1d335cf4b983e1fbdaa35
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3260236
Auto-Submit: anthonyvd <anthonyvd@chromium.org>
Owners-Override: anthonyvd <anthonyvd@chromium.org>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/heads/main@{#938770}
NOKEYCHECK=True
GitOrigin-RevId: 367479e4b169662f146b1053e9deaeedee035208
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
This is a reland of 5e37873045b5e4c8cd4244480476a2b0c23b8114
The change here is that the new tests should call GetPrimaryFrameTree
instead of GetFrameTree.

Original change's description:
> Fenced Frames: Navigations always replace the current entry
>
> Background:
> Fenced frames can navigate themselves but their history is not part of
> the browser back/forward list as that could be a communication channel
> from the fenced frame to the embedding page. This aligns with MPArch's
> disjoint back/forward list for nested frame trees. (For shadowDOM, this
> would be achieved with additional API level restrictions like
> history.length always returning 1 etc.)
>
> Current Change:
> This CL focuses on fenced frames to always have a replacement-only
> navigation which was decided due to being a simpler model since it
> doesn't imply that there's a hidden list of back/forward entries for
> the nested page, only accessible via history APIs and not via the
> back/forward buttons. This is also consistent with the iframes new
> opt-in mode for disjoint session history as discussed in
> whatwg/html#6501.
> This change affects both shadowDOM and MPArch versions.
>
> Design:
> https://docs.google.com/document/d/17rtX55WkxMcfh6ipuhP4mNULIVxUApvYt4ZYXfX2x-s/edit#heading=h.af2cik2j1rbs
>
> This CL includes browser tests to check the NavigationController's entry
> count and
> https://chromium-review.googlesource.com/c/chromium/src/+/3227344
> added WPTs for all the history API surface.
>
>
> Bug: 1242533
> Change-Id: Ic574ee1bf87ce3a53dde7d280abaa46233d85b0d
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3216452
> Reviewed-by: Reilly Grant <reillyg@chromium.org>
> Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
> Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
> Reviewed-by: Jeremy Roman <jbroman@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
> Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
> Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#938761}

Bug: 1242533
Change-Id: Ifc03b2021f8734b4b09cb1f5b647432d654bf8e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3260313
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Owners-Override: Dave Tapuska <dtapuska@chromium.org>
Commit-Queue: Shivani Sharma <shivanisha@chromium.org>
Cr-Commit-Position: refs/heads/main@{#938856}
NOKEYCHECK=True
GitOrigin-RevId: f405bf0de9c265c35b4570dbe16aa4b60483dc27
@brandonmcconnell
Copy link

brandonmcconnell commented Jan 31, 2023

Having opened a related issue here: #8773, I wanted to contribute some thoughts here re iframe's maintaining their own history.

I side most closely with @jakearchibald's proposed spec.

Firstly, I think it's essential that iframes have their own maintained history, and as far as I understand, using replaceState for every navigation within iframes would make it impossible for iframes to traverse their own history. Isolating the history of iframes so they don't pollute the history of the parent window would be a great and logical next step in the evolution of iframes. However, if in doing so, iframes lose the ability to control their own bwd/fwd navigation, I think we create an even larger issue and one that might be harder to come back from.

I think its imperative that iframes can manage their own history, and that the history for an iframe can be traversed fwd/bwd from the parent frame, at least so that the parent frame can present backward/forward/refresh navigation options.

I don't think we need to worry about restoring nested joint session histories, only the top-level. As for nested iframes-in-iframes, I think it's perfectly suitable for a nested iframe-in-iframe to also self maintain its own history, and how that works would be dictated by the setting of those nested iframes allow attribute values.

For example:

Top-level window (A) ◄┄┄┄┄┐ ◄┄┄╳┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
 │                        ┆                             ┆
 ├── iframe:not([allow]) (B1) ◄┐                        ┆
 │    │                        ┆                        ┆
 │    └── iframe:not([allow]) (C) ◄┄┄╳┄┄┄┄┄┄┄┄┄┄┄┐      ┆
 │         │                                     ┆      ┆
 │         └── iframe[allow="isolated-history"] (D) ◄┄┐ ┆
 │              │                        ┌┄┄┄┄┄┄┄┄┄┄┄┄┘ ┆
 │              └── iframe:not([allow]) (E)             ┆
 │                                                      ┆
 └── iframe[allow="isolated-history"] (B2) ┄┄┄┄┄┄┄┄┄┄┄┄┄┘

In the above example, without using allow="isolated-history", an iframe's history entries will be added to its parent's frames, and its parent's parent, and so on until a frame is reached which isolates the history.

With this in mind…

  • A history is the top-level history
  • B1 history will bubble history entries up to A since it does not use allow="isolated-history"
  • C history will bubble history entries up to A and B1 since neither it nor B1 use allow="isolated-history"
  • D history will act a new top-level-like context because it isolates its history using allow="isolated-history", so any new history entries from D will not bubble up
  • E history will bubble history entries up to D since it does not use allow="isolated-history"
  • B2 (also a direct child of A and sibling of B1) history will act a new top-level-like context because it isolates its history using allow="isolated-history", so any new history entries from B2 will not bubble up

Note, rather than allow'ing history to bubble up, we allow the history to be isolated so as not to cause any breaking changes on the web, in the heart of "don't break the web".

isolate-history seems clear enough to me, though something else like nested-history or suppress-history could work as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment