1/*
2 * Copyright (C) 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "NavigationState.h"
28
29#if WK_API_ENABLED
30
31#import "APINavigationData.h"
32#import "APIString.h"
33#import "APIURL.h"
34#import "AuthenticationDecisionListener.h"
35#import "CompletionHandlerCallChecker.h"
36#import "NavigationActionData.h"
37#import "PageLoadState.h"
38#import "WKBackForwardListInternal.h"
39#import "WKBackForwardListItemInternal.h"
40#import "WKFrameInfoInternal.h"
41#import "WKHistoryDelegatePrivate.h"
42#import "WKNSURLAuthenticationChallenge.h"
43#import "WKNSURLExtras.h"
44#import "WKNSURLProtectionSpace.h"
45#import "WKNSURLRequest.h"
46#import "WKNavigationActionInternal.h"
47#import "WKNavigationDataInternal.h"
48#import "WKNavigationDelegatePrivate.h"
49#import "WKNavigationInternal.h"
50#import "WKNavigationResponseInternal.h"
51#import "WKReloadFrameErrorRecoveryAttempter.h"
52#import "WKWebViewInternal.h"
53#import "WebCredential.h"
54#import "WebFrameProxy.h"
55#import "WebPageProxy.h"
56#import "WebProcessProxy.h"
57#import "_WKErrorRecoveryAttempting.h"
58#import "_WKFrameHandleInternal.h"
59#import <WebCore/AuthenticationMac.h>
60#import <wtf/NeverDestroyed.h>
61
62#if USE(QUICK_LOOK)
63#import "QuickLookDocumentData.h"
64#endif
65
66namespace WebKit {
67
68static HashMap<WebPageProxy*, NavigationState*>& navigationStates()
69{
70    static NeverDestroyed<HashMap<WebPageProxy*, NavigationState*>> navigationStates;
71
72    return navigationStates;
73}
74
75NavigationState::NavigationState(WKWebView *webView)
76    : m_webView(webView)
77    , m_navigationDelegateMethods()
78    , m_historyDelegateMethods()
79{
80    ASSERT(m_webView->_page);
81    ASSERT(!navigationStates().contains(m_webView->_page.get()));
82
83    navigationStates().add(m_webView->_page.get(), this);
84    m_webView->_page->pageLoadState().addObserver(*this);
85}
86
87NavigationState::~NavigationState()
88{
89    ASSERT(navigationStates().get(m_webView->_page.get()) == this);
90
91    navigationStates().remove(m_webView->_page.get());
92    m_webView->_page->pageLoadState().removeObserver(*this);
93}
94
95NavigationState& NavigationState::fromWebPage(WebPageProxy& webPageProxy)
96{
97    ASSERT(navigationStates().contains(&webPageProxy));
98
99    return *navigationStates().get(&webPageProxy);
100}
101
102std::unique_ptr<API::LoaderClient> NavigationState::createLoaderClient()
103{
104    return std::make_unique<LoaderClient>(*this);
105}
106
107std::unique_ptr<API::PolicyClient> NavigationState::createPolicyClient()
108{
109    return std::make_unique<PolicyClient>(*this);
110}
111
112RetainPtr<id <WKNavigationDelegate> > NavigationState::navigationDelegate()
113{
114    return m_navigationDelegate.get();
115}
116
117void NavigationState::setNavigationDelegate(id <WKNavigationDelegate> delegate)
118{
119    m_navigationDelegate = delegate;
120
121    m_navigationDelegateMethods.webViewDecidePolicyForNavigationActionDecisionHandler = [delegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:decisionHandler:)];
122    m_navigationDelegateMethods.webViewDecidePolicyForNavigationResponseDecisionHandler = [delegate respondsToSelector:@selector(webView:decidePolicyForNavigationResponse:decisionHandler:)];
123
124    m_navigationDelegateMethods.webViewDidStartProvisionalNavigation = [delegate respondsToSelector:@selector(webView:didStartProvisionalNavigation:)];
125    m_navigationDelegateMethods.webViewDidReceiveServerRedirectForProvisionalNavigation = [delegate respondsToSelector:@selector(webView:didReceiveServerRedirectForProvisionalNavigation:)];
126    m_navigationDelegateMethods.webViewDidFailProvisionalNavigationWithError = [delegate respondsToSelector:@selector(webView:didFailProvisionalNavigation:withError:)];
127    m_navigationDelegateMethods.webViewDidCommitNavigation = [delegate respondsToSelector:@selector(webView:didCommitNavigation:)];
128    m_navigationDelegateMethods.webViewDidFinishNavigation = [delegate respondsToSelector:@selector(webView:didFinishNavigation:)];
129    m_navigationDelegateMethods.webViewDidFailNavigationWithError = [delegate respondsToSelector:@selector(webView:didFailNavigation:withError:)];
130
131    m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError = [delegate respondsToSelector:@selector(_webView:navigation:didFailProvisionalLoadInSubframe:withError:)];
132    m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad = [delegate respondsToSelector:@selector(_webView:navigationDidFinishDocumentLoad:)];
133    m_navigationDelegateMethods.webViewNavigationDidSameDocumentNavigation = [delegate respondsToSelector:@selector(_webView:navigation:didSameDocumentNavigation:)];
134    m_navigationDelegateMethods.webViewRenderingProgressDidChange = [delegate respondsToSelector:@selector(_webView:renderingProgressDidChange:)];
135    m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler = [delegate respondsToSelector:@selector(webView:didReceiveAuthenticationChallenge:completionHandler:)];
136    m_navigationDelegateMethods.webViewCanAuthenticateAgainstProtectionSpace = [delegate respondsToSelector:@selector(_webView:canAuthenticateAgainstProtectionSpace:)];
137    m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallenge = [delegate respondsToSelector:@selector(_webView:didReceiveAuthenticationChallenge:)];
138    m_navigationDelegateMethods.webViewWebProcessDidCrash = [delegate respondsToSelector:@selector(_webViewWebProcessDidCrash:)];
139    m_navigationDelegateMethods.webCryptoMasterKeyForWebView = [delegate respondsToSelector:@selector(_webCryptoMasterKeyForWebView:)];
140    m_navigationDelegateMethods.webViewDidBeginNavigationGesture = [delegate respondsToSelector:@selector(_webViewDidBeginNavigationGesture:)];
141    m_navigationDelegateMethods.webViewWillEndNavigationGestureWithNavigationToBackForwardListItem = [delegate respondsToSelector:@selector(_webViewWillEndNavigationGesture:withNavigationToBackForwardListItem:)];
142    m_navigationDelegateMethods.webViewDidEndNavigationGestureWithNavigationToBackForwardListItem = [delegate respondsToSelector:@selector(_webViewDidEndNavigationGesture:withNavigationToBackForwardListItem:)];
143    m_navigationDelegateMethods.webViewWillSnapshotBackForwardListItem = [delegate respondsToSelector:@selector(_webView:willSnapshotBackForwardListItem:)];
144#if USE(QUICK_LOOK)
145    m_navigationDelegateMethods.webViewDidStartLoadForQuickLookDocumentInMainFrame = [delegate respondsToSelector:@selector(_webView:didStartLoadForQuickLookDocumentInMainFrameWithFileName:uti:)];
146    m_navigationDelegateMethods.webViewDidFinishLoadForQuickLookDocumentInMainFrame = [delegate respondsToSelector:@selector(_webView:didFinishLoadForQuickLookDocumentInMainFrame:)];
147#endif
148}
149
150RetainPtr<id <WKHistoryDelegatePrivate> > NavigationState::historyDelegate()
151{
152    return m_historyDelegate.get();
153}
154
155void NavigationState::setHistoryDelegate(id <WKHistoryDelegatePrivate> historyDelegate)
156{
157    m_historyDelegate = historyDelegate;
158
159    m_historyDelegateMethods.webViewDidNavigateWithNavigationData = [historyDelegate respondsToSelector:@selector(_webView:didNavigateWithNavigationData:)];
160    m_historyDelegateMethods.webViewDidPerformClientRedirectFromURLToURL = [historyDelegate respondsToSelector:@selector(_webView:didPerformClientRedirectFromURL:toURL:)];
161    m_historyDelegateMethods.webViewDidPerformServerRedirectFromURLToURL = [historyDelegate respondsToSelector:@selector(_webView:didPerformServerRedirectFromURL:toURL:)];
162    m_historyDelegateMethods.webViewDidUpdateHistoryTitleForURL = [historyDelegate respondsToSelector:@selector(_webView:didUpdateHistoryTitle:forURL:)];
163}
164
165RetainPtr<WKNavigation> NavigationState::createLoadRequestNavigation(uint64_t navigationID, NSURLRequest *request)
166{
167    ASSERT(!m_navigations.contains(navigationID));
168
169    RetainPtr<WKNavigation> navigation = adoptNS([[WKNavigation alloc] init]);
170    navigation->_request = request;
171
172    m_navigations.set(navigationID, navigation);
173
174    return navigation;
175}
176
177RetainPtr<WKNavigation> NavigationState::createBackForwardNavigation(uint64_t navigationID, const WebBackForwardListItem& item)
178{
179    ASSERT(!m_navigations.contains(navigationID));
180
181    auto navigation = adoptNS([[WKNavigation alloc] init]);
182
183    m_navigations.set(navigationID, navigation);
184
185    return navigation;
186}
187
188RetainPtr<WKNavigation> NavigationState::createReloadNavigation(uint64_t navigationID)
189{
190    ASSERT(!m_navigations.contains(navigationID));
191
192    auto navigation = adoptNS([[WKNavigation alloc] init]);
193
194    m_navigations.set(navigationID, navigation);
195
196    return navigation;
197}
198
199RetainPtr<WKNavigation> NavigationState::createLoadDataNavigation(uint64_t navigationID)
200{
201    ASSERT(!m_navigations.contains(navigationID));
202
203    auto navigation = adoptNS([[WKNavigation alloc] init]);
204
205    m_navigations.set(navigationID, navigation);
206
207    return navigation;
208}
209
210void NavigationState::didNavigateWithNavigationData(const WebKit::WebNavigationDataStore& navigationDataStore)
211{
212    if (!m_historyDelegateMethods.webViewDidNavigateWithNavigationData)
213        return;
214
215    auto historyDelegate = m_historyDelegate.get();
216    if (!historyDelegate)
217        return;
218
219    [historyDelegate _webView:m_webView didNavigateWithNavigationData:wrapper(*API::NavigationData::create(navigationDataStore))];
220}
221
222void NavigationState::didPerformClientRedirect(const WTF::String& sourceURL, const WTF::String& destinationURL)
223{
224    if (!m_historyDelegateMethods.webViewDidPerformClientRedirectFromURLToURL)
225        return;
226
227    auto historyDelegate = m_historyDelegate.get();
228    if (!historyDelegate)
229        return;
230
231    [historyDelegate _webView:m_webView didPerformClientRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
232}
233
234void NavigationState::didPerformServerRedirect(const WTF::String& sourceURL, const WTF::String& destinationURL)
235{
236    if (!m_historyDelegateMethods.webViewDidPerformServerRedirectFromURLToURL)
237        return;
238
239    auto historyDelegate = m_historyDelegate.get();
240    if (!historyDelegate)
241        return;
242
243    [historyDelegate _webView:m_webView didPerformServerRedirectFromURL:[NSURL _web_URLWithWTFString:sourceURL] toURL:[NSURL _web_URLWithWTFString:destinationURL]];
244}
245
246void NavigationState::didUpdateHistoryTitle(const WTF::String& title, const WTF::String& url)
247{
248    if (!m_historyDelegateMethods.webViewDidUpdateHistoryTitleForURL)
249        return;
250
251    auto historyDelegate = m_historyDelegate.get();
252    if (!historyDelegate)
253        return;
254
255    [historyDelegate _webView:m_webView didUpdateHistoryTitle:title forURL:[NSURL _web_URLWithWTFString:url]];
256}
257
258void NavigationState::navigationGestureDidBegin()
259{
260    if (!m_navigationDelegateMethods.webViewDidBeginNavigationGesture)
261        return;
262
263    auto navigationDelegate = m_navigationDelegate.get();
264    if (!navigationDelegate)
265        return;
266
267    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewDidBeginNavigationGesture:m_webView];
268}
269
270void NavigationState::navigationGestureWillEnd(bool willNavigate, WebBackForwardListItem& item)
271{
272    if (!m_navigationDelegateMethods.webViewWillEndNavigationGestureWithNavigationToBackForwardListItem)
273        return;
274
275    auto navigationDelegate = m_navigationDelegate.get();
276    if (!navigationDelegate)
277        return;
278
279    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewWillEndNavigationGesture:m_webView withNavigationToBackForwardListItem:willNavigate ? wrapper(item) : nil];
280}
281
282void NavigationState::navigationGestureDidEnd(bool willNavigate, WebBackForwardListItem& item)
283{
284    if (!m_navigationDelegateMethods.webViewDidEndNavigationGestureWithNavigationToBackForwardListItem)
285        return;
286
287    auto navigationDelegate = m_navigationDelegate.get();
288    if (!navigationDelegate)
289        return;
290
291    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webViewDidEndNavigationGesture:m_webView withNavigationToBackForwardListItem:willNavigate ? wrapper(item) : nil];
292}
293
294void NavigationState::willRecordNavigationSnapshot(WebBackForwardListItem& item)
295{
296    if (!m_navigationDelegateMethods.webViewWillSnapshotBackForwardListItem)
297        return;
298
299    auto navigationDelegate = m_navigationDelegate.get();
300    if (!navigationDelegate)
301        return;
302
303    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate) _webView:m_webView willSnapshotBackForwardListItem:wrapper(item)];
304}
305
306NavigationState::PolicyClient::PolicyClient(NavigationState& navigationState)
307    : m_navigationState(navigationState)
308{
309}
310
311NavigationState::PolicyClient::~PolicyClient()
312{
313}
314
315void 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)
316{
317    RetainPtr<NSURLRequest> nsURLRequest = adoptNS(wrapper(*API::URLRequest::create(request).leakRef()));
318
319    if (listener->navigationID())
320        m_navigationState.createLoadRequestNavigation(listener->navigationID(), nsURLRequest.get());
321
322    if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationActionDecisionHandler) {
323        if (!destinationFrame) {
324            listener->use();
325            return;
326        }
327
328        if ([NSURLConnection canHandleRequest:nsURLRequest.get()]) {
329            listener->use();
330            return;
331        }
332
333#if PLATFORM(MAC)
334        // A file URL shouldn't fall through to here, but if it did,
335        // it would be a security risk to open it.
336        if (![[nsURLRequest URL] isFileURL])
337            [[NSWorkspace sharedWorkspace] openURL:[nsURLRequest URL]];
338#endif
339        listener->ignore();
340        return;
341    }
342
343    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
344    if (!navigationDelegate)
345        return;
346
347    auto navigationAction = adoptNS([[WKNavigationAction alloc] _initWithNavigationActionData:navigationActionData]);
348
349    if (destinationFrame)
350        [navigationAction setTargetFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*destinationFrame]).get()];
351
352    if (sourceFrame) {
353        if (sourceFrame == destinationFrame)
354            [navigationAction setSourceFrame:[navigationAction targetFrame]];
355        else
356            [navigationAction setSourceFrame:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*sourceFrame]).get()];
357    }
358
359    [navigationAction setRequest:nsURLRequest.get()];
360    [navigationAction _setOriginalURL:originalRequest.url()];
361
362    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationAction:decisionHandler:));
363    [navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationAction:navigationAction.get() decisionHandler:[listener, checker](WKNavigationActionPolicy actionPolicy) {
364        checker->didCallCompletionHandler();
365
366        switch (actionPolicy) {
367        case WKNavigationActionPolicyAllow:
368            listener->use();
369            break;
370
371        case WKNavigationActionPolicyCancel:
372            listener->ignore();
373            break;
374
375// FIXME: Once we have a new enough compiler everywhere we don't need to ignore -Wswitch.
376#pragma clang diagnostic push
377#pragma clang diagnostic ignored "-Wswitch"
378        case _WKNavigationActionPolicyDownload:
379#pragma clang diagnostic pop
380            listener->download();
381            break;
382        }
383    }];
384}
385
386void NavigationState::PolicyClient::decidePolicyForNewWindowAction(WebPageProxy* webPageProxy, WebFrameProxy* sourceFrame, const NavigationActionData& navigationActionData, const WebCore::ResourceRequest& request, const WTF::String& frameName, RefPtr<WebFramePolicyListenerProxy> listener, API::Object* userData)
387{
388    decidePolicyForNavigationAction(webPageProxy, nullptr, navigationActionData, sourceFrame, request, request, WTF::move(listener), userData);
389}
390
391void NavigationState::PolicyClient::decidePolicyForResponse(WebPageProxy*, WebFrameProxy* frame, const WebCore::ResourceResponse& resourceResponse, const WebCore::ResourceRequest& resourceRequest, bool canShowMIMEType, RefPtr<WebFramePolicyListenerProxy> listener, API::Object* userData)
392{
393    if (!m_navigationState.m_navigationDelegateMethods.webViewDecidePolicyForNavigationResponseDecisionHandler) {
394        NSURL *url = resourceResponse.nsURLResponse().URL;
395        if ([url isFileURL]) {
396            BOOL isDirectory = NO;
397            BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:url.path isDirectory:&isDirectory];
398
399            if (exists && !isDirectory && canShowMIMEType)
400                listener->use();
401            else
402                listener->ignore();
403            return;
404        }
405
406        if (canShowMIMEType)
407            listener->use();
408        else
409            listener->ignore();
410        return;
411    }
412
413    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
414    if (!navigationDelegate)
415        return;
416
417    auto navigationResponse = adoptNS([[WKNavigationResponse alloc] init]);
418
419    navigationResponse->_frame = adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*frame]);
420    navigationResponse->_request = resourceRequest.nsURLRequest(WebCore::DoNotUpdateHTTPBody);
421    [navigationResponse setResponse:resourceResponse.nsURLResponse()];
422    [navigationResponse setCanShowMIMEType:canShowMIMEType];
423
424    RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationResponse:decisionHandler:));
425    [navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationResponse:navigationResponse.get() decisionHandler:[listener, checker](WKNavigationResponsePolicy responsePolicy) {
426        checker->didCallCompletionHandler();
427
428        switch (responsePolicy) {
429        case WKNavigationResponsePolicyAllow:
430            listener->use();
431            break;
432
433        case WKNavigationResponsePolicyCancel:
434            listener->ignore();
435            break;
436
437// FIXME: Once we have a new enough compiler everywhere we don't need to ignore -Wswitch.
438#pragma clang diagnostic push
439#pragma clang diagnostic ignored "-Wswitch"
440        case _WKNavigationResponsePolicyBecomeDownload:
441#pragma clang diagnostic pop
442            listener->download();
443            break;
444        }
445    }];
446}
447
448NavigationState::LoaderClient::LoaderClient(NavigationState& navigationState)
449    : m_navigationState(navigationState)
450{
451}
452
453NavigationState::LoaderClient::~LoaderClient()
454{
455}
456
457void NavigationState::LoaderClient::didStartProvisionalLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
458{
459    if (!webFrameProxy->isMainFrame())
460        return;
461
462    if (!m_navigationState.m_navigationDelegateMethods.webViewDidStartProvisionalNavigation)
463        return;
464
465    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
466    if (!navigationDelegate)
467        return;
468
469    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
470    WKNavigation *navigation = nil;
471    if (navigationID)
472        navigation = m_navigationState.m_navigations.get(navigationID).get();
473
474    [navigationDelegate webView:m_navigationState.m_webView didStartProvisionalNavigation:navigation];
475}
476
477void NavigationState::LoaderClient::didReceiveServerRedirectForProvisionalLoadForFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
478{
479    if (!webFrameProxy->isMainFrame())
480        return;
481
482    if (!m_navigationState.m_navigationDelegateMethods.webViewDidReceiveServerRedirectForProvisionalNavigation)
483        return;
484
485    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
486    if (!navigationDelegate)
487        return;
488
489    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
490    WKNavigation *navigation = nil;
491    if (navigationID)
492        navigation = m_navigationState.m_navigations.get(navigationID).get();
493
494    [navigationDelegate webView:m_navigationState.m_webView didReceiveServerRedirectForProvisionalNavigation:navigation];
495}
496
497static RetainPtr<NSError> createErrorWithRecoveryAttempter(WKWebView *webView, WebFrameProxy& webFrameProxy, NSError *originalError)
498{
499    RefPtr<API::FrameHandle> frameHandle = API::FrameHandle::create(webFrameProxy.frameID());
500
501    auto recoveryAttempter = adoptNS([[WKReloadFrameErrorRecoveryAttempter alloc] initWithWebView:webView frameHandle:wrapper(*frameHandle) urlString:originalError.userInfo[NSURLErrorFailingURLStringErrorKey]]);
502
503    auto userInfo = adoptNS([[NSMutableDictionary alloc] initWithObjectsAndKeys:recoveryAttempter.get(), _WKRecoveryAttempterErrorKey, nil]);
504
505    if (NSDictionary *originalUserInfo = originalError.userInfo)
506        [userInfo addEntriesFromDictionary:originalUserInfo];
507
508    return adoptNS([[NSError alloc] initWithDomain:originalError.domain code:originalError.code userInfo:userInfo.get()]);
509}
510
511void NavigationState::LoaderClient::didFailProvisionalLoadWithErrorForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, const WebCore::ResourceError& error, API::Object*)
512{
513    if (!webFrameProxy->isMainFrame()) {
514        if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFailProvisionalLoadInSubframeWithError)
515            return;
516
517        auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
518        auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
519        // FIXME: Get the main frame's current navigation.
520        [(id <WKNavigationDelegatePrivate>)navigationDelegate _webView:m_navigationState.m_webView navigation:nil didFailProvisionalLoadInSubframe:adoptNS([[WKFrameInfo alloc] initWithWebFrameProxy:*webFrameProxy]).get() withError:errorWithRecoveryAttempter.get()];
521
522        return;
523    }
524
525    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
526    RetainPtr<WKNavigation> navigation;
527    if (navigationID)
528        navigation = m_navigationState.m_navigations.take(navigationID);
529
530    // FIXME: Set the error on the navigation object.
531
532    if (!m_navigationState.m_navigationDelegateMethods.webViewDidFailProvisionalNavigationWithError)
533        return;
534
535    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
536    if (!navigationDelegate)
537        return;
538
539    auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
540    [navigationDelegate webView:m_navigationState.m_webView didFailProvisionalNavigation:navigation.get() withError:errorWithRecoveryAttempter.get()];
541}
542
543void NavigationState::LoaderClient::didCommitLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
544{
545    if (!webFrameProxy->isMainFrame())
546        return;
547
548    if (!m_navigationState.m_navigationDelegateMethods.webViewDidCommitNavigation)
549        return;
550
551    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
552    if (!navigationDelegate)
553        return;
554
555    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
556    WKNavigation *navigation = nil;
557    if (navigationID)
558        navigation = m_navigationState.m_navigations.get(navigationID).get();
559
560    [navigationDelegate webView:m_navigationState.m_webView didCommitNavigation:navigation];
561}
562
563void NavigationState::LoaderClient::didFinishDocumentLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
564{
565    if (!webFrameProxy->isMainFrame())
566        return;
567
568    if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidFinishDocumentLoad)
569        return;
570
571    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
572    if (!navigationDelegate)
573        return;
574
575    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
576    WKNavigation *navigation = nil;
577    if (navigationID)
578        navigation = m_navigationState.m_navigations.get(navigationID).get();
579
580    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigationDidFinishDocumentLoad:navigation];
581}
582
583void NavigationState::LoaderClient::didFinishLoadForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, API::Object*)
584{
585    if (!webFrameProxy->isMainFrame())
586        return;
587
588    if (!m_navigationState.m_navigationDelegateMethods.webViewDidFinishNavigation)
589        return;
590
591    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
592    if (!navigationDelegate)
593        return;
594
595    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
596    WKNavigation *navigation = nil;
597    if (navigationID)
598        navigation = m_navigationState.m_navigations.get(navigationID).get();
599
600    [navigationDelegate webView:m_navigationState.m_webView didFinishNavigation:navigation];
601}
602
603void NavigationState::LoaderClient::didFailLoadWithErrorForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, const WebCore::ResourceError& error, API::Object* userData)
604{
605    if (!webFrameProxy->isMainFrame())
606        return;
607
608    if (!m_navigationState.m_navigationDelegateMethods.webViewDidFailNavigationWithError)
609        return;
610
611    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
612    if (!navigationDelegate)
613        return;
614
615    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
616    WKNavigation *navigation = nil;
617    if (navigationID)
618        navigation = m_navigationState.m_navigations.get(navigationID).get();
619
620    auto errorWithRecoveryAttempter = createErrorWithRecoveryAttempter(m_navigationState.m_webView, *webFrameProxy, error);
621    [navigationDelegate webView:m_navigationState.m_webView didFailNavigation:navigation withError:errorWithRecoveryAttempter.get()];
622}
623
624static _WKSameDocumentNavigationType toWKSameDocumentNavigationType(SameDocumentNavigationType navigationType)
625{
626    switch (navigationType) {
627    case SameDocumentNavigationAnchorNavigation:
628        return _WKSameDocumentNavigationTypeAnchorNavigation;
629    case SameDocumentNavigationSessionStatePush:
630        return _WKSameDocumentNavigationTypeSessionStatePush;
631    case SameDocumentNavigationSessionStateReplace:
632        return _WKSameDocumentNavigationTypeSessionStateReplace;
633    case SameDocumentNavigationSessionStatePop:
634        return _WKSameDocumentNavigationTypeSessionStatePop;
635    }
636
637    ASSERT_NOT_REACHED();
638    return _WKSameDocumentNavigationTypeAnchorNavigation;
639}
640
641void NavigationState::LoaderClient::didSameDocumentNavigationForFrame(WebPageProxy*, WebFrameProxy* webFrameProxy, uint64_t navigationID, SameDocumentNavigationType navigationType, API::Object*)
642{
643    if (!webFrameProxy->isMainFrame())
644        return;
645
646    if (!m_navigationState.m_navigationDelegateMethods.webViewNavigationDidSameDocumentNavigation)
647        return;
648
649    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
650    if (!navigationDelegate)
651        return;
652
653    // FIXME: We should assert that navigationID is not zero here, but it's currently zero for some navigations through the page cache.
654    WKNavigation *navigation = nil;
655    if (navigationID)
656        navigation = m_navigationState.m_navigations.get(navigationID).get();
657
658    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView navigation:navigation didSameDocumentNavigation:toWKSameDocumentNavigationType(navigationType)];
659}
660
661void NavigationState::LoaderClient::didDestroyNavigation(WebPageProxy*, uint64_t navigationID)
662{
663    m_navigationState.m_navigations.remove(navigationID);
664}
665
666static _WKRenderingProgressEvents renderingProgressEvents(WebCore::LayoutMilestones milestones)
667{
668    _WKRenderingProgressEvents events = 0;
669
670    if (milestones & WebCore::DidFirstLayout)
671        events |= _WKRenderingProgressEventFirstLayout;
672
673    if (milestones & WebCore::DidHitRelevantRepaintedObjectsAreaThreshold)
674        events |= _WKRenderingProgressEventFirstPaintWithSignificantArea;
675
676    return events;
677}
678
679void NavigationState::LoaderClient::didLayout(WebKit::WebPageProxy*, WebCore::LayoutMilestones layoutMilestones, API::Object*)
680{
681    if (!m_navigationState.m_navigationDelegateMethods.webViewRenderingProgressDidChange)
682        return;
683
684    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
685    if (!navigationDelegate)
686        return;
687
688    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView renderingProgressDidChange:renderingProgressEvents(layoutMilestones)];
689}
690
691bool NavigationState::LoaderClient::canAuthenticateAgainstProtectionSpaceInFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy*, WebKit::WebProtectionSpace* protectionSpace)
692{
693    if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler)
694        return protectionSpace->authenticationScheme() != WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested;
695
696    if (!m_navigationState.m_navigationDelegateMethods.webViewCanAuthenticateAgainstProtectionSpace)
697        return false;
698
699    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
700    if (!navigationDelegate)
701        return false;
702
703    return [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView canAuthenticateAgainstProtectionSpace:wrapper(*protectionSpace)];
704}
705
706void NavigationState::LoaderClient::didReceiveAuthenticationChallengeInFrame(WebPageProxy*, WebFrameProxy*, AuthenticationChallengeProxy* authenticationChallenge)
707{
708#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090
709    if (m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallengeCompletionHandler) {
710        ASSERT(authenticationChallenge->protectionSpace()->authenticationScheme() != WebCore::ProtectionSpaceAuthenticationSchemeServerTrustEvaluationRequested);
711        auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
712        if (!navigationDelegate) {
713            authenticationChallenge->listener()->performDefaultHandling();
714            return;
715        }
716
717        RefPtr<AuthenticationChallengeProxy> challenge = authenticationChallenge;
718        RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:didReceiveAuthenticationChallenge:completionHandler:));
719        [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) webView:m_navigationState.m_webView didReceiveAuthenticationChallenge:wrapper(*authenticationChallenge)
720            completionHandler:[challenge, checker](NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential) {
721                checker->didCallCompletionHandler();
722
723                switch (disposition) {
724                case NSURLSessionAuthChallengeUseCredential: {
725                    RefPtr<WebCredential> webCredential;
726                    if (credential)
727                        webCredential = WebCredential::create(WebCore::core(credential));
728
729                    challenge->listener()->useCredential(webCredential.get());
730                    break;
731                }
732
733                case NSURLSessionAuthChallengePerformDefaultHandling:
734                    challenge->listener()->performDefaultHandling();
735                    break;
736
737                case NSURLSessionAuthChallengeCancelAuthenticationChallenge:
738                    challenge->listener()->cancel();
739                    break;
740
741                case NSURLSessionAuthChallengeRejectProtectionSpace:
742                    challenge->listener()->rejectProtectionSpaceAndContinue();
743                    break;
744
745                default:
746                    [NSException raise:NSInvalidArgumentException format:@"Invalid NSURLSessionAuthChallengeDisposition (%ld)", (long)disposition];
747                }
748            }
749        ];
750        return;
751    }
752#endif
753
754    if (!m_navigationState.m_navigationDelegateMethods.webViewDidReceiveAuthenticationChallenge)
755        return;
756
757    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
758    if (!navigationDelegate)
759        return;
760
761    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didReceiveAuthenticationChallenge:wrapper(*authenticationChallenge)];
762}
763
764void NavigationState::LoaderClient::processDidCrash(WebKit::WebPageProxy*)
765{
766    m_navigationState.m_navigations.clear();
767
768    if (!m_navigationState.m_navigationDelegateMethods.webViewWebProcessDidCrash)
769        return;
770
771    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
772    if (!navigationDelegate)
773        return;
774
775    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webViewWebProcessDidCrash:m_navigationState.m_webView];
776}
777
778PassRefPtr<API::Data> NavigationState::LoaderClient::webCryptoMasterKey(WebKit::WebPageProxy&)
779{
780    if (!m_navigationState.m_navigationDelegateMethods.webCryptoMasterKeyForWebView)
781        return nullptr;
782
783    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
784    if (!navigationDelegate)
785        return nullptr;
786
787    RetainPtr<NSData> data = [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webCryptoMasterKeyForWebView:m_navigationState.m_webView];
788
789    return API::Data::createWithoutCopying((const unsigned char*)[data bytes], [data length], [] (unsigned char*, const void* data) {
790        [(NSData *)data release];
791    }, data.leakRef());
792}
793
794#if USE(QUICK_LOOK)
795void NavigationState::LoaderClient::didStartLoadForQuickLookDocumentInMainFrame(const String& fileName, const String& uti)
796{
797    if (!m_navigationState.m_navigationDelegateMethods.webViewDidStartLoadForQuickLookDocumentInMainFrame)
798        return;
799
800    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
801    if (!navigationDelegate)
802        return;
803
804    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didStartLoadForQuickLookDocumentInMainFrameWithFileName:fileName uti:uti];
805}
806
807void NavigationState::LoaderClient::didFinishLoadForQuickLookDocumentInMainFrame(const WebKit::QuickLookDocumentData& data)
808{
809    if (!m_navigationState.m_navigationDelegateMethods.webViewDidFinishLoadForQuickLookDocumentInMainFrame)
810        return;
811
812    auto navigationDelegate = m_navigationState.m_navigationDelegate.get();
813    if (!navigationDelegate)
814        return;
815
816    [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) _webView:m_navigationState.m_webView didFinishLoadForQuickLookDocumentInMainFrame:(NSData *)data.decodedData()];
817}
818#endif
819
820void NavigationState::willChangeIsLoading()
821{
822    [m_webView willChangeValueForKey:@"loading"];
823}
824
825void NavigationState::didChangeIsLoading()
826{
827#if PLATFORM(IOS)
828    if (m_webView->_page->pageLoadState().isLoading())
829        m_activityToken = std::make_unique<ProcessThrottler::BackgroundActivityToken>(m_webView->_page->process().throttler());
830    else
831        m_activityToken = nullptr;
832#endif
833
834    [m_webView didChangeValueForKey:@"loading"];
835}
836
837void NavigationState::willChangeTitle()
838{
839    [m_webView willChangeValueForKey:@"title"];
840}
841
842void NavigationState::didChangeTitle()
843{
844    [m_webView didChangeValueForKey:@"title"];
845}
846
847void NavigationState::willChangeActiveURL()
848{
849    [m_webView willChangeValueForKey:@"URL"];
850}
851
852void NavigationState::didChangeActiveURL()
853{
854    [m_webView didChangeValueForKey:@"URL"];
855}
856
857void NavigationState::willChangeHasOnlySecureContent()
858{
859    [m_webView willChangeValueForKey:@"hasOnlySecureContent"];
860}
861
862void NavigationState::didChangeHasOnlySecureContent()
863{
864    [m_webView didChangeValueForKey:@"hasOnlySecureContent"];
865}
866
867void NavigationState::willChangeEstimatedProgress()
868{
869    [m_webView willChangeValueForKey:@"estimatedProgress"];
870}
871
872void NavigationState::didChangeEstimatedProgress()
873{
874    [m_webView didChangeValueForKey:@"estimatedProgress"];
875}
876
877void NavigationState::willChangeCanGoBack()
878{
879    [m_webView willChangeValueForKey:@"canGoBack"];
880}
881
882void NavigationState::didChangeCanGoBack()
883{
884    [m_webView didChangeValueForKey:@"canGoBack"];
885}
886
887void NavigationState::willChangeCanGoForward()
888{
889    [m_webView willChangeValueForKey:@"canGoForward"];
890}
891
892void NavigationState::didChangeCanGoForward()
893{
894    [m_webView didChangeValueForKey:@"canGoForward"];
895}
896
897void NavigationState::willChangeNetworkRequestsInProgress()
898{
899    [m_webView willChangeValueForKey:@"_networkRequestsInProgress"];
900}
901
902void NavigationState::didChangeNetworkRequestsInProgress()
903{
904    [m_webView didChangeValueForKey:@"_networkRequestsInProgress"];
905}
906
907} // namespace WebKit
908
909#endif
910