Title: [230640] trunk
Revision
230640
Author
beid...@apple.com
Date
2018-04-13 11:04:22 -0700 (Fri, 13 Apr 2018)

Log Message

Introduce SuspendedPageProxy to keep old web processes around after their WebPageProxy has been swapped to a new one.
https://bugs.webkit.org/show_bug.cgi?id=184559

Reviewed by Alex Christensen.

Source/WebCore:

Covered by new API test.

WebCore changes rework the meaning of a "ForSuspension" policy to simply navigate the page to about:blank.

* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::redirectReceived):
(WebCore::DocumentLoader::willSendRequest):
(WebCore::DocumentLoader::startLoadingMainResource):
* loader/DocumentLoader.h:

* loader/FrameLoader.cpp:
(WebCore::FrameLoader::init):
(WebCore::FrameLoader::continueLoadAfterNavigationPolicy):

Source/WebKit:

Before this patch, when a WebPageProxy navigates and is swapped to a new process, the old process almost always goes away.

This is not desirable for a few reasons:
1 - We can't keep the PageCache working for back/forward scenarios
2 - We throw away a "foo.com" web process, meaning the next time we need to host a "foo.com" web page we have to launch
    and initialize a new web process.

This patch adds a SuspendedPageProxy object to keep around the old web process and to manage communication with it.

For now, a WebPageProxy keeps exactly one "suspended page" representing the most recently visited page and its process.
Additionally, that process is never reused.

So no benefit is achieved with this patch, but it enables future benefits.

* Platform/Logging.h:

* Shared/WebBackForwardListItem.cpp:
(WebKit::WebBackForwardListItem::setSuspendedPage):
* Shared/WebBackForwardListItem.h:

New object to represent the state of a WebPageProxy in an old web process that is not currently hosting the view.
* UIProcess/SuspendedPageProxy.cpp: Added.
(WebKit::SuspendedPageProxy::SuspendedPageProxy):
(WebKit::SuspendedPageProxy::~SuspendedPageProxy):
(WebKit::SuspendedPageProxy::webProcessDidClose):
(WebKit::SuspendedPageProxy::didFinishLoad):
(WebKit::SuspendedPageProxy::didReceiveMessage):
(WebKit::SuspendedPageProxy::loggingString const):
* UIProcess/SuspendedPageProxy.h: Copied from Source/WebKit/Platform/Logging.h.
(WebKit::SuspendedPageProxy::create):
(WebKit::SuspendedPageProxy::page const):
(WebKit::SuspendedPageProxy::process const):
(WebKit::SuspendedPageProxy::item const):
(WebKit::SuspendedPageProxy::finishedSuspending const):

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::reattachToWebProcess):
(WebKit::WebPageProxy::attachToProcessForNavigation):
(WebKit::WebPageProxy::maybeCreateSuspendedPage):
(WebKit::WebPageProxy::suspendedPageProcessClosed):
(WebKit::WebPageProxy::receivedPolicyDecision):
(WebKit::WebPageProxy::didFinishLoadForFrame):
* UIProcess/WebPageProxy.h:

* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::suspendWebPageProxy):
(WebKit::WebProcessProxy::suspendedPageWasDestroyed):
(WebKit::WebProcessProxy::removeWebPage):
(WebKit::WebProcessProxy::didReceiveMessage): Optionally pass WebPageProxy messages along to SuspendedPageProxy objects.
(WebKit::WebProcessProxy::didClose):
(WebKit::WebProcessProxy::maybeShutDown):
(WebKit::WebProcessProxy::canTerminateChildProcess): Don't terminate child processes if they still have suspended pages.
* UIProcess/WebProcessProxy.h:

* WebKit.xcodeproj/project.pbxproj:

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::setIsSuspended):
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage::isSuspended const): For now, used only by WebProcess::updateActivePages. Will have more uses soon.
* WebProcess/WebPage/WebPage.messages.in:

* WebProcess/WebProcess.messages.in:
* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::updateActivePages): Allow the UIProcess to request an update of the web processes user visible name.

Source/WTF:

* wtf/DebugUtilities.h:
(WTF::debugString): Add a debug utility to easily construct a "const char*" that is released after a spin of the run loop.
  This greatly eases uses our String classes and functions inside of "%s" style environments like printf and LOG.

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (230639 => 230640)


--- trunk/Source/WTF/ChangeLog	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WTF/ChangeLog	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1,3 +1,14 @@
+2018-04-13  Brady Eidson  <beid...@apple.com>
+
+        Introduce SuspendedPageProxy to keep old web processes around after their WebPageProxy has been swapped to a new one.
+        https://bugs.webkit.org/show_bug.cgi?id=184559
+
+        Reviewed by Alex Christensen.
+
+        * wtf/DebugUtilities.h:
+        (WTF::debugString): Add a debug utility to easily construct a "const char*" that is released after a spin of the run loop.
+          This greatly eases uses our String classes and functions inside of "%s" style environments like printf and LOG.
+
 2018-04-12  Michael Catanzaro  <mcatanz...@igalia.com>
 
         Remove unused crash hook functionality

Modified: trunk/Source/WTF/wtf/DebugUtilities.h (230639 => 230640)


--- trunk/Source/WTF/wtf/DebugUtilities.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WTF/wtf/DebugUtilities.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,11 +23,11 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef WTF_DebugUtilities_h
-#define WTF_DebugUtilities_h
+#pragma once
 
 #include <wtf/Assertions.h>
 #include <wtf/ProcessID.h>
+#include <wtf/text/StringConcatenate.h>
 
 #define SLEEP_THREAD_FOR_DEBUGGER() \
 do { \
@@ -40,4 +40,24 @@
     WTFBreakpointTrap(); \
 } while (0)
 
-#endif /* WTF_DebugUtilities_h */
+namespace WTF {
+
+template<typename... StringTypes>
+const char* debugString(StringTypes... strings)
+{
+    String result = tryMakeString(strings...);
+    if (!result)
+        CRASH();
+
+    auto cString = result.utf8();
+    const char* cStringData = cString.data();
+
+    callOnMainThread([cString = WTFMove(cString)] {
+    });
+
+    return cStringData;
+}
+
+} // namespace WTF
+
+using WTF::debugString;

Modified: trunk/Source/WebCore/ChangeLog (230639 => 230640)


--- trunk/Source/WebCore/ChangeLog	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebCore/ChangeLog	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1,3 +1,24 @@
+2018-04-13  Brady Eidson  <beid...@apple.com>
+
+        Introduce SuspendedPageProxy to keep old web processes around after their WebPageProxy has been swapped to a new one.
+        https://bugs.webkit.org/show_bug.cgi?id=184559
+
+        Reviewed by Alex Christensen.
+
+        Covered by new API test.
+
+        WebCore changes rework the meaning of a "ForSuspension" policy to simply navigate the page to about:blank.
+
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::redirectReceived):
+        (WebCore::DocumentLoader::willSendRequest):
+        (WebCore::DocumentLoader::startLoadingMainResource):
+        * loader/DocumentLoader.h:
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::init):
+        (WebCore::FrameLoader::continueLoadAfterNavigationPolicy):
+
 2018-04-13  Chris Dumez  <cdu...@apple.com>
 
         input.webkitEntries does not work as expected when folder contains accented chars

Modified: trunk/Source/WebCore/loader/DocumentLoader.cpp (230639 => 230640)


--- trunk/Source/WebCore/loader/DocumentLoader.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebCore/loader/DocumentLoader.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -519,7 +519,7 @@
     ASSERT_UNUSED(resource, &resource == m_mainResource);
 #if ENABLE(SERVICE_WORKER)
     bool isRedirectionFromServiceWorker = redirectResponse.source() == ResourceResponse::Source::ServiceWorker;
-    willSendRequest(WTFMove(request), redirectResponse, [isRedirectionFromServiceWorker, completionHandler = WTFMove(completionHandler), protectedThis = makeRef(*this), this] (auto&& request) mutable {
+    willSendRequest(WTFMove(request), redirectResponse, ShouldContinue::Yes, [isRedirectionFromServiceWorker, completionHandler = WTFMove(completionHandler), protectedThis = makeRef(*this), this] (auto&& request) mutable {
         ASSERT(!m_substituteData.isValid());
         if (request.isNull() || !m_mainDocumentError.isNull() || !m_frame) {
             completionHandler({ });
@@ -552,11 +552,11 @@
         });
     });
 #else
-    willSendRequest(WTFMove(request), redirectResponse, WTFMove(completionHandler));
+    willSendRequest(WTFMove(request), redirectResponse, ShouldContinue::Yes, WTFMove(completionHandler));
 #endif
 }
 
-void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const ResourceResponse& redirectResponse, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
+void DocumentLoader::willSendRequest(ResourceRequest&& newRequest, const ResourceResponse& redirectResponse, ShouldContinue shouldContinue, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
 {
     // Note that there are no asserts here as there are for the other callbacks. This is due to the
     // fact that this "callback" is sent when starting every load, and the state of callback
@@ -564,6 +564,8 @@
     // callbacks is meant to prevent.
     ASSERT(!newRequest.isNull());
 
+    ASSERT(shouldContinue != ShouldContinue::No);
+
     bool didReceiveRedirectResponse = !redirectResponse.isNull();
     if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url(), didReceiveRedirectResponse)) {
         cancelMainResourceLoad(frameLoader()->cancelledError(newRequest));
@@ -630,21 +632,16 @@
 
     setRequest(newRequest);
 
-    // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate
-    // listener. But there's no way to do that in practice. So instead we cancel later if the
-    // listener tells us to. In practice that means the navigation policy needs to be decided
-    // synchronously for these redirect cases.
-    if (!didReceiveRedirectResponse)
+    if (!didReceiveRedirectResponse && shouldContinue != ShouldContinue::ForSuspension)
         return completionHandler(WTFMove(newRequest));
 
-    ASSERT(!m_waitingForNavigationPolicy);
-    m_waitingForNavigationPolicy = true;
-    frameLoader()->policyChecker().checkNavigationPolicy(ResourceRequest(newRequest), didReceiveRedirectResponse, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (ResourceRequest&& request, FormState*, ShouldContinue shouldContinue) mutable {
+    auto navigationPolicyCompletionHandler = [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (ResourceRequest&& request, FormState*, ShouldContinue shouldContinue) mutable {
         m_waitingForNavigationPolicy = false;
         switch (shouldContinue) {
         case ShouldContinue::ForSuspension:
-            FALLTHROUGH;
-            // FIXME: Setup this page for suspension (e.g. Page Cache) here.
+            // We handle suspension by navigating forward to about:blank, which leaves us setup to navigate back to resume.
+            request = { blankURL() };
+            break;
         case ShouldContinue::No:
             stopLoadingForPolicyChange();
             break;
@@ -653,7 +650,17 @@
         }
 
         completionHandler(WTFMove(request));
-    });
+    };
+
+    ASSERT(!m_waitingForNavigationPolicy);
+    m_waitingForNavigationPolicy = true;
+
+    if (shouldContinue == ShouldContinue::ForSuspension) {
+        navigationPolicyCompletionHandler(WTFMove(newRequest), nullptr, shouldContinue);
+        return;
+    }
+
+    frameLoader()->policyChecker().checkNavigationPolicy(ResourceRequest(newRequest), didReceiveRedirectResponse, WTFMove(navigationPolicyCompletionHandler));
 }
 
 bool DocumentLoader::tryLoadingRequestFromApplicationCache()
@@ -1655,8 +1662,10 @@
     return true;
 }
 
-void DocumentLoader::startLoadingMainResource()
+void DocumentLoader::startLoadingMainResource(ShouldContinue shouldContinue)
 {
+    ASSERT(shouldContinue != ShouldContinue::No);
+
     m_mainDocumentError = ResourceError();
     timing().markStartTimeAndFetchStart();
     ASSERT(!m_mainResource);
@@ -1681,7 +1690,7 @@
     ASSERT(timing().startTime());
     ASSERT(timing().fetchStart());
 
-    willSendRequest(ResourceRequest(m_request), ResourceResponse(), [this, protectedThis = makeRef(*this)] (ResourceRequest&& request) mutable {
+    willSendRequest(ResourceRequest(m_request), ResourceResponse(), shouldContinue, [this, protectedThis = makeRef(*this)] (ResourceRequest&& request) mutable {
         m_request = request;
 
         // willSendRequest() may lead to our Frame being detached or cancelling the load via nulling the ResourceRequest.

Modified: trunk/Source/WebCore/loader/DocumentLoader.h (230639 => 230640)


--- trunk/Source/WebCore/loader/DocumentLoader.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebCore/loader/DocumentLoader.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -84,6 +84,8 @@
 class SubresourceLoader;
 class SubstituteResource;
 
+enum class ShouldContinue;
+
 using ResourceLoaderMap = HashMap<unsigned long, RefPtr<ResourceLoader>>;
 
 enum class AutoplayPolicy {
@@ -237,7 +239,7 @@
     void setDefersLoading(bool);
     void setMainResourceDataBufferingPolicy(DataBufferingPolicy);
 
-    void startLoadingMainResource();
+    void startLoadingMainResource(ShouldContinue);
     WEBCORE_EXPORT void cancelMainResourceLoad(const ResourceError&);
     void willContinueMainResourceLoadAfterRedirect(const ResourceRequest&);
 
@@ -351,7 +353,7 @@
     void clearArchiveResources();
 #endif
 
-    void willSendRequest(ResourceRequest&&, const ResourceResponse&, CompletionHandler<void(ResourceRequest&&)>&&);
+    void willSendRequest(ResourceRequest&&, const ResourceResponse&, ShouldContinue, CompletionHandler<void(ResourceRequest&&)>&&);
     void finishedLoading();
     void mainReceivedError(const ResourceError&);
     WEBCORE_EXPORT void redirectReceived(CachedResource&, ResourceRequest&&, const ResourceResponse&, CompletionHandler<void(ResourceRequest&&)>&&) override;

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (230639 => 230640)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -306,7 +306,7 @@
     // This somewhat odd set of steps gives the frame an initial empty document.
     setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).ptr());
     setProvisionalDocumentLoader(m_policyDocumentLoader.get());
-    m_provisionalDocumentLoader->startLoadingMainResource();
+    m_provisionalDocumentLoader->startLoadingMainResource(ShouldContinue::Yes);
 
     Ref<Frame> protect(m_frame);
     m_frame.document()->cancelParsing();
@@ -3231,7 +3231,7 @@
         diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultFail, ShouldSample::Yes);
     }
 
-    WTF::Function<void(void)> completionHandler = [this] {
+    CompletionHandler<void(void)> completionHandler = [this, shouldContinue] {
         if (!m_provisionalDocumentLoader)
             return;
         
@@ -3251,7 +3251,12 @@
         }
         
         m_loadingFromCachedPage = false;
-        m_provisionalDocumentLoader->startLoadingMainResource();
+
+        // We handle suspension by navigating forward to about:blank, which leaves us setup to navigate back to resume.
+        if (shouldContinue == ShouldContinue::ForSuspension)
+            m_provisionalDocumentLoader->willContinueMainResourceLoadAfterRedirect({ blankURL() });
+
+        m_provisionalDocumentLoader->startLoadingMainResource(shouldContinue);
     };
     
     if (!formState) {

Modified: trunk/Source/WebKit/ChangeLog (230639 => 230640)


--- trunk/Source/WebKit/ChangeLog	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/ChangeLog	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1,3 +1,76 @@
+2018-04-13  Brady Eidson  <beid...@apple.com>
+
+        Introduce SuspendedPageProxy to keep old web processes around after their WebPageProxy has been swapped to a new one.
+        https://bugs.webkit.org/show_bug.cgi?id=184559
+
+        Reviewed by Alex Christensen.
+
+        Before this patch, when a WebPageProxy navigates and is swapped to a new process, the old process almost always goes away.
+
+        This is not desirable for a few reasons:
+        1 - We can't keep the PageCache working for back/forward scenarios
+        2 - We throw away a "foo.com" web process, meaning the next time we need to host a "foo.com" web page we have to launch
+            and initialize a new web process.
+
+        This patch adds a SuspendedPageProxy object to keep around the old web process and to manage communication with it.
+
+        For now, a WebPageProxy keeps exactly one "suspended page" representing the most recently visited page and its process.
+        Additionally, that process is never reused.
+
+        So no benefit is achieved with this patch, but it enables future benefits.
+
+        * Platform/Logging.h:
+
+        * Shared/WebBackForwardListItem.cpp:
+        (WebKit::WebBackForwardListItem::setSuspendedPage):
+        * Shared/WebBackForwardListItem.h:
+
+        New object to represent the state of a WebPageProxy in an old web process that is not currently hosting the view.
+        * UIProcess/SuspendedPageProxy.cpp: Added.
+        (WebKit::SuspendedPageProxy::SuspendedPageProxy):
+        (WebKit::SuspendedPageProxy::~SuspendedPageProxy):
+        (WebKit::SuspendedPageProxy::webProcessDidClose):
+        (WebKit::SuspendedPageProxy::didFinishLoad):
+        (WebKit::SuspendedPageProxy::didReceiveMessage):
+        (WebKit::SuspendedPageProxy::loggingString const):
+        * UIProcess/SuspendedPageProxy.h: Copied from Source/WebKit/Platform/Logging.h.
+        (WebKit::SuspendedPageProxy::create):
+        (WebKit::SuspendedPageProxy::page const):
+        (WebKit::SuspendedPageProxy::process const):
+        (WebKit::SuspendedPageProxy::item const):
+        (WebKit::SuspendedPageProxy::finishedSuspending const):
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::reattachToWebProcess):
+        (WebKit::WebPageProxy::attachToProcessForNavigation):
+        (WebKit::WebPageProxy::maybeCreateSuspendedPage):
+        (WebKit::WebPageProxy::suspendedPageProcessClosed):
+        (WebKit::WebPageProxy::receivedPolicyDecision):
+        (WebKit::WebPageProxy::didFinishLoadForFrame):
+        * UIProcess/WebPageProxy.h:
+
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::suspendWebPageProxy):
+        (WebKit::WebProcessProxy::suspendedPageWasDestroyed):
+        (WebKit::WebProcessProxy::removeWebPage):
+        (WebKit::WebProcessProxy::didReceiveMessage): Optionally pass WebPageProxy messages along to SuspendedPageProxy objects.
+        (WebKit::WebProcessProxy::didClose):
+        (WebKit::WebProcessProxy::maybeShutDown):
+        (WebKit::WebProcessProxy::canTerminateChildProcess): Don't terminate child processes if they still have suspended pages.
+        * UIProcess/WebProcessProxy.h:
+
+        * WebKit.xcodeproj/project.pbxproj:
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::setIsSuspended):
+        * WebProcess/WebPage/WebPage.h:
+        (WebKit::WebPage::isSuspended const): For now, used only by WebProcess::updateActivePages. Will have more uses soon.
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        * WebProcess/WebProcess.messages.in:
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::updateActivePages): Allow the UIProcess to request an update of the web processes user visible name.
+
 2018-04-13  Daniel Bates  <daba...@apple.com>
 
         Inline NetworkLoad::sharedWillSendRedirectedRequest() into NetworkLoad::willPerformHTTPRedirection()

Modified: trunk/Source/WebKit/Platform/Logging.h (230639 => 230640)


--- trunk/Source/WebKit/Platform/Logging.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/Platform/Logging.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -62,6 +62,7 @@
     M(Printing) \
     M(Process) \
     M(ProcessSuspension) \
+    M(ProcessSwapping) \
     M(RemoteLayerTree) \
     M(Resize) \
     M(ResourceLoadStatistics) \

Modified: trunk/Source/WebKit/Shared/WebBackForwardListItem.cpp (230639 => 230640)


--- trunk/Source/WebKit/Shared/WebBackForwardListItem.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/Shared/WebBackForwardListItem.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -103,4 +103,9 @@
     return highestItemID;
 }
 
+void WebBackForwardListItem::setSuspendedPage(SuspendedPageProxy& page)
+{
+    m_suspendedPage = &page;
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/Shared/WebBackForwardListItem.h (230639 => 230640)


--- trunk/Source/WebKit/Shared/WebBackForwardListItem.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/Shared/WebBackForwardListItem.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -41,6 +41,8 @@
 
 namespace WebKit {
 
+class SuspendedPageProxy;
+
 class WebBackForwardListItem : public API::ObjectImpl<API::Object::Type::BackForwardListItem> {
 public:
     static Ref<WebBackForwardListItem> create(BackForwardListItemState&&, uint64_t pageID);
@@ -63,6 +65,7 @@
     ViewSnapshot* snapshot() const { return m_itemState.snapshot.get(); }
     void setSnapshot(RefPtr<ViewSnapshot>&& snapshot) { m_itemState.snapshot = WTFMove(snapshot); }
 #endif
+    void setSuspendedPage(SuspendedPageProxy&);
 
     static uint64_t highestUsedItemID();
 
@@ -71,6 +74,7 @@
 
     BackForwardListItemState m_itemState;
     uint64_t m_pageID;
+    SuspendedPageProxy* m_suspendedPage { nullptr };
 };
 
 typedef Vector<Ref<WebBackForwardListItem>> BackForwardListItemVector;

Added: trunk/Source/WebKit/UIProcess/SuspendedPageProxy.cpp (0 => 230640)


--- trunk/Source/WebKit/UIProcess/SuspendedPageProxy.cpp	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/SuspendedPageProxy.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SuspendedPageProxy.h"
+
+#include "Logging.h"
+#include "WebPageMessages.h"
+#include "WebPageProxy.h"
+#include "WebPageProxyMessages.h"
+#include "WebProcessMessages.h"
+#include "WebProcessProxy.h"
+#include <wtf/DebugUtilities.h>
+
+namespace WebKit {
+
+#if !LOG_DISABLED
+static const HashSet<IPC::StringReference>& messageNamesToIgnoreWhileSuspended()
+{
+    static NeverDestroyed<HashSet<IPC::StringReference>> messageNames;
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [] {
+        messageNames.get().add("BackForwardAddItem");
+        messageNames.get().add("ClearAllEditCommands");
+        messageNames.get().add("DidChangeContentSize");
+        messageNames.get().add("DidChangeMainDocument");
+        messageNames.get().add("DidChangeProgress");
+        messageNames.get().add("DidCommitLoadForFrame");
+        messageNames.get().add("DidDestroyNavigation");
+        messageNames.get().add("DidFinishDocumentLoadForFrame");
+        messageNames.get().add("DidFinishProgress");
+        messageNames.get().add("DidFirstLayoutForFrame");
+        messageNames.get().add("DidFirstVisuallyNonEmptyLayoutForFrame");
+        messageNames.get().add("DidNavigateWithNavigationData");
+        messageNames.get().add("DidReachLayoutMilestone");
+        messageNames.get().add("DidSaveToPageCache");
+        messageNames.get().add("DidStartProgress");
+        messageNames.get().add("DidStartProvisionalLoadForFrame");
+        messageNames.get().add("EditorStateChanged");
+        messageNames.get().add("PageExtendedBackgroundColorDidChange");
+        messageNames.get().add("SetRenderTreeSize");
+        messageNames.get().add("SetStatusText");
+    });
+
+    return messageNames;
+}
+#endif
+
+SuspendedPageProxy::SuspendedPageProxy(WebPageProxy& page, WebProcessProxy& process, WebBackForwardListItem& item)
+    : m_page(page)
+    , m_process(&process)
+    , m_backForwardListItem(item)
+{
+    m_backForwardListItem->setSuspendedPage(*this);
+    m_process->send(Messages::WebPage::SetIsSuspended(true), m_page.pageID());
+}
+
+SuspendedPageProxy::~SuspendedPageProxy()
+{
+    if (m_process)
+        m_process->suspendedPageWasDestroyed(*this);
+}
+
+void SuspendedPageProxy::webProcessDidClose(WebProcessProxy& process)
+{
+    ASSERT_UNUSED(process, &process == m_process);
+    m_process = nullptr;
+
+    m_page.suspendedPageProcessClosed(*this);
+}
+
+void SuspendedPageProxy::didFinishLoad()
+{
+    ASSERT(m_process);
+    LOG(ProcessSwapping, "SuspendedPageProxy %s from process %i finished transition to suspended", loggingString(), m_process->processIdentifier());
+
+    m_finishedSuspending = true;
+
+    m_process->send(Messages::WebProcess::UpdateActivePages(), 0);
+}
+
+void SuspendedPageProxy::didReceiveMessage(IPC::Connection&, IPC::Decoder& decoder)
+{
+    ASSERT(decoder.messageReceiverName() == Messages::WebPageProxy::messageReceiverName());
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidFinishLoadForFrame::name()) {
+        didFinishLoad();
+        return;
+    }
+#if !LOG_DISABLED
+    if (messageNamesToIgnoreWhileSuspended().contains(decoder.messageName()))
+        LOG(ProcessSwapping, "SuspendedPageProxy received unexpected WebPageProxy message '%s'", decoder.messageName().toString().data());
+#endif
+}
+
+#if !LOG_DISABLED
+const char* SuspendedPageProxy::loggingString() const
+{
+    return debugString("(", String::format("%p", this), "page ID ", String::number(m_page.pageID()), ", m_finishedSuspending ", String::number(m_finishedSuspending), ")");
+}
+#endif
+
+} // namespace WebKit

Copied: trunk/Source/WebKit/UIProcess/SuspendedPageProxy.h (from rev 230639, trunk/Source/WebKit/Platform/Logging.h) (0 => 230640)


--- trunk/Source/WebKit/UIProcess/SuspendedPageProxy.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/SuspendedPageProxy.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "Connection.h"
+#include "WebBackForwardListItem.h"
+#include <wtf/RefCounted.h>
+
+namespace WebKit {
+
+class WebPageProxy;
+class WebProcessProxy;
+
+class SuspendedPageProxy : public RefCounted<SuspendedPageProxy> {
+public:
+    static Ref<SuspendedPageProxy> create(WebPageProxy& page, WebProcessProxy& process, WebBackForwardListItem& item)
+    {
+        return adoptRef(*new SuspendedPageProxy(page, process, item));
+    }
+
+    virtual ~SuspendedPageProxy();
+
+    void didReceiveMessage(IPC::Connection&, IPC::Decoder&);
+
+    WebPageProxy& page() const { return m_page; }
+    WebProcessProxy* process() const { return m_process; }
+    WebBackForwardListItem& item() const { return m_backForwardListItem; }
+
+    bool finishedSuspending() const { return m_finishedSuspending; }
+
+    void webProcessDidClose(WebProcessProxy&);
+
+#if !LOG_DISABLED
+    const char* loggingString() const;
+#endif
+
+private:
+    SuspendedPageProxy(WebPageProxy&, WebProcessProxy&, WebBackForwardListItem&);
+
+    void didFinishLoad();
+
+    WebPageProxy& m_page;
+    WebProcessProxy* m_process;
+    Ref<WebBackForwardListItem> m_backForwardListItem;
+
+    bool m_finishedSuspending { false };
+};
+
+} // namespace WebKit

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (230639 => 230640)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -642,7 +642,7 @@
 void WebPageProxy::reattachToWebProcess()
 {
     auto process = makeRef(m_process->processPool().createNewWebProcessRespectingProcessCountLimit(m_websiteDataStore.get()));
-    reattachToWebProcess(WTFMove(process));
+    reattachToWebProcess(WTFMove(process), false);
 }
 
 void WebPageProxy::attachToProcessForNavigation(Ref<WebProcessProxy>&& process)
@@ -653,18 +653,45 @@
 
     // FIXME: this is to fix the ASSERT(isValid()) inside reattachToWebProcess, some other way to fix this is needed.
     m_isValid = false;
-    reattachToWebProcess(WTFMove(process));
+    reattachToWebProcess(WTFMove(process), true);
 }
 
-void WebPageProxy::reattachToWebProcess(Ref<WebProcessProxy>&& process)
+SuspendedPageProxy* WebPageProxy::maybeCreateSuspendedPage(WebProcessProxy& process)
 {
+    ASSERT(!m_suspendedPage || m_suspendedPage->process() != &process);
+
+    auto* currentItem = m_backForwardList->currentItem();
+    if (!currentItem) {
+        LOG(ProcessSwapping, "WebPageProxy %" PRIu64 " unable to create suspended page for process pid %i - No current back/forward item", pageID(), process.processIdentifier());
+        return nullptr;
+    }
+
+    m_suspendedPage = SuspendedPageProxy::create(*this, process, *currentItem);
+
+    LOG(ProcessSwapping, "WebPageProxy %" PRIu64 " created suspended page %s for process pid %i, back/forward item %" PRIu64, pageID(), m_suspendedPage->loggingString(), process.processIdentifier(), currentItem->itemID());
+
+    return m_suspendedPage.get();
+}
+
+void WebPageProxy::suspendedPageProcessClosed(SuspendedPageProxy& page)
+{
+    ASSERT_UNUSED(page, &page == m_suspendedPage.get());
+    m_suspendedPage = nullptr;
+}
+
+void WebPageProxy::reattachToWebProcess(Ref<WebProcessProxy>&& process, bool suspendInOldProcess)
+{
     ASSERT(!m_isClosed);
     ASSERT(!isValid());
 
     m_isValid = true;
-    m_process->removeWebPage(*this, m_pageID);
-    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID);
 
+    if (!suspendInOldProcess) {
+        m_process->removeWebPage(*this, m_pageID);
+        m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID);
+    } else
+        m_process->suspendWebPageProxy(*this);
+
     m_process = WTFMove(process);
 
     ASSERT(m_process->state() != ChildProcessProxy::State::Terminated);
@@ -2354,7 +2381,7 @@
             auto proposedProcess = process().processPool().processForNavigation(*this, *navigation, action);
 
             if (proposedProcess.ptr() != &process()) {
-                LOG(Loading, "Switching from process %i to new process for navigation %" PRIu64 " '%s'", processIdentifier(), navigation->navigationID(), navigation->loggingString().utf8().data());
+                LOG(ProcessSwapping, "Switching from process %i to new process for navigation %" PRIu64 " '%s'", processIdentifier(), navigation->navigationID(), navigation->loggingString().utf8().data());
 
                 RunLoop::main().dispatch([this, protectedThis = makeRef(*this), navigation = makeRef(*navigation), proposedProcess = WTFMove(proposedProcess)]() mutable {
                     continueNavigationInNewProcess(navigation.get(), WTFMove(proposedProcess));
@@ -3571,6 +3598,8 @@
 
 void WebPageProxy::didFinishLoadForFrame(uint64_t frameID, uint64_t navigationID, const UserData& userData)
 {
+    LOG(Loading, "WebPageProxy::didFinishLoadForFrame - WebPageProxy %p with navigationID %llu didFinishLoad", this, navigationID);
+
     PageClientProtector protector(m_pageClient);
 
     WebFrameProxy* frame = m_process->webFrame(frameID);

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (230639 => 230640)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -44,6 +44,7 @@
 #include "ProcessThrottler.h"
 #include "SandboxExtension.h"
 #include "ShareableBitmap.h"
+#include "SuspendedPageProxy.h"
 #include "SystemPreviewController.h"
 #include "UserMediaPermissionRequestManagerProxy.h"
 #include "VisibleContentRectUpdateInfo.h"
@@ -1286,6 +1287,9 @@
 
     WebPreferencesStore preferencesStore() const;
 
+    SuspendedPageProxy* maybeCreateSuspendedPage(WebProcessProxy&);
+    void suspendedPageProcessClosed(SuspendedPageProxy&);
+
 private:
     WebPageProxy(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&);
     void platformInitialize();
@@ -1436,7 +1440,7 @@
 
     void reattachToWebProcess();
     void attachToProcessForNavigation(Ref<WebProcessProxy>&&);
-    void reattachToWebProcess(Ref<WebProcessProxy>&&);
+    void reattachToWebProcess(Ref<WebProcessProxy>&&, bool suspendInOldProcess);
 
     RefPtr<API::Navigation> reattachToWebProcessForReload();
     RefPtr<API::Navigation> reattachToWebProcessWithItem(WebBackForwardListItem&);
@@ -2129,6 +2133,11 @@
 #endif
 
     std::optional<MonotonicTime> m_pageLoadStart;
+
+    // FIXME: Support more than one suspended page per WebPageProxy,
+    // and have a global collection of them per process pool
+    // (e.g. for that process pool's page cache)
+    RefPtr<SuspendedPageProxy> m_suspendedPage;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp (230639 => 230640)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -34,6 +34,7 @@
 #include "Logging.h"
 #include "PluginInfoStore.h"
 #include "PluginProcessManager.h"
+#include "SuspendedPageProxy.h"
 #include "TextChecker.h"
 #include "TextCheckerState.h"
 #include "UIMessagePortChannelProvider.h"
@@ -395,6 +396,27 @@
     updateBackgroundResponsivenessTimer();
 }
 
+void WebProcessProxy::suspendWebPageProxy(WebPageProxy& webPage)
+{
+    if (auto* suspendedPage = webPage.maybeCreateSuspendedPage(*this)) {
+        LOG(ProcessSwapping, "WebProcessProxy pid %i added suspended page %s", processIdentifier(), suspendedPage->loggingString());
+        m_suspendedPageMap.set(webPage.pageID(), suspendedPage);
+    }
+
+    removeWebPage(webPage, webPage.pageID());
+    removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), webPage.pageID());
+}
+
+void WebProcessProxy::suspendedPageWasDestroyed(SuspendedPageProxy& suspendedPage)
+{
+    LOG(ProcessSwapping, "WebProcessProxy pid %i suspended page %s was destroyed", processIdentifier(), suspendedPage.loggingString());
+
+    ASSERT(m_suspendedPageMap.contains(suspendedPage.page().pageID()));
+    m_suspendedPageMap.remove(suspendedPage.page().pageID());
+
+    maybeShutDown();
+}
+
 void WebProcessProxy::removeWebPage(WebPageProxy& webPage, uint64_t pageID)
 {
     auto* removedPage = m_pageMap.take(pageID);
@@ -414,12 +436,7 @@
     for (auto itemID : itemIDsToRemove)
         m_backForwardListItemMap.remove(itemID);
 
-    // If this was the last WebPage open in that web process, and we have no other reason to keep it alive, let it go.
-    // We only allow this when using a network process, as otherwise the WebProcess needs to preserve its session state.
-    if (state() == State::Terminated || !canTerminateChildProcess())
-        return;
-
-    shutDown();
+    maybeShutDown();
 }
 
 void WebProcessProxy::addVisitedLinkStore(VisitedLinkStore& store)
@@ -637,6 +654,15 @@
         return;
     }
 
+    // WebPageProxy messages are normally handled by the normal "dispatchMessage" up above.
+    // If they were not handled there, then they may potentially be handled by SuspendedPageProxy objects.
+    if (decoder.messageReceiverName() == Messages::WebPageProxy::messageReceiverName()) {
+        if (auto* suspendedPage = m_suspendedPageMap.get(decoder.destinationID())) {
+            suspendedPage->didReceiveMessage(connection, decoder);
+            return;
+        }
+    }
+
     // FIXME: Add unhandled message logging.
 }
 
@@ -680,6 +706,10 @@
     for (auto& page : pages)
         page->processDidTerminate(ProcessTerminationReason::Crash);
 
+    for (auto* suspendedPage : copyToVectorOf<SuspendedPageProxy*>(m_suspendedPageMap.values()))
+        suspendedPage->webProcessDidClose(*this);
+
+    m_suspendedPageMap.clear();
 }
 
 void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection& connection, IPC::StringReference messageReceiverName, IPC::StringReference messageName)
@@ -827,9 +857,17 @@
     m_userInitiatedActionMap.remove(identifier);
 }
 
+void WebProcessProxy::maybeShutDown()
+{
+    if (state() == State::Terminated || !canTerminateChildProcess())
+        return;
+
+    shutDown();
+}
+
 bool WebProcessProxy::canTerminateChildProcess()
 {
-    if (!m_pageMap.isEmpty())
+    if (!m_pageMap.isEmpty() || !m_suspendedPageMap.isEmpty())
         return false;
 
     if (!m_processPool->shouldTerminate(this))

Modified: trunk/Source/WebKit/UIProcess/WebProcessProxy.h (230639 => 230640)


--- trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/UIProcess/WebProcessProxy.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -66,6 +66,7 @@
 class NetworkProcessProxy;
 class ObjCObjectGraph;
 class PageClient;
+class SuspendedPageProxy;
 class UserMediaCaptureManagerProxy;
 class VisitedLinkStore;
 class WebBackForwardListItem;
@@ -208,6 +209,9 @@
     void didCommitProvisionalLoad() { m_hasCommittedAnyProvisionalLoads = true; }
     bool hasCommittedAnyProvisionalLoads() const { return m_hasCommittedAnyProvisionalLoads; }
 
+    void suspendWebPageProxy(WebPageProxy&);
+    void suspendedPageWasDestroyed(SuspendedPageProxy&);
+
 protected:
     static uint64_t generatePageID();
     WebProcessProxy(WebProcessPool&, WebsiteDataStore&);
@@ -221,6 +225,7 @@
     // Called when the web process has crashed or we know that it will terminate soon.
     // Will potentially cause the WebProcessProxy object to be freed.
     void shutDown();
+    void maybeShutDown();
 
     // IPC message handlers.
     void addOrUpdateBackForwardItem(uint64_t itemID, uint64_t pageID, const PageState&);
@@ -298,6 +303,7 @@
     HashSet<String> m_localPathsWithAssumedReadAccess;
 
     WebPageProxyMap m_pageMap;
+    HashMap<uint64_t, SuspendedPageProxy*> m_suspendedPageMap;
     WebFrameProxyMap m_frameMap;
     WebBackForwardListItemMap m_backForwardListItemMap;
     UserInitiatedActionMap m_userInitiatedActionMap;

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (230639 => 230640)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1070,6 +1070,7 @@
 		515BE1B41D5917FF00DD7C68 /* UIGamepad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 515BE1AC1D555C5100DD7C68 /* UIGamepad.cpp */; };
 		515BE1B51D5917FF00DD7C68 /* UIGamepad.h in Headers */ = {isa = PBXBuildFile; fileRef = 515BE1AD1D555C5100DD7C68 /* UIGamepad.h */; };
 		515BE1B71D5A94FD00DD7C68 /* UIGamepadProviderMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 515BE1B61D5A94F900DD7C68 /* UIGamepadProviderMac.mm */; };
+		515C415C207D7CAE00726E02 /* SuspendedPageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 515C415A207D74E000726E02 /* SuspendedPageProxy.cpp */; };
 		515E7727183DD6F60007203F /* AsyncRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 515E7725183DD6F60007203F /* AsyncRequest.cpp */; };
 		515E7728183DD6F60007203F /* AsyncRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = 515E7726183DD6F60007203F /* AsyncRequest.h */; };
 		5160BFE113381DF900918999 /* LoggingFoundation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5160BFE013381DF900918999 /* LoggingFoundation.mm */; };
@@ -3486,6 +3487,8 @@
 		515BE1B01D59006900DD7C68 /* GamepadData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GamepadData.h; sourceTree = "<group>"; };
 		515BE1B11D5902B600DD7C68 /* GamepadData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GamepadData.cpp; sourceTree = "<group>"; };
 		515BE1B61D5A94F900DD7C68 /* UIGamepadProviderMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = UIGamepadProviderMac.mm; path = UIProcess/Gamepad/mac/UIGamepadProviderMac.mm; sourceTree = SOURCE_ROOT; };
+		515C415A207D74E000726E02 /* SuspendedPageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SuspendedPageProxy.cpp; sourceTree = "<group>"; };
+		515C415B207D74E100726E02 /* SuspendedPageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SuspendedPageProxy.h; sourceTree = "<group>"; };
 		515E7725183DD6F60007203F /* AsyncRequest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AsyncRequest.cpp; sourceTree = "<group>"; };
 		515E7726183DD6F60007203F /* AsyncRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AsyncRequest.h; sourceTree = "<group>"; };
 		5160BFE013381DF900918999 /* LoggingFoundation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoggingFoundation.mm; sourceTree = "<group>"; };
@@ -7269,6 +7272,8 @@
 				414DEDD51F9EDDDF0047C40D /* ServiceWorkerProcessProxy.h */,
 				51A4D5A816CAC4FF000E615E /* StatisticsRequest.cpp */,
 				514BDED216C98EDD00E4E25E /* StatisticsRequest.h */,
+				515C415A207D74E000726E02 /* SuspendedPageProxy.cpp */,
+				515C415B207D74E100726E02 /* SuspendedPageProxy.h */,
 				318A1F04204F4764003480BC /* SystemPreviewController.cpp */,
 				3157135D2040A9B20084F9CF /* SystemPreviewController.h */,
 				1AA417C912C00CCA002BE67B /* TextChecker.h */,
@@ -10893,6 +10898,7 @@
 				5118E9AC1F295977003EF9F5 /* StorageToWebProcessConnectionMessageReceiver.cpp in Sources */,
 				1AE00D6B18327C1200087DD7 /* StringReference.cpp in Sources */,
 				296BD85E15019BC30071F424 /* StringUtilities.mm in Sources */,
+				515C415C207D7CAE00726E02 /* SuspendedPageProxy.cpp in Sources */,
 				318A1F05204F4764003480BC /* SystemPreviewController.cpp in Sources */,
 				3157135E2040A9B20084F9CF /* SystemPreviewControllerCocoa.mm in Sources */,
 				1ZZ417EF12C00D87002BE67B /* TextCheckerCompletion.cpp in Sources */,

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (230639 => 230640)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2018-04-13 18:04:22 UTC (rev 230640)
@@ -5866,6 +5866,11 @@
     handler->taskDidComplete(taskIdentifier, error);
 }
 
+void WebPage::setIsSuspended(bool suspended)
+{
+    m_isSuspended = suspended;
+}
+
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
 static uint64_t nextRequestStorageAccessContextId()
 {

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (230639 => 230640)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1072,6 +1072,8 @@
 
     UserContentControllerIdentifier userContentControllerIdentifier() const { return m_userContentController->identifier(); }
 
+    bool isSuspended() const { return m_isSuspended; }
+
 private:
     WebPage(uint64_t pageID, WebPageCreationParameters&&);
 
@@ -1388,6 +1390,8 @@
     void urlSchemeTaskDidReceiveData(uint64_t handlerIdentifier, uint64_t taskIdentifier, const IPC::DataReference&);
     void urlSchemeTaskDidComplete(uint64_t handlerIdentifier, uint64_t taskIdentifier, const WebCore::ResourceError&);
 
+    void setIsSuspended(bool);
+
     RefPtr<WebImage> snapshotAtSize(const WebCore::IntRect&, const WebCore::IntSize& bitmapSize, SnapshotOptions);
     RefPtr<WebImage> snapshotNode(WebCore::Node&, SnapshotOptions, unsigned maximumPixelCount = std::numeric_limits<unsigned>::max());
 #if USE(CF)
@@ -1705,6 +1709,7 @@
     std::unique_ptr<WebCredentialsMessenger> m_credentialsMessenger;
 #endif
 
+    bool m_isSuspended { false };
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (230639 => 230640)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2018-04-13 18:04:22 UTC (rev 230640)
@@ -491,6 +491,8 @@
     URLSchemeTaskDidReceiveData(uint64_t handlerIdentifier, uint64_t taskIdentifier, IPC::DataReference data)
     URLSchemeTaskDidComplete(uint64_t handlerIdentifier, uint64_t taskIdentifier, WebCore::ResourceError error)
 
+    SetIsSuspended(bool suspended)
+
 #if HAVE(CFNETWORK_STORAGE_PARTITIONING)
     StorageAccessResponse(bool wasGranted, uint64_t contextId)
 #endif

Modified: trunk/Source/WebKit/WebProcess/WebProcess.messages.in (230639 => 230640)


--- trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebProcess/WebProcess.messages.in	2018-04-13 18:04:22 UTC (rev 230640)
@@ -128,6 +128,8 @@
     CheckProcessLocalPortForActivity(struct WebCore::MessagePortIdentifier port, uint64_t callbackIdentifier)
     MessagesAvailableForPort(struct WebCore::MessagePortIdentifier port)
 
+    UpdateActivePages()
+
 #if PLATFORM(MAC)
     SetScreenProperties(uint32_t primaryScreenID, HashMap<uint32_t, WebCore::ScreenProperties> screenProperties)
 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101400

Modified: trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm (230639 => 230640)


--- trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm	2018-04-13 18:04:22 UTC (rev 230640)
@@ -387,7 +387,7 @@
     auto activePageURLs = adoptNS([[NSMutableArray alloc] init]);
 
     for (auto& page : m_pageMap.values()) {
-        if (page->usesEphemeralSession())
+        if (page->usesEphemeralSession() || page->isSuspended())
             continue;
 
         if (NSURL *originAsURL = origin(*page))

Modified: trunk/Tools/ChangeLog (230639 => 230640)


--- trunk/Tools/ChangeLog	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Tools/ChangeLog	2018-04-13 18:04:22 UTC (rev 230640)
@@ -1,3 +1,12 @@
+2018-04-13  Brady Eidson  <beid...@apple.com>
+
+        Introduce SuspendedPageProxy to keep old web processes around after their WebPageProxy has been swapped to a new one.
+        https://bugs.webkit.org/show_bug.cgi?id=184559
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+
 2018-04-13  Chris Dumez  <cdu...@apple.com>
 
         input.webkitEntries does not work as expected when folder contains accented chars

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm (230639 => 230640)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm	2018-04-13 17:37:31 UTC (rev 230639)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm	2018-04-13 18:04:22 UTC (rev 230640)
@@ -751,4 +751,44 @@
     EXPECT_EQ(1u, seenPIDs.size());
 }
 
+TEST(ProcessSwap, OnePreviousProcessRemains)
+{
+    auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    [processPoolConfiguration setProcessSwapsOnNavigation:YES];
+    auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
+
+    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [webViewConfiguration setProcessPool:processPool.get()];
+    auto handler = adoptNS([[PSONScheme alloc] init]);
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"PSON"];
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host1/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host2/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://host3/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    // Navigations to 3 different domains, we expect to have seen 3 different PIDs
+    EXPECT_EQ(3u, seenPIDs.size());
+
+    // But only 2 of those processes should still be alive
+    EXPECT_EQ(2u, [processPool _webProcessCount]);
+}
+
 #endif // WK_API_ENABLED
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to