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
+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
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; }
ResourceRequest m_resourceRequest;
String m_frameName;
SubstituteData m_substituteData;
+ String m_clientRedirectSourceForHistory;
bool m_shouldCheckNewWindowPolicy { false };
bool m_shouldTreatAsContinuingLoad { false };
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));
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());
}
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;
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();
}
}
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*);
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.
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
+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
encoder << shouldTreatAsContinuingLoad;
encoder << userData;
encoder << forSafeBrowsing;
+ encoder.encodeEnum(lockHistory);
+ encoder.encodeEnum(lockBackForwardList);
+ encoder << clientRedirectSourceForHistory;
platformEncode(encoder);
}
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;
#include "DataReference.h"
#include "SandboxExtension.h"
#include "UserData.h"
+#include <WebCore/FrameLoaderTypes.h>
#include <WebCore/ResourceRequest.h>
OBJC_CLASS NSDictionary;
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;
encoder << opener;
encoder << requesterOrigin;
encoder << targetBackForwardItemIdentifier;
+ encoder.encodeEnum(lockHistory);
+ encoder.encodeEnum(lockBackForwardList);
+ encoder << clientRedirectSourceForHistory;
}
std::optional<NavigationActionData> NavigationActionData::decode(IPC::Decoder& decoder)
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
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;
};
}
void WebBackForwardListItem::setSuspendedPage(SuspendedPageProxy& page)
{
- ASSERT(!m_suspendedPage);
m_suspendedPage = makeWeakPtr(page);
}
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
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
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*) { }
- (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;
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;
bool webViewDidFailProvisionalNavigationWithError : 1;
bool webViewNavigationDidFailProvisionalLoadInSubframeWithError : 1;
bool webViewWillPerformClientRedirect : 1;
+ bool webViewDidPerformClientRedirect : 1;
bool webViewDidCancelClientRedirect : 1;
bool webViewDidCommitNavigation : 1;
bool webViewNavigationDidFinishDocumentLoad : 1;
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:)];
[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)
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&);
#include "WebImage.h"
#include "WebInspectorProxy.h"
#include "WebInspectorUtilities.h"
+#include "WebNavigationDataStore.h"
#include "WebNavigationState.h"
#include "WebNotificationManagerProxy.h"
#include "WebOpenPanelResultListenerProxy.h"
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();
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());
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)
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))
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);
}
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)
if (!documentLoader)
documentLoader = static_cast<WebDocumentLoader*>(coreFrame->loader().documentLoader());
+ navigationActionData.clientRedirectSourceForHistory = documentLoader->clientRedirectSourceForHistory();
+
// Notify the UIProcess.
Ref<WebFrame> protect(*m_frame);
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));
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();
void restoreSession(const Vector<BackForwardListItemState>&);
void didRemoveBackForwardItem(const WebCore::BackForwardItemIdentifier&);
void updateBackForwardListForReattach(const Vector<WebKit::BackForwardListItemState>&);
+ void setCurrentHistoryItemForReattach(WebKit::BackForwardListItemState&&);
void requestFontAttributesAtSelectionStart(CallbackID);
RestoreSession(Vector<WebKit::BackForwardListItemState> itemStates)
UpdateBackForwardListForReattach(Vector<WebKit::BackForwardListItemState> itemStates)
+ SetCurrentHistoryItemForReattach(struct WebKit::BackForwardListItemState itemState)
DidRemoveBackForwardItem(struct WebCore::BackForwardItemIdentifier backForwardItemID)
+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
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
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;
</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(
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>