Title: [293803] trunk
Revision
293803
Author
cdu...@apple.com
Date
2022-05-04 16:51:59 -0700 (Wed, 04 May 2022)

Log Message

WebKit should only reload WebViews automatically on crash if the view is visible
https://bugs.webkit.org/show_bug.cgi?id=240079
<rdar://92442052>

Reviewed by Geoffrey Garen and Darin Adler.

If the client app doesn't override WKNavigationDelegate.webViewWebContentProcessDidTerminate,
WebKit would previously reload the web view automatically. However, for some apps with a lot
of non-visible WebViews, this could cause a lot of churn when coming back to the foreground,
after many of these WebContent processes were jetsammed in the background.

To address this, WebKit now delays the automatic web view reload until the view becomes
visible (still reloads right away when the view is visible when the crash occurs).

* Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm:
(TEST):
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::launchProcess):
(WebKit::WebPageProxy::activityStateDidChange):
(WebKit::WebPageProxy::viewIsBecomingVisible):
(WebKit::WebPageProxy::dispatchProcessDidTerminate):
* Source/WebKit/UIProcess/WebPageProxy.h:

Canonical link: https://commits.webkit.org/250277@main

Modified Paths

Diff

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (293802 => 293803)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2022-05-04 23:27:43 UTC (rev 293802)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2022-05-04 23:51:59 UTC (rev 293803)
@@ -884,6 +884,7 @@
         m_process = processPool.processForRegistrableDomain(m_websiteDataStore.get(), registrableDomain, shouldEnableCaptivePortalMode() ? WebProcessProxy::CaptivePortalMode::Enabled : WebProcessProxy::CaptivePortalMode::Disabled);
 
     m_hasRunningProcess = true;
+    m_shouldReloadDueToCrashWhenVisible = false;
     m_isCaptivePortalModeExplicitlySet = m_configuration->isCaptivePortalModeExplicitlySet();
 
     m_process->addExistingWebPage(*this, WebProcessProxy::BeginsUsingDataStore::Yes);
@@ -2165,6 +2166,17 @@
     m_potentiallyChangedActivityStateFlags.add(mayHaveChanged);
     m_activityStateChangeWantsSynchronousReply = m_activityStateChangeWantsSynchronousReply || replyMode == ActivityStateChangeReplyMode::Synchronous;
 
+    // We need to do this here instead of inside dispatchActivityStateChange() or viewIsBecomingVisible() because these don't run when the view doesn't
+    // have a running WebProcess. For the same reason, we need to rely on PageClient::isViewVisible() instead of WebPageProxy::isViewVisible().
+    if (m_potentiallyChangedActivityStateFlags & ActivityState::IsVisible && m_shouldReloadDueToCrashWhenVisible && pageClient().isViewVisible()) {
+        RunLoop::main().dispatch([this, weakThis = WeakPtr { *this }] {
+            if (weakThis && std::exchange(m_shouldReloadDueToCrashWhenVisible, false)) {
+                WEBPAGEPROXY_RELEASE_LOG(ViewState, "activityStateDidChange: view is becoming visible after a crash, attempt a reload");
+                tryReloadAfterProcessTermination();
+            }
+        });
+    }
+
     if (m_suppressVisibilityUpdates && dispatchMode != ActivityStateChangeDispatchMode::Immediate)
         return;
 
@@ -5303,6 +5315,7 @@
 
 void WebPageProxy::viewIsBecomingVisible()
 {
+    WEBPAGEPROXY_RELEASE_LOG(ViewState, "viewIsBecomingVisible:");
     m_process->markProcessAsRecentlyUsed();
 #if ENABLE(MEDIA_STREAM)
     if (m_userMediaPermissionRequestManager)
@@ -7969,8 +7982,15 @@
     else
         handledByClient = m_navigationClient->processDidTerminate(*this, reason);
 
-    if (!handledByClient && shouldReloadAfterProcessTermination(reason))
-        tryReloadAfterProcessTermination();
+    if (!handledByClient && shouldReloadAfterProcessTermination(reason)) {
+        // We delay the view reload until it becomes visible.
+        if (isViewVisible())
+            tryReloadAfterProcessTermination();
+        else {
+            WEBPAGEPROXY_RELEASE_LOG_ERROR(Loading, "dispatchProcessDidTerminate: Not eagerly reloading the view because it is not currently visible");
+            m_shouldReloadDueToCrashWhenVisible = true;
+        }
+    }
 }
 
 void WebPageProxy::tryReloadAfterProcessTermination()

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (293802 => 293803)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2022-05-04 23:27:43 UTC (rev 293802)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2022-05-04 23:51:59 UTC (rev 293803)
@@ -3014,6 +3014,7 @@
     bool m_isShowingNavigationGestureSnapshot { false };
 
     bool m_mainFramePluginHandlesPageScaleGesture { false };
+    bool m_shouldReloadDueToCrashWhenVisible { false };
 
     unsigned m_pageCount { 0 };
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm (293802 => 293803)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm	2022-05-04 23:27:43 UTC (rev 293802)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WebContentProcessDidTerminate.mm	2022-05-04 23:51:59 UTC (rev 293803)
@@ -179,9 +179,10 @@
     EXPECT_TRUE(receivedScriptMessage);
 }
 
-TEST(WKNavigation, AutomaticViewReloadAfterWebProcessCrash)
+TEST(WKNavigation, AutomaticVisibleViewReloadAfterWebProcessCrash)
 {
-    RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100)]);
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration.get() addToWindow:YES]);
 
     auto delegate = adoptNS([[BasicNavigationDelegateWithoutCrashHandler alloc] init]);
     [webView setNavigationDelegate:delegate.get()];
@@ -213,6 +214,43 @@
     EXPECT_FALSE(startedLoad);
 }
 
+TEST(WKNavigation, AutomaticHiddenViewDelayedReloadAfterWebProcessCrash)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 100, 100) configuration:configuration.get() addToWindow:YES]);
+
+    auto delegate = adoptNS([[BasicNavigationDelegateWithoutCrashHandler alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+
+    // Make sure the view is not visible.
+    [webView removeFromSuperview];
+
+    startedLoad = false;
+    finishedLoad = false;
+
+    [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"rich-and-plain-text" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+    TestWebKitAPI::Util::run(&finishedLoad);
+
+    startedLoad = false;
+    finishedLoad = false;
+
+    // Simulate crash.
+    [webView _killWebContentProcess];
+
+    TestWebKitAPI::Util::sleep(0.5);
+
+    // WebKit should not have attempted a reload since the view is not visible.
+    EXPECT_FALSE(startedLoad);
+    EXPECT_FALSE(finishedLoad);
+
+    // Make the view visible.
+    [webView addToTestWindow];
+    [webView focus];
+
+    // WebKit should have triggered a reload when the view became visible.
+    TestWebKitAPI::Util::run(&finishedLoad);
+}
+
 TEST(WKNavigation, ProcessCrashDuringCallback)
 {
     auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to