Skip to content

Nested contexts and unload prompts #6446

Closed
@jakearchibald

Description

@jakearchibald

This came up as I was working through the session history rewrite. How should navigations behave when a page, an iframe, or multiple iframes request to show an unload prompt?

Here's what I've noticed by poking browsers:

Firefox

For top-level navigations, Firefox shows unload prompts, and if the user cancels, they stay on the same session history entry.

Firefox ignores requests to show unload prompts for iframes unless the user has interacted with the iframe (eg clicked it). Otherwise:

For iframes, if going back 1 position, Firefox shows unload prompts, and if the user cancels, they stay on the same session history entry.

If going back more than 1 position, things get weird. Firefox shows unload prompts, and if the user cancels, the cancelled frame doesn't navigate, but the position in session history is changed, which is weird.

If going back 2 positions, involving 2 iframes each with an unload prompt, you get two prompts and if you cancel both you stay on the same session history entry.

Chrome

For top-level navigations, Chrome shows unload prompts, and if the user cancels, they stay on the same session history entry.

For iframes, if going back 1 position, it does the same. Chrome shows unload prompts, and if the user cancels, they stay on the same session history entry.

If going back more than 1 position, things get weird. Chrome shows unload prompts, and if the user cancels, the cancelled frame doesn't navigate, but the position in session history is changed, which is weird.

If going back 2 positions, involving 2 iframes each with an unload prompt, you get two prompts but even if you cancel both Chrome will navigate one of the iframes anyway 🙃. It also changes the position in session history.

Safari

For top-level same-origin navigations, Safari shows unload prompts, and if the user cancels, they stay on the same session history entry.

For top-level cross-origin, Safari does not show a prompt.

Safari allows unload prompts for iframes. It changes the position in session history but doesn't navigate the cancelled frames. Again this is weird, but it seems to do it more consistently in Chrome. Not sure if this is a good thing.

Due to moving the session history position, but not navigating the page, Safari can be 'tricked' into displaying the wrong URL in the URL bar. However, this only happens for same-origin so it isn't a security issue.

The 'correct' behaviour

If we want to allow unload prompts for iframes, I like to spec it so: If any unloading document requests an unload prompt, show one prompt. If cancelled, abort the navigation/traversal. However, I'm not sure UAs are in a good position to 'synchronise' navigations in a way where unloading happens across all documents before moving to the next steps.

When I first saw Firefox's behaviour, I thought we could maybe ignore all unload prompts from iframes, but given it allows them after interaction, maybe not.

I'm less sure about Safari's behaviour of ignoring unload prompts for cross-origin navigations. Feels like that would miss a lot of legitimate use-cases.

The tests

Test: Same origin top level navigation

  1. Go to https://iframe-session-history.glitch.me/page.html?one
  2. Go to https://iframe-session-history.glitch.me/
  3. Add unload prompt
  4. Go back, cancel prompt

Firefox: Shows prompt. Cancelling keeps you on the same session history entry.

Chrome: Shows prompt. Cancelling keeps you on the same session history entry.

Safari: Shows prompt. Cancelling keeps you on the same session history entry.

Behaviour here seems to make sense.

Test: Cross origin top level navigation

  1. Go to https://example.com
  2. Go to https://iframe-session-history.glitch.me/
  3. Add unload prompt
  4. Go back, cancel prompt

Firefox: Shows prompt. Cancelling keeps you on the same session history entry.

Chrome: Shows prompt. Cancelling keeps you on the same session history entry.

Safari: No prompt. Navigates.

Interesting that Safari is different.

Test: Same origin top level navigation, go(-2)

  1. Go to https://iframe-session-history.glitch.me/page.html?one
  2. Go to https://iframe-session-history.glitch.me/
  3. iframe-1, navigate to 'one'
  4. Add unload prompt
  5. Go back two entries at once (using history.go(-2) or long-press back button), cancel prompt

Firefox: Shows prompt. Cancelling keeps you on the same session history entry.

Chrome: Shows prompt. Cancelling keeps you on the same session history entry.

Safari: Prompt shown. Cancelling keeps you on the same session history entry, however, the URL bar changes to https://iframe-session-history.glitch.me/page.html?one (thankfully location.href doesn't change).

Safari's behaviour here is a bug, surely.

Test: Traverse single nested context with prompt

https://iframe-session-history.glitch.me/

  1. iframe-1, navigate to 'one'
  2. iframe-2, navigate to 'one'
  3. iframe-2, add unload prompt
  4. Go back, cancel prompt.

Firefox: No prompt shown unless the iframe has been interacted with (eg clicked). Otherwise, prompt shown. Cancelling keeps you in the same position in session history.

Chrome: Prompt shown. Cancelling keeps you in the same position in session history.

Safari: Prompt shown. Cancelling moves you to the previous position in session history, but the iframe doesn't navigate. Going back again doesn't reshow the prompt (it navigates iframe-1 as expected). Going forward twice (to the point where iframe-2 was navigated) shows the prompt again, and reloads the iframe page unless cancelled.

Safari's behaviour seems broken here. Chrome/Firefox behaviour is better.

Test: Traverse two nested contexts at once, one with prompt

https://iframe-session-history.glitch.me/

  1. iframe-1, navigate to 'one'
  2. iframe-2, navigate to 'one'
  3. iframe-1, add unload prompt
  4. Go back two entries at once (using history.go(-2) or long-press back button), cancel prompt

Firefox: No prompt shown unless the iframe has been interacted with (eg clicked). Otherwise, cancelling moves you back two places in session history. ifame-2 navigates, iframe-1 does not. Going forward shows the prompt again.

Chrome: Prompt shown. Cancelling moves you back two places in session history. ifame-2 navigates, iframe-1 does not. Going forward shows the prompt again.

Safari: Prompt shown. Cancelling moves you back two places in session history. ifame-2 navigates, iframe-1 does not. Same behaviour as previous test in terms of navigating back & forth.

Safari's behaviour is weird but consistent with the last test. The way Chrome/Firefox seems to have changed behaviour with this example is weird.

The results are the same if iframe-2 has the prompt instead.

Test: Traverse two nested contexts at once, both with prompt

  1. iframe-1, navigate to 'one'
  2. iframe-2, navigate to 'one'
  3. iframe-1, add unload prompt
  4. iframe-2, add unload prompt
  5. Go back two entries at once (using history.go(-2) or long-press back button), cancel prompts.

Firefox: No prompt shown unless the iframe has been interacted with (eg clicked). Otherwise, two prompts are shown. Cancelling both keeps you on the same session history entry.

Chrome: Two prompts shown. Cancelling both moves you back two places in session history. However, iframe-2 is still navigated.

Safari: Two prompts show. Cancelling both moves you back two places in session history. It prevents the navigation of both frames. Going forward doesn't show a prompt, but going forward again shows a prompt for iframe-2.

Safari's behaviour is kinda consistent with previous behaviours, although the lack of prompt when going forward seems unusual compared to previous results. Chrome navigating iframe-2 is just a bug, surely.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions