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

Defer persisted state restore when view transition is started #9996

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

noamr
Copy link
Contributor

@noamr noamr commented Dec 15, 2023

When starting a view transition right before state restoration, e.g. addEventListener("popstate", () => startViewTransition(() => {...})), according to the current spec the scroll position would be restored before the startViewTransition callback is called, which means that it would be captured as part of the old state, even though it belongs to the new state.

This change makes it so that we defer persisted state restoration until after the old state is captured.

Together with w3c/csswg-drafts#9676

  • At least two implementers are interested (and none opposed):
  • Tests are written and can be reviewed and commented upon at:
  • Implementation bugs are filed:
    • Chromium: …
    • Gecko: …
    • WebKit: …
    • Deno (only for timers, structured clone, base64 utils, channel messaging, module resolution, web workers, and web storage): …
    • Node.js (only for timers, structured clone, base64 utils, channel messaging, and module resolution): …
  • MDN issue is filed: …
  • The top of this comment includes a clear commit message to use.

(See WHATWG Working Mode: Changes for more details.)


/browsing-the-web.html ( diff )
/infrastructure.html ( diff )

When starting a view transition right before state restoration,
e.g. `addEventListener("popstate", () => startViewTransition(() => {...}))`,
according to the current spec the scroll position would be restored
before the `startViewTransition` callback is called, which means that
it would be captured as part of the old state, even though it belongs
to the new state.

This change makes it so that we defer persisted state restoration
until after the old state is captured.

Together with w3c/csswg-drafts#9676
Copy link
Member

@domenic domenic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little scared of changing the timing of this step. At the very least we'll need extensive tests. But other things I'm wondering about:

  • Your example shows popstate. But will people also expect it to work for hashchange?
  • Is this kind of change desirable for both call sites of "restore persisted state", i.e. the new document case and the same-document case? Your example is only for the same-document case, but the spec change impacts both.
  • This will impact the timing of form-associated custom elements formStateRestoreCallback. Do we think that will be OK for developers?
  • Can you explain when "restore persisted state" gets called, if it's being delayed? I tried to read through https://github.com/w3c/csswg-drafts/pull/9676/files but it was a bit complicated...

@@ -4031,6 +4031,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li><dfn data-x-href="https://drafts.csswg.org/css-view-transitions/#activate-view-transition">activate view transition</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-view-transitions/#viewtransition"><code>ViewTransition</code></dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-view-transitions-2/#document-resolve-cross-document-view-transition">resolving cross-document view-transition</dfn></li>
<li><dfn data-x-href="https://drafts.csswg.org/css-view-transitions/#viewtransition">potentially defer persisted state restoration due to view transition</dfn>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem to go to the right place

@noamr
Copy link
Contributor Author

noamr commented Jan 22, 2024

I'm a little scared of changing the timing of this step. At the very least we'll need extensive tests. But other things I'm wondering about:

  • Your example shows popstate. But will people also expect it to work for hashchange?

I believe so. @bokand feel free to chime in, let's figure out if this covers all the use cases with view-transitions.

  • Is this kind of change desirable for both call sites of "restore persisted state", i.e. the new document case and the same-document case? Your example is only for the same-document case, but the spec change impacts both.

This would have no effect on new documents, as there would never be an active view transition before activating the document.

  • This will impact the timing of form-associated custom elements formStateRestoreCallback. Do we think that will be OK for developers?

It has to be. All these use cases are really the same issue: view-transitions need to keep the DOM in its old state until the next "update the rendering" before updating the DOM to the "new state". So things that directly affect the rendering, such as scroll-state, should generally be rendering-aligned.

At the latest, it would be called during the next update the rendering phase. So this should not have a visual effect, apart from the desired one (having the state restore count as part of the "new state").

@bokand
Copy link
Contributor

bokand commented Jan 22, 2024

Your example shows popstate. But will people also expect it to work for hashchange?
I believe so. @bokand feel free to chime in, let's figure out if this covers all the use cases with view-transitions.

Yeah, if we're doing popstate a view transition should also work from a hashchange. That would let authors do transitions between "scroll-only fragment navigations"

@domenic
Copy link
Member

domenic commented Jan 23, 2024

I believe so. @bokand feel free to chime in, let's figure out if this covers all the use cases with view-transitions.

Isn't it a problem, then, that this changes the timing of state restoration relative to hashchange (but not popstate)?

It has to be. All these use cases are really the same issue: view-transitions need to keep the DOM in its old state until the next "update the rendering" before updating the DOM to the "new state". So things that directly affect the rendering, such as scroll-state, should generally be rendering-aligned.

I don't see how these are related. The point is that form state restoration is not rendering-related; it affects, e.g. form.value.

@noamr
Copy link
Contributor Author

noamr commented Jan 23, 2024

I believe so. @bokand feel free to chime in, let's figure out if this covers all the use cases with view-transitions.

Isn't it a problem, then, that this changes the timing of state restoration relative to hashchange (but not popstate)?

https://html.spec.whatwg.org/multipage/browsing-the-web.html#updating-the-document (6.4.4) calls "restore persisted state" which would get here. Am I missing something?

It has to be. All these use cases are really the same issue: view-transitions need to keep the DOM in its old state until the next "update the rendering" before updating the DOM to the "new state". So things that directly affect the rendering, such as scroll-state, should generally be rendering-aligned.

I don't see how these are related. The point is that form state restoration is not rendering-related; it affects, e.g. form.value.

The spec says: "Optionally, update other aspects of entry's document and its rendering, for instance values of form fields, that the user agent had previously recorded in entry's persisted user state."

This is very open-ended and (more than) implies that it also updates the rendering. Because it's so open-ended I'm not sure what tests would apply here. The only behavior I'm sure about is that scroll restoration should be rendering-aligned if there's a view-transition pending.

@domenic
Copy link
Member

domenic commented Jan 25, 2024

Two concerns:

  1. Compat and testing

Restore persisted state is currently after popstate, but before hashchange. After your change, IIUC, it will be after both popstate and hashchange.

This is what I'm worried about.E.g. right now developers can assume that inside popstate, inputEl.value is its original value. But if we defer state restoration, this will change; inputEl.value will the value restored from history.

Additionally, for form-associated custom elements, this changes the timing of formStateRestoreCallback.

Maybe these are fine, since view transitions are opt-in, but they definitely need tests. And I worry that they will break the assumptions of various libraries. E.g., right now it's a valid technique for a library to try to detect form restoration by saving the old value in popstate, and comparing it to the new value in hashchange or at some other later point. It sounds like that will not work any more.

  1. Does this work with hashchange at all?

My understanding of how the spec would work after this PR is:

  • popstate fires
  • We check if a view transition has been started. If so, defer persistent state restoration. Otherwise, restore now.
  • hashchange fires.

If this is the case, then the developer is not able to call startViewTransition() inside hashchange to delay persisted state restoration. By the time we get to the "hashchange fires" step, persisted state restoration has already finished.

@noamr
Copy link
Contributor Author

noamr commented Jan 25, 2024

Two concerns:

  1. Compat and testing

Restore persisted state is currently after popstate, but before hashchange. After your change, IIUC, it will be after both popstate and hashchange.

This is what I'm worried about.E.g. right now developers can assume that inside popstate, inputEl.value is its original value. But if we defer state restoration, this will change; inputEl.value will the value restored from history.

Additionally, for form-associated custom elements, this changes the timing of formStateRestoreCallback.

Perhaps we should limit this to be about scroll restoration only.

Maybe these are fine, since view transitions are opt-in, but they definitely need tests. And I worry that they will break the assumptions of various libraries. E.g., right now it's a valid technique for a library to try to detect form restoration by saving the old value in popstate, and comparing it to the new value in hashchange or at some other later point. It sounds like that will not work any more.

  1. Does this work with hashchange at all?

My understanding of how the spec would work after this PR is:

  • popstate fires
  • We check if a view transition has been started. If so, defer persistent state restoration. Otherwise, restore now.
  • hashchange fires.

If this is the case, then the developer is not able to call startViewTransition() inside hashchange to delay persisted state restoration. By the time we get to the "hashchange fires" step, persisted state restoration has already finished.

Hmm I see the issue with hashchange. Feels really brittle.
What this makes me think is that we should put our weight behind the navigation API being the right way to go here rather than try to fix these issues piecemeal for the classic APIs. If someone wants to support this in the classic APIs they would have to deal with scroll restoration manually.

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

Successfully merging this pull request may close these issues.

None yet

3 participants