← All issues

Multi-process BFCache suspension for Site Isolation

41894db

Source/WebKit/UIProcess/SuspendedPageProxy.cpp

+void SuspendedPageProxy::startSuspension(std::optional<BackForwardFrameItemIdentifier> mainFrameItemID)
+{
+ ASSERT(m_suspensionState == SuspensionState::BeforeStart);
+ ASSERT(!m_browsingContextGroup->hasMultiplePages());
+
+ m_process->addSuspendedPageProxy(*this);
+ m_suspensionState = SuspensionState::Suspending;
+
+ if (mainFrameItemID)
+ suspendSubframeProcesses(*mainFrameItemID);
+ else
+ m_allSubframesSuspended = true;
+
+ m_messageReceiverRegistration.startReceivingMessages(m_process, m_webPageID, *this, *this);
+ m_suspensionTimeoutTimer.startOneShot(suspensionTimeout);
+ sendWithAsyncReply(Messages::WebPage::SetIsSuspended(true), [weakThis = WeakPtr { *this }](std::optional<bool> didSuspend) {
+ RefPtr protectedThis = weakThis.get();
+ if (!protectedThis || !didSuspend)
+ return;
+ protectedThis->didProcessRequestToSuspend(*didSuspend ? SuspensionState::Suspended : SuspensionState::FailedToSuspend);
+ });
+}

Source/WebKit/UIProcess/SuspendedPageProxy.cpp

+void SuspendedPageProxy::suspendSubframeProcesses(BackForwardFrameItemIdentifier mainFrameItemID)
+{
+ auto aggregator = MainRunLoopSuccessCallbackAggregator::create([weakThis = WeakPtr { *this }](bool success) {
+ RefPtr protectedThis = weakThis.get();
+ if (!protectedThis || protectedThis->m_suspensionState != SuspensionState::Suspending)
+ return;
+ if (!success) {
+ protect(protectedThis->backForwardCache())->removeEntry(*protectedThis);
+ return;
+ }
+ protectedThis->m_allSubframesSuspended = true;
+ protectedThis->maybeCompleteSuspension();
+ });
+
+ m_browsingContextGroup->forEachRemotePage(*page, [suspendedPage = Ref { *this }, &aggregator, mainFrameItemID](auto& remotePage) {
+ Ref process = remotePage.siteIsolatedProcess();
+ process->addSuspendedPageProxy(suspendedPage);
+ process->sendWithAsyncReply(Messages::WebPage::SetSubframesSuspended(true, mainFrameItemID), aggregator->chain(), remotePage.identifierInSiteIsolatedProcess());
+ });
+}

BFCache는 페이지를 벗어날 때 완전히 살아있는 snapshot을 보존하여 뒤로/앞으로 탐색 시 즉각적인 복원을 가능하게 합니다. Site Isolation 환경에서는 cross-origin iframe이 각각의 WebProcess 인스턴스에서 실행되며, 각 프로세스는 자체 per-process BackForwardCache singleton을 갖습니다. 이전까지 BFCache entry의 suspension은 완전히 단일 프로세스 방식으로만 동작했습니다.

이 commit은 UIProcess 내 SuspendedPageProxy를 coordinator로 확장합니다. 각 subframe의 WebProcess에 신규 SetSubframesSuspended IPC 메시지를 전송하며, 이때 main frame의 BackForwardFrameItemIdentifier를 함께 전달합니다. 이 식별자는 navigation entry마다 안정적으로 유지되는 키로, 각 WebProcess가 이 공유 키 아래에 자신의 CachedPage를 저장하는 데 사용됩니다. CallbackAggregator는 모든 프로세스에 걸친 완료 상태를 추적합니다. subframe 중 하나라도 suspension에 실패하면 BFCache entry가 제거되고 해당 subframe 프로세스들이 종료됩니다.

UIProcess                        WebProcess (main)       WebProcess (subframe N)
  │                                   │                        │
  ├─ startSuspension()                │                        │
  │    │                              │                        │
  │    ├──► SetIsSuspended(true) ────►│ addIfCacheable()       │
  │    │                              │  (HistoryItem path)    │
  │    │                              │                        │
  │    ├──► SetSubframesSuspended ────┼───────────────────────►│
  │    │    (mainFrameItemIdentifier) │                        │ addIfCacheable(id, page)
  │    │                              │                        │
  │    └─ CallbackAggregator tracks all completions            │
  │              │                    │                        │
  │         any fail? ◄───────────────┴────────────────────────┘
  │              └──► removeEntry(*this)
  │                   teardown subframe processes

BFCache를 Site Isolation 환경으로 확장하는 중요한 아키텍처 변경입니다. 다수의 WebProcess에 걸쳐 UIProcess 생명주기 조율이 동시에 이루어지며, 새로운 IPC surface, cross-process 공유 식별자, 그리고 보안에 민감한 navigation 인프라 내의 새로운 teardown 경로가 도입되었습니다.

🔒

New cross-process lifecycle paths and IPC surface introduce several edge cases worth security investigation.

더 확인하려면 구독해 주세요