NavigationState.mm [plain text]
/*
* Copyright (C) 2014 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.
*/
#import "config.h"
#import "NavigationState.h"
#if WK_API_ENABLED
#import "APINavigationData.h"
#import "APIString.h"
#import "APIURL.h"
#import "AuthenticationDecisionListener.h"
#import "CompletionHandlerCallChecker.h"
#import "NavigationActionData.h"
#import "PageLoadState.h"
#import "WKBackForwardListInternal.h"
#import "WKBackForwardListItemInternal.h"
#import "WKFrameInfoInternal.h"
#import "WKHistoryDelegatePrivate.h"
#import "WKNSURLAuthenticationChallenge.h"
#import "WKNSURLExtras.h"
#import "WKNSURLProtectionSpace.h"
#import "WKNSURLRequest.h"
#import "WKNavigationActionInternal.h"
#import "WKNavigationDataInternal.h"
#import "WKNavigationDelegatePrivate.h"
#import "WKNavigationInternal.h"
#import "WKNavigationResponseInternal.h"
#import "WKReloadFrameErrorRecoveryAttempter.h"
#import "WKWebViewInternal.h"
#import "WebCredential.h"
#import "WebFrameProxy.h"
#import "WebPageProxy.h"
#import "WebProcessProxy.h"
#import "_WKErrorRecoveryAttempting.h"
#import "_WKFrameHandleInternal.h"
#import <WebCore/AuthenticationMac.h>
#import <wtf/NeverDestroyed.h>
#if USE(QUICK_LOOK)
#import "QuickLookDocumentData.h"
#endif
namespace WebKit {
static HashMap<WebPageProxy*, NavigationState*>& navigationStates()
{
static NeverDestroyed<HashMap<WebPageProxy*, NavigationState*>> navigationStates;
return navigationStates;
}
NavigationState::NavigationState(WKWebView *webView)
: m_webView(webView)
, m_navigationDelegateMethods()
, m_historyDelegateMethods()
{
ASSERT(m_webView->_page);
ASSERT(!navigationStates().contains(m_webView->_page.get()));
navigationStates().add(m_webView->_page.get(), this);
m_webView->_page->pageLoadState().addObserver(*this);
}
NavigationState::~NavigationState()
{
ASSERT(navigationStates().get(m_webView->_page.get()) == this);
navigationStates().remove(m_webView->_page.get());
m_webView->_page->pageLoadState().removeObserver(*this);
}
NavigationState& NavigationState::fromWebPage(WebPageProxy& webPageProxy)
{
ASSERT(navigationStates().contains(&webPageProxy));
return *navigationStates().get(&webPageProxy);
}
std::unique_ptr<API::LoaderClient> NavigationState::createLoaderClient()
{
return std::make_unique<LoaderClient>(*this);
}
std::unique_ptr<API::PolicyClient> NavigationState::createPolicyClient()
{
return std::make_unique<PolicyClient>(*this);
}
RetainPtr<id <WKNavigationDelegate> > NavigationState::navigationDelegate()
{
return m_navigationDelegate.get();
}
void NavigationState::setNavigationDelegate(id <WKNavigationDelegate> delegate)
{
m_navigationDelegate = delegate;
m_navigationDelegateMethods.webViewDecidePolicyForNavigationActionDecisionHandler = [delegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)];
m_navigationDelegateMethods.webViewDecidePolicyForNavigationResponseDecisionHandler = [delegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)];
m_navigationDelegateMethods.webViewDidStartProvisionalNavigation = [delegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)];
m_navigationDelegateMethods.webViewDidReceiveServerRedirectForProvisionalNavigation = [delegate respondsToSelector:@selector(webView:didReceiveServerRedirectForProvisionalNavigation:)];
m_navigationDelegateMethods.webViewDidFailProvisionalNavigationWithError = [delegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)];
m_navigationDelegateMethods.webViewDidCommitNavigation = [delegate respondsToSelector:@selector(webView:didCommitNavigation:)];
m_navigationDelegateMethods.webViewDidFinishNavigation = [delegate respondsToSelector:@selector(webView:didFinishNavigation:)];
m_navigationDelegateMethods.webViewDidFailNavigationWithError = [delegate respondsToSelector:@selector(webView:didFailNavigation:withError:)];
m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError = [delegate respondsToSelector:@selector(_webView:navigation:didFailProvisionalLoadInSubframe:withError:)];
m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad = [delegate respondsToSelector:@selector(_webView:navigationDidFinishDocumentLoad:)];
m_navigationDelegateMethods.webViewNavigationDidSameDocumentNavigation = [delegate respondsToSelector:@selector(_webView:navigation:didSameDocumentNavigation:)];
m_navigationDelegateMethods.webViewRenderingProgressDidChange = [delegate respondsToSelector:@selector(_webView:renderingProgressDidChange:)];
m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler = [delegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)];
m_navigationDelegateMethods.webViewCanAuthenticateAgainstProtectionSpace = [delegate respondsToSelector:@selector(_webView:canAuthenticateAgainstProtectionSpace:)];
m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallenge = [delegate respondsToSelector:@selector(_webView:didReceiveAuthenticationChallenge:)];
m_navigationDelegateMethods.webViewWebProcessDidCrash = [delegate respondsToSelector:@selector(_webViewWebProcessDidCrash:)];
m_navigationDelegateMethods.webCryptoMasterKeyForWebView = [delegate respondsToSelector:@selector(_webCryptoMasterKeyForWebView:)];
m_navigationDelegateMethods.webViewDidBeginNavigationGesture = [delegate respondsToSelector:@selector(_webViewDidBeginNavigationGesture:)];
m_navigationDelegateMethods.webViewWillEndNavigationGestureWithNavigationToBackForwardListItem = [delegate respondsToSelector:@selector(_webViewWillEndNavigationGesture:withNavigationToBackForwardListItem:)];
m_navigationDelegateMethods.webViewDidEndNavigationGestureWithNavigationToBackForwardListItem = [delegate respondsToSelector:@selector(_webViewDidEndNavigationGesture:withNavigationToBackForwardListItem:)];
m_navigationDelegateMethods.webViewWillSnapshotBackForwardListItem = [delegate respondsToSelector:@selector(_webView:willSnapshotBackForwardListItem:)];
#if USE(QUICK_LOOK)
m_navigationDelegateMethods.webViewDidStartLoadForQuickLookDocumentInMainFrame = [delegate respondsToSelector:@selector(_webView:didStartLoadForQuickLookDocumentInMainFrameWithFileName:uti:)];
m_navigationDelegateMethods.webViewDidFinishLoadForQuickLookDocumentInMainFrame = [delegate respondsToSelector:@selector(_webView:didFinishLoadForQuickLookDocumentInMainFrame:)];
#endif
}
RetainPtr<id <WKHistoryDelegatePrivate> > NavigationState::historyDelegate()
{
return m_historyDelegate.get();
}
void NavigationState::setHistoryDelegate(id <WKHistoryDelegatePrivate> historyDelegate)
{
m_historyDelegate = historyDelegate;
m_historyDelegateMethods.webViewDidNavigateWithNavigationData = [historyDelegate respondsToSelector:@selector(_webView:didNavigateWithNavigationData:)];
m_historyDelegateMethods.webViewDidPerformClientRedirectFromURLToURL = [historyDelegate respondsToSelector:@selector(_webView:didPerformClientRedirectFromURL:toURL:)];
m_historyDelegateMethods.webViewDidPerformServerRedirectFromURLToURL = [historyDelegate respondsToSelector:@selector(_webView:didPerformServerRedirectFromURL:toURL:)];
m_historyDelegateMethods.webViewDidUpdateHistoryTitleForURL = [historyDelegate respondsToSelector:@selector(_webView:didUpdateHistoryTitle:forURL:)];
}
RetainPtr<WKNavigation> NavigationState::createLoadRequestNavigation(uint64_t navigationID, NSURLRequest *request)
{
ASSERT(!m_navigations.contains(navigationID));
RetainPtr<WKNavigation> navigation = adoptNS([[WKNavigation alloc] init]);
navigation->_request = request;
m_navigations.set(navigationID, navigation);
return navigation;
}
RetainPtr<WKNavigation> NavigationState::createBackForwardNavigation(uint64_t navigationID, const WebBackForwardListItem& item)
{
ASSERT(!m_navigations.contains(navigationID));
auto navigation = adoptNS([[WKNavigation alloc] init]);
m_navigations.set(navigationID, navigation);
return navigation;
}
RetainPtr<WKNavigation> NavigationState::createReloadNavigation(uint64_t navigationID)
{
ASSERT(!m_navigations.contains(navigationID));
auto navigation = adoptNS([[WKNavigation alloc] init]);
m_navigations.set(navigationID, navigation);
return navigation;
}
RetainPtr<WKNavigation> NavigationState::createLoadDataNavigation(uint64_t navigationID)
{
ASSERT(!m_navigations.contains(navigationID));
auto navigation = adoptNS([[WKNavigation alloc] init]);
m_navigations.set(navigationID, navigation);
return navigation;
}
void NavigationState::didNavigateWithNavigationData(const WebKit::WebNavigationDataStore& navigationDataStore)
{
if (!m_historyDelegateMethods.webViewDidNavigateWithNavigationData)
return;
auto historyDelegate = m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_webView didNavigateWithNavigationData:wrapper(*API::NavigationData::create(navigationDataStore))];
}
void NavigationState::didPerformClientRedirect(const WTF::String& sourceURL, const WTF::String& destinationURL)
{
if (!m_historyDelegateMethods.webViewDidPerformClientRedirectFromURLToURL)
return;
auto historyDelegate = m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_webView didPerformClientRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
}
void NavigationState::didPerformServerRedirect(const WTF::String& sourceURL, const WTF::String& destinationURL)
{
if (!m_historyDelegateMethods.webViewDidPerformServerRedirectFromURLToURL)
return;
auto historyDelegate = m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_webView didPerformServerRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
}
void NavigationState::didUpdateHistoryTitle(const WTF::String& title, const WTF::String& url)
{
if (!m_historyDelegateMethods.webViewDidUpdateHistoryTitleForURL)
return;
auto historyDelegate = m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_webView didUpdateHistoryTitle:title forURL:[NSURL _web_URLWithWTFString:url]];
}
void NavigationState::navigationGestureDidBegin()
{
if (!m_navigationDelegateMethods.webViewDidBeginNavigationGesture)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewDidBeginNavigationGesture:m_webView];
}
void NavigationState::navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem& item)
{
if (!m_navigationDelegateMethods.webViewWillEndNavigationGestureWithNavigationToBackForwardListItem)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewWillEndNavigationGesture:m_webView withNavigationToBackForwardListItem:willNavigate ? wrapper(item) : nil];
}
void NavigationState::navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem& item)
{
if (!m_navigationDelegateMethods.webViewDidEndNavigationGestureWithNavigationToBackForwardListItem)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewDidEndNavigationGesture:m_webView withNavigationToBackForwardListItem:willNavigate ? wrapper(item) : nil];
}
void NavigationState::willRecordNavigationSnapshot(WebBackForwardListItem& item)
{
if (!m_navigationDelegateMethods.webViewWillSnapshotBackForwardListItem)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webView:m_webView willSnapshotBackForwardListItem:wrapper(item)];
}
NavigationState::PolicyClient::PolicyClient(NavigationState& navigationState)
: m_navigationState(navigationState)
{
}
NavigationState::PolicyClient::~PolicyClient()
{
}
void NavigationState::PolicyClient::decidePolicyForNavigationAction(WebPageProxy*, WebFrameProxy* destinationFrame, const NavigationActionData& navigationActionData, WebFrameProxy* sourceFrame, const WebCore::ResourceRequest& originalRequest, const WebCore::ResourceRequest& request, RefPtr<WebFramePolicyListenerProxy> listener, API::Object* userData)
{
RetainPtr<NSURLRequest> nsURLRequest = adoptNS(wrapper(*API::URLRequest::create(request).leakRef()));
if (listener->navigationID())
m_navigationState.createLoadRequestNavigation(listener->navigationID(), nsURLRequest.get());
if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationActionDecisionHandler) {
if (!destinationFrame) {
listener->use();
return;
}
if ([NSURLConnection canHandleRequest:nsURLRequest.get()]) {
listener->use();
return;
}
#if PLATFORM(MAC)
// A file URL shouldn't fall through to here, but if it did,
// it would be a security risk to open it.
if (![[nsURLRequest URL] isFileURL])
[[NSWorkspace sharedWorkspace] openURL:[nsURLRequest URL]];
#endif
listener->ignore();
return;
}
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
auto navigationAction = adoptNS([[WKNavigationAction alloc] _initWithNavigationActionData:navigationActionData]);
if (destinationFrame)
[navigationAction setTargetFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*destinationFrame]).get()];
if (sourceFrame) {
if (sourceFrame == destinationFrame)
[navigationAction setSourceFrame:[navigationAction targetFrame]];
else
[navigationAction setSourceFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*sourceFrame]).get()];
}
[navigationAction setRequest:nsURLRequest.get()];
[navigationAction _setOriginalURL:originalRequest.url()];
RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationAction:decisionHandler:));
[navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationAction:navigationAction.get() decisionHandler:[listener, checker](WKNavigationActionPolicy actionPolicy) {
checker->didCallCompletionHandler();
switch (actionPolicy) {
case WKNavigationActionPolicyAllow:
listener->use();
break;
case WKNavigationActionPolicyCancel:
listener->ignore();
break;
// FIXME: Once we have a new enough compiler everywhere we don't need to ignore -Wswitch.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch"
case _WKNavigationActionPolicyDownload:
#pragma clang diagnostic pop
listener->download();
break;
}
}];
}
void NavigationState::PolicyClient::decidePolicyForNewWindowAction(WebPageProxy* webPageProxy, WebFrameProxy* sourceFrame, const NavigationActionData& navigationActionData, const WebCore::ResourceRequest& request, const WTF::String& frameName, RefPtr<WebFramePolicyListenerProxy> listener, API::Object* userData)
{
decidePolicyForNavigationAction(webPageProxy, nullptr, navigationActionData, sourceFrame, request, request, WTF::move(listener), userData);
}
void NavigationState::PolicyClient::decidePolicyForResponse(WebPageProxy*, WebFrameProxy* frame, const WebCore::ResourceResponse& resourceResponse, const WebCore::ResourceRequest& resourceRequest, bool canShowMIMEType, RefPtr<WebFramePolicyListenerProxy> listener, API::Object* userData)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationResponseDecisionHandler) {
NSURL *url = resourceResponse.nsURLResponse().URL;
if ([url isFileURL]) {
BOOL isDirectory = NO;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:url.path isDirectory:&isDirectory];
if (exists && !isDirectory && canShowMIMEType)
listener->use();
else
listener->ignore();
return;
}
if (canShowMIMEType)
listener->use();
else
listener->ignore();
return;
}
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
auto navigationResponse = adoptNS([[WKNavigationResponse alloc] init]);
navigationResponse->_frame = adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*frame]);
navigationResponse->_request = resourceRequest.nsURLRequest(WebCore::DoNotUpdateHTTPBody);
[navigationResponse setResponse:resourceResponse.nsURLResponse()];
[navigationResponse setCanShowMIMEType:canShowMIMEType];
RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationResponse:decisionHandler:));
[navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationResponse:navigationResponse.get() decisionHandler:[listener, checker](WKNavigationResponsePolicy responsePolicy) {
checker->didCallCompletionHandler();
switch (responsePolicy) {
case WKNavigationResponsePolicyAllow:
listener->use();
break;
case WKNavigationResponsePolicyCancel:
listener->ignore();
break;
// FIXME: Once we have a new enough compiler everywhere we don't need to ignore -Wswitch.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch"
case _WKNavigationResponsePolicyBecomeDownload:
#pragma clang diagnostic pop
listener->download();
break;
}
}];
}
NavigationState::LoaderClient::LoaderClient(NavigationState& navigationState)
: m_navigationState(navigationState)
{
}
NavigationState::LoaderClient::~LoaderClient()
{
}
void NavigationState::LoaderClient::didStartProvisionalLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewDidStartProvisionalNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[navigationDelegate webView:m_navigationState.m_webView didStartProvisionalNavigation:navigation];
}
void NavigationState::LoaderClient::didReceiveServerRedirectForProvisionalLoadForFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewDidReceiveServerRedirectForProvisionalNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[navigationDelegate webView:m_navigationState.m_webView didReceiveServerRedirectForProvisionalNavigation:navigation];
}
static RetainPtr<NSError> createErrorWithRecoveryAttempter(WKWebView *webView, WebFrameProxy& webFrameProxy, NSError *originalError)
{
RefPtr<API::FrameHandle> frameHandle = API::FrameHandle::create(webFrameProxy.frameID());
auto recoveryAttempter = adoptNS([[WKReloadFrameErrorRecoveryAttempter alloc] initWithWebView:webView frameHandle:wrapper(*frameHandle) urlString:originalError.userInfo[NSURLErrorFailingURLStringErrorKey]]);
auto userInfo = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:recoveryAttempter.get(), _WKRecoveryAttempterErrorKey, nil]);
if (NSDictionary *originalUserInfo = originalError.userInfo)
[userInfo addEntriesFromDictionary:originalUserInfo];
return adoptNS([[NSError alloc] initWithDomain:originalError.domain code:originalError.code userInfo:userInfo.get()]);
}
void NavigationState::LoaderClient::didFailProvisionalLoadWithErrorForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, const WebCore::ResourceError& error, API::Object*)
{
if (!webFrameProxy->isMainFrame()) {
if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
// FIXME: Get the main frame's current navigation.
[(id <WKNavigationDelegatePrivate>)navigationDelegate _webView:m_navigationState.m_webView navigation:nil didFailProvisionalLoadInSubframe:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*webFrameProxy]).get() withError:errorWithRecoveryAttempter.get()];
return;
}
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
RetainPtr<WKNavigation> navigation;
if (navigationID)
navigation = m_navigationState.m_navigations.take(navigationID);
// FIXME: Set the error on the navigation object.
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFailProvisionalNavigationWithError)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
[navigationDelegate webView:m_navigationState.m_webView didFailProvisionalNavigation:navigation.get() withError:errorWithRecoveryAttempter.get()];
}
void NavigationState::LoaderClient::didCommitLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewDidCommitNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[navigationDelegate webView:m_navigationState.m_webView didCommitNavigation:navigation];
}
void NavigationState::LoaderClient::didFinishDocumentLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigationDidFinishDocumentLoad:navigation];
}
void NavigationState::LoaderClient::didFinishLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFinishNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[navigationDelegate webView:m_navigationState.m_webView didFinishNavigation:navigation];
}
void NavigationState::LoaderClient::didFailLoadWithErrorForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, const WebCore::ResourceError& error, API::Object* userData)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFailNavigationWithError)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
[navigationDelegate webView:m_navigationState.m_webView didFailNavigation:navigation withError:errorWithRecoveryAttempter.get()];
}
static _WKSameDocumentNavigationType toWKSameDocumentNavigationType(SameDocumentNavigationType navigationType)
{
switch (navigationType) {
case SameDocumentNavigationAnchorNavigation:
return _WKSameDocumentNavigationTypeAnchorNavigation;
case SameDocumentNavigationSessionStatePush:
return _WKSameDocumentNavigationTypeSessionStatePush;
case SameDocumentNavigationSessionStateReplace:
return _WKSameDocumentNavigationTypeSessionStateReplace;
case SameDocumentNavigationSessionStatePop:
return _WKSameDocumentNavigationTypeSessionStatePop;
}
ASSERT_NOT_REACHED();
return _WKSameDocumentNavigationTypeAnchorNavigation;
}
void NavigationState::LoaderClient::didSameDocumentNavigationForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, SameDocumentNavigationType navigationType, API::Object*)
{
if (!webFrameProxy->isMainFrame())
return;
if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidSameDocumentNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
WKNavigation *navigation = nil;
if (navigationID)
navigation = m_navigationState.m_navigations.get(navigationID).get();
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigation:navigation didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
}
void NavigationState::LoaderClient::didDestroyNavigation(WebPageProxy*, uint64_t navigationID)
{
m_navigationState.m_navigations.remove(navigationID);
}
static _WKRenderingProgressEvents renderingProgressEvents(WebCore::LayoutMilestones milestones)
{
_WKRenderingProgressEvents events = 0;
if (milestones & WebCore::DidFirstLayout)
events |= _WKRenderingProgressEventFirstLayout;
if (milestones & WebCore::DidHitRelevantRepaintedObjectsAreaThreshold)
events |= _WKRenderingProgressEventFirstPaintWithSignificantArea;
return events;
}
void NavigationState::LoaderClient::didLayout(WebKit::WebPageProxy*, WebCore::LayoutMilestones layoutMilestones, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewRenderingProgressDidChange)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView renderingProgressDidChange:renderingProgressEvents(layoutMilestones)];
}
bool NavigationState::LoaderClient::canAuthenticateAgainstProtectionSpaceInFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy*, WebKit::WebProtectionSpace* protectionSpace)
{
if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler)
return protectionSpace->authenticationScheme() != WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
if (!m_navigationState.m_navigationDelegateMethods.webViewCanAuthenticateAgainstProtectionSpace)
return false;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return false;
return [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView canAuthenticateAgainstProtectionSpace:wrapper(*protectionSpace)];
}
void NavigationState::LoaderClient::didReceiveAuthenticationChallengeInFrame(WebPageProxy*, WebFrameProxy*, AuthenticationChallengeProxy* authenticationChallenge)
{
#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler) {
ASSERT(authenticationChallenge->protectionSpace()->authenticationScheme() != WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested);
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate) {
authenticationChallenge->listener()->performDefaultHandling();
return;
}
RefPtr<AuthenticationChallengeProxy> challenge = authenticationChallenge;
RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:didReceiveAuthenticationChallenge:completionHandler:));
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) webView:m_navigationState.m_webView didReceiveAuthenticationChallenge:wrapper(*authenticationChallenge)
completionHandler:[challenge, checker](NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential) {
checker->didCallCompletionHandler();
switch (disposition) {
case NSURLSessionAuthChallengeUseCredential: {
RefPtr<WebCredential> webCredential;
if (credential)
webCredential = WebCredential::create(WebCore::core(credential));
challenge->listener()->useCredential(webCredential.get());
break;
}
case NSURLSessionAuthChallengePerformDefaultHandling:
challenge->listener()->performDefaultHandling();
break;
case NSURLSessionAuthChallengeCancelAuthenticationChallenge:
challenge->listener()->cancel();
break;
case NSURLSessionAuthChallengeRejectProtectionSpace:
challenge->listener()->rejectProtectionSpaceAndContinue();
break;
default:
[NSException raise:NSInvalidArgumentException format:@"Invalid NSURLSessionAuthChallengeDisposition (%ld)", (long)disposition];
}
}
];
return;
}
#endif
if (!m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallenge)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didReceiveAuthenticationChallenge:wrapper(*authenticationChallenge)];
}
void NavigationState::LoaderClient::processDidCrash(WebKit::WebPageProxy*)
{
m_navigationState.m_navigations.clear();
if (!m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidCrash:m_navigationState.m_webView];
}
PassRefPtr<API::Data> NavigationState::LoaderClient::webCryptoMasterKey(WebKit::WebPageProxy&)
{
if (!m_navigationState.m_navigationDelegateMethods.webCryptoMasterKeyForWebView)
return nullptr;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return nullptr;
RetainPtr<NSData> data = [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webCryptoMasterKeyForWebView:m_navigationState.m_webView];
return API::Data::createWithoutCopying((const unsigned char*)[data bytes], [data length], [] (unsigned char*, const void* data) {
[(NSData *)data release];
}, data.leakRef());
}
#if USE(QUICK_LOOK)
void NavigationState::LoaderClient::didStartLoadForQuickLookDocumentInMainFrame(const String& fileName, const String& uti)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidStartLoadForQuickLookDocumentInMainFrame)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didStartLoadForQuickLookDocumentInMainFrameWithFileName:fileName uti:uti];
}
void NavigationState::LoaderClient::didFinishLoadForQuickLookDocumentInMainFrame(const WebKit::QuickLookDocumentData& data)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFinishLoadForQuickLookDocumentInMainFrame)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didFinishLoadForQuickLookDocumentInMainFrame:(NSData *)data.decodedData()];
}
#endif
void NavigationState::willChangeIsLoading()
{
[m_webView willChangeValueForKey:@"loading"];
}
void NavigationState::didChangeIsLoading()
{
#if PLATFORM(IOS)
if (m_webView->_page->pageLoadState().isLoading())
m_activityToken = std::make_unique<ProcessThrottler::BackgroundActivityToken>(m_webView->_page->process().throttler());
else
m_activityToken = nullptr;
#endif
[m_webView didChangeValueForKey:@"loading"];
}
void NavigationState::willChangeTitle()
{
[m_webView willChangeValueForKey:@"title"];
}
void NavigationState::didChangeTitle()
{
[m_webView didChangeValueForKey:@"title"];
}
void NavigationState::willChangeActiveURL()
{
[m_webView willChangeValueForKey:@"URL"];
}
void NavigationState::didChangeActiveURL()
{
[m_webView didChangeValueForKey:@"URL"];
}
void NavigationState::willChangeHasOnlySecureContent()
{
[m_webView willChangeValueForKey:@"hasOnlySecureContent"];
}
void NavigationState::didChangeHasOnlySecureContent()
{
[m_webView didChangeValueForKey:@"hasOnlySecureContent"];
}
void NavigationState::willChangeEstimatedProgress()
{
[m_webView willChangeValueForKey:@"estimatedProgress"];
}
void NavigationState::didChangeEstimatedProgress()
{
[m_webView didChangeValueForKey:@"estimatedProgress"];
}
void NavigationState::willChangeCanGoBack()
{
[m_webView willChangeValueForKey:@"canGoBack"];
}
void NavigationState::didChangeCanGoBack()
{
[m_webView didChangeValueForKey:@"canGoBack"];
}
void NavigationState::willChangeCanGoForward()
{
[m_webView willChangeValueForKey:@"canGoForward"];
}
void NavigationState::didChangeCanGoForward()
{
[m_webView didChangeValueForKey:@"canGoForward"];
}
void NavigationState::willChangeNetworkRequestsInProgress()
{
[m_webView willChangeValueForKey:@"_networkRequestsInProgress"];
}
void NavigationState::didChangeNetworkRequestsInProgress()
{
[m_webView didChangeValueForKey:@"_networkRequestsInProgress"];
}
} // namespace WebKit
#endif