Regression(PSON) View becomes blank after click a cross-site download link
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jan 2019 19:01:43 +0000 (19:01 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 15 Jan 2019 19:01:43 +0000 (19:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193361
<rdar://problem/47099573>

Reviewed by Geoff Garen.

Source/WebCore:

* loader/FrameLoader.cpp:
(WebCore::FrameLoader::commitProvisionalLoad):
When restoring from PageCache, make sure we notify the client that the load was committed
*before* we tell it that the navigation is complete. This would confuse the ProvisionalPageProxy
logic in the UIProcess.

Source/WebKit:

The issue tracked by rdar://problem/47099573 is that a provisional load may get
canceled (or converted into a download) *after* we've decided to process-swap.
In such cases, the view should keep displaying the current site and it should
still be interactive. However, with the previous PSON model, the view (pageProxy)
would have already swapped to the new process and would end up displaying the
initially empty document.

To address the issue, this patch introduces the concept of a provisional load
in the UIProcess, handled by a ProvisionalPageProxy which has its own privisional
process. The WebPageProxy owns the ProvisionalPageProxy but we do not commit the
provisional page until after the load was committed in the new process. This means
that the view / WebPageProxy keeps using the old process and displays the current
content until a load has committed in the provisional page. If no load commits
in the provisional process (e.g. because the load is cancelled or converted into
a download), then we merely destroy the ProvisionalPageProxy and terminate its
process, without impacting the WebPageProxy.

* Shared/WebPageCreationParameters.cpp:
(WebKit::WebPageCreationParameters::encode const):
(WebKit::WebPageCreationParameters::decode):
* Shared/WebPageCreationParameters.h:
Rename isSwapFromSuspended to isProcessSwap for clarity as we always want to
delay attaching the drawing area in case of a process-swap, no matter what
now that the previous process is kept alive until the provisional load is
committed in the new process.

* Shared/WebPolicyAction.h:
Drop Suspend policy action. At decidePolicyForNavigationAction time, in case of
process-swap, we now tell the previous process to simply ignore the load, while
we create a ProvisionalPageProxy to do the new load in a new process.
Suspension of the previous page in the old process, happens later, when a load
is actually committed in the new process.

* Sources.txt:
Add new ProvisionalPageProxy file.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _killWebContentProcessAndResetState]):
When calling _killWebContentProcessAndResetState on a WKWebView, kill both the current
process and the provisional one, to maintain previous behavior in our API tests.

* UIProcess/PageClient.h:
Tiny build fix.

* UIProcess/ProvisionalPageProxy.cpp: Added.
(WebKit::ProvisionalPageProxy::ProvisionalPageProxy):
(WebKit::ProvisionalPageProxy::~ProvisionalPageProxy):
(WebKit::ProvisionalPageProxy::takeDrawingArea):
(WebKit::ProvisionalPageProxy::cancel):
(WebKit::ProvisionalPageProxy::initializeWebPage):
(WebKit::ProvisionalPageProxy::loadDataWithNavigation):
(WebKit::ProvisionalPageProxy::loadRequestWithNavigation):
(WebKit::ProvisionalPageProxy::goToBackForwardItem):
(WebKit::ProvisionalPageProxy::didCreateMainFrame):
(WebKit::ProvisionalPageProxy::didStartProvisionalLoadForFrame):
(WebKit::ProvisionalPageProxy::didFailProvisionalLoadForFrame):
(WebKit::ProvisionalPageProxy::didCommitLoadForFrame):
(WebKit::ProvisionalPageProxy::didReceiveMessage):
(WebKit::ProvisionalPageProxy::didReceiveSyncMessage):
* UIProcess/ProvisionalPageProxy.h: Added.
(WebKit::ProvisionalPageProxy::page):
(WebKit::ProvisionalPageProxy::mainFrame const):
(WebKit::ProvisionalPageProxy::process):
(WebKit::ProvisionalPageProxy::processSwapRequestedByClient const):
(WebKit::ProvisionalPageProxy::navigationID const):
Add new ProvisionalPageProxy class to wrap the provisional load in the new process
after a swap. The provisional page is owned by the WebPageProxy and we only commit
the provisional page when the load is committed. Until then, the WebPageProxy keeps
using the old process and displaying the current content.

* UIProcess/SuspendedPageProxy.cpp:
(WebKit::SuspendedPageProxy::~SuspendedPageProxy):
(WebKit::SuspendedPageProxy::unsuspend):
(WebKit::SuspendedPageProxy::didProcessRequestToSuspend):
Unregister the SuspendedPageProxy as an IPC message receiver a little bit earlier
when we're going to unsuspend it. This avoids conflicting with the ProvisionalPageProxy
which tries to register itself as an IPC message receiver for the same pageID when
a process-swap occurs and we're switching to a suspended page.

* UIProcess/WebFrameProxy.cpp:
(WebKit::WebFrameProxy::isMainFrame const):
WebFrameProxy::isMainFrame() relies on checking that the frame is the WebPageProxy's m_mainFrame.
Now that the WebPageProxy can have a ProvisionalPageProxy, also check if it is the ProvisionalPageProxy's
m_mainFrame to maintain previous behavior.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::swapToWebProcess):
swapToWebProcess() no longer takes care of unsuspending the SuspendedPage because we now call swapToWebProcess()
later, when a load is actually committed in the provisional page / process. swapToWebProcess() now also needs
to initialize some data members such as the drawing area and the main frame as it is transferring them over from
the ProvisionalPageProxy which started the provisional load.

(WebKit::WebPageProxy::finishAttachingToWebProcess):
We no longer need IsSwapFromSuspended parameter as this is called later now, after a load has actually been
committed in the provisional process.

(WebKit::WebPageProxy::initializeWebPage):
- We no longer need IsSwapFromSuspended parameter as this is called later now, after a load has actually been
  committed in the provisional process.
- Factor some code out to WebPageProxy::setDrawingArea() so that it can be shared with swapToWebProcess().

(WebKit::WebPageProxy::loadRequestWithNavigation):
Only call setPendingAPIRequestURL() in loadRequestWithNavigation() only if ShouldTreatAsContinuingLoad is not
Yes. This avoids hitting some assertions as this was already called during the first API call if needed.

(WebKit::WebPageProxy::receivedNavigationPolicyDecision):
In case of process swap, tell the previous process to ignore the load instead of suspending it right away.
Suspension now happens later, only if we end up committing the provisional load in the new process.
Also discard the SuspendedPage if it failed to suspend (we only reuse its process in this case). This used
to happen a bit later but it is clearer if we do this as early as possible I think.

(WebKit::WebPageProxy::commitProvisionalPage):
When the load is committed in the new process, we call WebPageProxy::commitProvisionalPage(). It takes care of:
- Actually swapping the WebPageProxy to the new process by calling processDidTerminate() / swapToWebProcess().
- Suspending the current page.
- Letting the client know the load is comitted
- Destroy the ProvisionalPageProxy.
This work used to happen earlier in continueNavigationInNewProcess().

(WebKit::WebPageProxy::continueNavigationInNewProcess):
Moved some of the logic to commitProvisionalPage(). We now merely start the load in a new ProvisionalPageProxy,
without actually having the WebPageProxy switch to the new process yet.

(WebKit::WebPageProxy::didCreateMainFrame):
(WebKit::WebPageProxy::didCreateWindow):
Drop some code that is no longer needed, now that the ProvisionalPageProxy takes care of this.

(WebKit::WebPageProxy::didDestroyNavigation):
On process-swap, when telling the previous process to ignore the load and starting the new load in a new
ProvisionalPageProxy, the previous WebPage attempts to destroy the navigation. In this case, we ignore
the call since the navigation is merely taken over by the ProvisionalPageProxy.

(WebKit::WebPageProxy::didStartProvisionalLoadForFrame):
Moved some PSON logic to the ProvisionalPageProxy instead.

(WebKit::WebPageProxy::didFailProvisionalLoadForFrame):
When didFailProvisionalLoadForFrame() is called for a ProvisionalPageProxy, destroy it.

(WebKit::WebPageProxy::decidePolicyForNavigationActionAsync):
(WebKit::WebPageProxy::decidePolicyForResponse):
Capture the process in the lambda, to make sure we send the policy decision to the same process that
asked for it, so as to not get confused by process swaps.

(WebKit::WebPageProxy::resetState):
Drop some code that is no longer needed.

(WebKit::WebPageProxy::creationParameters):
Move the hasRegisteredServiceWorkers flag initialization from the call site to here now that we have
more than one call site. This was just some bad factoring.

(WebKit::WebPageProxy::PageProcessOverride::PageProcessOverride):
(WebKit::WebPageProxy::PageProcessOverride::~PageProcessOverride):
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::temporarilyOverrideProcess):
Add utility class to temporarily override the WebPageProxy's m_process with the provisional one when
the ProvisionalPageProxy interacts with the WebPageProxy.

* UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::hasProvisionalPageWithID const):
(WebKit::WebProcessProxy::updateBackForwardItem):
Before updating a BackForwardListItem, we normally make sure the process has a WebPageProxy with the
item's pageID. We have to tweak the logic because there may now be no WebPageProxy with this pageID
associated with this process yet, because it is still a ProvisionalPageProxy.

(WebKit::WebProcessProxy::canTerminateChildProcess):
Do not terminate the WebProcess if there are ProvisionalPageProxy objects using it.

* UIProcess/WebProcessProxy.h:
(WebKit::WebProcessProxy::addProvisionalPageProxy):
(WebKit::WebProcessProxy::removeProvisionalPageProxy):

* WebKit.xcodeproj/project.pbxproj:

* WebProcess/WebPage/WebFrame.cpp:
(WebKit::toPolicyAction):
(WebKit::WebFrame::didReceivePolicyDecision):
Stop dealing with WebPolicyAction::Suspend as it no longer exists.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::reinitializeWebPage):
(WebKit::WebPage::didReceivePolicyDecision):

(WebKit::WebPage::setIsSuspended):
Suspend the Page when setIsSuspended(true) is called, now that there is no longer a WebPolicyAction::Suspend.
setIsSuspended(true) IPC is sent when we actually commit the provisional page.

* WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
(WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):

Tools:

Add API test coverage.

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
(-[PSONNavigationDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]):

LayoutTests:

Skip test testing the cross-process DOMWindow API as I broke it with this patch.
The feature is far from ready and off by default anyway. I will add support back
in a follow-up.

* platform/wk2/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@239993 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/loader/FrameLoader.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebPageCreationParameters.cpp
Source/WebKit/Shared/WebPageCreationParameters.h
Source/WebKit/Shared/WebPolicyAction.h
Source/WebKit/Sources.txt
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/PageClient.h
Source/WebKit/UIProcess/ProvisionalPageProxy.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/ProvisionalPageProxy.h [new file with mode: 0644]
Source/WebKit/UIProcess/SuspendedPageProxy.cpp
Source/WebKit/UIProcess/WebFrameProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebProcessProxy.cpp
Source/WebKit/UIProcess/WebProcessProxy.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/WebPage/WebFrame.cpp
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm

index 411004b..0140f81 100644 (file)
@@ -1,3 +1,17 @@
+2019-01-15  Chris Dumez  <cdumez@apple.com>
+
+        Regression(PSON) View becomes blank after click a cross-site download link
+        https://bugs.webkit.org/show_bug.cgi?id=193361
+        <rdar://problem/47099573>
+
+        Reviewed by Geoff Garen.
+
+        Skip test testing the cross-process DOMWindow API as I broke it with this patch.
+        The feature is far from ready and off by default anyway. I will add support back
+        in a follow-up.
+
+        * platform/wk2/TestExpectations:
+
 2019-01-15  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][Out-of-flow] Ignore bottom when the vertical values are over-constrained
index 8e959ed..9d90be8 100644 (file)
@@ -733,8 +733,8 @@ http/tests/resourceLoadStatistics/remove-blocking-in-redirect.html [ Skip ]
 http/tests/resourceLoadStatistics/non-prevalent-resources-can-access-cookies-in-a-third-party-context.html [ Skip ]
 http/tests/resourceLoadStatistics/cap-cache-max-age-for-prevalent-resource.html [ Skip ]
 
-# Process swapping is only implemented on WebKit2.
-http/tests/navigation/process-swap-window-open.html [ Pass ]
+# This feature is currently disabled by default and unfinished <rdar://problem/38925077>.
+http/tests/navigation/process-swap-window-open.html [ Skip ]
 
 # Cross-Origin-Resource-Policy response header is only implemented in WebKit2.
 http/wpt/cross-origin-resource-policy/ [ Pass ]
index 1e425ca..b238b0b 100644 (file)
@@ -1,3 +1,17 @@
+2019-01-15  Chris Dumez  <cdumez@apple.com>
+
+        Regression(PSON) View becomes blank after click a cross-site download link
+        https://bugs.webkit.org/show_bug.cgi?id=193361
+        <rdar://problem/47099573>
+
+        Reviewed by Geoff Garen.
+
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::commitProvisionalLoad):
+        When restoring from PageCache, make sure we notify the client that the load was committed
+        *before* we tell it that the navigation is complete. This would confuse the ProvisionalPageProxy
+        logic in the UIProcess.
+
 2019-01-15  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][Out-of-flow] Ignore bottom when the vertical values are over-constrained
index 4c26a69..ebf950b 100644 (file)
@@ -2032,11 +2032,11 @@ void FrameLoader::commitProvisionalLoad()
 
         Optional<HasInsecureContent> hasInsecureContent = cachedPage->cachedMainFrame()->hasInsecureContent();
 
+        dispatchDidCommitLoad(hasInsecureContent);
+
         // FIXME: This API should be turned around so that we ground CachedPage into the Page.
         cachedPage->restore(*m_frame.page());
 
-        dispatchDidCommitLoad(hasInsecureContent);
-
 #if PLATFORM(IOS_FAMILY)
         m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(false);
         m_frame.page()->chrome().dispatchViewportPropertiesDidChange(m_frame.page()->viewportArguments());
index 5c01c37..d199a1f 100644 (file)
@@ -1,3 +1,200 @@
+2019-01-15  Chris Dumez  <cdumez@apple.com>
+
+        Regression(PSON) View becomes blank after click a cross-site download link
+        https://bugs.webkit.org/show_bug.cgi?id=193361
+        <rdar://problem/47099573>
+
+        Reviewed by Geoff Garen.
+
+        The issue tracked by rdar://problem/47099573 is that a provisional load may get
+        canceled (or converted into a download) *after* we've decided to process-swap.
+        In such cases, the view should keep displaying the current site and it should
+        still be interactive. However, with the previous PSON model, the view (pageProxy)
+        would have already swapped to the new process and would end up displaying the
+        initially empty document.
+
+        To address the issue, this patch introduces the concept of a provisional load
+        in the UIProcess, handled by a ProvisionalPageProxy which has its own privisional
+        process. The WebPageProxy owns the ProvisionalPageProxy but we do not commit the
+        provisional page until after the load was committed in the new process. This means
+        that the view / WebPageProxy keeps using the old process and displays the current
+        content until a load has committed in the provisional page. If no load commits
+        in the provisional process (e.g. because the load is cancelled or converted into
+        a download), then we merely destroy the ProvisionalPageProxy and terminate its
+        process, without impacting the WebPageProxy.
+
+        * Shared/WebPageCreationParameters.cpp:
+        (WebKit::WebPageCreationParameters::encode const):
+        (WebKit::WebPageCreationParameters::decode):
+        * Shared/WebPageCreationParameters.h:
+        Rename isSwapFromSuspended to isProcessSwap for clarity as we always want to
+        delay attaching the drawing area in case of a process-swap, no matter what
+        now that the previous process is kept alive until the provisional load is
+        committed in the new process.
+
+        * Shared/WebPolicyAction.h:
+        Drop Suspend policy action. At decidePolicyForNavigationAction time, in case of
+        process-swap, we now tell the previous process to simply ignore the load, while
+        we create a ProvisionalPageProxy to do the new load in a new process.
+        Suspension of the previous page in the old process, happens later, when a load
+        is actually committed in the new process.
+
+        * Sources.txt:
+        Add new ProvisionalPageProxy file.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _killWebContentProcessAndResetState]):
+        When calling _killWebContentProcessAndResetState on a WKWebView, kill both the current
+        process and the provisional one, to maintain previous behavior in our API tests.
+
+        * UIProcess/PageClient.h:
+        Tiny build fix.
+
+        * UIProcess/ProvisionalPageProxy.cpp: Added.
+        (WebKit::ProvisionalPageProxy::ProvisionalPageProxy):
+        (WebKit::ProvisionalPageProxy::~ProvisionalPageProxy):
+        (WebKit::ProvisionalPageProxy::takeDrawingArea):
+        (WebKit::ProvisionalPageProxy::cancel):
+        (WebKit::ProvisionalPageProxy::initializeWebPage):
+        (WebKit::ProvisionalPageProxy::loadDataWithNavigation):
+        (WebKit::ProvisionalPageProxy::loadRequestWithNavigation):
+        (WebKit::ProvisionalPageProxy::goToBackForwardItem):
+        (WebKit::ProvisionalPageProxy::didCreateMainFrame):
+        (WebKit::ProvisionalPageProxy::didStartProvisionalLoadForFrame):
+        (WebKit::ProvisionalPageProxy::didFailProvisionalLoadForFrame):
+        (WebKit::ProvisionalPageProxy::didCommitLoadForFrame):
+        (WebKit::ProvisionalPageProxy::didReceiveMessage):
+        (WebKit::ProvisionalPageProxy::didReceiveSyncMessage):
+        * UIProcess/ProvisionalPageProxy.h: Added.
+        (WebKit::ProvisionalPageProxy::page):
+        (WebKit::ProvisionalPageProxy::mainFrame const):
+        (WebKit::ProvisionalPageProxy::process):
+        (WebKit::ProvisionalPageProxy::processSwapRequestedByClient const):
+        (WebKit::ProvisionalPageProxy::navigationID const):
+        Add new ProvisionalPageProxy class to wrap the provisional load in the new process
+        after a swap. The provisional page is owned by the WebPageProxy and we only commit
+        the provisional page when the load is committed. Until then, the WebPageProxy keeps
+        using the old process and displaying the current content.
+
+        * UIProcess/SuspendedPageProxy.cpp:
+        (WebKit::SuspendedPageProxy::~SuspendedPageProxy):
+        (WebKit::SuspendedPageProxy::unsuspend):
+        (WebKit::SuspendedPageProxy::didProcessRequestToSuspend):
+        Unregister the SuspendedPageProxy as an IPC message receiver a little bit earlier
+        when we're going to unsuspend it. This avoids conflicting with the ProvisionalPageProxy
+        which tries to register itself as an IPC message receiver for the same pageID when
+        a process-swap occurs and we're switching to a suspended page.
+
+        * UIProcess/WebFrameProxy.cpp:
+        (WebKit::WebFrameProxy::isMainFrame const):
+        WebFrameProxy::isMainFrame() relies on checking that the frame is the WebPageProxy's m_mainFrame.
+        Now that the WebPageProxy can have a ProvisionalPageProxy, also check if it is the ProvisionalPageProxy's
+        m_mainFrame to maintain previous behavior.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::swapToWebProcess):
+        swapToWebProcess() no longer takes care of unsuspending the SuspendedPage because we now call swapToWebProcess()
+        later, when a load is actually committed in the provisional page / process. swapToWebProcess() now also needs
+        to initialize some data members such as the drawing area and the main frame as it is transferring them over from
+        the ProvisionalPageProxy which started the provisional load.
+
+        (WebKit::WebPageProxy::finishAttachingToWebProcess):
+        We no longer need IsSwapFromSuspended parameter as this is called later now, after a load has actually been
+        committed in the provisional process.
+
+        (WebKit::WebPageProxy::initializeWebPage):
+        - We no longer need IsSwapFromSuspended parameter as this is called later now, after a load has actually been
+          committed in the provisional process.
+        - Factor some code out to WebPageProxy::setDrawingArea() so that it can be shared with swapToWebProcess().
+
+        (WebKit::WebPageProxy::loadRequestWithNavigation):
+        Only call setPendingAPIRequestURL() in loadRequestWithNavigation() only if ShouldTreatAsContinuingLoad is not
+        Yes. This avoids hitting some assertions as this was already called during the first API call if needed.
+
+        (WebKit::WebPageProxy::receivedNavigationPolicyDecision):
+        In case of process swap, tell the previous process to ignore the load instead of suspending it right away.
+        Suspension now happens later, only if we end up committing the provisional load in the new process.
+        Also discard the SuspendedPage if it failed to suspend (we only reuse its process in this case). This used
+        to happen a bit later but it is clearer if we do this as early as possible I think.
+
+        (WebKit::WebPageProxy::commitProvisionalPage):
+        When the load is committed in the new process, we call WebPageProxy::commitProvisionalPage(). It takes care of:
+        - Actually swapping the WebPageProxy to the new process by calling processDidTerminate() / swapToWebProcess().
+        - Suspending the current page.
+        - Letting the client know the load is comitted
+        - Destroy the ProvisionalPageProxy.
+        This work used to happen earlier in continueNavigationInNewProcess().
+
+        (WebKit::WebPageProxy::continueNavigationInNewProcess):
+        Moved some of the logic to commitProvisionalPage(). We now merely start the load in a new ProvisionalPageProxy,
+        without actually having the WebPageProxy switch to the new process yet.
+
+        (WebKit::WebPageProxy::didCreateMainFrame):
+        (WebKit::WebPageProxy::didCreateWindow):
+        Drop some code that is no longer needed, now that the ProvisionalPageProxy takes care of this.
+
+        (WebKit::WebPageProxy::didDestroyNavigation):
+        On process-swap, when telling the previous process to ignore the load and starting the new load in a new
+        ProvisionalPageProxy, the previous WebPage attempts to destroy the navigation. In this case, we ignore
+        the call since the navigation is merely taken over by the ProvisionalPageProxy.
+
+        (WebKit::WebPageProxy::didStartProvisionalLoadForFrame):
+        Moved some PSON logic to the ProvisionalPageProxy instead.
+
+        (WebKit::WebPageProxy::didFailProvisionalLoadForFrame):
+        When didFailProvisionalLoadForFrame() is called for a ProvisionalPageProxy, destroy it.
+
+        (WebKit::WebPageProxy::decidePolicyForNavigationActionAsync):
+        (WebKit::WebPageProxy::decidePolicyForResponse):
+        Capture the process in the lambda, to make sure we send the policy decision to the same process that
+        asked for it, so as to not get confused by process swaps.
+
+        (WebKit::WebPageProxy::resetState):
+        Drop some code that is no longer needed.
+
+        (WebKit::WebPageProxy::creationParameters):
+        Move the hasRegisteredServiceWorkers flag initialization from the call site to here now that we have
+        more than one call site. This was just some bad factoring.
+
+        (WebKit::WebPageProxy::PageProcessOverride::PageProcessOverride):
+        (WebKit::WebPageProxy::PageProcessOverride::~PageProcessOverride):
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::temporarilyOverrideProcess):
+        Add utility class to temporarily override the WebPageProxy's m_process with the provisional one when
+        the ProvisionalPageProxy interacts with the WebPageProxy.
+
+        * UIProcess/WebProcessProxy.cpp:
+        (WebKit::WebProcessProxy::hasProvisionalPageWithID const):
+        (WebKit::WebProcessProxy::updateBackForwardItem):
+        Before updating a BackForwardListItem, we normally make sure the process has a WebPageProxy with the
+        item's pageID. We have to tweak the logic because there may now be no WebPageProxy with this pageID
+        associated with this process yet, because it is still a ProvisionalPageProxy.
+
+        (WebKit::WebProcessProxy::canTerminateChildProcess):
+        Do not terminate the WebProcess if there are ProvisionalPageProxy objects using it.
+
+        * UIProcess/WebProcessProxy.h:
+        (WebKit::WebProcessProxy::addProvisionalPageProxy):
+        (WebKit::WebProcessProxy::removeProvisionalPageProxy):
+
+        * WebKit.xcodeproj/project.pbxproj:
+
+        * WebProcess/WebPage/WebFrame.cpp:
+        (WebKit::toPolicyAction):
+        (WebKit::WebFrame::didReceivePolicyDecision):
+        Stop dealing with WebPolicyAction::Suspend as it no longer exists.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::reinitializeWebPage):
+        (WebKit::WebPage::didReceivePolicyDecision):
+
+        (WebKit::WebPage::setIsSuspended):
+        Suspend the Page when setIsSuspended(true) is called, now that there is no longer a WebPolicyAction::Suspend.
+        setIsSuspended(true) IPC is sent when we actually commit the provisional page.
+
+        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
+        (WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):
+
 2019-01-15  Brian Burg  <bburg@apple.com>
 
         WebDriver: update Create Window implementation to support newest specification
index ca2df40..6d07c03 100644 (file)
@@ -73,7 +73,7 @@ void WebPageCreationParameters::encode(IPC::Encoder& encoder) const
     encoder.encodeEnum(layerHostingMode);
     encoder << mimeTypesWithCustomContentProviders;
     encoder << controlledByAutomation;
-    encoder << isSwapFromSuspended;
+    encoder << isProcessSwap;
 
 #if PLATFORM(MAC)
     encoder << colorSpace;
@@ -227,7 +227,7 @@ Optional<WebPageCreationParameters> WebPageCreationParameters::decode(IPC::Decod
         return WTF::nullopt;
     if (!decoder.decode(parameters.controlledByAutomation))
         return WTF::nullopt;
-    if (!decoder.decode(parameters.isSwapFromSuspended))
+    if (!decoder.decode(parameters.isProcessSwap))
         return WTF::nullopt;
 
 #if PLATFORM(MAC)
index ae4325c..b382cee 100644 (file)
@@ -129,7 +129,7 @@ struct WebPageCreationParameters {
     Vector<String> mimeTypesWithCustomContentProviders;
 
     bool controlledByAutomation;
-    bool isSwapFromSuspended { false };
+    bool isProcessSwap { false };
 
 #if PLATFORM(MAC)
     ColorSpaceData colorSpace;
index 142a518..3971795 100644 (file)
 
 namespace WebKit {
 
+// FIXME: Remove this type and use WebCore::PolicyAction instead.
 enum class WebPolicyAction : uint8_t {
     Use,
     Download,
-    Ignore,
-    Suspend
+    Ignore
 };
 
 }
@@ -45,8 +45,7 @@ template<> struct EnumTraits<WebKit::WebPolicyAction> {
         WebKit::WebPolicyAction,
         WebKit::WebPolicyAction::Use,
         WebKit::WebPolicyAction::Download,
-        WebKit::WebPolicyAction::Ignore,
-        WebKit::WebPolicyAction::Suspend
+        WebKit::WebPolicyAction::Ignore
     >;
 };
 
index a0b6f72..8aecb86 100644 (file)
@@ -230,6 +230,7 @@ UIProcess/InspectorTargetProxy.cpp
 UIProcess/PageLoadState.cpp
 UIProcess/ProcessAssertion.cpp
 UIProcess/ProcessThrottler.cpp
+UIProcess/ProvisionalPageProxy.cpp
 UIProcess/RemoteWebInspectorProxy.cpp
 UIProcess/ResponsivenessTimer.cpp
 UIProcess/ServiceWorkerProcessProxy.cpp
index 074cddf..4dfad64 100644 (file)
@@ -44,6 +44,7 @@
 #import "NavigationState.h"
 #import "ObjCObjectGraph.h"
 #import "PageClient.h"
+#import "ProvisionalPageProxy.h"
 #import "RemoteLayerTreeScrollingPerformanceData.h"
 #import "RemoteLayerTreeTransaction.h"
 #import "RemoteObjectRegistry.h"
@@ -4700,6 +4701,11 @@ FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
 {
     Ref<WebKit::WebProcessProxy> protectedProcessProxy(_page->process());
     protectedProcessProxy->requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
+
+    if (auto* provisionalPageProxy = _page->provisionalPageProxy()) {
+        Ref<WebKit::WebProcessProxy> protectedProcessProxy(provisionalPageProxy->process());
+        protectedProcessProxy->requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
+    }
 }
 
 #if PLATFORM(MAC)
index 149714a..82c7732 100644 (file)
@@ -48,6 +48,7 @@
 
 OBJC_CLASS CALayer;
 OBJC_CLASS NSFileWrapper;
+OBJC_CLASS NSMenu;
 OBJC_CLASS NSSet;
 OBJC_CLASS WKDrawingView;
 OBJC_CLASS _WKRemoteObjectRegistry;
diff --git a/Source/WebKit/UIProcess/ProvisionalPageProxy.cpp b/Source/WebKit/UIProcess/ProvisionalPageProxy.cpp
new file mode 100644 (file)
index 0000000..164e096
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2019 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 "ProvisionalPageProxy.h"
+
+#include "APINavigation.h"
+#include "DrawingAreaProxy.h"
+#include "Logging.h"
+#include "PageClient.h"
+#include "WebBackForwardList.h"
+#include "WebBackForwardListItem.h"
+#include "WebErrors.h"
+#include "WebNavigationState.h"
+#include "WebPageMessages.h"
+#include "WebPageProxy.h"
+#include "WebPageProxyMessages.h"
+#include "WebProcessMessages.h"
+#include "WebProcessProxy.h"
+#include <WebCore/ShouldTreatAsContinuingLoad.h>
+
+namespace WebKit {
+
+#define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - ProvisionalPageProxy::" fmt, this, ##__VA_ARGS__)
+#define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(m_page.isAlwaysOnLoggingAllowed(), channel, "%p - ProvisionalPageProxy::" fmt, this, ##__VA_ARGS__)
+
+ProvisionalPageProxy::ProvisionalPageProxy(WebPageProxy& page, Ref<WebProcessProxy>&& process, std::unique_ptr<SuspendedPageProxy> suspendedPage, uint64_t navigationID, bool isServerRedirect, const WebCore::ResourceRequest& request, ProcessSwapRequestedByClient processSwapRequestedByClient)
+    : m_page(page)
+    , m_process(WTFMove(process))
+    , m_navigationID(navigationID)
+    , m_isServerRedirect(isServerRedirect)
+    , m_request(request)
+    , m_processSwapRequestedByClient(processSwapRequestedByClient)
+#if PLATFORM(IOS_FAMILY)
+    , m_suspensionToken(m_process->throttler().foregroundActivityToken())
+#endif
+{
+    m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID(), *this);
+    m_process->addProvisionalPageProxy(*this);
+
+    // If we are reattaching to a SuspendedPage, then the WebProcess' WebPage already exists and
+    // WebPageProxy::didCreateMainFrame() will not be called to initialize m_mainFrame. In such
+    // case, we need to initialize m_mainFrame to reflect the fact the the WebProcess' WebPage
+    // already exists and already has a main frame.
+    if (suspendedPage) {
+        ASSERT(&suspendedPage->process() == m_process.ptr());
+        suspendedPage->unsuspend();
+        m_mainFrame = WebFrameProxy::create(m_page, suspendedPage->mainFrameID());
+        m_process->frameCreated(suspendedPage->mainFrameID(), *m_mainFrame);
+    }
+
+    initializeWebPage();
+}
+
+ProvisionalPageProxy::~ProvisionalPageProxy()
+{
+    m_process->removeProvisionalPageProxy(*this);
+
+    if (m_wasCommitted)
+        return;
+
+    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
+    m_process->send(Messages::WebPage::Close(), m_page.pageID());
+
+    if (m_drawingArea) {
+        auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+        m_drawingArea = nullptr;
+    }
+
+    RunLoop::main().dispatch([process = m_process.copyRef()] {
+        process->maybeShutDown();
+    });
+}
+
+void ProvisionalPageProxy::processDidTerminate()
+{
+    RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSwapping, "processDidTerminate: pageID = %" PRIu64, m_page.pageID());
+    m_page.provisionalProcessDidTerminate();
+}
+
+std::unique_ptr<DrawingAreaProxy> ProvisionalPageProxy::takeDrawingArea()
+{
+    return WTFMove(m_drawingArea);
+}
+
+void ProvisionalPageProxy::cancel()
+{
+    // If the provisional load started, then indicate that it failed due to cancellation by calling didFailProvisionalLoadForFrame().
+    if (m_provisionalLoadURL.isEmpty())
+        return;
+
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "cancel: Simulating a didFailProvisionalLoadForFrame for pageID = %" PRIu64, m_page.pageID());
+    ASSERT(m_mainFrame);
+    auto error = WebKit::cancelledError(m_request);
+    error.setType(WebCore::ResourceError::Type::Cancellation);
+    didFailProvisionalLoadForFrame(m_mainFrame->frameID(), { }, m_navigationID, m_provisionalLoadURL, error, UserData { }); // Will delete |this|.
+}
+
+void ProvisionalPageProxy::processDidFinishLaunching()
+{
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "processDidFinishLaunching: pageID = %" PRIu64, m_page.pageID());
+    finishInitializingWebPageAfterProcessLaunch();
+}
+
+void ProvisionalPageProxy::finishInitializingWebPageAfterProcessLaunch()
+{
+    ASSERT(m_process->state() == WebProcessProxy::State::Running);
+
+    // FIXME: The WebPageProxy delays adding the visited link store until after the process has launched
+    // so the ProvisionalPageProxy does the same. However, do we really need to?
+    m_process->addVisitedLinkStore(m_page.visitedLinkStore());
+}
+
+void ProvisionalPageProxy::initializeWebPage()
+{
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+    m_drawingArea = m_page.pageClient().createDrawingAreaProxy();
+
+    auto parameters = m_page.creationParameters();
+    parameters.isProcessSwap = true;
+    m_process->send(Messages::WebProcess::CreateWebPage(m_page.pageID(), parameters), 0);
+
+    if (m_process->state() == WebProcessProxy::State::Running)
+        finishInitializingWebPageAfterProcessLaunch();
+}
+
+void ProvisionalPageProxy::loadDataWithNavigation(API::Navigation& navigation, const IPC::DataReference& data, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, Optional<WebsitePoliciesData>&& websitePolicies)
+{
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "loadDataWithNavigation: pageID = %" PRIu64, m_page.pageID());
+
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+    m_page.loadDataWithNavigation(navigation, data, MIMEType, encoding, baseURL, userData, WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
+}
+
+void ProvisionalPageProxy::loadRequestWithNavigation(API::Navigation& navigation, WebCore::ResourceRequest&& request, WebCore::ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, API::Object* userData, Optional<WebsitePoliciesData>&& websitePolicies)
+{
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "loadRequestWithNavigation: pageID = %" PRIu64, m_page.pageID());
+
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+    m_page.loadRequestWithNavigation(navigation, WTFMove(request), shouldOpenExternalURLsPolicy, userData, WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
+}
+
+void ProvisionalPageProxy::goToBackForwardItem(API::Navigation& navigation, WebBackForwardListItem& item, Optional<WebsitePoliciesData>&& websitePolicies)
+{
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "goToBackForwardItem: pageID = %" PRIu64, m_page.pageID());
+
+    auto itemStates = m_page.backForwardList().filteredItemStates([this, targetItem = &item](auto& item) {
+        if (auto* page = item.suspendedPage()) {
+            if (&page->process() == m_process.ptr())
+                return false;
+        }
+        return &item != targetItem;
+    });
+    m_process->send(Messages::WebPage::UpdateBackForwardListForReattach(WTFMove(itemStates)), m_page.pageID());
+    m_process->send(Messages::WebPage::GoToBackForwardItem(navigation.navigationID(), item.itemID(), *navigation.backForwardFrameLoadType(), WebCore::ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies)), m_page.pageID());
+    m_process->responsivenessTimer().start();
+}
+
+void ProvisionalPageProxy::didCreateMainFrame(uint64_t frameID)
+{
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didCreateMainFrame: pageID = %" PRIu64 ", frameID = %" PRIu64, m_page.pageID(), frameID);
+    ASSERT(!m_mainFrame);
+
+    m_mainFrame = WebFrameProxy::create(m_page, frameID);
+
+    // Add the frame to the process wide map.
+    m_process->frameCreated(frameID, *m_mainFrame);
+
+    // This navigation was destroyed so no need to notify of redirect.
+    if (!m_page.navigationState().hasNavigation(m_navigationID))
+        return;
+
+    // Restore the main frame's committed URL as some clients may rely on it until the next load is committed.
+    if (auto* mainFrame = m_page.mainFrame())
+        m_mainFrame->frameLoadState().setURL(mainFrame->frameLoadState().url());
+
+    // Normally, notification of a server redirect comes from the WebContent process.
+    // If we are process swapping in response to a server redirect then that notification will not come from the new WebContent process.
+    // In this case we have the UIProcess synthesize the redirect notification at the appropriate time.
+    if (m_isServerRedirect) {
+        m_mainFrame->frameLoadState().didStartProvisionalLoad(m_request.url());
+
+        auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+        m_page.didReceiveServerRedirectForProvisionalLoadForFrame(m_mainFrame->frameID(), m_navigationID, WTFMove(m_request), { });
+    }
+}
+
+void ProvisionalPageProxy::didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, URL&& url, URL&& unreachableURL, const UserData& userData)
+{
+    // If the previous provisional load used the same process, we may receive IPC for this previous provisional's main frame that we need to ignore.
+    if (!m_mainFrame || m_mainFrame->frameID() != frameID)
+        return;
+
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didStartProvisionalLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
+    ASSERT(m_provisionalLoadURL.isNull());
+    m_provisionalLoadURL = url;
+
+    // Merely following a server side redirect so there is no need to send a didStartProvisionalLoad again.
+    if (m_isServerRedirect)
+        return;
+
+    // Clients expect the Page's main frame's expectedURL to be the provisional one when a provisional load is started.
+    if (auto* pageMainFrame = m_page.mainFrame())
+        pageMainFrame->didStartProvisionalLoad(url);
+
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+    m_page.didStartProvisionalLoadForFrame(frameID, navigationID, WTFMove(url), WTFMove(unreachableURL), userData);
+}
+
+void ProvisionalPageProxy::didFailProvisionalLoadForFrame(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const WebCore::ResourceError& error, const UserData& userData)
+{
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+
+    RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSwapping, "didFailProvisionalLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
+    m_provisionalLoadURL = { };
+
+    // Make sure the Page's main frame's expectedURL gets cleared since we updated it in didStartProvisionalLoad.
+    if (auto* pageMainFrame = m_page.mainFrame())
+        pageMainFrame->didFailProvisionalLoad();
+
+    m_page.didFailProvisionalLoadForFrame(frameID, frameSecurityOrigin, navigationID, provisionalURL, error, userData); // Will delete |this|.
+}
+
+void ProvisionalPageProxy::didCommitLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo& certificateInfo, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData& userData)
+{
+    // If the previous provisional load used the same process, we may receive IPC for this previous provisional's main frame that we need to ignore.
+    if (!m_mainFrame || m_mainFrame->frameID() != frameID)
+        return;
+
+    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "didCommitLoadForFrame: pageID = %" PRIu64 ", frameID = %" PRIu64 ", navigationID = %" PRIu64, m_page.pageID(), frameID, navigationID);
+    m_provisionalLoadURL = { };
+    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
+
+    m_wasCommitted = true;
+    m_page.commitProvisionalPage(frameID, navigationID, mimeType, frameHasCustomContentProvider, frameLoadType, certificateInfo, containsPluginDocument, forcedHasInsecureContent, userData); // Will delete |this|.
+}
+
+void ProvisionalPageProxy::didReceiveMessage(IPC::Connection& connection, IPC::Decoder& decoder)
+{
+    ASSERT(decoder.messageReceiverName() == Messages::WebPageProxy::messageReceiverName());
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidStartProgress::name()
+        || decoder.messageName() == Messages::WebPageProxy::DidChangeProgress::name()
+        || decoder.messageName() == Messages::WebPageProxy::BackForwardAddItem::name()
+        || decoder.messageName() == Messages::WebPageProxy::LogDiagnosticMessage::name()
+        || decoder.messageName() == Messages::WebPageProxy::LogDiagnosticMessageWithEnhancedPrivacy::name()
+        || decoder.messageName() == Messages::WebPageProxy::SetNetworkRequestsInProgress::name()
+#if PLATFORM(COCOA)
+        || decoder.messageName() == Messages::WebPageProxy::RegisterWebProcessAccessibilityToken::name()
+#endif
+        || decoder.messageName() == Messages::WebPageProxy::DecidePolicyForNavigationActionAsync::name()
+        || decoder.messageName() == Messages::WebPageProxy::StartURLSchemeTask::name()
+        || decoder.messageName() == Messages::WebPageProxy::DecidePolicyForResponse::name()
+        || decoder.messageName() == Messages::WebPageProxy::DidPerformClientRedirect::name()
+        || decoder.messageName() == Messages::WebPageProxy::DidChangeProvisionalURLForFrame::name()
+        || decoder.messageName() == Messages::WebPageProxy::DidNavigateWithNavigationData::name())
+    {
+        auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+        m_page.didReceiveMessage(connection, decoder);
+        return;
+    }
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidCreateMainFrame::name()) {
+        IPC::handleMessage<Messages::WebPageProxy::DidCreateMainFrame>(decoder, this, &ProvisionalPageProxy::didCreateMainFrame);
+        return;
+    }
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidStartProvisionalLoadForFrame::name()) {
+        IPC::handleMessage<Messages::WebPageProxy::DidStartProvisionalLoadForFrame>(decoder, this, &ProvisionalPageProxy::didStartProvisionalLoadForFrame);
+        return;
+    }
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidFailProvisionalLoadForFrame::name()) {
+        IPC::handleMessage<Messages::WebPageProxy::DidFailProvisionalLoadForFrame>(decoder, this, &ProvisionalPageProxy::didFailProvisionalLoadForFrame);
+        return;
+    }
+
+    if (decoder.messageName() == Messages::WebPageProxy::DidCommitLoadForFrame::name()) {
+        IPC::handleMessage<Messages::WebPageProxy::DidCommitLoadForFrame>(decoder, this, &ProvisionalPageProxy::didCommitLoadForFrame);
+        return;
+    }
+
+    LOG(ProcessSwapping, "Unhandled message %s::%s from provisional process", decoder.messageReceiverName().toString().data(), decoder.messageName().toString().data());
+}
+
+void ProvisionalPageProxy::didReceiveSyncMessage(IPC::Connection& connection, IPC::Decoder& decoder, std::unique_ptr<IPC::Encoder>& encoder)
+{
+    auto pageProcessOverride = m_page.temporarilyOverrideProcess(m_process);
+    m_page.didReceiveSyncMessage(connection, decoder, encoder);
+}
+
+} // namespace WebKit
diff --git a/Source/WebKit/UIProcess/ProvisionalPageProxy.h b/Source/WebKit/UIProcess/ProvisionalPageProxy.h
new file mode 100644 (file)
index 0000000..8a0f71d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 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 "MessageReceiver.h"
+#include <wtf/WeakPtr.h>
+
+namespace WebCore {
+class ResourceRequest;
+}
+
+namespace WebKit {
+
+class DrawingAreaProxy;
+class SuspendedPageProxy;
+class WebFrameProxy;
+class WebPageProxy;
+class WebProcessProxy;
+
+class ProvisionalPageProxy : public IPC::MessageReceiver, public CanMakeWeakPtr<ProvisionalPageProxy> {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    ProvisionalPageProxy(WebPageProxy&, Ref<WebProcessProxy>&&, std::unique_ptr<SuspendedPageProxy>, uint64_t navigationID, bool isServerRedirect, const WebCore::ResourceRequest&, ProcessSwapRequestedByClient);
+    ~ProvisionalPageProxy();
+
+    WebPageProxy& page() { return m_page; }
+    WebFrameProxy* mainFrame() const { return m_mainFrame.get(); }
+    WebProcessProxy& process() { return m_process.get(); }
+    ProcessSwapRequestedByClient processSwapRequestedByClient() const { return m_processSwapRequestedByClient; }
+    uint64_t navigationID() const { return m_navigationID; }
+
+    DrawingAreaProxy* drawingArea() const { return m_drawingArea.get(); }
+    std::unique_ptr<DrawingAreaProxy> takeDrawingArea();
+
+    void loadDataWithNavigation(API::Navigation&, const IPC::DataReference&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, Optional<WebsitePoliciesData>&& = WTF::nullopt);
+    void loadRequestWithNavigation(API::Navigation&, WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData, Optional<WebsitePoliciesData>&& = WTF::nullopt);
+    void goToBackForwardItem(API::Navigation&, WebBackForwardListItem&, Optional<WebsitePoliciesData>&&);
+    void cancel();
+
+    void processDidFinishLaunching();
+    void processDidTerminate();
+
+private:
+    // IPC::MessageReceiver
+    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
+    void didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, std::unique_ptr<IPC::Encoder>&) final;
+
+    void didCreateMainFrame(uint64_t frameID);
+    void didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, URL&&, URL&& unreachableURL, const UserData&);
+    void didCommitLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo&, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData&);
+    void didFailProvisionalLoadForFrame(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const WebCore::ResourceError&, const UserData&);
+
+    void initializeWebPage();
+    void finishInitializingWebPageAfterProcessLaunch();
+
+    WebPageProxy& m_page;
+    Ref<WebProcessProxy> m_process;
+    std::unique_ptr<DrawingAreaProxy> m_drawingArea;
+    RefPtr<WebFrameProxy> m_mainFrame;
+    uint64_t m_navigationID;
+    bool m_isServerRedirect;
+    WebCore::ResourceRequest m_request;
+    ProcessSwapRequestedByClient m_processSwapRequestedByClient;
+    bool m_wasCommitted { false };
+    URL m_provisionalLoadURL;
+
+#if PLATFORM(IOS_FAMILY)
+    ProcessThrottler::BackgroundActivityToken m_suspensionToken;
+#endif
+};
+
+} // namespace WebKit
index f198a81..ff76d9f 100644 (file)
@@ -100,7 +100,9 @@ SuspendedPageProxy::~SuspendedPageProxy()
     // If the suspended page was not consumed before getting destroyed, then close the corresponding page
     // on the WebProcess side.
     m_process->send(Messages::WebPage::Close(), m_page.pageID());
-    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
+
+    if (m_suspensionState == SuspensionState::Suspending)
+        m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
 
     // We call maybeShutDown() asynchronously since the SuspendedPage is currently being removed from the WebProcessPool
     // and we want to avoid re-entering WebProcessPool methods.
@@ -134,7 +136,6 @@ void SuspendedPageProxy::unsuspend()
     ASSERT(m_suspensionState == SuspensionState::Suspended);
 
     m_suspensionState = SuspensionState::Resumed;
-    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
     m_process->send(Messages::WebPage::SetIsSuspended(false), m_page.pageID());
 }
 
@@ -151,6 +152,8 @@ void SuspendedPageProxy::didProcessRequestToSuspend(SuspensionState newSuspensio
     m_suspensionToken = nullptr;
 #endif
 
+    m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_page.pageID());
+
     if (m_readyToUnsuspendHandler)
         m_readyToUnsuspendHandler(this);
 }
index c77a26b..53c7196 100644 (file)
@@ -27,6 +27,7 @@
 #include "WebFrameProxy.h"
 
 #include "APINavigation.h"
+#include "ProvisionalPageProxy.h"
 #include "WebCertificateInfo.h"
 #include "WebFramePolicyListenerProxy.h"
 #include "WebPageMessages.h"
@@ -74,7 +75,7 @@ bool WebFrameProxy::isMainFrame() const
     if (!m_page)
         return false;
 
-    return this == m_page->mainFrame();
+    return this == m_page->mainFrame() || (m_page->provisionalPageProxy() && this == m_page->provisionalPageProxy()->mainFrame());
 }
 
 void WebFrameProxy::loadURL(const URL& url)
index c8ab703..b04873a 100644 (file)
@@ -77,6 +77,7 @@
 #include "PluginInformation.h"
 #include "PluginProcessManager.h"
 #include "PrintInfo.h"
+#include "ProvisionalPageProxy.h"
 #include "SafeBrowsingWarning.h"
 #include "ShareSheetCallbackID.h"
 #include "SharedBufferDataReference.h"
@@ -545,6 +546,13 @@ PAL::SessionID WebPageProxy::sessionID() const
     return m_websiteDataStore->sessionID();
 }
 
+DrawingAreaProxy* WebPageProxy::drawingArea() const
+{
+    if (m_provisionalPage && m_process.ptr() == &m_provisionalPage->process() && m_provisionalPage->drawingArea())
+        return m_provisionalPage->drawingArea();
+    return m_drawingArea.get();
+}
+
 void WebPageProxy::changeWebsiteDataStore(WebsiteDataStore& websiteDataStore)
 {
     m_process->processPool().pageEndUsingWebsiteDataStore(*this);
@@ -768,38 +776,26 @@ bool WebPageProxy::suspendCurrentPageIfPossible(API::Navigation& navigation, Opt
     return true;
 }
 
-void WebPageProxy::swapToWebProcess(Ref<WebProcessProxy>&& process, std::unique_ptr<SuspendedPageProxy>&& destinationSuspendedPage, IsSwapFromSuspended isSwapFromSuspended)
+void WebPageProxy::swapToWebProcess(Ref<WebProcessProxy>&& process, std::unique_ptr<DrawingAreaProxy>&& drawingArea, RefPtr<WebFrameProxy>&& mainFrame)
 {
     ASSERT(!m_isClosed);
     RELEASE_LOG_IF_ALLOWED(Loading, "swapToWebProcess: webPID = %i, pageID = %" PRIu64, m_process->processIdentifier(), m_pageID);
 
     m_process = WTFMove(process);
+    ASSERT(!m_drawingArea);
+    setDrawingArea(WTFMove(drawingArea));
+    ASSERT(!m_mainFrame);
+    m_mainFrame = WTFMove(mainFrame);
     m_isValid = true;
 
-    // If we are reattaching to a SuspendedPage, then the WebProcess' WebPage already exists and
-    // WebPageProxy::didCreateMainFrame() will not be called to initialize m_mainFrame. In such
-    // case, we need to initialize m_mainFrame to reflect the fact the the WebProcess' WebPage
-    // already exists and already has a main frame.
-    if (destinationSuspendedPage) {
-        if (!destinationSuspendedPage->failedToSuspend()) {
-            ASSERT(!m_mainFrame);
-            ASSERT(&destinationSuspendedPage->process() == m_process.ptr());
-            destinationSuspendedPage->unsuspend();
-            m_mainFrame = WebFrameProxy::create(*this, destinationSuspendedPage->mainFrameID());
-            m_process->frameCreated(destinationSuspendedPage->mainFrameID(), *m_mainFrame);
-        } else {
-            // We failed to suspend the page so destroy it now and merely reuse its WebContent process.
-            destinationSuspendedPage = nullptr;
-        }
-    }
-
     m_process->addExistingWebPage(*this, m_pageID, WebProcessProxy::BeginsUsingDataStore::No);
     m_process->addMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID, *this);
 
-    finishAttachingToWebProcess(isSwapFromSuspended);
+    // No need to initialize the WebPage since it was created when we created the ProvisionalPageProxy.
+    finishAttachingToWebProcess(ShouldInitializeWebPage::No);
 }
 
-void WebPageProxy::finishAttachingToWebProcess(IsSwapFromSuspended isSwapFromSuspended)
+void WebPageProxy::finishAttachingToWebProcess(ShouldInitializeWebPage shouldInitializePage)
 {
     ASSERT(m_process->state() != ChildProcessProxy::State::Terminated);
 
@@ -835,7 +831,8 @@ void WebPageProxy::finishAttachingToWebProcess(IsSwapFromSuspended isSwapFromSus
     m_editableImageController = std::make_unique<EditableImageController>(*this);
 #endif
 
-    initializeWebPage(isSwapFromSuspended);
+    if (shouldInitializePage == ShouldInitializeWebPage::Yes)
+        initializeWebPage();
 
     m_inspector->updateForNewPageProcess(this);
 
@@ -900,12 +897,11 @@ RefPtr<API::Navigation> WebPageProxy::reattachToWebProcessWithItem(WebBackForwar
     return WTFMove(navigation);
 }
 
-void WebPageProxy::initializeWebPage(IsSwapFromSuspended isSwapFromSuspended)
+void WebPageProxy::setDrawingArea(std::unique_ptr<DrawingAreaProxy>&& drawingArea)
 {
-    ASSERT(isValid());
-
-    m_drawingArea = pageClient().createDrawingAreaProxy();
-    ASSERT(m_drawingArea);
+    m_drawingArea = WTFMove(drawingArea);
+    if (!m_drawingArea)
+        return;
 
 #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA)
     if (m_drawingArea->type() == DrawingAreaTypeRemoteLayerTree) {
@@ -916,15 +912,16 @@ void WebPageProxy::initializeWebPage(IsSwapFromSuspended isSwapFromSuspended)
 #endif
     }
 #endif
+}
 
-    auto parameters = creationParameters();
-    parameters.isSwapFromSuspended = isSwapFromSuspended == IsSwapFromSuspended::Yes;
+void WebPageProxy::initializeWebPage()
+{
+    ASSERT(isValid());
 
-#if ENABLE(SERVICE_WORKER)
-    parameters.hasRegisteredServiceWorkers = process().processPool().mayHaveRegisteredServiceWorkers(m_websiteDataStore);
-#endif
+    setDrawingArea(pageClient().createDrawingAreaProxy());
+    ASSERT(m_drawingArea);
 
-    process().send(Messages::WebProcess::CreateWebPage(m_pageID, parameters), 0);
+    process().send(Messages::WebProcess::CreateWebPage(m_pageID, creationParameters()), 0);
 
     m_needsToFinishInitializingWebPageAfterProcessLaunch = true;
     finishInitializingWebPageAfterProcessLaunch();
@@ -1076,7 +1073,8 @@ void WebPageProxy::loadRequestWithNavigation(API::Navigation& navigation, Resour
     auto transaction = m_pageLoadState.transaction();
 
     auto url = request.url();
-    m_pageLoadState.setPendingAPIRequestURL(transaction, url);
+    if (shouldTreatAsContinuingLoad != ShouldTreatAsContinuingLoad::Yes)
+        m_pageLoadState.setPendingAPIRequestURL(transaction, url);
 
     if (!isValid())
         reattachToWebProcess();
@@ -1294,6 +1292,10 @@ void WebPageProxy::stopLoading()
     }
 
     m_process->send(Messages::WebPage::StopLoading(), m_pageID);
+    if (m_provisionalPage) {
+        m_provisionalPage->cancel();
+        m_provisionalPage = nullptr;
+    }
     m_process->responsivenessTimer().start();
 }
 
@@ -2698,7 +2700,7 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebPolicyAction policyAction
         }
 
         if (processForNavigation.ptr() != &process()) {
-            policyAction = isPageOpenedByDOMShowingInitialEmptyDocument() ? WebPolicyAction::Ignore : WebPolicyAction::Suspend;
+            policyAction = WebPolicyAction::Ignore;
             RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "decidePolicyForNavigationAction, swapping process %i with process %i for navigation, reason: %{public}s", processIdentifier(), processForNavigation->processIdentifier(), reason.utf8().data());
             LOG(ProcessSwapping, "(ProcessSwapping) Switching from process %i to new process (%i) for navigation %" PRIu64 " '%s'", processIdentifier(), processForNavigation->processIdentifier(), navigation->navigationID(), navigation->loggingString());
         } else
@@ -2707,8 +2709,14 @@ void WebPageProxy::receivedNavigationPolicyDecision(WebPolicyAction policyAction
         bool shouldProcessSwap = processForNavigation.ptr() != &process();
         receivedPolicyDecision(policyAction, navigation.ptr(), shouldProcessSwap ? WTF::nullopt : WTFMove(data), WTFMove(sender));
 
-        if (shouldProcessSwap)
-            continueNavigationInNewProcess(navigation, destinationSuspendedPage ? process().processPool().takeSuspendedPage(*destinationSuspendedPage) : nullptr, WTFMove(processForNavigation), processSwapRequestedByClient, WTFMove(data));
+        if (!shouldProcessSwap)
+            return;
+
+        auto suspendedPage = destinationSuspendedPage ? process().processPool().takeSuspendedPage(*destinationSuspendedPage) : nullptr;
+        if (suspendedPage && suspendedPage->failedToSuspend())
+            suspendedPage = nullptr;
+
+        continueNavigationInNewProcess(navigation, WTFMove(suspendedPage), WTFMove(processForNavigation), processSwapRequestedByClient, WTFMove(data));
     });
 }
 
@@ -2744,24 +2752,45 @@ void WebPageProxy::receivedPolicyDecision(WebPolicyAction action, API::Navigatio
     sender->send(action, navigation ? navigation->navigationID() : 0, downloadID, WTFMove(websitePolicies));
 }
 
-void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, std::unique_ptr<SuspendedPageProxy>&& suspendedPageProxy, Ref<WebProcessProxy>&& process, ProcessSwapRequestedByClient processSwapRequestedByClient, Optional<WebsitePoliciesData>&& websitePolicies)
+void WebPageProxy::commitProvisionalPage(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo& certificateInfo, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData& userData)
 {
-    RELEASE_LOG_IF_ALLOWED(Loading, "continueNavigationInNewProcess: webPID = %i, pageID = %" PRIu64, m_process->processIdentifier(), m_pageID);
-    LOG(Loading, "Continuing navigation %" PRIu64 " '%s' in a new web process", navigation.navigationID(), navigation.loggingString());
+    ASSERT(m_provisionalPage);
+    RELEASE_LOG_IF_ALLOWED(Loading, "commitProvisionalPage: previousPID = %i, newPID = %i, pageID = %" PRIu64, m_process->processIdentifier(), m_provisionalPage->process().processIdentifier(), m_pageID);
 
-    Ref<WebProcessProxy> previousProcess = m_process.copyRef();
     Optional<uint64_t> mainFrameIDInPreviousProcess = m_mainFrame ? makeOptional(m_mainFrame->frameID()) : WTF::nullopt;
-    auto mainFrameURL = m_mainFrame ? m_mainFrame->url() : URL();
 
-    ASSERT(m_process.ptr() != process.ptr());
+    ASSERT(m_process.ptr() != &m_provisionalPage->process());
 
     processDidTerminate(ProcessTerminationReason::NavigationSwap);
 
     m_process->removeMessageReceiver(Messages::WebPageProxy::messageReceiverName(), m_pageID);
-    bool didSuspendPreviousPage = suspendCurrentPageIfPossible(navigation, mainFrameIDInPreviousProcess, processSwapRequestedByClient);
+    auto* navigation = navigationState().navigation(m_provisionalPage->navigationID());
+    bool didSuspendPreviousPage = navigation ? suspendCurrentPageIfPossible(*navigation, mainFrameIDInPreviousProcess, m_provisionalPage->processSwapRequestedByClient()) : false;
     m_process->removeWebPage(*this, m_pageID, WebProcessProxy::EndsUsingDataStore::No);
 
-    swapToWebProcess(WTFMove(process), WTFMove(suspendedPageProxy), didSuspendPreviousPage ? IsSwapFromSuspended::Yes : IsSwapFromSuspended::No);
+    // There is no way we'll be able to return to the page in the previous page so close it.
+    if (!didSuspendPreviousPage)
+        m_process->send(Messages::WebPage::Close(), pageID());
+
+    swapToWebProcess(m_provisionalPage->process(), m_provisionalPage->takeDrawingArea(), m_provisionalPage->mainFrame());
+
+    didCommitLoadForFrame(frameID, navigationID, mimeType, frameHasCustomContentProvider, frameLoadType, certificateInfo, containsPluginDocument, forcedHasInsecureContent, userData);
+
+    m_provisionalPage = nullptr;
+}
+
+void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, std::unique_ptr<SuspendedPageProxy>&& suspendedPageProxy, Ref<WebProcessProxy>&& newProcess, ProcessSwapRequestedByClient processSwapRequestedByClient, Optional<WebsitePoliciesData>&& websitePolicies)
+{
+    RELEASE_LOG_IF_ALLOWED(Loading, "continueNavigationInNewProcess: webPID = %i, pageID = %" PRIu64, m_process->processIdentifier(), m_pageID);
+    LOG(Loading, "Continuing navigation %" PRIu64 " '%s' in a new web process", navigation.navigationID(), navigation.loggingString());
+
+    if (m_provisionalPage) {
+        if (m_provisionalPage->navigationID() != navigation.navigationID())
+            m_provisionalPage->cancel();
+        m_provisionalPage = nullptr;
+    }
+
+    m_provisionalPage = std::make_unique<ProvisionalPageProxy>(*this, newProcess.copyRef(), WTFMove(suspendedPageProxy), navigation.navigationID(), navigation.currentRequestIsRedirect(), navigation.currentRequest(), processSwapRequestedByClient);
 
     if (auto* item = navigation.targetItem()) {
         LOG(Loading, "WebPageProxy %p continueNavigationInNewProcess to back item URL %s", this, item->url().utf8().data());
@@ -2769,67 +2798,22 @@ void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, s
         auto transaction = m_pageLoadState.transaction();
         m_pageLoadState.setPendingAPIRequestURL(transaction, item->url());
 
-        auto itemStates = m_backForwardList->filteredItemStates([this, targetItem = item](WebBackForwardListItem& item) {
-            if (auto* page = item.suspendedPage()) {
-                if (&page->process() == m_process.ptr())
-                    return false;
-            }
-            return &item != targetItem;
-        });
-        m_process->send(Messages::WebPage::UpdateBackForwardListForReattach(WTFMove(itemStates)), m_pageID);
-        m_process->send(Messages::WebPage::GoToBackForwardItem(navigation.navigationID(), item->itemID(), *navigation.backForwardFrameLoadType(), ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies)), m_pageID);
-        m_process->responsivenessTimer().start();
-
+        m_provisionalPage->goToBackForwardItem(navigation, *item, WTFMove(websitePolicies));
         return;
     }
 
     if (m_backForwardList->currentItem() && (navigation.lockBackForwardList() == LockBackForwardList::Yes || navigation.lockHistory() == LockHistory::Yes)) {
         // If WebCore is supposed to lock the history for this load, then the new process needs to know about the current history item so it can update
         // it instead of creating a new one.
-        m_process->send(Messages::WebPage::SetCurrentHistoryItemForReattach(m_backForwardList->currentItem()->itemState()), m_pageID);
+        newProcess->send(Messages::WebPage::SetCurrentHistoryItemForReattach(m_backForwardList->currentItem()->itemState()), m_pageID);
     }
 
     // FIXME: Work out timing of responding with the last policy delegate, etc
     ASSERT(!navigation.currentRequest().isEmpty());
     if (auto& substituteData = navigation.substituteData())
-        loadDataWithNavigation(navigation, { substituteData->content.data(), substituteData->content.size() }, substituteData->MIMEType, substituteData->encoding, substituteData->baseURL, substituteData->userData.get(), ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
+        m_provisionalPage->loadDataWithNavigation(navigation, { substituteData->content.data(), substituteData->content.size() }, substituteData->MIMEType, substituteData->encoding, substituteData->baseURL, substituteData->userData.get(), WTFMove(websitePolicies));
     else
-        loadRequestWithNavigation(navigation, ResourceRequest { navigation.currentRequest() }, WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes, nullptr, ShouldTreatAsContinuingLoad::Yes, WTFMove(websitePolicies));
-
-    ASSERT(!m_mainFrame);
-    m_mainFrameCreationHandler = [this, weakThis = makeWeakPtr(*this), navigationID = navigation.navigationID(), request =  navigation.currentRequest(), mainFrameURL, isServerRedirect = navigation.currentRequestIsRedirect()]() mutable {
-        if (!weakThis)
-            return;
-
-        ASSERT(m_mainFrame);
-        // This navigation was destroyed so no need to notify of redirect.
-        if (!navigationState().navigation(navigationID))
-            return;
-
-        // Restore the main frame's committed URL as some clients may rely on it until the next load is committed.
-        m_mainFrame->frameLoadState().setURL(mainFrameURL);
-
-        // Normally, notification of a server redirect comes from the WebContent process.
-        // If we are process swapping in response to a server redirect then that notification will not come from the new WebContent process.
-        // In this case we have the UIProcess synthesize the redirect notification at the appropriate time.
-        if (isServerRedirect) {
-            m_mainFrame->frameLoadState().didStartProvisionalLoad(request.url());
-            didReceiveServerRedirectForProvisionalLoadForFrame(m_mainFrame->frameID(), navigationID, WTFMove(request), { });
-        }
-    };
-
-    if (!m_process->processPool().configuration().processSwapsOnWindowOpenWithOpener() || !isPageOpenedByDOMShowingInitialEmptyDocument() || !mainFrameIDInPreviousProcess) {
-        // There is no way we'll be able to return to the page in the previous page so close it.
-        if (!didSuspendPreviousPage)
-            previousProcess->send(Messages::WebPage::Close(), m_pageID);
-        return;
-    }
-
-    m_mainFrameWindowCreationHandler = [this, previousProcess = WTFMove(previousProcess), mainFrameIDInPreviousProcess = *mainFrameIDInPreviousProcess](const GlobalWindowIdentifier& windowIdentifier) {
-        ASSERT(m_mainFrame);
-        GlobalFrameIdentifier navigatedFrameIdentifierInNewProcess { pageID(), m_mainFrame->frameID() };
-        previousProcess->send(Messages::WebPage::FrameBecameRemote(mainFrameIDInPreviousProcess, navigatedFrameIdentifierInNewProcess, windowIdentifier), pageID());
-    };
+        m_provisionalPage->loadRequestWithNavigation(navigation, ResourceRequest { navigation.currentRequest() }, WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes, nullptr, WTFMove(websitePolicies));
 }
 
 bool WebPageProxy::isPageOpenedByDOMShowingInitialEmptyDocument() const
@@ -3694,11 +3678,6 @@ void WebPageProxy::didCreateMainFrame(uint64_t frameID)
 
     // Add the frame to the process wide map.
     m_process->frameCreated(frameID, *m_mainFrame);
-
-    if (m_mainFrameCreationHandler) {
-        m_mainFrameCreationHandler();
-        m_mainFrameCreationHandler = nullptr;
-    }
 }
 
 void WebPageProxy::didCreateSubframe(uint64_t frameID)
@@ -3723,10 +3702,6 @@ void WebPageProxy::didCreateSubframe(uint64_t frameID)
 
 void WebPageProxy::didCreateWindow(uint64_t frameID, GlobalWindowIdentifier&& windowIdentifier)
 {
-    if (m_mainFrame && m_mainFrame->frameID() == frameID) {
-        if (auto mainFrameWindowCreationHandler = WTFMove(m_mainFrameWindowCreationHandler))
-            mainFrameWindowCreationHandler(windowIdentifier);
-    }
 }
 
 double WebPageProxy::estimatedProgress() const
@@ -3779,6 +3754,10 @@ void WebPageProxy::didDestroyNavigation(uint64_t navigationID)
 {
     PageClientProtector protector(pageClient());
 
+    // On process-swap, the previous process tries to destroy the navigation but the provisional process is actually taking over the navigation.
+    if (m_provisionalPage && m_provisionalPage->navigationID() == navigationID)
+        return;
+
     // FIXME: Message check the navigationID.
     m_navigationState->didDestroyNavigation(navigationID);
 }
@@ -3796,14 +3775,6 @@ void WebPageProxy::didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t na
     if (frame->isMainFrame() && navigationID)
         navigation = navigationState().navigation(navigationID);
 
-    // If this seemingly new load is actually continuing a server redirect for a previous navigation in a new process,
-    // then we ignore this notification.
-    if (navigation && navigation->currentRequestIsRedirect()) {
-        auto navigationProcessIdentifier = navigation->currentRequestProcessIdentifier();
-        if (navigationProcessIdentifier && *navigationProcessIdentifier != m_process->coreProcessIdentifier())
-            return;
-    }
-
     LOG(Loading, "WebPageProxy %" PRIu64 " in process pid %i didStartProvisionalLoadForFrame to frameID %" PRIu64 ", navigationID %" PRIu64 ", url %s", m_pageID, m_process->processIdentifier(), frameID, navigationID, url.string().utf8().data());
     RELEASE_LOG_IF_ALLOWED(Loading, "didStartProvisionalLoadForFrame: webPID = %i, pageID = %" PRIu64 ", frameID = %" PRIu64, m_process->processIdentifier(), m_pageID, frameID);
 
@@ -3911,6 +3882,11 @@ void WebPageProxy::didFailProvisionalLoadForFrame(uint64_t frameID, const Securi
 
     PageClientProtector protector(pageClient());
 
+    if (m_provisionalPage && m_provisionalPage->navigationID() == navigationID && &m_provisionalPage->process() != m_process.ptr()) {
+        // The load did not fail, it is merely happening in a new provisional process.
+        return;
+    }
+
     WebFrameProxy* frame = m_process->webFrame(frameID);
     MESSAGE_CHECK(frame);
 
@@ -3949,6 +3925,10 @@ void WebPageProxy::didFailProvisionalLoadForFrame(uint64_t frameID, const Securi
     }
 
     m_failingProvisionalLoadURL = { };
+
+    // If the provisional page's load fails then we destroy the provisional page.
+    if (m_provisionalPage && m_provisionalPage->mainFrame() == frame)
+        m_provisionalPage = nullptr;
 }
 
 void WebPageProxy::clearLoadDependentCallbacks()
@@ -4340,8 +4320,8 @@ void WebPageProxy::decidePolicyForNavigationActionAsync(uint64_t frameID, WebCor
     auto* frame = m_process->webFrame(frameID);
     MESSAGE_CHECK(frame);
 
-    decidePolicyForNavigationAction(*frame, WTFMove(frameSecurityOrigin), navigationID, WTFMove(navigationActionData), WTFMove(frameInfoData), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, PolicyDecisionSender::create([this, protectedThis = makeRef(*this), frameID, listenerID] (auto... args) {
-        m_process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, args...), m_pageID);
+    decidePolicyForNavigationAction(*frame, WTFMove(frameSecurityOrigin), navigationID, WTFMove(navigationActionData), WTFMove(frameInfoData), originatingPageID, originalRequest, WTFMove(request), WTFMove(requestBody), WTFMove(redirectResponse), userData, PolicyDecisionSender::create([this, protectedThis = makeRef(*this), frameID, listenerID, process = m_process.copyRef()] (auto... args) {
+        process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, args...), m_pageID);
     }));
 }
 
@@ -4575,12 +4555,12 @@ void WebPageProxy::decidePolicyForResponse(uint64_t frameID, const SecurityOrigi
     MESSAGE_CHECK_URL(response.url());
 
     RefPtr<API::Navigation> navigation = navigationID ? m_navigationState->navigation(navigationID) : nullptr;
-    auto listener = makeRef(frame->setUpPolicyListenerProxy([this, protectedThis = makeRef(*this), frameID, listenerID, navigation = WTFMove(navigation)] (WebPolicyAction policyAction, API::WebsitePolicies*, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning) mutable {
+    auto listener = makeRef(frame->setUpPolicyListenerProxy([this, protectedThis = makeRef(*this), frameID, listenerID, navigation = WTFMove(navigation), process = m_process.copyRef()] (WebPolicyAction policyAction, API::WebsitePolicies*, ProcessSwapRequestedByClient processSwapRequestedByClient, RefPtr<SafeBrowsingWarning>&& safeBrowsingWarning) mutable {
         // FIXME: Assert the API::WebsitePolicies* is nullptr here once clients of WKFramePolicyListenerUseWithPolicies go away.
         RELEASE_ASSERT(processSwapRequestedByClient == ProcessSwapRequestedByClient::No);
         ASSERT_UNUSED(safeBrowsingWarning, !safeBrowsingWarning);
-        receivedPolicyDecision(policyAction, navigation.get(), WTF::nullopt, PolicyDecisionSender::create([this, protectedThis = WTFMove(protectedThis), frameID, listenerID] (auto... args) {
-            m_process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, args...), m_pageID);
+        receivedPolicyDecision(policyAction, navigation.get(), WTF::nullopt, PolicyDecisionSender::create([this, protectedThis = WTFMove(protectedThis), frameID, listenerID, process = WTFMove(process)] (auto... args) {
+            process->send(Messages::WebPage::DidReceivePolicyDecision(frameID, listenerID, args...), m_pageID);
         }));
     }, ShouldExpectSafeBrowsingResult::No));
 
@@ -6400,6 +6380,13 @@ void WebPageProxy::processDidTerminate(ProcessTerminationReason reason)
     stopAllURLSchemeTasks();
 }
 
+void WebPageProxy::provisionalProcessDidTerminate()
+{
+    ASSERT(m_provisionalPage);
+    m_provisionalPage->cancel();
+    m_provisionalPage = nullptr;
+}
+
 static bool shouldReloadAfterProcessTermination(ProcessTerminationReason reason)
 {
     switch (reason) {
@@ -6487,8 +6474,6 @@ void WebPageProxy::processWillBecomeForeground()
 void WebPageProxy::resetState(ResetStateReason resetStateReason)
 {
     m_mainFrame = nullptr;
-    m_mainFrameCreationHandler = nullptr;
-    m_mainFrameWindowCreationHandler = nullptr;
 
 #if PLATFORM(COCOA)
     m_scrollingPerformanceData = nullptr;
@@ -6779,6 +6764,10 @@ WebPageCreationParameters WebPageProxy::creationParameters()
     parameters.applicationManifest = m_configuration->applicationManifest() ? Optional<WebCore::ApplicationManifest>(m_configuration->applicationManifest()->applicationManifest()) : WTF::nullopt;
 #endif
 
+#if ENABLE(SERVICE_WORKER)
+    parameters.hasRegisteredServiceWorkers = process().processPool().mayHaveRegisteredServiceWorkers(m_websiteDataStore);
+#endif
+
     parameters.needsFontAttributes = m_needsFontAttributes;
 
     m_process->addWebUserContentControllerProxy(m_userContentController, parameters);
@@ -8540,6 +8529,21 @@ void WebPageProxy::simulateDeviceOrientationChange(double alpha, double beta, do
     m_process->send(Messages::WebPage::SimulateDeviceOrientationChange(alpha, beta, gamma), m_pageID);
 }
 
+WebPageProxy::PageProcessOverride::PageProcessOverride(WebPageProxy& page, WebProcessProxy& newProcess)
+    : m_page(&page)
+    , m_previousProcess(page.process())
+{
+    page.m_process = newProcess;
+}
+
+WebPageProxy::PageProcessOverride::PageProcessOverride(PageProcessOverride&&) = default;
+
+WebPageProxy::PageProcessOverride::~PageProcessOverride()
+{
+    if (m_page)
+        m_page->m_process = WTFMove(m_previousProcess);
+}
+
 #if ENABLE(DATA_DETECTION)
 
 void WebPageProxy::detectDataInAllFrames(WebCore::DataDetectorTypes types, CompletionHandler<void(const DataDetectionResult&)>&& completionHandler)
index de98694..4582f0a 100644 (file)
@@ -225,6 +225,7 @@ class NativeWebKeyboardEvent;
 class NativeWebMouseEvent;
 class NativeWebWheelEvent;
 class PageClient;
+class ProvisionalPageProxy;
 class RemoteLayerTreeHost;
 class RemoteLayerTreeScrollingPerformanceData;
 class RemoteLayerTreeTransaction;
@@ -356,7 +357,7 @@ public:
     WebFrameProxy* focusedFrame() const { return m_focusedFrame.get(); }
     WebFrameProxy* frameSetLargestFrame() const { return m_frameSetLargestFrame.get(); }
 
-    DrawingAreaProxy* drawingArea() const { return m_drawingArea.get(); }
+    DrawingAreaProxy* drawingArea() const;
 
     WebNavigationState& navigationState() { return *m_navigationState.get(); }
 
@@ -379,6 +380,7 @@ public:
 
     bool addsVisitedLinks() const { return m_addsVisitedLinks; }
     void setAddsVisitedLinks(bool addsVisitedLinks) { m_addsVisitedLinks = addsVisitedLinks; }
+    VisitedLinkStore& visitedLinkStore() { return m_visitedLinkStore; }
 
     void exitFullscreenImmediately();
     void fullscreenMayReturnToInline();
@@ -458,8 +460,8 @@ public:
     API::IconLoadingClient& iconLoadingClient() { return *m_iconLoadingClient; }
     void setIconLoadingClient(std::unique_ptr<API::IconLoadingClient>&&);
 
-    enum class IsSwapFromSuspended { No, Yes };
-    void initializeWebPage(IsSwapFromSuspended = IsSwapFromSuspended::No);
+    void initializeWebPage();
+    void setDrawingArea(std::unique_ptr<DrawingAreaProxy>&&);
 
     WeakPtr<SecKeyProxyStore> secKeyProxyStore(const WebCore::AuthenticationChallenge&);
         
@@ -997,6 +999,7 @@ public:
     void processDidBecomeUnresponsive();
     void processDidBecomeResponsive();
     void processDidTerminate(ProcessTerminationReason);
+    void provisionalProcessDidTerminate();
     void dispatchProcessDidTerminate(ProcessTerminationReason);
     void willChangeProcessIsResponsive();
     void didChangeProcessIsResponsive();
@@ -1025,6 +1028,19 @@ public:
     WebProcessProxy& process() { return m_process; }
     ProcessID processIdentifier() const;
 
+    class PageProcessOverride {
+    public:
+        PageProcessOverride(WebPageProxy&, WebProcessProxy& newProcess);
+        PageProcessOverride(PageProcessOverride&&);
+        ~PageProcessOverride();
+
+    private:
+        RefPtr<WebPageProxy> m_page;
+        Ref<WebProcessProxy> m_previousProcess;
+    };
+
+    PageProcessOverride temporarilyOverrideProcess(WebProcessProxy& newProcess) { return PageProcessOverride(*this, newProcess); }
+
     WebPreferences& preferences() { return m_preferences; }
     void setPreferences(WebPreferences&);
 
@@ -1415,6 +1431,21 @@ public:
     EditableImageController& editableImageController() { return *m_editableImageController; }
 #endif
 
+    ProvisionalPageProxy* provisionalPageProxy() const { return m_provisionalPage.get(); }
+    void commitProvisionalPage(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo&, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData&);
+
+    void didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, URL&&, URL&& unreachableURL, const UserData&);
+    void didFailProvisionalLoadForFrame(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const WebCore::ResourceError&, const UserData&);
+    void didReceiveServerRedirectForProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::ResourceRequest&&, const UserData&);
+
+    void loadDataWithNavigation(API::Navigation&, const IPC::DataReference&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, WebCore::ShouldTreatAsContinuingLoad, Optional<WebsitePoliciesData>&& = WTF::nullopt);
+    void loadRequestWithNavigation(API::Navigation&, WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData, WebCore::ShouldTreatAsContinuingLoad, Optional<WebsitePoliciesData>&& = WTF::nullopt);
+
+    // IPC::MessageReceiver
+    // Implemented in generated WebPageProxyMessageReceiver.cpp
+    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
+    void didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, std::unique_ptr<IPC::Encoder>&) override;
+
 private:
     WebPageProxy(PageClient&, WebProcessProxy&, uint64_t pageID, Ref<API::PageConfiguration>&&);
     void platformInitialize();
@@ -1439,12 +1470,6 @@ private:
 
     void setUserAgent(String&&);
 
-    // IPC::MessageReceiver
-    // Implemented in generated WebPageProxyMessageReceiver.cpp
-    void didReceiveMessage(IPC::Connection&, IPC::Decoder&) override;
-    void didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, std::unique_ptr<IPC::Encoder>&) override;
-
-
     // IPC::MessageSender
     bool sendMessage(std::unique_ptr<IPC::Encoder>, OptionSet<IPC::SendOption>) override;
     IPC::Connection* messageSenderConnection() override;
@@ -1466,12 +1491,9 @@ private:
     void didCreateSubframe(uint64_t frameID);
     void didCreateWindow(uint64_t frameID, WebCore::GlobalWindowIdentifier&&);
 
-    void didStartProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, URL&&, URL&& unreachableURL, const UserData&);
-    void didReceiveServerRedirectForProvisionalLoadForFrame(uint64_t frameID, uint64_t navigationID, WebCore::ResourceRequest&&, const UserData&);
     void willPerformClientRedirectForFrame(uint64_t frameID, const String& url, double delay, WebCore::LockBackForwardList);
     void didCancelClientRedirectForFrame(uint64_t frameID);
     void didChangeProvisionalURLForFrame(uint64_t frameID, uint64_t navigationID, URL&&);
-    void didFailProvisionalLoadForFrame(uint64_t frameID, const WebCore::SecurityOriginData& frameSecurityOrigin, uint64_t navigationID, const String& provisionalURL, const WebCore::ResourceError&, const UserData&);
     void didCommitLoadForFrame(uint64_t frameID, uint64_t navigationID, const String& mimeType, bool frameHasCustomContentProvider, uint32_t frameLoadType, const WebCore::CertificateInfo&, bool containsPluginDocument, Optional<WebCore::HasInsecureContent> forcedHasInsecureContent, const UserData&);
     void didFinishDocumentLoadForFrame(uint64_t frameID, uint64_t navigationID, const UserData&);
     void didFinishLoadForFrame(uint64_t frameID, uint64_t navigationID, const UserData&);
@@ -1575,18 +1597,16 @@ private:
     void setCanShortCircuitHorizontalWheelEvents(bool canShortCircuitHorizontalWheelEvents) { m_canShortCircuitHorizontalWheelEvents = canShortCircuitHorizontalWheelEvents; }
 
     void reattachToWebProcess();
-    void swapToWebProcess(Ref<WebProcessProxy>&&, std::unique_ptr<SuspendedPageProxy>&&, IsSwapFromSuspended);
+    void swapToWebProcess(Ref<WebProcessProxy>&&, std::unique_ptr<DrawingAreaProxy>&&, RefPtr<WebFrameProxy>&& mainFrame);
     void didFailToSuspendAfterProcessSwap();
     void didSuspendAfterProcessSwap();
 
-    void finishAttachingToWebProcess(IsSwapFromSuspended = IsSwapFromSuspended::No);
+    enum class ShouldInitializeWebPage { No, Yes };
+    void finishAttachingToWebProcess(ShouldInitializeWebPage = ShouldInitializeWebPage::Yes);
 
     RefPtr<API::Navigation> reattachToWebProcessForReload();
     RefPtr<API::Navigation> reattachToWebProcessWithItem(WebBackForwardListItem&);
 
-    void loadDataWithNavigation(API::Navigation&, const IPC::DataReference&, const String& MIMEType, const String& encoding, const String& baseURL, API::Object* userData, WebCore::ShouldTreatAsContinuingLoad, Optional<WebsitePoliciesData>&& = WTF::nullopt);
-    void loadRequestWithNavigation(API::Navigation&, WebCore::ResourceRequest&&, WebCore::ShouldOpenExternalURLsPolicy, API::Object* userData, WebCore::ShouldTreatAsContinuingLoad, Optional<WebsitePoliciesData>&& = WTF::nullopt);
-
     void requestNotificationPermission(uint64_t notificationID, const String& originString);
     void showNotification(const String& title, const String& body, const String& iconURL, const String& tag, const String& lang, WebCore::NotificationDirection, const String& originString, uint64_t notificationID);
     void cancelNotification(uint64_t notificationID);
@@ -1952,8 +1972,6 @@ private:
     Ref<WebsiteDataStore> m_websiteDataStore;
 
     RefPtr<WebFrameProxy> m_mainFrame;
-    Function<void()> m_mainFrameCreationHandler;
-    Function<void(const WebCore::GlobalWindowIdentifier&)> m_mainFrameWindowCreationHandler;
 
     RefPtr<WebFrameProxy> m_focusedFrame;
     RefPtr<WebFrameProxy> m_frameSetLargestFrame;
@@ -2324,6 +2342,8 @@ private:
     bool m_needsFontAttributes { false };
     bool m_mayHaveUniversalFileReadSandboxExtension { false };
 
+    std::unique_ptr<ProvisionalPageProxy> m_provisionalPage;
+
 #if HAVE(PENCILKIT)
     std::unique_ptr<EditableImageController> m_editableImageController;
 #endif
index db1fd58..6be9dc3 100644 (file)
@@ -34,6 +34,7 @@
 #include "Logging.h"
 #include "PluginInfoStore.h"
 #include "PluginProcessManager.h"
+#include "ProvisionalPageProxy.h"
 #include "TextChecker.h"
 #include "TextCheckerState.h"
 #include "UIMessagePortChannelProvider.h"
@@ -584,6 +585,15 @@ bool WebProcessProxy::fullKeyboardAccessEnabled()
 }
 #endif
 
+bool WebProcessProxy::hasProvisionalPageWithID(uint64_t pageID) const
+{
+    for (auto* provisionalPage : m_provisionalPages) {
+        if (provisionalPage->page().pageID() == pageID)
+            return true;
+    }
+    return false;
+}
+
 void WebProcessProxy::updateBackForwardItem(const BackForwardListItemState& itemState)
 {
     if (auto* item = WebBackForwardListItem::itemForID(itemState.identifier)) {
@@ -591,7 +601,7 @@ void WebProcessProxy::updateBackForwardItem(const BackForwardListItemState& item
         // the back/forward items page.
         // e.g. The old web process is navigating to about:blank for suspension.
         // We ignore these updates.
-        if (m_pageMap.contains(item->pageID()))
+        if (m_pageMap.contains(item->pageID()) || hasProvisionalPageWithID(item->pageID()))
             item->setPageState(itemState.pageState);
     }
 }
@@ -694,6 +704,7 @@ void WebProcessProxy::processDidTerminateOrFailedToLaunch()
         webConnection->didClose();
 
     auto pages = copyToVectorOf<RefPtr<WebPageProxy>>(m_pageMap.values());
+    auto provisionalPages = WTF::map(m_provisionalPages, [](auto* provisionalPage) { return makeWeakPtr(provisionalPage); });
 
     shutDown();
 
@@ -708,6 +719,11 @@ void WebProcessProxy::processDidTerminateOrFailedToLaunch()
 
     for (auto& page : pages)
         page->processDidTerminate(ProcessTerminationReason::Crash);
+
+    for (auto& provisionalPage : provisionalPages) {
+        if (provisionalPage)
+            provisionalPage->processDidTerminate();
+    }
 }
 
 void WebProcessProxy::didReceiveInvalidMessage(IPC::Connection& connection, IPC::StringReference messageReceiverName, IPC::StringReference messageName)
@@ -780,6 +796,12 @@ void WebProcessProxy::didFinishLaunching(ProcessLauncher* launcher, IPC::Connect
         page->processDidFinishLaunching();
     }
 
+    for (auto* provisionalPage : m_provisionalPages) {
+        ASSERT(this == &provisionalPage->process());
+        provisionalPage->processDidFinishLaunching();
+    }
+
+
     RELEASE_ASSERT(!m_webConnection);
     m_webConnection = WebConnectionToWebProcess::create(this);
 
@@ -873,7 +895,7 @@ void WebProcessProxy::maybeShutDown()
 
 bool WebProcessProxy::canTerminateChildProcess()
 {
-    if (!m_pageMap.isEmpty() || m_processPool->hasSuspendedPageFor(*this))
+    if (!m_pageMap.isEmpty() || m_processPool->hasSuspendedPageFor(*this) || !m_provisionalPages.isEmpty())
         return false;
 
     if (!m_processPool->shouldTerminate(this))
index 75f65a7..5042630 100644 (file)
@@ -70,6 +70,7 @@ namespace WebKit {
 class NetworkProcessProxy;
 class ObjCObjectGraph;
 class PageClient;
+class ProvisionalPageProxy;
 class UserMediaCaptureManagerProxy;
 class VisitedLinkStore;
 class WebBackForwardListItem;
@@ -124,6 +125,9 @@ public:
     enum class EndsUsingDataStore : bool { No, Yes };
     void removeWebPage(WebPageProxy&, uint64_t pageID, EndsUsingDataStore);
 
+    void addProvisionalPageProxy(ProvisionalPageProxy& provisionalPage) { ASSERT(!m_provisionalPages.contains(&provisionalPage)); m_provisionalPages.add(&provisionalPage); }
+    void removeProvisionalPageProxy(ProvisionalPageProxy& provisionalPage) { ASSERT(m_provisionalPages.contains(&provisionalPage)); m_provisionalPages.remove(&provisionalPage); }
+
     typename WebPageProxyMap::ValuesConstIteratorRange pages() const { return m_pageMap.values(); }
     unsigned pageCount() const { return m_pageMap.size(); }
     unsigned visiblePageCount() const { return m_visiblePageCounter.value(); }
@@ -330,6 +334,8 @@ private:
 
     void logDiagnosticMessageForResourceLimitTermination(const String& limitKey);
 
+    bool hasProvisionalPageWithID(uint64_t pageID) const;
+
     enum class IsWeak { No, Yes };
     template<typename T> class WeakOrStrongPtr {
     public:
@@ -373,6 +379,7 @@ private:
 
     WebPageProxyMap m_pageMap;
     WebFrameProxyMap m_frameMap;
+    HashSet<ProvisionalPageProxy*> m_provisionalPages;
     UserInitiatedActionMap m_userInitiatedActionMap;
 
     HashSet<VisitedLinkStore*> m_visitedLinkStores;
index dedbadc..d7da27a 100644 (file)
                466BC0381FA266C9002FA9C1 /* WebSWContextManagerConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebSWContextManagerConnection.cpp; sourceTree = "<group>"; };
                466BC0391FA266C9002FA9C1 /* WebSWContextManagerConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebSWContextManagerConnection.h; sourceTree = "<group>"; };
                466BC03A1FA266C9002FA9C1 /* WebSWContextManagerConnection.messages.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = WebSWContextManagerConnection.messages.in; sourceTree = "<group>"; };
+               4683569A21E81CC7006E27A3 /* ProvisionalPageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProvisionalPageProxy.h; sourceTree = "<group>"; };
+               4683569B21E81CC7006E27A3 /* ProvisionalPageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProvisionalPageProxy.cpp; sourceTree = "<group>"; };
                46A2B6061E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BackgroundProcessResponsivenessTimer.cpp; sourceTree = "<group>"; };
                46A2B6071E5675A200C3DEDA /* BackgroundProcessResponsivenessTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackgroundProcessResponsivenessTimer.h; sourceTree = "<group>"; };
                46DF06391F3905E5001980BB /* NetworkCORSPreflightChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkCORSPreflightChecker.cpp; sourceTree = "<group>"; };
                                86E67A22190F411800004AB7 /* ProcessThrottler.cpp */,
                                86E67A21190F411800004AB7 /* ProcessThrottler.h */,
                                83048AE51ACA45DC0082C832 /* ProcessThrottlerClient.h */,
+                               4683569B21E81CC7006E27A3 /* ProvisionalPageProxy.cpp */,
+                               4683569A21E81CC7006E27A3 /* ProvisionalPageProxy.h */,
                                A55BA81B1BA25B1E007CD33D /* RemoteWebInspectorProxy.cpp */,
                                A55BA8191BA25B1E007CD33D /* RemoteWebInspectorProxy.h */,
                                A55BA81A1BA25B1E007CD33D /* RemoteWebInspectorProxy.messages.in */,
index 00749b4..ffed00c 100644 (file)
@@ -263,8 +263,6 @@ static WebCore::PolicyAction toPolicyAction(WebPolicyAction policyAction)
         return WebCore::PolicyAction::Ignore;
     case WebPolicyAction::Download:
         return WebCore::PolicyAction::Download;
-    case WebPolicyAction::Suspend:
-        break;
     }
     ASSERT_NOT_REACHED();
     return WebCore::PolicyAction::Ignore;
@@ -272,11 +270,8 @@ static WebCore::PolicyAction toPolicyAction(WebPolicyAction policyAction)
 
 void WebFrame::didReceivePolicyDecision(uint64_t listenerID, WebPolicyAction action, uint64_t navigationID, DownloadID downloadID, Optional<WebsitePoliciesData>&& websitePolicies)
 {
-    if (!m_coreFrame || !m_policyListenerID || listenerID != m_policyListenerID || !m_policyFunction) {
-        if (action == WebPolicyAction::Suspend)
-            page()->send(Messages::WebPageProxy::DidFailToSuspendAfterProcessSwap());
+    if (!m_coreFrame || !m_policyListenerID || listenerID != m_policyListenerID || !m_policyFunction)
         return;
-    }
 
     FramePolicyFunction function = WTFMove(m_policyFunction);
     bool forNavigationAction = m_policyFunctionForNavigationAction == ForNavigationAction::Yes;
@@ -292,16 +287,7 @@ void WebFrame::didReceivePolicyDecision(uint64_t listenerID, WebPolicyAction act
             documentLoader->setNavigationID(navigationID);
     }
 
-    bool shouldSuspend = false;
-    if (action == WebPolicyAction::Suspend) {
-        shouldSuspend = true;
-        action = WebPolicyAction::Ignore;
-    }
-
     function(toPolicyAction(action));
-
-    if (shouldSuspend)
-        page()->suspendForProcessSwap();
 }
 
 void WebFrame::startDownload(const WebCore::ResourceRequest& request, const String& suggestedName)
index 803ba8b..10e5a77 100644 (file)
@@ -401,7 +401,7 @@ WebPage::WebPage(uint64_t pageID, WebPageCreationParameters&& parameters)
     , m_overrideContentSecurityPolicy { parameters.overrideContentSecurityPolicy }
     , m_cpuLimit(parameters.cpuLimit)
 #if PLATFORM(MAC)
-    , m_shouldAttachDrawingAreaOnPageTransition(parameters.isSwapFromSuspended)
+    , m_shouldAttachDrawingAreaOnPageTransition(parameters.isProcessSwap)
 #endif
 {
     ASSERT(m_pageID);
@@ -471,8 +471,8 @@ WebPage::WebPage(uint64_t pageID, WebPageCreationParameters&& parameters)
     m_drawingArea->setPaintingEnabled(false);
     m_drawingArea->setShouldScaleViewToFitDocument(parameters.shouldScaleViewToFitDocument);
 
-    if (parameters.isSwapFromSuspended)
-        freezeLayerTree(LayerTreeFreezeReason::SwapFromSuspended);
+    if (parameters.isProcessSwap)
+        freezeLayerTree(LayerTreeFreezeReason::ProcessSwap);
 
 #if ENABLE(ASYNC_SCROLLING)
     m_useAsyncScrolling = parameters.store.getBoolValueForKey(WebPreferencesKey::threadedScrollingEnabledKey());
@@ -696,7 +696,7 @@ void WebPage::reinitializeWebPage(WebPageCreationParameters&& parameters)
         m_drawingArea->updatePreferences(parameters.store);
         m_drawingArea->setPaintingEnabled(true);
 #if PLATFORM(MAC)
-        m_shouldAttachDrawingAreaOnPageTransition = parameters.isSwapFromSuspended;
+        m_shouldAttachDrawingAreaOnPageTransition = parameters.isProcessSwap;
 #endif
         unfreezeLayerTree(LayerTreeFreezeReason::PageSuspended);
     }
@@ -3053,12 +3053,6 @@ void WebPage::didReceivePolicyDecision(uint64_t frameID, uint64_t listenerID, We
     WebFrame* frame = WebProcess::singleton().webFrame(frameID);
     if (!frame)
         return;
-    if (policyAction == WebPolicyAction::Suspend) {
-        ASSERT(frame == m_mainFrame);
-        setIsSuspended(true);
-
-        WebProcess::singleton().sendPrewarmInformation(mainWebFrame()->url());
-    }
     frame->didReceivePolicyDecision(listenerID, policyAction, navigationID, downloadID, WTFMove(websitePolicies));
 }
 
@@ -3100,7 +3094,7 @@ void WebPage::didCompletePageTransition()
 
     bool isInitialEmptyDocument = !m_mainFrame;
     if (!isInitialEmptyDocument)
-        unfreezeLayerTree(LayerTreeFreezeReason::SwapFromSuspended);
+        unfreezeLayerTree(LayerTreeFreezeReason::ProcessSwap);
 
 #if PLATFORM(MAC)
     if (m_shouldAttachDrawingAreaOnPageTransition && !isInitialEmptyDocument) {
@@ -6312,6 +6306,10 @@ void WebPage::setIsSuspended(bool suspended)
     if (m_isSuspended) {
         // Unfrozen on drawing area reset.
         freezeLayerTree(LayerTreeFreezeReason::PageSuspended);
+
+        WebProcess::singleton().sendPrewarmInformation(mainWebFrame()->url());
+
+        suspendForProcessSwap();
     } else
         m_shouldResetDrawingAreaAfterSuspend = true;
 }
index 715e124..f3b934e 100644 (file)
@@ -696,7 +696,7 @@ public:
         ProcessSuspended        = 1 << 2,
         PageSuspended           = 1 << 3,
         Printing                = 1 << 4,
-        SwapFromSuspended       = 1 << 5,
+        ProcessSwap             = 1 << 5,
     };
     void freezeLayerTree(LayerTreeFreezeReason);
     void unfreezeLayerTree(LayerTreeFreezeReason);
index 91ad71a..83b6599 100644 (file)
@@ -97,7 +97,7 @@ TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea(WebPage& webPage, c
     updateLayerHostingContext();
     setColorSpace(parameters.colorSpace);
 
-    if (!parameters.isSwapFromSuspended)
+    if (!parameters.isProcessSwap)
         attach();
 }
 
index 050aee9..ff14b37 100644 (file)
@@ -1,3 +1,16 @@
+2019-01-15  Chris Dumez  <cdumez@apple.com>
+
+        Regression(PSON) View becomes blank after click a cross-site download link
+        https://bugs.webkit.org/show_bug.cgi?id=193361
+        <rdar://problem/47099573>
+
+        Reviewed by Geoff Garen.
+
+        Add API test coverage.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+        (-[PSONNavigationDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]):
+
 2019-01-15  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][Out-of-flow] Ignore bottom when the vertical values are over-constrained
index f35d8ba..a445bf1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -72,6 +72,7 @@ static bool serverRedirected;
 static HashSet<pid_t> seenPIDs;
 static bool willPerformClientRedirect;
 static bool didPerformClientRedirect;
+static bool shouldConvertToDownload;
 static RetainPtr<NSURL> clientRedirectSourceURL;
 static RetainPtr<NSURL> clientRedirectDestinationURL;
 
@@ -128,6 +129,11 @@ static RetainPtr<NSURL> clientRedirectDestinationURL;
     didRepondToPolicyDecisionCall = true;
 }
 
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+    decisionHandler(shouldConvertToDownload ? _WKNavigationResponsePolicyBecomeDownload : WKNavigationResponsePolicyAllow);
+}
+
 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
 {
     seenPIDs.add([webView _webProcessIdentifier]);
@@ -1405,7 +1411,7 @@ TEST(ProcessSwap, ServerRedirect)
 
     EXPECT_FALSE(serverRedirected);
     EXPECT_EQ(3, numberOfDecidePolicyCalls);
-    EXPECT_EQ(2u, seenPIDs.size());
+    EXPECT_EQ(3u, seenPIDs.size());
 }
 
 TEST(ProcessSwap, ServerRedirect2)
@@ -1467,6 +1473,109 @@ TEST(ProcessSwap, ServerRedirect2)
     EXPECT_WK_STREQ(@"pson://www.webkit.org/main1.html", [[webView URL] absoluteString]);
 }
 
+static const char* linkToWebKitBytes = R"PSONRESOURCE(
+<body>
+  <a id="testLink" href="pson://www.webkit.org/main.html">Link</a>
+</body>
+)PSONRESOURCE";
+
+TEST(ProcessSwap, PolicyCancelAfterServerRedirect)
+{
+    auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    processPoolConfiguration.get().processSwapsOnNavigation = 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]);
+    [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:linkToWebKitBytes];
+    [handler addRedirectFromURLString:@"pson://www.webkit.org/main.html" toURLString:@"pson://www.apple.com/ignore.html"];
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+
+    navigationDelegate->decidePolicyForNavigationAction = ^(WKNavigationAction *action, void (^decisionHandler)(WKNavigationActionPolicy)) {
+        if ([action.request.URL.absoluteString hasSuffix:@"ignore.html"]) {
+            decisionHandler(WKNavigationActionPolicyCancel);
+            return;
+        }
+        decisionHandler(WKNavigationActionPolicyAllow);
+    };
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+    auto pidAfterFirstLoad = [webView _webProcessIdentifier];
+
+    EXPECT_EQ(1, numberOfDecidePolicyCalls);
+
+    [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil];
+
+    TestWebKitAPI::Util::run(&failed);
+    failed = false;
+    done = false;
+
+    EXPECT_EQ(3, numberOfDecidePolicyCalls);
+
+    // We should still be on google.com.
+    EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]);
+    EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [[webView URL] absoluteString]);
+
+    [webView evaluateJavaScript:@"testLink.innerText" completionHandler: [&] (id innerText, NSError *error) {
+        EXPECT_WK_STREQ(@"Link", innerText);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
+
+TEST(ProcessSwap, CrossSiteDownload)
+{
+    auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    processPoolConfiguration.get().processSwapsOnNavigation = 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]);
+    [handler addMappingFromURLString:@"pson://www.google.com/main.html" toData:linkToWebKitBytes];
+    [handler addMappingFromURLString:@"pson://www.webkit.org/main.html" toData:"Hello"];
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto navigationDelegate = adoptNS([[PSONNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:navigationDelegate.get()];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.google.com/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+    auto pidAfterFirstLoad = [webView _webProcessIdentifier];
+
+    shouldConvertToDownload = true;
+    [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil];
+
+    TestWebKitAPI::Util::run(&failed);
+    failed = false;
+    shouldConvertToDownload = false;
+
+    // We should still be on google.com.
+    EXPECT_EQ(pidAfterFirstLoad, [webView _webProcessIdentifier]);
+    EXPECT_WK_STREQ(@"pson://www.google.com/main.html", [[webView URL] absoluteString]);
+
+    [webView evaluateJavaScript:@"testLink.innerText" completionHandler: [&] (id innerText, NSError *error) {
+        EXPECT_WK_STREQ(@"Link", innerText);
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+}
+
 enum class ShouldEnablePSON { No, Yes };
 static void runClientSideRedirectTest(ShouldEnablePSON shouldEnablePSON)
 {
@@ -2232,12 +2341,27 @@ TEST(ProcessSwap, PageShowHide)
 
     EXPECT_EQ(7u, [receivedMessages count]);
     EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow NOT persisted", receivedMessages.get()[0]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[1]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow NOT persisted", receivedMessages.get()[2]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pagehide persisted", receivedMessages.get()[3]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow persisted", receivedMessages.get()[4]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[5]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow persisted", receivedMessages.get()[6]);
+    if ([receivedMessages.get()[1] hasPrefix:@"pson://www.webkit.org/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[1]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow NOT persisted", receivedMessages.get()[2]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow NOT persisted", receivedMessages.get()[1]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[2]);
+    }
+    if ([receivedMessages.get()[3] hasPrefix:@"pson://www.apple.com/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pagehide persisted", receivedMessages.get()[3]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow persisted", receivedMessages.get()[4]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pageshow persisted", receivedMessages.get()[3]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pagehide persisted", receivedMessages.get()[4]);
+    }
+    if ([receivedMessages.get()[5] hasPrefix:@"pson://www.webkit.org/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[5]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow persisted", receivedMessages.get()[6]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - pageshow persisted", receivedMessages.get()[5]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - pagehide persisted", receivedMessages.get()[6]);
+    }
 }
 
 // Disabling the page cache explicitly is (for some reason) not available on iOS.
@@ -2307,12 +2431,27 @@ TEST(ProcessSwap, LoadUnload)
 
     EXPECT_EQ(7u, [receivedMessages count]);
     EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[0]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[1]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[2]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - unload", receivedMessages.get()[3]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[4]);
-    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[5]);
-    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[6]);
+    if ([receivedMessages.get()[1] hasPrefix:@"pson://www.webkit.org/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[1]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[2]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[1]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[2]);
+    }
+    if ([receivedMessages.get()[3] hasPrefix:@"pson://www.apple.com/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - unload", receivedMessages.get()[3]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[4]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - load", receivedMessages.get()[3]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - unload", receivedMessages.get()[4]);
+    }
+    if ([receivedMessages.get()[5] hasPrefix:@"pson://www.webkit.org/main.html"]) {
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[5]);
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[6]);
+    } else {
+        EXPECT_WK_STREQ(@"pson://www.apple.com/main.html - load", receivedMessages.get()[5]);
+        EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html - unload", receivedMessages.get()[6]);
+    }
 }
 
 TEST(ProcessSwap, WebInspector)