blob: 180777f1574398d709fe961ccf0ed92597a92d48 [file] [log] [blame]
/*
* Copyright (C) 2014-2016 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 "APIFrameInfo.h"
#import "APINavigationData.h"
#import "APINavigationResponse.h"
#import "APIString.h"
#import "APIURL.h"
#import "AuthenticationDecisionListener.h"
#import "CompletionHandlerCallChecker.h"
#import "Logging.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 "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 "WebNavigationState.h"
#import "WebPageProxy.h"
#import "WebProcessProxy.h"
#import "WebProtectionSpace.h"
#import "_WKErrorRecoveryAttempting.h"
#import "_WKFrameHandleInternal.h"
#import "_WKRenderingProgressEventsInternal.h"
#import "_WKSameDocumentNavigationTypeInternal.h"
#import <WebCore/Credential.h>
#import <WebCore/SecurityOriginData.h>
#import <WebCore/URL.h>
#import <wtf/NeverDestroyed.h>
#if HAVE(APP_LINKS)
#import <WebCore/LaunchServicesSPI.h>
#endif
#if USE(QUICK_LOOK)
#import "QuickLookDocumentData.h"
#endif
using namespace WebCore;
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()
#if PLATFORM(IOS)
, m_releaseActivityTimer(*this, &NavigationState::releaseNetworkActivityToken)
#endif
{
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::NavigationClient> NavigationState::createNavigationClient()
{
return std::make_unique<NavigationClient>(*this);
}
std::unique_ptr<API::HistoryClient> NavigationState::createHistoryClient()
{
return std::make_unique<HistoryClient>(*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.webViewWebContentProcessDidTerminate = [delegate respondsToSelector:@selector(webViewWebContentProcessDidTerminate:)];
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.webViewWebProcessDidBecomeResponsive = [delegate respondsToSelector:@selector(_webViewWebProcessDidBecomeResponsive:)];
m_navigationDelegateMethods.webViewWebProcessDidBecomeUnresponsive = [delegate respondsToSelector:@selector(_webViewWebProcessDidBecomeUnresponsive:)];
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:)];
m_navigationDelegateMethods.webViewNavigationGestureSnapshotWasRemoved = [delegate respondsToSelector:@selector(_webViewDidRemoveNavigationGestureSnapshot:)];
#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:)];
}
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)];
}
void NavigationState::navigationGestureSnapshotWasRemoved()
{
if (!m_navigationDelegateMethods.webViewNavigationGestureSnapshotWasRemoved)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewDidRemoveNavigationGestureSnapshot:m_webView];
}
void NavigationState::didFirstPaint()
{
if (!m_navigationDelegateMethods.webViewRenderingProgressDidChange)
return;
auto navigationDelegate = m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webView:m_webView renderingProgressDidChange:_WKRenderingProgressEventFirstPaint];
}
NavigationState::NavigationClient::NavigationClient(NavigationState& navigationState)
: m_navigationState(navigationState)
{
}
NavigationState::NavigationClient::~NavigationClient()
{
}
static void tryAppLink(RefPtr<API::NavigationAction> navigationAction, const String& currentMainFrameURL, std::function<void (bool)> completionHandler)
{
#if HAVE(APP_LINKS)
if (!navigationAction->shouldOpenAppLinks()) {
completionHandler(false);
return;
}
auto* localCompletionHandler = new std::function<void (bool)>(WTFMove(completionHandler));
[LSAppLink openWithURL:navigationAction->request().url() completionHandler:[localCompletionHandler](BOOL success, NSError *) {
dispatch_async(dispatch_get_main_queue(), [localCompletionHandler, success] {
(*localCompletionHandler)(success);
delete localCompletionHandler;
});
}];
#else
completionHandler(false);
#endif
}
void NavigationState::NavigationClient::decidePolicyForNavigationAction(WebPageProxy& webPageProxy, API::NavigationAction& navigationAction, Ref<WebFramePolicyListenerProxy>&& listener, API::Object* userData)
{
String mainFrameURLString = webPageProxy.mainFrame()->url();
if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationActionDecisionHandler) {
RefPtr<API::NavigationAction> localNavigationAction = &navigationAction;
RefPtr<WebFramePolicyListenerProxy> localListener = WTFMove(listener);
tryAppLink(localNavigationAction, mainFrameURLString, [localListener, localNavigationAction] (bool followedLinkToApp) {
if (followedLinkToApp) {
localListener->ignore();
return;
}
if (!localNavigationAction->targetFrame()) {
localListener->use();
return;
}
RetainPtr<NSURLRequest> nsURLRequest = adoptNS(wrapper(API::URLRequest::create(localNavigationAction->request()).leakRef()));
if ([NSURLConnection canHandleRequest:nsURLRequest.get()]) {
if (localNavigationAction->shouldPerformDownload())
localListener->download();
else
localListener->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
localListener->ignore();
});
return;
}
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
RefPtr<API::NavigationAction> localNavigationAction = &navigationAction;
RefPtr<WebFramePolicyListenerProxy> localListener = WTFMove(listener);
RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationAction:decisionHandler:));
[navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationAction:wrapper(navigationAction) decisionHandler:[localListener, localNavigationAction, checker, mainFrameURLString](WKNavigationActionPolicy actionPolicy) {
checker->didCallCompletionHandler();
switch (actionPolicy) {
case WKNavigationActionPolicyAllow:
tryAppLink(localNavigationAction, mainFrameURLString, [localListener](bool followedLinkToApp) {
if (followedLinkToApp) {
localListener->ignore();
return;
}
localListener->use();
});
break;
case WKNavigationActionPolicyCancel:
localListener->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
localListener->download();
break;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch"
case _WKNavigationActionPolicyAllowWithoutTryingAppLink:
#pragma clang diagnostic pop
localListener->use();
break;
}
}];
}
void NavigationState::NavigationClient::decidePolicyForNavigationResponse(WebPageProxy&, API::NavigationResponse& navigationResponse, Ref<WebFramePolicyListenerProxy>&& listener, API::Object* userData)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationResponseDecisionHandler) {
NSURL *url = navigationResponse.response().nsURLResponse().URL;
if ([url isFileURL]) {
BOOL isDirectory = NO;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:url.path isDirectory:&isDirectory];
if (exists && !isDirectory && navigationResponse.canShowMIMEType())
listener->use();
else
listener->ignore();
return;
}
if (navigationResponse.canShowMIMEType())
listener->use();
else
listener->ignore();
return;
}
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
RefPtr<WebFramePolicyListenerProxy> localListener = WTFMove(listener);
RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationResponse:decisionHandler:));
[navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationResponse:wrapper(navigationResponse) decisionHandler:[localListener, checker](WKNavigationResponsePolicy responsePolicy) {
checker->didCallCompletionHandler();
switch (responsePolicy) {
case WKNavigationResponsePolicyAllow:
localListener->use();
break;
case WKNavigationResponsePolicyCancel:
localListener->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
localListener->download();
break;
}
}];
}
void NavigationState::NavigationClient::didStartProvisionalNavigation(WebPageProxy& page, API::Navigation* navigation, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidStartProvisionalNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[navigationDelegate webView:m_navigationState.m_webView didStartProvisionalNavigation:wkNavigation];
}
void NavigationState::NavigationClient::didReceiveServerRedirectForProvisionalNavigation(WebPageProxy& page, API::Navigation* navigation, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidReceiveServerRedirectForProvisionalNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[navigationDelegate webView:m_navigationState.m_webView didReceiveServerRedirectForProvisionalNavigation:wkNavigation];
}
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()]);
}
// FIXME: Shouldn't need to pass the WebFrameProxy in here. At most, a FrameHandle.
void NavigationState::NavigationClient::didFailProvisionalNavigationWithError(WebPageProxy& page, WebFrameProxy& webFrameProxy, API::Navigation* navigation, const WebCore::ResourceError& error, API::Object*)
{
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
RetainPtr<WKNavigation> wkNavigation;
if (navigation)
wkNavigation = wrapper(*navigation);
// 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:wkNavigation.get() withError:errorWithRecoveryAttempter.get()];
}
// FIXME: Shouldn't need to pass the WebFrameProxy in here. At most, a FrameHandle.
void NavigationState::NavigationClient::didFailProvisionalLoadInSubframeWithError(WebPageProxy& page, WebFrameProxy& webFrameProxy, const SecurityOriginData& securityOrigin, API::Navigation* navigation, const WebCore::ResourceError& error, API::Object*)
{
// FIXME: We should assert that navigation is not null here, but it's currently null because WebPageProxy::didFailProvisionalLoadForFrame passes null.
RetainPtr<WKNavigation> wkNavigation;
if (navigation)
wkNavigation = wrapper(*navigation);
if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, webFrameProxy, error);
[(id <WKNavigationDelegatePrivate>)navigationDelegate _webView:m_navigationState.m_webView navigation:nil didFailProvisionalLoadInSubframe:wrapper(API::FrameInfo::create(webFrameProxy, securityOrigin.securityOrigin())) withError:errorWithRecoveryAttempter.get()];
}
void NavigationState::NavigationClient::didCommitNavigation(WebPageProxy& page, API::Navigation* navigation, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidCommitNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[navigationDelegate webView:m_navigationState.m_webView didCommitNavigation:wkNavigation];
}
void NavigationState::NavigationClient::didFinishDocumentLoad(WebPageProxy& page, API::Navigation* navigation, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigationDidFinishDocumentLoad:wkNavigation];
}
void NavigationState::NavigationClient::didFinishNavigation(WebPageProxy& page, API::Navigation* navigation, API::Object*)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFinishNavigation)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[navigationDelegate webView:m_navigationState.m_webView didFinishNavigation:wkNavigation];
}
// FIXME: Shouldn't need to pass the WebFrameProxy in here. At most, a FrameHandle.
void NavigationState::NavigationClient::didFailNavigationWithError(WebPageProxy& page, WebFrameProxy& webFrameProxy, API::Navigation* navigation, const WebCore::ResourceError& error, API::Object* userData)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewDidFailNavigationWithError)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// FIXME: We should assert that navigation is not null here, but it's currently null for some navigations through the page cache.
WKNavigation *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, webFrameProxy, error);
[navigationDelegate webView:m_navigationState.m_webView didFailNavigation:wkNavigation withError:errorWithRecoveryAttempter.get()];
}
void NavigationState::NavigationClient::didSameDocumentNavigation(WebPageProxy&, API::Navigation* navigation, SameDocumentNavigationType navigationType, API::Object*)
{
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 *wkNavigation = nil;
if (navigation)
wkNavigation = wrapper(*navigation);
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigation:wkNavigation didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
}
void NavigationState::NavigationClient::renderingProgressDidChange(WebKit::WebPageProxy&, WebCore::LayoutMilestones layoutMilestones)
{
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::NavigationClient::canAuthenticateAgainstProtectionSpace(WebKit::WebPageProxy&, WebKit::WebProtectionSpace* protectionSpace)
{
if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler)
return true;
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:protectionSpace->protectionSpace().nsSpace()];
}
void NavigationState::NavigationClient::didReceiveAuthenticationChallenge(WebPageProxy&, AuthenticationChallengeProxy* authenticationChallenge)
{
if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler) {
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::Credential(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;
}
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::NavigationClient::processDidCrash(WebKit::WebPageProxy& page)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminate && !m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
// We prefer webViewWebContentProcessDidTerminate: over _webViewWebProcessDidCrash:.
if (m_navigationState.m_navigationDelegateMethods.webViewWebContentProcessDidTerminate) {
[navigationDelegate webViewWebContentProcessDidTerminate:m_navigationState.m_webView];
return;
}
if (m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash)
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidCrash:m_navigationState.m_webView];
}
void NavigationState::NavigationClient::processDidBecomeResponsive(WebKit::WebPageProxy& page)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidBecomeResponsive)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidBecomeResponsive:m_navigationState.m_webView];
}
void NavigationState::NavigationClient::processDidBecomeUnresponsive(WebKit::WebPageProxy& page)
{
if (!m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidBecomeUnresponsive)
return;
auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
if (!navigationDelegate)
return;
[static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidBecomeUnresponsive:m_navigationState.m_webView];
}
RefPtr<API::Data> NavigationState::NavigationClient::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::NavigationClient::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::NavigationClient::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
// HistoryDelegatePrivate support
NavigationState::HistoryClient::HistoryClient(NavigationState& navigationState)
: m_navigationState(navigationState)
{
}
NavigationState::HistoryClient::~HistoryClient()
{
}
void NavigationState::HistoryClient::didNavigateWithNavigationData(WebKit::WebPageProxy&, const WebKit::WebNavigationDataStore& navigationDataStore)
{
if (!m_navigationState.m_historyDelegateMethods.webViewDidNavigateWithNavigationData)
return;
auto historyDelegate = m_navigationState.m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_navigationState.m_webView didNavigateWithNavigationData:wrapper(API::NavigationData::create(navigationDataStore))];
}
void NavigationState::HistoryClient::didPerformClientRedirect(WebKit::WebPageProxy&, const WTF::String& sourceURL, const WTF::String& destinationURL)
{
if (!m_navigationState.m_historyDelegateMethods.webViewDidPerformClientRedirectFromURLToURL)
return;
auto historyDelegate = m_navigationState.m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_navigationState.m_webView didPerformClientRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
}
void NavigationState::HistoryClient::didPerformServerRedirect(WebKit::WebPageProxy&, const WTF::String& sourceURL, const WTF::String& destinationURL)
{
if (!m_navigationState.m_historyDelegateMethods.webViewDidPerformServerRedirectFromURLToURL)
return;
auto historyDelegate = m_navigationState.m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_navigationState.m_webView didPerformServerRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
}
void NavigationState::HistoryClient::didUpdateHistoryTitle(WebKit::WebPageProxy&, const WTF::String& title, const WTF::String& url)
{
if (!m_navigationState.m_historyDelegateMethods.webViewDidUpdateHistoryTitleForURL)
return;
auto historyDelegate = m_navigationState.m_historyDelegate.get();
if (!historyDelegate)
return;
[historyDelegate _webView:m_navigationState.m_webView didUpdateHistoryTitle:title forURL:[NSURL _web_URLWithWTFString:url]];
}
void NavigationState::willChangeIsLoading()
{
[m_webView willChangeValueForKey:@"loading"];
}
#if PLATFORM(IOS)
void NavigationState::releaseNetworkActivityToken()
{
RELEASE_LOG_IF(m_webView->_page->isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p UIProcess is releasing a background assertion because a page load completed", this);
ASSERT(m_activityToken);
m_activityToken = nullptr;
}
#endif
void NavigationState::didChangeIsLoading()
{
#if PLATFORM(IOS)
if (m_webView->_page->pageLoadState().isLoading()) {
if (m_releaseActivityTimer.isActive())
m_releaseActivityTimer.stop();
else {
RELEASE_LOG_IF(m_webView->_page->isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - UIProcess is taking a background assertion because a page load started", this);
ASSERT(!m_activityToken);
m_activityToken = m_webView->_page->process().throttler().backgroundActivityToken();
}
} else {
// Delay releasing the background activity for 3 seconds to give the application a chance to start another navigation
// before suspending the WebContent process <rdar://problem/27910964>.
m_releaseActivityTimer.startOneShot(3s);
}
#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"];
}
void NavigationState::willChangeCertificateInfo()
{
[m_webView willChangeValueForKey:@"serverTrust"];
[m_webView willChangeValueForKey:@"certificateChain"];
}
void NavigationState::didChangeCertificateInfo()
{
[m_webView didChangeValueForKey:@"certificateChain"];
[m_webView didChangeValueForKey:@"serverTrust"];
}
void NavigationState::willChangeWebProcessIsResponsive()
{
[m_webView willChangeValueForKey:@"_webProcessIsResponsive"];
}
void NavigationState::didChangeWebProcessIsResponsive()
{
[m_webView didChangeValueForKey:@"_webProcessIsResponsive"];
}
} // namespace WebKit
#endif