[Site Isolation] Add multi-process BFCache restoration with Site Isolation
Component: WebKit | fceeb85
BFCache preserves a full page snapshot in memory so back/forward navigation is instant. Under Site Isolation, cross-origin iframes run in separate WebContent processes, so a single cached page can span N processes. SuspendedPageProxy is the UI-process object holding suspended state, and BrowsingContextGroup tracks which WebPageProxy belongs to which process group and routes RemotePageProxy references out to subframe processes.
Source/WebKit/UIProcess/SuspendedPageProxy.cpp
Source/WebKit/UIProcess/BrowsingContextGroup.cpp
The single SetIsSuspended IPC is split into separate SuspendWithFrameItem and RestoreWithFrameItem messages. unsuspend() now fans RestoreWithFrameItem out to every subframe process and collects results through a MainRunLoopSuccessCallbackAggregator, triggering a full reload(ExpiredOnly) if any process fails to restore. Process selection during navigation also changes: the suspended page's browsing context group is reused for the new navigation so existing RemotePageProxy handles stay valid, which is what motivates addPage() tolerating a page already present.
Before (single-process BFCache):
SuspendedPageProxy
├─► SetIsSuspended(true) ──────► WebPage [main frame process]
└─► SetIsSuspended(false) ──────► WebPage [main frame process]
After (multi-process BFCache, Site Isolation):
SuspendedPageProxy
├─ startSuspension():
│ └─► SuspendWithFrameItem(id) ─────► WebPage [main frame process]
│ └─◄ didSuspend: bool ◄────────┘
│
└─ unsuspend(mainFrameItemID):
├─► SetIsSuspended(false) ──────────► WebPage [main frame process]
│
├── create MainRunLoopSuccessCallbackAggregator
│
└─► forEachRemotePage:
└─► RestoreWithFrameItem(id) ─► WebPage [subframe process 1]
└─► RestoreWithFrameItem(id) ─► WebPage [subframe process 2]
└─◄ success/fail ◄─────────┘
└─ [if any fail → page.reload(ExpiredOnly)]
Significance
BFCache restoration now coordinates suspend/restore across N WebContent processes simultaneously, making cached Site-Isolated pages survive back/forward navigation. The new IPC paths, the aggregated failure handling, and the process-routing changes all land on multi-process boundaries that carry cross-origin isolation guarantees.
Audit directions
Narrow: the partial-restoration window. SetIsSuspended(false) reaches the main frame process before the aggregator exists and before any RestoreWithFrameItem is sent. If a subframe subsequently fails, the aggregator fires reload(ExpiredOnly) on an already-live main frame — a window in which the main frame is restored and subframes are in an unknown state. Any navigation or script executing in that gap runs against a mixed-state page. The forward-facing version of this pattern: audit other multi-process fan-out restorations in WebKit/UIProcess/ where one participant is committed before the aggregator that decides whether the operation succeeded is constructed. The tell is a send/sendWithAsyncReply textually preceding the create of the aggregator meant to gate it.
Wider: invariants demoted to ASSERT on a multi-process routing path. addPage() now returns silently in release builds when the page is already present, with only ASSERT(!hasMultiplePages()) guarding the "suspended BCG holds exactly one page" claim. If a re-entrant navigation or a race between suspension and a new page attach violates it, process routing is corrupted with no observable error. This is a portable pattern: sweep BrowsingContextGroup, WebProcessProxy, and WebPageProxy for early-returns whose only correctness argument is a debug-only assertion on a security-relevant routing decision. The review tell is an if (...) { ASSERT(...); return; } where the assertion, not the condition, carries the invariant.
Widest: BFCache entry lifetime versus process reuse. removeEntriesForPageAndProcess is narrowed to the main frame only, so cross-site iframe process swaps no longer evict main-frame BFCache entries from the destination process — stale entries can accumulate in a process that is later recycled for a different origin. Combined with the browsing-context-group reuse during restoration, where RemotePageProxy references established before suspension remain bound during restore, the question generalizes: for any cache keyed on (page, process), audit whether process recycling across origins is paired with eviction. Start with a navigation that begins as a BFCache hit and transitions to a network load mid-flight, which is the shape most likely to route subframe processes into the wrong context group.