← All issues

[Site Isolation] Enable same-site BFCache with cross-site iframes via UIProcess coordination

444bd55

Site Isolation runs cross-origin iframes in separate WebContent processes; under that model, suspending or restoring a BFCache'd page requires coordinating an arbitrary number of renderer processes in parallel. BFCache itself preserves a complete page snapshot (DOM, JS heap, media state) so back/forward traversal is instant; previously, pages with cross-site iframes were simply excluded from it.

Source/WebCore/loader/FrameLoader.cpp

- if (RefPtr provisionalItem = history().provisionalItem(); provisionalItem && BackForwardCache::singleton().get(*provisionalItem, protect(frame->page()).get())) {
+ RefPtr provisionalItem = history().provisionalItem();
+ bool hasCachedPage = provisionalItem && BackForwardCache::singleton().get(*provisionalItem, protect(frame->page()).get());
+ if (hasCachedPage && shouldRestoreFromBackForwardCache != ShouldRestoreFromBackForwardCache::No) {
loadProvisionalItemFromCachedPage();
return;
}
+ if (hasCachedPage) {
+ BackForwardCache::singleton().remove(*provisionalItem);
+ } else if (shouldRestoreFromBackForwardCache == ShouldRestoreFromBackForwardCache::Yes)
+ FRAMELOADER_RELEASE_LOG_ERROR(ResourceLoading, "...");

This commit makes the UIProcess the sole BFCache authority via a three-valued ShouldRestoreFromBackForwardCache signal (Yes/No/Unspecified). On cache, UIProcess walks the live frame tree and dispatches SuspendWithFrameItem to each iframe process; on restore, takeForRestoration() atomically takes ownership and dispatches RestoreWithFrameItem to each iframe process before commit. Legacy non-SI paths pass ::Unspecified and fall through to previous hasCachedPage semantics. A race guard handles DidCacheBackForwardItem IPCs arriving after the user has already navigated back.

This is the architectural ground-truth for BFCache under Site Isolation, shifting cache lifecycle authority from the WebProcess to the UIProcess and introducing new ownership semantics (takeForRestoration(), m_pagesPendingClose-style mirror entries) across multiple renderer processes.

Several high-value angles: (1) The race guard in didCacheBackForwardItem compares the cached item's URL against pageLoadState.pendingAPIRequestURL() — URL normalization differences (fragments, trailing slashes, encoding) could cause false negatives, leaving a stale UIProcess mirror entry. (2) markAsTakenForRestoration() clears m_backForwardFrameItemID to suppress the destructor's ClearCachedPage IPC; called on the wrong entry or racing the destructor, the WebProcess retains a cached Page it believes consumed. (3) Confirm takeForRestoration()'s claimed atomicity covers both iframe-process collection and child consumption under one lock. (4) The ::No path evicts the WebProcess-side entry but a concurrent RestoreWithFrameItem to an iframe may already be in flight. (5) The RestoredFromBackForwardCache value threaded through DidCommitLoadForFrame gates iframe subtree reattachment — a spoofed or mis-threaded value could mix iframe state between navigations.