Skip to content

Only reload the WebView on a cold boot visit when it's already on the visit location - #207

Merged
mbarta merged 1 commit into
mainfrom
cold-boot-reload-location
Jul 27, 2026
Merged

Only reload the WebView on a cold boot visit when it's already on the visit location#207
mbarta merged 1 commit into
mainfrom
cold-boot-reload-location

Conversation

@mbarta

@mbarta mbarta commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Symptoms

When a form inside a modal context screen is submitted and the server responds with refresh_or_redirect_to (a redirect to /refresh_historical_location), the modal is dismissed — but intermittently the screen behind it ends up rendering the modal's page instead of its own. For example, posting a comment from a modal composer screen sometimes leaves the underlying screen showing a fresh, empty composer form (with the underlying screen's toolbar), even though the comment was created successfully. Navigating back reveals the parent page with the submitted content.

Root cause

Dismissing a modal with a REFRESH modal result triggers two concurrent operations on the shared WebView from the underlying destination's onStartAfterModalResult:

  1. HotwireWebFragmentDelegate.onStartAfterModalResult()initNavigationVisit() → a RESTORE visit to the destination's location (turbo.js updates the WebView's history asynchronously), and
  2. HotwireFragmentDelegate.onStartAfterModalResult() → routes the modal result → NavigatorMode.REFRESHrefresh()session.reset() + a cold boot visit with reload: true, which calls WebView.reload() in Session.visitLocationAsColdBoot().

These race. When the cold boot runs before the restore visit has changed the WebView's history, the WebView's current page is still the dismissed modal's page — so WebView.reload() re-loads the modal's URL instead of the destination's location. The session then treats the cold boot as completed for the destination's location while the WebView actually renders the modal's page.

Debug log from a failing run (message screen behind a dismissed comment composer modal):

visitLocationAsColdBoot ... [location: https://example.com/buckets/1/messages/2]
onPageStarted ............. [location: https://example.com/buckets/1/recordings/2/comments/new]

Fix

WebView.reload() exists to handle same-page visits for URLs with anchors (see the comment above the change). That only applies when the WebView is already on the visit location. Guard the reload() call accordingly and load the visit location directly otherwise:

if (visit.reload && webView.url == visit.location) {
    webView.reload()
} else {
    webView.loadUrl(visit.location)
}

Testing

  • Added two unit tests covering both branches of the cold boot reload.
  • Verified in a Hotwire Native app against the failing scenario: with the fix, the same race ordering now loads the destination's location (onPageStarted reports the underlying screen's URL) and the modal flow behaves correctly — modal closes, underlying screen refreshes with the submitted content. Exercised repeatedly via modal form submissions ending in refresh_or_redirect_to.

🤖 Generated with Claude Code

… visit location

When a form submission inside a modal screen redirects to
/refresh_historical_location, the modal is dismissed and the underlying
screen is refreshed via a visit with reload: true. That reload races
with the underlying screen's restore visit: when the reload runs first,
the WebView's current page is still the dismissed modal's page, so
WebView.reload() renders the modal's page on the underlying screen
instead of its own location.

Reload only when the WebView is already on the visit location,
otherwise load the visit location directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbarta
mbarta requested review from jayohms and jhutarek July 27, 2026 12:39
@mbarta mbarta self-assigned this Jul 27, 2026

@jayohms jayohms left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good, thanks.

@mbarta
mbarta merged commit a49cb9b into main Jul 27, 2026
1 check passed
@mbarta
mbarta deleted the cold-boot-reload-location branch July 27, 2026 14:26
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.

2 participants