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

Do not invalidate pages in visits that don't render #1144

Merged
merged 3 commits into from
Jan 30, 2024
Merged

Conversation

afcapel
Copy link
Collaborator

@afcapel afcapel commented Jan 29, 2024

Fixes #1047
Supersedes #1143

Rendering a frame with a data-turbo-action was rendering the response twice.

First, the FrameController in charge of the frame renders the response using a FrameRenderer, while processing callback the loadResponse callback.

await this.view.render(renderer)

But when we update a frame with a data-turbo-action, we also issue a visit to update the browser URL & history:

session.visit(frame.src, options)

This session visit was also triggering a loadResponse callback, which in turn was instantiating a PageSnapshot and rendering it.

This caused problems when the response for the frame didn't include all the tracked assets in the page. The page snapshot considered the assets stale and triggered a reload of the page with reason tracked_element_mismatch.

This commit fixes the issue by not rendering the page snapshot when navigating from a frame.

The commit includes a new test that reproduces the issue returning a turbo frame in an HTML document with an empty <head>. Before this fix, the response would trigger a full page reload.

This is the call stack before the fix. Notice the two calls to loadResponse.

Screenshot 2024-01-29 at 17 30 43

Fixes #1047

Rendering a frame with a data-turbo-action was rendering the response
twice.

First, the `FrameController` in charge of the frame renders the response using a
FrameRenderer.

https://github.com/hotwired/turbo/blob/b7187f13b216cee8c855ef1fb6ac790b40dde361/src/core/frames/frame_controller.js#L314

But when we update a frame with a data-turbo-action, we also issue a visit
to update the browser URL & history:

https://github.com/hotwired/turbo/blob/b7187f13b216cee8c855ef1fb6ac790b40dde361/src/core/frames/frame_controller.js#L373

This session visit was also triggering a loadResponse callback, which
in turn was instantiating a `PageSnapshot` and rendering it.

This caused problems when the response for the frame didn't include all
the tracked assets in the page. The page snapshot considered the assets
stale and triggered a reload of the page with reason `tracked_element_mismatch`.

This commit fixes the issue by not rendering the page snapshot when
navigating within a frame.

The commit includes a new test that reproduces the issue returning a
turbo frame in an HTML document with an empty `<head>`. Before this fix,
the response would trigger a full page reload.
@domchristie
Copy link
Contributor

Nice! A couple of things:

Is fromFrame set for the same purpose as willRender. As far as I can tell, willRender is only set to false for this frame visit case, in which case, could we just re-use that?

What is the frame visit actually doing? It doesn’t render and it’s instantiated with updateHistory: false. It looks like FrameController#changeHistory is already called and it doesn’t render, so is it needed at all?

@domchristie
Copy link
Contributor

What is the frame visit actually doing? It doesn’t render and it’s instantiated with updateHistory: false. It looks like FrameController#changeHistory is already called and it doesn’t render, so is it needed at all?

😵‍💫 From the looks of the failing tests (any my own brief manual tests), it looks like the visit is doing a fair bit (on the caching front?)

Skipping the rendering when the visit comes from a frame navigation
breaks a few tests related to caching.

Instead, let's make sure we don't invalidate the view if the visit is
not rendering.
@afcapel
Copy link
Collaborator Author

afcapel commented Jan 30, 2024

As far as I can tell, willRender is only set to false for this frame visit case, in which case, could we just re-use that?

That's a good idea. I've updated the PR so now we use willRender to ensure we are not invalidating a page in a visit that wouldn't render anyway.

What is the frame visit actually doing? It doesn’t render and it’s instantiated with updateHistory: false. It looks like FrameController#changeHistory is already called and it doesn’t render, so is it needed at all?

This was introduced in #398 to add navigation-like behavior to to frame navigations with data-turbo-action. It's a bit difficult to follow everything that's happening, and it'd be nice if we had some abstractions to do this without having to issue a new visit but that's a big change that would require changing many internal parts of Turbo.

For now this small patch fixes #1047, which is the main blocker for a Turbo 8 final release. If we're happy with this I think are ready for the final release.

@afcapel afcapel changed the title Do not render page snapshots when navigating within a frame Do not invalidate pages in visits that don't render Jan 30, 2024
src/core/view.js Outdated
@@ -77,7 +78,7 @@ export class View {
this.#resolveRenderPromise(undefined)
delete this.renderPromise
}
} else {
} else if (willRender) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think it necessarily needs addressed here, but I find it confusing that we have shouldRender and willRender cases. It's not clear how they are different. If you should render, doesn't that imply that you will?

I think the easiest answer is probably to rename willRender. It really means that the view is being used in a context where rendering is not required. Or more specifically, it's being used to do all the other operations that normally accompany a page render, but after a frame update has already rendered.

But ideally we'd separate the responsibilities to avoid this sort of conditional check happening here. So maybe we're as well to stick with the existing language for now, and then look at refactoring in another PR which keeps the same behaviour?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, agreed to all of that 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree the shouldRender / willRender is confusing here.

Is there a plan to refactor before releasing v8? If not then I reckon we should clarify willRender by renaming and adding a comment. e.g.:

// A workaround to ignore tracked element mismatch reloads when performing a promoted Visit from a frame navigation

const shouldInvalidate = willRender

Copy link
Contributor

@seanpdoyle seanpdoyle Jan 30, 2024

Choose a reason for hiding this comment

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

I'd opened #741 to limit the occurrence of willRender.

I'd also opened #430 to try and introduce some abstractions for Frame navigation. They've both been kept up to date originally, but I eventually stopped dragging them forward. I've rebased them to catch up with the latest.

Would either be worthwhile to consider for inclusion in 8?

@brunoprietog
Copy link
Collaborator

If we're happy with this I think are ready for the final release.

@afcapel Sorry to bother with this but, have you considered any of these concerns before releasing the stable version? #1080 (Turbo crashes completely, I opened #1098 to address it). Other concerns with full page reloading when assets are diferent #1105 and #1116. Thanks!

@pfeiffer
Copy link
Contributor

If we're happy with this I think are ready for the final release.

@afcapel Sorry to bother with this but, have you considered any of these concerns before releasing the stable version? #1080 (Turbo crashes completely, I opened #1098 to address it)

I second that closing #1080 (causing blank pages with replace navigations!), would need to be part of a stable release. The potential fix in #1098 makes a lot of sense. We're seeing flashes of unwanted content (stale snapshots) while performing refreshes with morphing. It kinda defeats the purpose of the new shiny morph refreshes.

Let me know if we can do anything to help out here?

@afcapel
Copy link
Collaborator Author

afcapel commented Jan 30, 2024

Ok, I'll do a final round before publishing a RC 👍

Aiming to fix:

All of them have PRs already open to address them, so it's a matter of validating and merging.

Anything else we should consider a blocker?

@pfeiffer
Copy link
Contributor

Anything else we should consider a blocker?

Would #1123 make sense to include as well?

@brunoprietog
Copy link
Collaborator

@seanpdoyle has made a big effort with contributions and there are still many unmerged, several bug fixes and some very old ones. If you have time, it would be great if you could review as much as you can, obviously within your availability.

@afcapel
Copy link
Collaborator Author

afcapel commented Jan 30, 2024

Sure, I'd also like to clean up the remaining PRs, but unfortunately right now I'm focused on another project and have limited availability. We could always do more, but for context, we've already merged 72 PRs since v7.3.0.

@seanpdoyle
Copy link
Contributor

seanpdoyle commented Jan 30, 2024

Out of the pull requests that I've opened at the moment, I consider two worth merging into 8:

The others either require additional troubleshooting, or are refactors and abstractions.

@afcapel
Copy link
Collaborator Author

afcapel commented Jan 30, 2024

Cool, thanks @seanpdoyle I'll be sure to review those two as well before the RC.

As for other refactorings, they are of course welcome but they'll probably have to wait until after the v8 release.

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.

Promoted Frame Visits cause tracked_element_mismatch reloads
6 participants