Persist webview session in storage rather than memory#8709
Conversation
394553d to
36f8212
Compare
36f8212 to
f660778
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f660778. Configure here.
| dao.upsert(WebViewSessionEntity(tabId, bytes, System.currentTimeMillis())) | ||
| logcat(INFO) { "Saved WebView session for $tabId (${bytes.size} bytes)" } | ||
| }.onFailure { t -> logcat(WARN) { "Failed to save session for $tabId: ${t.asLog()}" } } | ||
| } |
There was a problem hiding this comment.
Async session save races restore
Medium Severity
RoomWebViewSessionStorage.saveSession persists the marshalled bundle on a background coroutine, while onSaveInstanceState returns as soon as that work is scheduled. On configuration changes or other quick recreate paths, the new tab view can run restoreSession before the upsert finishes, so Room still holds an older or missing row. Restore then fails or loads stale history while the legacy in-memory path updated the LRU cache synchronously in the same call.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f660778. Configure here.
anikiki
left a comment
There was a problem hiding this comment.
Looks good and works as expected! 🎉
f660778 to
b20510e
Compare



Task/Issue URL: https://app.asana.com/1/137249556945/project/715106103902962/task/1215107805550918?focus=true
Description
Persist each tab's WebView back/forward history to Room so back navigation works after the OS kills the app. Today the WebView session bundle is held only in a 10 MiB in-memory
LruCache, soafter process death the back button on a restored tab has nothing to walk back through and closes the tab instead. This PR replaces the in-memory cache with a Room-backed implementation,
gates the swap behind a kill-switch feature flag, and adds observability for unusually-large bundles.
What changes:
webview_sessionsRoom table (FK totabs(tabId)withON DELETE CASCADE), DAO, andAppDatabasemigration 61 → 62.RoomWebViewSessionStoragemarshalsWebView.saveState(Bundle)to bytes and persists per-tab. On restore, it unmarshals and callsWebView.restoreState. Bundle-level errors are caughtand treated as a restore miss (defensive against WebView version skew).
WebViewSessionStorageProxyis the boundWebViewSessionStorageimplementation. It delegates to eitherRoomWebViewSessionStorage(new behaviour) or the resurrectedInMemoryWebViewSessionStorage(legacy LruCache), based on the newwebViewSessionPersistencetoggle underAndroidBrowserConfigFeature(default ON). The toggle is read once viaby lazyso the routing decision is stable for the process lifetime — kill-switch flips take effect on next launch.
WebViewSessionStorage.restoreSessionanddeleteAllSessionsare nowsuspend.BrowserTabViewModel.restoreWebViewStatebecomessuspendand is launched from the fragment'sonViewStateRestoredviaviewLifecycleOwner.lifecycleScope.onViewReadyno longer auto-navigates toTabEntity.url;restoreWebViewStateis the single navigation entry point on first load (restore from Room→ success, otherwise fall back to omnibar text or
TabEntity.url). This avoids a double-navigation race that produced duplicate history entries after restore.m_webview_session_large_bytes_countpixel fires when a marshalled bundle exceeds 256 KiB, so we can monitor the size distribution before deciding whether to cap or compress.RoomWebViewSessionStorage(round-trip, defensive paths, pixel-firing),WebViewSessionStorageProxy(routingper flag state), and
BrowserTabViewModel's new cold-start fallback path.Steps to test this PR
Cold-start back navigation
Fire button still wipes saved sessions
webview_sessionstable inside the app DB.Tab close path
Escape hatch
Kill-switch rollback
webViewSessionPersistencesubfeature OFF via internal config tooling.Note
Medium Risk
Touches core tab restore/navigation after process death and adds a DB migration; incorrect restore timing or fallback could cause double-loads or lost back stack, though a feature flag allows rollback to in-memory behavior.
Overview
Per-tab WebView back/forward history can now survive process death by persisting
WebView.saveStatebundles in a newwebview_sessionsRoom table (DB v62, FK totabswith cascade delete), viaRoomWebViewSessionStorage.WebViewSessionStorageProxyis the injected implementation: it picks Room vs legacy in-memory LRU from the newwebViewSessionPersistenceremote toggle (stable for the process vialazy). The manualBrowserModulein-memory binding is removed; restore/delete-all APIs becomesuspend, andrestoreWebViewStateruns fromonViewStateRestoredin a lifecycle coroutine.Cold-start navigation is deduplicated:
onViewReadyno longer auto-loads the tab URL; restore is the single entry point, with fallback to omnibar text orTabEntity.url. Awebview_session_large_bytespixel (256 KiB threshold) plus param cleaning was added for bundle-size monitoring.Reviewed by Cursor Bugbot for commit b20510e. Bugbot is set up for automated code reviews on this repo. Configure here.