[PSON] Add support for cross-site client-side redirects
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2018 17:54:34 +0000 (17:54 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Oct 2018 17:54:34 +0000 (17:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=190806
<rdar://problem/45047344>

Reviewed by Geoffrey Garen.

Source/WebCore:

Add support for cross-site client-side redirects so that it swaps process and so that the back
forward list looks as expected. To support this, the following changes had to be done:
- The NavigationAction now provides additional information so that the WebProcess can communicate
  things about the client-side redirect to the UIProcess: lockHistory / lockBackForwardList and
  clientRedirectSourceForHistory.
- If the UIProcess decides to process-swap on a client-side redirect, we pass the client-side
  redirect information to the new WebContent process via LoadRequest struct. WebCore then takes
  care of setting things up using this information so that it recognizes that it is continuing
  a load that is a client side redirect.
- We also need to pass the current BackForwardListItem / HistoryItem to the new WebContent
  process so that the new process can truly lock history and keep updating the *current*
  HistoryItem, instead of creating a new HistoryItem.
- After a process swap, when we re-construct the WebFrameProxy for the main frame in the new
  process, we now set the frame's URL in the UIProcess to the URL it had before we swapped.
  Clients such as Safari, rely on the main frame's URL being the expected value (the last
  committed load URL) until the next load is committed when receiving didPerformRedirect
  calls. Because we are destroying the main frame on process-swapping, we were losing the
  last committed URL and Safari would hit assertions.

With this model, the willPerformClientRedirect IPC is still sent from the previous WebProcess
and the didPerformClientRedirect IPC is now sent by the new WebProcess. No change should be
observable from the client's point of view.

* loader/FrameLoadRequest.h:
(WebCore::FrameLoadRequest::setLockHistory):
(WebCore::FrameLoadRequest::setlockBackForwardList):
(WebCore::FrameLoadRequest::clientRedirectSourceForHistory const):
(WebCore::FrameLoadRequest::setClientRedirectSourceForHistory):
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::loadURL):
(WebCore::FrameLoader::load):
* loader/HistoryController.cpp:
(WebCore::HistoryController::updateForRedirectWithLockedBackForwardList):
* loader/HistoryController.h:
* loader/NavigationAction.h:
(WebCore::NavigationAction::lockHistory const):
(WebCore::NavigationAction::setLockHistory):
(WebCore::NavigationAction::lockBackForwardList const):
(WebCore::NavigationAction::setLockBackForwardList):

Source/WebKit:

* Shared/LoadParameters.cpp:
(WebKit::LoadParameters::encode const):
(WebKit::LoadParameters::decode):
* Shared/LoadParameters.h:
* Shared/NavigationActionData.cpp:
(WebKit::NavigationActionData::encode const):
(WebKit::NavigationActionData::decode):
* Shared/NavigationActionData.h:
* Shared/WebBackForwardListItem.cpp:
(WebKit::WebBackForwardListItem::setSuspendedPage):
* UIProcess/API/APINavigation.h:
(API::Navigation::setLockHistory):
(API::Navigation::lockHistory const):
(API::Navigation::setLockBackForwardList):
(API::Navigation::lockBackForwardList const):
(API::Navigation::setClientRedirectSourceForHistory):
(API::Navigation::clientRedirectSourceForHistory const):
* UIProcess/API/APINavigationClient.h:
(API::NavigationClient::willPerformClientRedirect):
(API::NavigationClient::didPerformClientRedirect):
* UIProcess/API/Cocoa/WKNavigationDelegatePrivate.h:
* UIProcess/Cocoa/NavigationState.h:
* UIProcess/Cocoa/NavigationState.mm:
(WebKit::NavigationState::setNavigationDelegate):
(WebKit::NavigationState::NavigationClient::didPerformClientRedirect):
* UIProcess/FrameLoadState.h:
(WebKit::FrameLoadState::setURL):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::loadRequestWithNavigation):
(WebKit::WebPageProxy::continueNavigationInNewProcess):
(WebKit::WebPageProxy::decidePolicyForNavigationAction):
(WebKit::WebPageProxy::didPerformClientRedirect):
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::loadRequest):
(WebKit::WebPage::setCurrentHistoryItemForReattach):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Tools:

Add API test coverage.

* TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
(-[PSONNavigationDelegate _webView:willPerformClientRedirectToURL:delay:]):
(-[PSONNavigationDelegate _webView:didPerformClientRedirectFromURL:toURL:]):

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

25 files changed:
Source/WebCore/ChangeLog
Source/WebCore/loader/FrameLoadRequest.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/HistoryController.cpp
Source/WebCore/loader/HistoryController.h
Source/WebCore/loader/NavigationAction.h
Source/WebKit/ChangeLog
Source/WebKit/Shared/LoadParameters.cpp
Source/WebKit/Shared/LoadParameters.h
Source/WebKit/Shared/NavigationActionData.cpp
Source/WebKit/Shared/NavigationActionData.h
Source/WebKit/Shared/WebBackForwardListItem.cpp
Source/WebKit/UIProcess/API/APINavigation.h
Source/WebKit/UIProcess/API/APINavigationClient.h
Source/WebKit/UIProcess/API/Cocoa/WKNavigationDelegatePrivate.h
Source/WebKit/UIProcess/Cocoa/NavigationState.h
Source/WebKit/UIProcess/Cocoa/NavigationState.mm
Source/WebKit/UIProcess/FrameLoadState.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/WebPage.messages.in
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm

index 53f1285..481b2dc 100644 (file)
@@ -1,3 +1,51 @@
+2018-10-23  Chris Dumez  <cdumez@apple.com>
+
+        [PSON] Add support for cross-site client-side redirects
+        https://bugs.webkit.org/show_bug.cgi?id=190806
+        <rdar://problem/45047344>
+
+        Reviewed by Geoffrey Garen.
+
+        Add support for cross-site client-side redirects so that it swaps process and so that the back
+        forward list looks as expected. To support this, the following changes had to be done:
+        - The NavigationAction now provides additional information so that the WebProcess can communicate
+          things about the client-side redirect to the UIProcess: lockHistory / lockBackForwardList and
+          clientRedirectSourceForHistory.
+        - If the UIProcess decides to process-swap on a client-side redirect, we pass the client-side
+          redirect information to the new WebContent process via LoadRequest struct. WebCore then takes
+          care of setting things up using this information so that it recognizes that it is continuing
+          a load that is a client side redirect.
+        - We also need to pass the current BackForwardListItem / HistoryItem to the new WebContent
+          process so that the new process can truly lock history and keep updating the *current*
+          HistoryItem, instead of creating a new HistoryItem.
+        - After a process swap, when we re-construct the WebFrameProxy for the main frame in the new
+          process, we now set the frame's URL in the UIProcess to the URL it had before we swapped.
+          Clients such as Safari, rely on the main frame's URL being the expected value (the last
+          committed load URL) until the next load is committed when receiving didPerformRedirect
+          calls. Because we are destroying the main frame on process-swapping, we were losing the
+          last committed URL and Safari would hit assertions.
+
+        With this model, the willPerformClientRedirect IPC is still sent from the previous WebProcess
+        and the didPerformClientRedirect IPC is now sent by the new WebProcess. No change should be
+        observable from the client's point of view.
+
+        * loader/FrameLoadRequest.h:
+        (WebCore::FrameLoadRequest::setLockHistory):
+        (WebCore::FrameLoadRequest::setlockBackForwardList):
+        (WebCore::FrameLoadRequest::clientRedirectSourceForHistory const):
+        (WebCore::FrameLoadRequest::setClientRedirectSourceForHistory):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::loadURL):
+        (WebCore::FrameLoader::load):
+        * loader/HistoryController.cpp:
+        (WebCore::HistoryController::updateForRedirectWithLockedBackForwardList):
+        * loader/HistoryController.h:
+        * loader/NavigationAction.h:
+        (WebCore::NavigationAction::lockHistory const):
+        (WebCore::NavigationAction::setLockHistory):
+        (WebCore::NavigationAction::lockBackForwardList const):
+        (WebCore::NavigationAction::setLockBackForwardList):
+
 2018-10-23  Jer Noble  <jer.noble@apple.com>
 
         Use WeakPtr and GenericTaskQueue within ObjC classes used by MediaPlayerPrivateAVFoundationObjC
index 3970ab9..4c0c3d5 100644 (file)
@@ -72,7 +72,14 @@ public:
     bool hasSubstituteData() { return m_substituteData.isValid(); }
 
     LockHistory lockHistory() const { return m_lockHistory; }
+    void setLockHistory(LockHistory value) { m_lockHistory = value; }
+
     LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; }
+    void setlockBackForwardList(LockBackForwardList value) { m_lockBackForwardList = value; }
+
+    const String& clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; }
+    void setClientRedirectSourceForHistory(const String& clientRedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientRedirectSourceForHistory; }
+
     ShouldSendReferrer shouldSendReferrer() const { return m_shouldSendReferrer; }
     AllowNavigationToInvalidURL allowNavigationToInvalidURL() const { return m_allowNavigationToInvalidURL; }
     NewFrameOpenerPolicy newFrameOpenerPolicy() const { return m_newFrameOpenerPolicy; }
@@ -97,6 +104,7 @@ private:
     ResourceRequest m_resourceRequest;
     String m_frameName;
     SubstituteData m_substituteData;
+    String m_clientRedirectSourceForHistory;
 
     bool m_shouldCheckNewWindowPolicy { false };
     bool m_shouldTreatAsContinuingLoad { false };
index fe44f0a..b186822 100644 (file)
@@ -1360,6 +1360,8 @@ void FrameLoader::loadURL(FrameLoadRequest&& frameLoadRequest, const String& ref
     if (m_frame.page() && m_frame.page()->openedViaWindowOpenWithOpener())
         action.setOpenedViaWindowOpenWithOpener();
     action.setHasOpenedFrames(!m_openedFrames.isEmpty());
+    action.setLockHistory(lockHistory);
+    action.setLockBackForwardList(frameLoadRequest.lockBackForwardList());
 
     if (!targetFrame && !effectiveFrameName.isEmpty()) {
         action = action.copyWithShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, frameLoadRequest));
@@ -1458,6 +1460,13 @@ void FrameLoader::load(FrameLoadRequest&& request)
     addSameSiteInfoToRequestIfNeeded(loader->request());
     applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, request);
 
+    if (request.shouldTreatAsContinuingLoad() && request.lockHistory() == LockHistory::Yes) {
+        // The load we're continuing is a client-side redirect so set things up accordingly.
+        loader->setClientRedirectSourceForHistory(request.clientRedirectSourceForHistory());
+        loader->setIsClientRedirect(true);
+        m_loadType = FrameLoadType::RedirectWithLockedBackForwardList;
+    }
+
     SetForScope<bool> currentLoadShouldBeTreatedAsContinuingLoadGuard(m_currentLoadShouldBeTreatedAsContinuingLoad, request.shouldTreatAsContinuingLoad());
     load(loader.get(), request.shouldSkipSafeBrowsingCheck());
 }
@@ -1495,7 +1504,7 @@ void FrameLoader::load(DocumentLoader& newDocumentLoader, ShouldSkipSafeBrowsing
         type = FrameLoadType::Same;
     } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader.unreachableURL()) && isReload(m_loadType))
         type = m_loadType;
-    else if (m_loadType == FrameLoadType::RedirectWithLockedBackForwardList && !newDocumentLoader.unreachableURL().isEmpty() && newDocumentLoader.substituteData().isValid())
+    else if (m_loadType == FrameLoadType::RedirectWithLockedBackForwardList && ((!newDocumentLoader.unreachableURL().isEmpty() && newDocumentLoader.substituteData().isValid()) || m_currentLoadShouldBeTreatedAsContinuingLoad))
         type = FrameLoadType::RedirectWithLockedBackForwardList;
     else
         type = FrameLoadType::Standard;
index 2edb7b3..76c94b2 100644 (file)
@@ -440,7 +440,7 @@ void HistoryController::updateForRedirectWithLockedBackForwardList()
         if (Page* page = m_frame.page())
             addVisitedLink(*page, historyURL);
 
-        if (!m_frame.loader().documentLoader()->didCreateGlobalHistoryEntry() && m_frame.loader().documentLoader()->unreachableURL().isEmpty() && !m_frame.document()->url().isEmpty())
+        if (!m_frame.loader().documentLoader()->didCreateGlobalHistoryEntry() && m_frame.loader().documentLoader()->unreachableURL().isEmpty())
             m_frame.loader().client().updateGlobalHistoryRedirectLinks();
     }
 }
index 5d25fea..d95252c 100644 (file)
@@ -75,7 +75,7 @@ public:
     void updateForFrameLoadCompleted();
 
     HistoryItem* currentItem() const { return m_currentItem.get(); }
-    void setCurrentItem(HistoryItem&);
+    WEBCORE_EXPORT void setCurrentItem(HistoryItem&);
     void setCurrentItemTitle(const StringWithDirection&);
     bool currentItemShouldBeReplaced() const;
     WEBCORE_EXPORT void replaceCurrentItem(HistoryItem*);
index 9d721d6..39c564e 100644 (file)
@@ -130,6 +130,12 @@ public:
     void setTargetBackForwardItem(HistoryItem&);
     const std::optional<BackForwardItemIdentifier>& targetBackForwardItemIdentifier() const { return m_targetBackForwardItemIdentifier; }
 
+    LockHistory lockHistory() const { return m_lockHistory; }
+    void setLockHistory(LockHistory lockHistory) { m_lockHistory = lockHistory; }
+
+    LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; }
+    void setLockBackForwardList(LockBackForwardList lockBackForwardList) { m_lockBackForwardList = lockBackForwardList; }
+
 private:
     // Do not add a strong reference to the originating document or a subobject that holds the
     // originating document. See comment above the class for more details.
@@ -147,6 +153,8 @@ private:
     bool m_openedViaWindowOpenWithOpener { false };
     std::optional<PageIDAndFrameIDPair> m_opener;
     std::optional<BackForwardItemIdentifier> m_targetBackForwardItemIdentifier;
+    LockHistory m_lockHistory { LockHistory::No };
+    LockBackForwardList m_lockBackForwardList { LockBackForwardList::No };
 };
 
 } // namespace WebCore
index 7535e17..83f35af 100644 (file)
@@ -1,3 +1,51 @@
+2018-10-23  Chris Dumez  <cdumez@apple.com>
+
+        [PSON] Add support for cross-site client-side redirects
+        https://bugs.webkit.org/show_bug.cgi?id=190806
+        <rdar://problem/45047344>
+
+        Reviewed by Geoffrey Garen.
+
+        * Shared/LoadParameters.cpp:
+        (WebKit::LoadParameters::encode const):
+        (WebKit::LoadParameters::decode):
+        * Shared/LoadParameters.h:
+        * Shared/NavigationActionData.cpp:
+        (WebKit::NavigationActionData::encode const):
+        (WebKit::NavigationActionData::decode):
+        * Shared/NavigationActionData.h:
+        * Shared/WebBackForwardListItem.cpp:
+        (WebKit::WebBackForwardListItem::setSuspendedPage):
+        * UIProcess/API/APINavigation.h:
+        (API::Navigation::setLockHistory):
+        (API::Navigation::lockHistory const):
+        (API::Navigation::setLockBackForwardList):
+        (API::Navigation::lockBackForwardList const):
+        (API::Navigation::setClientRedirectSourceForHistory):
+        (API::Navigation::clientRedirectSourceForHistory const):
+        * UIProcess/API/APINavigationClient.h:
+        (API::NavigationClient::willPerformClientRedirect):
+        (API::NavigationClient::didPerformClientRedirect):
+        * UIProcess/API/Cocoa/WKNavigationDelegatePrivate.h:
+        * UIProcess/Cocoa/NavigationState.h:
+        * UIProcess/Cocoa/NavigationState.mm:
+        (WebKit::NavigationState::setNavigationDelegate):
+        (WebKit::NavigationState::NavigationClient::didPerformClientRedirect):
+        * UIProcess/FrameLoadState.h:
+        (WebKit::FrameLoadState::setURL):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::loadRequestWithNavigation):
+        (WebKit::WebPageProxy::continueNavigationInNewProcess):
+        (WebKit::WebPageProxy::decidePolicyForNavigationAction):
+        (WebKit::WebPageProxy::didPerformClientRedirect):
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::loadRequest):
+        (WebKit::WebPage::setCurrentHistoryItemForReattach):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2018-10-23  Claudio Saavedra  <csaavedra@igalia.com>
 
         [WPE][GTK] Pass full certificate chain in CertificateInfo coder
index 0b51ff0..beb6244 100644 (file)
@@ -50,6 +50,9 @@ void LoadParameters::encode(IPC::Encoder& encoder) const
     encoder << shouldTreatAsContinuingLoad;
     encoder << userData;
     encoder << forSafeBrowsing;
+    encoder.encodeEnum(lockHistory);
+    encoder.encodeEnum(lockBackForwardList);
+    encoder << clientRedirectSourceForHistory;
 
     platformEncode(encoder);
 }
@@ -108,7 +111,19 @@ bool LoadParameters::decode(IPC::Decoder& decoder, LoadParameters& data)
 
     if (!decoder.decode(data.forSafeBrowsing))
         return false;
-    
+
+    if (!decoder.decodeEnum(data.lockHistory))
+        return false;
+
+    if (!decoder.decodeEnum(data.lockBackForwardList))
+        return false;
+
+    std::optional<String> clientRedirectSourceForHistory;
+    decoder >> clientRedirectSourceForHistory;
+    if (!clientRedirectSourceForHistory)
+        return false;
+    data.clientRedirectSourceForHistory = WTFMove(*clientRedirectSourceForHistory);
+
     if (!platformDecode(decoder, data))
         return false;
 
index 574c7cd..7a4908d 100644 (file)
@@ -28,6 +28,7 @@
 #include "DataReference.h"
 #include "SandboxExtension.h"
 #include "UserData.h"
+#include <WebCore/FrameLoaderTypes.h>
 #include <WebCore/ResourceRequest.h>
 
 OBJC_CLASS NSDictionary;
@@ -63,6 +64,9 @@ struct LoadParameters {
     bool shouldTreatAsContinuingLoad { false };
     UserData userData;
     bool forSafeBrowsing { false };
+    WebCore::LockHistory lockHistory { WebCore::LockHistory::No };
+    WebCore::LockBackForwardList lockBackForwardList { WebCore::LockBackForwardList::No };
+    String clientRedirectSourceForHistory;
 
 #if PLATFORM(COCOA)
     RetainPtr<NSDictionary> dataDetectionContext;
index 394361d..38722e9 100644 (file)
@@ -51,6 +51,9 @@ void NavigationActionData::encode(IPC::Encoder& encoder) const
     encoder << opener;
     encoder << requesterOrigin;
     encoder << targetBackForwardItemIdentifier;
+    encoder.encodeEnum(lockHistory);
+    encoder.encodeEnum(lockBackForwardList);
+    encoder << clientRedirectSourceForHistory;
 }
 
 std::optional<NavigationActionData> NavigationActionData::decode(IPC::Decoder& decoder)
@@ -128,11 +131,24 @@ std::optional<NavigationActionData> NavigationActionData::decode(IPC::Decoder& d
     decoder >> targetBackForwardItemIdentifier;
     if (!targetBackForwardItemIdentifier)
         return std::nullopt;
-        
+
+    WebCore::LockHistory lockHistory;
+    if (!decoder.decodeEnum(lockHistory))
+        return std::nullopt;
+
+    WebCore::LockBackForwardList lockBackForwardList;
+    if (!decoder.decodeEnum(lockBackForwardList))
+        return std::nullopt;
+
+    std::optional<String> clientRedirectSourceForHistory;
+    decoder >> clientRedirectSourceForHistory;
+    if (!clientRedirectSourceForHistory)
+        return std::nullopt;
+
     return {{ WTFMove(navigationType), WTFMove(modifiers), WTFMove(mouseButton), WTFMove(syntheticClickType), WTFMove(*userGestureTokenIdentifier),
         WTFMove(*canHandleRequest), WTFMove(shouldOpenExternalURLsPolicy), WTFMove(*downloadAttribute), WTFMove(clickLocationInRootViewCoordinates),
         WTFMove(*isRedirect), *treatAsSameOriginNavigation, *hasOpenedFrames, *openedViaWindowOpenWithOpener, WTFMove(*opener), WTFMove(*requesterOrigin),
-        WTFMove(*targetBackForwardItemIdentifier) }};
+        WTFMove(*targetBackForwardItemIdentifier), lockHistory, lockBackForwardList, WTFMove(*clientRedirectSourceForHistory) }};
 }
 
 } // namespace WebKit
index 0839f1f..16f9767 100644 (file)
@@ -58,6 +58,9 @@ struct NavigationActionData {
     std::optional<std::pair<uint64_t, uint64_t>> opener;
     WebCore::SecurityOriginData requesterOrigin;
     std::optional<WebCore::BackForwardItemIdentifier> targetBackForwardItemIdentifier;
+    WebCore::LockHistory lockHistory;
+    WebCore::LockBackForwardList lockBackForwardList;
+    WTF::String clientRedirectSourceForHistory;
 };
 
 }
index df97350..612db23 100644 (file)
@@ -115,7 +115,6 @@ bool WebBackForwardListItem::itemIsInSameDocument(const WebBackForwardListItem&
 
 void WebBackForwardListItem::setSuspendedPage(SuspendedPageProxy& page)
 {
-    ASSERT(!m_suspendedPage);
     m_suspendedPage = makeWeakPtr(page);
 }
 
index c68e3bf..ba1e393 100644 (file)
@@ -101,6 +101,15 @@ public:
     void setRequesterOrigin(const WebCore::SecurityOriginData& origin) { m_requesterOrigin = origin; }
     const WebCore::SecurityOriginData& requesterOrigin() const { return m_requesterOrigin; }
 
+    void setLockHistory(WebCore::LockHistory lockHistory) { m_lockHistory = lockHistory; }
+    WebCore::LockHistory lockHistory() const { return m_lockHistory; }
+
+    void setLockBackForwardList(WebCore::LockBackForwardList lockBackForwardList) { m_lockBackForwardList = lockBackForwardList; }
+    WebCore::LockBackForwardList lockBackForwardList() const { return m_lockBackForwardList; }
+
+    void setClientRedirectSourceForHistory(const WTF::String& clientRedirectSourceForHistory) { m_clientRedirectSourceForHistory = clientRedirectSourceForHistory; }
+    WTF::String clientRedirectSourceForHistory() const { return m_clientRedirectSourceForHistory; }
+
 #if !LOG_DISABLED
     const char* loggingString() const;
 #endif
@@ -127,6 +136,9 @@ private:
     bool m_openedViaWindowOpenWithOpener { false };
     std::optional<std::pair<uint64_t, uint64_t>> m_opener;
     WebCore::SecurityOriginData m_requesterOrigin;
+    WebCore::LockHistory m_lockHistory;
+    WebCore::LockBackForwardList m_lockBackForwardList;
+    WTF::String m_clientRedirectSourceForHistory;
 };
 
 } // namespace API
index 34f0893..89d3e82 100644 (file)
@@ -73,7 +73,8 @@ public:
 
     virtual void didStartProvisionalNavigation(WebKit::WebPageProxy&, Navigation*, Object*) { }
     virtual void didReceiveServerRedirectForProvisionalNavigation(WebKit::WebPageProxy&, Navigation*, Object*) { }
-    virtual void willPerformClientRedirect(WebKit::WebPageProxy&, const WTF::String&, double) { }
+    virtual void willPerformClientRedirect(WebKit::WebPageProxy&, const WTF::String& destinationURL, double) { }
+    virtual void didPerformClientRedirect(WebKit::WebPageProxy&, const WTF::String& sourceURL, const WTF::String& destinationURL) { }
     virtual void didCancelClientRedirect(WebKit::WebPageProxy&) { }
     virtual void didFailProvisionalNavigationWithError(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, Navigation*, const WebCore::ResourceError&, Object*) { }
     virtual void didFailProvisionalLoadInSubframeWithError(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, const WebCore::SecurityOriginData&, Navigation*, const WebCore::ResourceError&, Object*) { }
index 48f8f0e..ba5686b 100644 (file)
@@ -68,6 +68,7 @@ static const WKNavigationResponsePolicy _WKNavigationResponsePolicyBecomeDownloa
 - (void)_webView:(WKWebView *)webView navigation:(WKNavigation *)navigation didFailProvisionalLoadInSubframe:(WKFrameInfo *)subframe withError:(NSError *)error;
 
 - (void)_webView:(WKWebView *)webView willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)delay;
+- (void)_webView:(WKWebView *)webView didPerformClientRedirectFromURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL;
 - (void)_webViewDidCancelClientRedirect:(WKWebView *)webView;
 
 - (void)_webView:(WKWebView *)webView navigationDidFinishDocumentLoad:(WKNavigation *)navigation;
index 0c9af95..a6a9747 100644 (file)
@@ -98,6 +98,7 @@ private:
         void didStartProvisionalNavigation(WebPageProxy&, API::Navigation*, API::Object*) override;
         void didReceiveServerRedirectForProvisionalNavigation(WebPageProxy&, API::Navigation*, API::Object*) override;
         void willPerformClientRedirect(WebPageProxy&, const WTF::String&, double) override;
+        void didPerformClientRedirect(WebPageProxy&, const WTF::String&, const WTF::String&) override;
         void didCancelClientRedirect(WebPageProxy&) override;
         void didFailProvisionalNavigationWithError(WebPageProxy&, WebFrameProxy&, API::Navigation*, const WebCore::ResourceError&, API::Object*) override;
         void didFailProvisionalLoadInSubframeWithError(WebPageProxy&, WebFrameProxy&, const WebCore::SecurityOriginData&, API::Navigation*, const WebCore::ResourceError&, API::Object*) override;
@@ -195,6 +196,7 @@ private:
         bool webViewDidFailProvisionalNavigationWithError : 1;
         bool webViewNavigationDidFailProvisionalLoadInSubframeWithError : 1;
         bool webViewWillPerformClientRedirect : 1;
+        bool webViewDidPerformClientRedirect : 1;
         bool webViewDidCancelClientRedirect : 1;
         bool webViewDidCommitNavigation : 1;
         bool webViewNavigationDidFinishDocumentLoad : 1;
index bf0bd07..06c4e51 100644 (file)
@@ -157,6 +157,7 @@ void NavigationState::setNavigationDelegate(id <WKNavigationDelegate> delegate)
 
     m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError = [delegate respondsToSelector:@selector(_webView:navigation:didFailProvisionalLoadInSubframe:withError:)];
     m_navigationDelegateMethods.webViewWillPerformClientRedirect = [delegate respondsToSelector:@selector(_webView:willPerformClientRedirectToURL:delay:)];
+    m_navigationDelegateMethods.webViewDidPerformClientRedirect = [delegate respondsToSelector:@selector(_webView:didPerformClientRedirectFromURL:toURL:)];
     m_navigationDelegateMethods.webViewDidCancelClientRedirect = [delegate respondsToSelector:@selector(_webViewDidCancelClientRedirect:)];
     m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad = [delegate respondsToSelector:@selector(_webView:navigationDidFinishDocumentLoad:)];
     m_navigationDelegateMethods.webViewNavigationDidSameDocumentNavigation = [delegate respondsToSelector:@selector(_webView:navigation:didSameDocumentNavigation:)];
@@ -704,6 +705,21 @@ void NavigationState::NavigationClient::willPerformClientRedirect(WebPageProxy&
     [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webView:m_navigationState.m_webView willPerformClientRedirectToURL:url delay:delay];
 }
 
+void NavigationState::NavigationClient::didPerformClientRedirect(WebPageProxy& page, const WTF::String& sourceURLString, const WTF::String& destinationURLString)
+{
+    if (!m_navigationState.m_navigationDelegateMethods.webViewDidPerformClientRedirect)
+        return;
+
+    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
+    if (!navigationDelegate)
+        return;
+
+    WebCore::URL sourceURL(WebCore::URL(), sourceURLString);
+    WebCore::URL destinationURL(WebCore::URL(), destinationURLString);
+
+    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webView:m_navigationState.m_webView didPerformClientRedirectFromURL:sourceURL toURL:destinationURL];
+}
+
 void NavigationState::NavigationClient::didCancelClientRedirect(WebPageProxy& page)
 {
     if (!m_navigationState.m_navigationDelegateMethods.webViewDidCancelClientRedirect)
index 98c325e..a89ad00 100644 (file)
@@ -51,6 +51,7 @@ public:
 
     State state() const { return m_state; }
     const WebCore::URL& url() const { return m_url; }
+    void setURL(const WebCore::URL& url) { m_url = url; }
     const WebCore::URL& provisionalURL() const { return m_provisionalURL; }
 
     void setUnreachableURL(const WebCore::URL&);
index f2737a2..9dea798 100644 (file)
 #include "WebImage.h"
 #include "WebInspectorProxy.h"
 #include "WebInspectorUtilities.h"
+#include "WebNavigationDataStore.h"
 #include "WebNavigationState.h"
 #include "WebNotificationManagerProxy.h"
 #include "WebOpenPanelResultListenerProxy.h"
@@ -1017,6 +1018,9 @@ void WebPageProxy::loadRequestWithNavigation(API::Navigation& navigation, Resour
     loadParameters.shouldOpenExternalURLsPolicy = (uint64_t)shouldOpenExternalURLsPolicy;
     loadParameters.userData = UserData(process().transformObjectsToHandles(userData).get());
     loadParameters.shouldTreatAsContinuingLoad = shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::Yes;
+    loadParameters.lockHistory = navigation.lockHistory();
+    loadParameters.lockBackForwardList = navigation.lockBackForwardList();
+    loadParameters.clientRedirectSourceForHistory = navigation.clientRedirectSourceForHistory();
     bool createdExtension = maybeInitializeSandboxExtensionHandle(url, loadParameters.sandboxExtensionHandle);
     if (createdExtension)
         m_process->willAcquireUniversalFileReadSandboxExtension();
@@ -2548,6 +2552,7 @@ void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, R
 
     Ref<WebProcessProxy> previousProcess = m_process.copyRef();
     std::optional<uint64_t> mainFrameIDInPreviousProcess = m_mainFrame ? std::make_optional(m_mainFrame->frameID()) : std::nullopt;
+    auto mainFrameURL = m_mainFrame ? m_mainFrame->url() : WebCore::URL();
 
     ASSERT(m_process.ptr() != process.ptr());
 
@@ -2575,21 +2580,34 @@ void WebPageProxy::continueNavigationInNewProcess(API::Navigation& navigation, R
         return;
     }
 
+    if (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.
+        auto itemStates = m_backForwardList->filteredItemStates([currentItem = m_backForwardList->currentItem()](WebBackForwardListItem& item) {
+            return &item == currentItem;
+        });
+        RELEASE_ASSERT(itemStates.size() == 1);
+        m_process->send(Messages::WebPage::SetCurrentHistoryItemForReattach(itemStates[0]), m_pageID);
+    }
+
     // FIXME: Work out timing of responding with the last policy delegate, etc
     ASSERT(!navigation.currentRequest().isEmpty());
     loadRequestWithNavigation(navigation, ResourceRequest { navigation.currentRequest() }, WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes, nullptr, ShouldTreatAsContinuingLoad::Yes);
 
-    // 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 (navigation.currentRequestIsRedirect()) {
-        ASSERT(!m_mainFrame);
-        m_mainFrameCreationHandler = [this, protectedThis = makeRef(*this), navigation = makeRef(navigation), request =  navigation.currentRequest()]() mutable {
-            ASSERT(m_mainFrame);
+    ASSERT(!m_mainFrame);
+    m_mainFrameCreationHandler = [this, protectedThis = makeRef(*this), navigation = makeRef(navigation), request =  navigation.currentRequest(), mainFrameURL, isServerRedirect = navigation.currentRequestIsRedirect()]() mutable {
+        ASSERT(m_mainFrame);
+        // 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(), navigation->navigationID(), WTFMove(request), { });
-        };
-    }
+        }
+    };
 
     bool isInitialNavigationInNewWindow = openedByDOM() && !hasCommittedAnyProvisionalLoads();
     if (!isInitialNavigationInNewWindow || !mainFrameIDInPreviousProcess)
@@ -4115,6 +4133,9 @@ void WebPageProxy::decidePolicyForNavigationAction(WebFrameProxy& frame, const W
         navigation->setOpenedViaWindowOpenWithOpener();
     navigation->setOpener(navigationActionData.opener);
     navigation->setRequesterOrigin(navigationActionData.requesterOrigin);
+    navigation->setLockHistory(navigationActionData.lockHistory);
+    navigation->setLockBackForwardList(navigationActionData.lockBackForwardList);
+    navigation->setClientRedirectSourceForHistory(navigationActionData.clientRedirectSourceForHistory);
 
 #if ENABLE(CONTENT_FILTERING)
     if (frame.didHandleContentFilterUnblockNavigation(request))
@@ -4302,8 +4323,10 @@ void WebPageProxy::didPerformClientRedirect(const String& sourceURLString, const
     MESSAGE_CHECK_URL(sourceURLString);
     MESSAGE_CHECK_URL(destinationURLString);
 
-    if (frame->isMainFrame())
+    if (frame->isMainFrame()) {
         m_historyClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString);
+        m_navigationClient->didPerformClientRedirect(*this, sourceURLString, destinationURLString);
+    }
     process().processPool().historyClient().didPerformClientRedirect(process().processPool(), *this, sourceURLString, destinationURLString, *frame);
 }
 
index 1d09af2..eb25673 100644 (file)
@@ -871,6 +871,8 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const Navigat
     if (auto& requester = navigationAction.requester())
         navigationActionData.requesterOrigin = requester->securityOrigin().data();
     navigationActionData.targetBackForwardItemIdentifier = navigationAction.targetBackForwardItemIdentifier();
+    navigationActionData.lockHistory = navigationAction.lockHistory();
+    navigationActionData.lockBackForwardList = navigationAction.lockBackForwardList();
 
     WebCore::Frame* coreFrame = m_frame->coreFrame();
     if (!coreFrame)
@@ -884,6 +886,8 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const Navigat
     if (!documentLoader)
         documentLoader = static_cast<WebDocumentLoader*>(coreFrame->loader().documentLoader());
 
+    navigationActionData.clientRedirectSourceForHistory = documentLoader->clientRedirectSourceForHistory();
+
     // Notify the UIProcess.
     Ref<WebFrame> protect(*m_frame);
 
index 572daee..9e26dc1 100644 (file)
@@ -1307,6 +1307,9 @@ void WebPage::loadRequest(LoadParameters&& loadParameters)
     ShouldOpenExternalURLsPolicy externalURLsPolicy = static_cast<ShouldOpenExternalURLsPolicy>(loadParameters.shouldOpenExternalURLsPolicy);
     frameLoadRequest.setShouldOpenExternalURLsPolicy(externalURLsPolicy);
     frameLoadRequest.setShouldTreatAsContinuingLoad(loadParameters.shouldTreatAsContinuingLoad);
+    frameLoadRequest.setLockHistory(loadParameters.lockHistory);
+    frameLoadRequest.setlockBackForwardList(loadParameters.lockBackForwardList);
+    frameLoadRequest.setClientRedirectSourceForHistory(loadParameters.clientRedirectSourceForHistory);
 
     corePage()->userInputBridge().loadRequest(WTFMove(frameLoadRequest));
 
@@ -2606,6 +2609,14 @@ void WebPage::updateBackForwardListForReattach(const Vector<WebKit::BackForwardL
     restoreSessionInternal(itemStates, WasRestoredByAPIRequest::No, WebBackForwardListProxy::OverwriteExistingItem::Yes);
 }
 
+void WebPage::setCurrentHistoryItemForReattach(WebKit::BackForwardListItemState&& itemState)
+{
+    auto historyItem = toHistoryItem(itemState);
+    auto& historyItemRef = historyItem.get();
+    static_cast<WebBackForwardListProxy&>(corePage()->backForward().client()).addItemFromUIProcess(itemState.identifier, WTFMove(historyItem), m_pageID, WebBackForwardListProxy::OverwriteExistingItem::Yes);
+    corePage()->mainFrame().loader().history().setCurrentItem(historyItemRef);
+}
+
 void WebPage::requestFontAttributesAtSelectionStart(CallbackID callbackID)
 {
     auto attributes = m_page->focusController().focusedOrMainFrame().editor().fontAttributesAtSelectionStart();
index ba52743..b2551e7 100644 (file)
@@ -1209,6 +1209,7 @@ private:
     void restoreSession(const Vector<BackForwardListItemState>&);
     void didRemoveBackForwardItem(const WebCore::BackForwardItemIdentifier&);
     void updateBackForwardListForReattach(const Vector<WebKit::BackForwardListItemState>&);
+    void setCurrentHistoryItemForReattach(WebKit::BackForwardListItemState&&);
 
     void requestFontAttributesAtSelectionStart(CallbackID);
 
index 97ad442..6c87104 100644 (file)
@@ -157,6 +157,7 @@ messages -> WebPage LegacyReceiver {
     
     RestoreSession(Vector<WebKit::BackForwardListItemState> itemStates)
     UpdateBackForwardListForReattach(Vector<WebKit::BackForwardListItemState> itemStates)
+    SetCurrentHistoryItemForReattach(struct WebKit::BackForwardListItemState itemState)
 
     DidRemoveBackForwardItem(struct WebCore::BackForwardItemIdentifier backForwardItemID)
 
index 89c111d..32a1101 100644 (file)
@@ -1,3 +1,17 @@
+2018-10-23  Chris Dumez  <cdumez@apple.com>
+
+        [PSON] Add support for cross-site client-side redirects
+        https://bugs.webkit.org/show_bug.cgi?id=190806
+        <rdar://problem/45047344>
+
+        Reviewed by Geoffrey Garen.
+
+        Add API test coverage.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
+        (-[PSONNavigationDelegate _webView:willPerformClientRedirectToURL:delay:]):
+        (-[PSONNavigationDelegate _webView:didPerformClientRedirectFromURL:toURL:]):
+
 2018-10-23  Claudio Saavedra  <csaavedra@igalia.com>
 
         [WPE][GTK] Pass full certificate chain in CertificateInfo coder
index bc29ef8..0fe3c3d 100644 (file)
@@ -68,6 +68,11 @@ bool didReceiveAlert;
 static bool receivedMessage;
 static bool serverRedirected;
 static HashSet<pid_t> seenPIDs;
+static bool willPerformClientRedirect;
+static bool didPerformClientRedirect;
+static RetainPtr<NSURL> clientRedirectSourceURL;
+static RetainPtr<NSURL> clientRedirectDestinationURL;
+
 @interface PSONMessageHandler : NSObject <WKScriptMessageHandler>
 @end
 
@@ -124,6 +129,20 @@ static HashSet<pid_t> seenPIDs;
     serverRedirected = true;
 }
 
+- (void)_webView:(WKWebView *)webView willPerformClientRedirectToURL:(NSURL *)URL delay:(NSTimeInterval)delay
+{
+    clientRedirectDestinationURL = URL;
+    willPerformClientRedirect = true;
+}
+
+- (void)_webView:(WKWebView *)webView didPerformClientRedirectFromURL:(NSURL *)sourceURL toURL:(NSURL *)destinationURL
+{
+    EXPECT_TRUE(willPerformClientRedirect);
+    EXPECT_WK_STREQ([clientRedirectDestinationURL absoluteString], [destinationURL absoluteString]);
+    clientRedirectSourceURL = sourceURL;
+    didPerformClientRedirect = true;
+}
+
 @end
 
 static RetainPtr<WKWebView> createdWebView;
@@ -245,6 +264,22 @@ window.onpageshow = function(evt) {
 </head>
 )PSONRESOURCE";
 
+static const char* linkToCrossSiteClientSideRedirectBytes = R"PSONRESOURCE(
+<body>
+  <a id="testLink" href="pson://www.google.com/clientSideRedirect.html">Link to cross-site client-side redirect</a>
+</body>
+)PSONRESOURCE";
+
+static const char* crossSiteClientSideRedirectBytes = R"PSONRESOURCE(
+<body>
+<script>
+onload = () => {
+  location = "pson://www.apple.com/main.html";
+};
+</script>
+</body>
+)PSONRESOURCE";
+
 #if PLATFORM(MAC)
 
 static const char* windowOpenCrossSiteNoOpenerTestBytes = R"PSONRESOURCE(
@@ -1097,6 +1132,143 @@ TEST(ProcessSwap, ServerRedirect2)
     EXPECT_EQ(2u, seenPIDs.size());
 }
 
+enum class ShouldEnablePSON { No, Yes };
+static void runClientSideRedirectTest(ShouldEnablePSON shouldEnablePSON)
+{
+    auto processPoolConfiguration = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes ? YES : NO;
+    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.webkit.org/main.html" toData:linkToCrossSiteClientSideRedirectBytes];
+    [handler addMappingFromURLString:@"pson://www.google.com/clientSideRedirect.html" toData:crossSiteClientSideRedirectBytes];
+    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
+
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
+    auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main.html"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    auto webkitPID = [webView _webProcessIdentifier];
+
+    // Navigate to the page doing a client-side redirect to apple.com.
+    [webView evaluateJavaScript:@"testLink.click()" completionHandler:nil];
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [[webView URL] absoluteString]);
+    auto googlePID = [webView _webProcessIdentifier];
+    if (shouldEnablePSON == ShouldEnablePSON::Yes)
+        EXPECT_NE(webkitPID, googlePID);
+    else
+        EXPECT_EQ(webkitPID, googlePID);
+
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]);
+
+    auto applePID = [webView _webProcessIdentifier];
+    if (shouldEnablePSON == ShouldEnablePSON::Yes) {
+        EXPECT_NE(webkitPID, applePID);
+        EXPECT_NE(webkitPID, googlePID);
+    } else {
+        EXPECT_EQ(webkitPID, applePID);
+        EXPECT_EQ(webkitPID, googlePID);
+    }
+
+    EXPECT_TRUE(willPerformClientRedirect);
+    EXPECT_TRUE(didPerformClientRedirect);
+    EXPECT_WK_STREQ(@"pson://www.google.com/clientSideRedirect.html", [clientRedirectSourceURL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [clientRedirectDestinationURL absoluteString]);
+
+    willPerformClientRedirect = false;
+    didPerformClientRedirect = false;
+    clientRedirectSourceURL = nullptr;
+    clientRedirectDestinationURL = nullptr;
+
+    // Validate Back/Forward list.
+    auto* backForwardList = [webView backForwardList];
+    auto* currentItem = backForwardList.currentItem;
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.URL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.initialURL absoluteString]);
+    EXPECT_TRUE(!backForwardList.forwardItem);
+
+    EXPECT_EQ(1U, backForwardList.backList.count);
+
+    auto* backItem = backForwardList.backItem;
+    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.URL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.initialURL absoluteString]);
+
+    // Navigate back.
+    [webView goBack];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [[webView URL] absoluteString]);
+    EXPECT_FALSE(willPerformClientRedirect);
+    EXPECT_FALSE(didPerformClientRedirect);
+
+    auto pidAfterBackNavigation = [webView _webProcessIdentifier];
+    EXPECT_EQ(webkitPID, pidAfterBackNavigation);
+
+    // Validate Back/Forward list.
+    currentItem = backForwardList.currentItem;
+    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [currentItem.URL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [currentItem.initialURL absoluteString]);
+
+    EXPECT_TRUE(!backForwardList.backItem);
+    EXPECT_EQ(1U, backForwardList.forwardList.count);
+
+    auto* forwardItem = backForwardList.forwardItem;
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [forwardItem.URL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [forwardItem.initialURL absoluteString]);
+
+    // Navigate forward.
+    [webView goForward];
+    TestWebKitAPI::Util::run(&done);
+    done = false;
+
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [[webView URL] absoluteString]);
+    EXPECT_FALSE(willPerformClientRedirect);
+    EXPECT_FALSE(didPerformClientRedirect);
+
+    auto pidAfterForwardNavigation = [webView _webProcessIdentifier];
+    EXPECT_EQ(applePID, pidAfterForwardNavigation);
+
+    // Validate Back/Forward list.
+    currentItem = backForwardList.currentItem;
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.URL absoluteString]);
+    EXPECT_WK_STREQ(@"pson://www.apple.com/main.html", [currentItem.initialURL absoluteString]);
+    EXPECT_TRUE(!backForwardList.forwardItem);
+
+    EXPECT_EQ(1U, backForwardList.backList.count);
+
+    // FIXME: uncomment the following once <rdar://problem/45058938> has been fixed. When enabling PSON
+    // the backItem's URL currently becomes about:blank.
+    // backItem = backForwardList.backItem;
+    // EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.URL absoluteString]);
+    // EXPECT_WK_STREQ(@"pson://www.webkit.org/main.html", [backItem.initialURL absoluteString]);
+}
+
+TEST(ProcessSwap, CrossSiteClientSideRedirectWithoutPSON)
+{
+    runClientSideRedirectTest(ShouldEnablePSON::No);
+}
+
+TEST(ProcessSwap, CrossSiteClientSideRedirectWithPSON)
+{
+    runClientSideRedirectTest(ShouldEnablePSON::Yes);
+}
+
 static const char* sessionStorageTestBytes = R"PSONRESOURCE(
 <head>
 <script>