Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ios / web / web_state / ui / crw_wk_web_view_web_controller.mm
blob88080cc599c0acabb587157af96cedda57bd0a95
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import "ios/web/web_state/ui/crw_wk_web_view_web_controller.h"
7 #import <WebKit/WebKit.h>
9 #include "base/ios/ios_util.h"
10 #include "base/ios/weak_nsobject.h"
11 #include "base/json/json_reader.h"
12 #import "base/mac/scoped_nsobject.h"
13 #include "base/macros.h"
14 #include "base/strings/sys_string_conversions.h"
15 #include "base/values.h"
16 #import "ios/net/http_response_headers_util.h"
17 #import "ios/web/crw_network_activity_indicator_manager.h"
18 #import "ios/web/navigation/crw_session_controller.h"
19 #include "ios/web/navigation/web_load_params.h"
20 #include "ios/web/public/web_client.h"
21 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
22 #import "ios/web/public/web_state/ui/crw_native_content_provider.h"
23 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
24 #import "ios/web/ui_web_view_util.h"
25 #include "ios/web/web_state/blocked_popup_info.h"
26 #import "ios/web/web_state/error_translation_util.h"
27 #include "ios/web/web_state/frame_info.h"
28 #import "ios/web/web_state/js/crw_js_window_id_manager.h"
29 #import "ios/web/web_state/js/page_script_util.h"
30 #import "ios/web/web_state/ui/crw_web_controller+protected.h"
31 #import "ios/web/web_state/ui/crw_wk_web_view_crash_detector.h"
32 #import "ios/web/web_state/ui/web_view_js_utils.h"
33 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
34 #import "ios/web/web_state/web_state_impl.h"
35 #import "ios/web/web_state/web_view_internal_creation_util.h"
36 #import "ios/web/webui/crw_web_ui_manager.h"
37 #import "net/base/mac/url_conversions.h"
39 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
40 #include "ios/web/public/cert_store.h"
41 #include "ios/web/public/navigation_item.h"
42 #include "ios/web/public/ssl_status.h"
43 #import "ios/web/web_state/wk_web_view_security_util.h"
44 #include "net/cert/x509_certificate.h"
45 #include "net/ssl/ssl_info.h"
46 #endif
48 namespace {
49 // Extracts Referer value from WKNavigationAction request header.
50 NSString* GetRefererFromNavigationAction(WKNavigationAction* action) {
51   return [action.request valueForHTTPHeaderField:@"Referer"];
54 NSString* const kScriptMessageName = @"crwebinvoke";
55 NSString* const kScriptImmediateName = @"crwebinvokeimmediate";
57 // Utility functions for storing the source of NSErrors received by WKWebViews:
58 // - Errors received by |-webView:didFailProvisionalNavigation:withError:| are
59 //   recorded using WKWebViewErrorSource::PROVISIONAL_LOAD.  These should be
60 //   aborted.
61 // - Errors received by |-webView:didFailNavigation:withError:| are recorded
62 //   using WKWebViewsource::NAVIGATION.  These errors should not be aborted, as
63 //   the WKWebView will automatically retry the load.
64 NSString* const kWKWebViewErrorSourceKey = @"ErrorSource";
65 typedef enum { NONE = 0, PROVISIONAL_LOAD, NAVIGATION } WKWebViewErrorSource;
66 NSError* WKWebViewErrorWithSource(NSError* error, WKWebViewErrorSource source) {
67   DCHECK(error);
68   base::scoped_nsobject<NSMutableDictionary> userInfo(
69       [error.userInfo mutableCopy]);
70   [userInfo setObject:@(source) forKey:kWKWebViewErrorSourceKey];
71   return [NSError errorWithDomain:error.domain
72                              code:error.code
73                          userInfo:userInfo];
75 WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
76   DCHECK(error);
77   return static_cast<WKWebViewErrorSource>(
78       [error.userInfo[kWKWebViewErrorSourceKey] integerValue]);
81 }  // namespace
83 @interface CRWWKWebViewWebController () <WKNavigationDelegate,
84                                          WKScriptMessageHandler,
85                                          WKUIDelegate> {
86   // The WKWebView managed by this instance.
87   base::scoped_nsobject<WKWebView> _wkWebView;
89   // The Watch Dog that detects and reports WKWebView crashes.
90   base::scoped_nsobject<CRWWKWebViewCrashDetector> _crashDetector;
92   // The actual URL of the document object (i.e., the last committed URL).
93   // TODO(stuartmorgan): Remove this in favor of just updating the session
94   // controller and treating that as authoritative. For now, this allows sharing
95   // the flow that's currently in the superclass.
96   GURL _documentURL;
98   // A set of script managers whose scripts have been injected into the current
99   // page.
100   // TODO(stuartmorgan): Revisit this approach; it's intended only as a stopgap
101   // measure to make all the existing script managers work. Longer term, there
102   // should probably be a couple of points where managers can register to have
103   // things happen automatically based on page lifecycle, and if they don't want
104   // to use one of those fixed points, they should make their scripts internally
105   // idempotent.
106   base::scoped_nsobject<NSMutableSet> _injectedScriptManagers;
108   // Referrer pending for the next navigated page. Lifetime of this value starts
109   // at |decidePolicyForNavigationAction| where the referrer is extracted from
110   // the request and ends at |didCommitNavigation| where the request is
111   // committed.
112   base::scoped_nsobject<NSString> _pendingReferrerString;
114   // Referrer for the current page.
115   base::scoped_nsobject<NSString> _currentReferrerString;
117   // Backs the property of the same name.
118   base::scoped_nsobject<NSString> _documentMIMEType;
120   // Whether the web page is currently performing window.history.pushState or
121   // window.history.replaceState
122   // Set to YES on window.history.willChangeState message. To NO on
123   // window.history.didPushState or window.history.didReplaceState.
124   BOOL _changingHistoryState;
126   // CRWWebUIManager object for loading WebUI pages.
127   base::scoped_nsobject<CRWWebUIManager> _webUIManager;
130 // Response's MIME type of the last known navigation.
131 @property(nonatomic, copy) NSString* documentMIMEType;
133 // Dictionary where keys are the names of WKWebView properties and values are
134 // selector names which should be called when a corresponding property has
135 // changed. e.g. @{ @"URL" : @"webViewURLDidChange" } means that
136 // -[self webViewURLDidChange] must be called every time when WKWebView.URL is
137 // changed.
138 @property(nonatomic, readonly) NSDictionary* wkWebViewObservers;
140 // Activity indicator group ID for this web controller.
141 @property(nonatomic, readonly) NSString* activityIndicatorGroupID;
143 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
144 // Identifier used for storing and retrieving certificates.
145 @property(nonatomic, readonly) int certGroupID;
146 #endif  // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
148 // Returns the WKWebViewConfigurationProvider associated with the web
149 // controller's BrowserState.
150 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider;
152 // Creates a web view with given |config|. No-op if web view is already created.
153 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config;
155 // Returns a new autoreleased web view created with given configuration.
156 - (WKWebView*)createWebViewWithConfiguration:(WKWebViewConfiguration*)config;
158 // Sets the value of the webView property, and performs its basic setup.
159 - (void)setWebView:(WKWebView*)webView;
161 // Returns whether the given navigation is triggered by a user link click.
162 - (BOOL)isLinkNavigation:(WKNavigationType)navigationType;
164 // Sets value of the pendingReferrerString property.
165 - (void)setPendingReferrerString:(NSString*)referrer;
167 // Extracts the referrer value from WKNavigationAction and sets it as a pending.
168 // The referrer is known in |decidePolicyForNavigationAction| however it must
169 // be in a pending state until |didCommitNavigation| where it becames current.
170 - (void)updatePendingReferrerFromNavigationAction:(WKNavigationAction*)action;
172 // Replaces the current referrer with the pending one. Referrer becames current
173 // at |didCommitNavigation| callback.
174 - (void)commitPendingReferrerString;
176 // Discards the pending referrer.
177 - (void)discardPendingReferrerString;
179 // Returns a new CRWWKWebViewCrashDetector created with the given |webView| or
180 // nil if |webView| is nil. Callers are responsible for releasing the object.
181 - (CRWWKWebViewCrashDetector*)newCrashDetectorWithWebView:(WKWebView*)webView;
183 // Called when web view process has been terminated.
184 - (void)webViewWebProcessDidCrash;
186 // Asynchronously returns the referrer policy for the current page.
187 - (void)queryPageReferrerPolicy:(void(^)(NSString*))responseHandler;
189 // Informs CWRWebDelegate that CRWWebController has detected and blocked a
190 // popup.
191 - (void)didBlockPopupWithURL:(GURL)popupURL
192                    sourceURL:(GURL)sourceURL
193               referrerPolicy:(const std::string&)referrerPolicyString;
195 // Convenience method to inform CWRWebDelegate about a blocked popup.
196 - (void)didBlockPopupWithURL:(GURL)popupURL sourceURL:(GURL)sourceURL;
198 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
199 // Called when a load ends in an SSL error.
200 - (void)handleSSLError:(NSError*)error;
201 #endif
203 // Adds an activity indicator tasks for this web controller.
204 - (void)addActivityIndicatorTask;
206 // Clears all activity indicator tasks for this web controller.
207 - (void)clearActivityIndicatorTasks;
209 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
210 // Updates SSL status for the current navigation item based on the information
211 // provided by web view.
212 - (void)updateSSLStatusForCurrentNavigationItem;
213 #endif
215 // Registers load request with empty referrer and link or client redirect
216 // transition based on user interaction state.
217 - (void)registerLoadRequest:(const GURL&)url;
219 // Called when a non-document-changing URL change occurs. Updates the
220 // _documentURL, and informs the superclass of the change.
221 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)URL;
223 // Returns new autoreleased instance of WKUserContentController which has
224 // early page script.
225 - (WKUserContentController*)createUserContentController;
227 // Attempts to handle a script message. Returns YES on success, NO otherwise.
228 - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage;
230 // Used to decide whether a load that generates errors with the
231 // NSURLErrorCancelled code should be cancelled.
232 - (BOOL)shouldAbortLoadForCancelledError:(NSError*)error;
234 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
235 // Called when WKWebView estimatedProgress has been changed.
236 - (void)webViewEstimatedProgressDidChange;
238 // Called when WKWebView certificateChain or hasOnlySecureContent property has
239 // changed.
240 - (void)webViewSecurityFeaturesDidChange;
241 #endif  // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
243 // Called when WKWebView loading state has been changed.
244 - (void)webViewLoadingStateDidChange;
246 // Called when WKWebView title has been changed.
247 - (void)webViewTitleDidChange;
249 // Called when WKWebView URL has been changed.
250 - (void)webViewURLDidChange;
252 @end
254 @implementation CRWWKWebViewWebController
256 #pragma mark CRWWebController public methods
258 - (instancetype)initWithWebState:(scoped_ptr<web::WebStateImpl>)webState {
259   return [super initWithWebState:webState.Pass()];
262 - (BOOL)keyboardDisplayRequiresUserAction {
263   // TODO(stuartmorgan): Find out whether YES or NO is correct; see comment
264   // in protected header.
265   NOTIMPLEMENTED();
266   return NO;
269 - (void)setKeyboardDisplayRequiresUserAction:(BOOL)requiresUserAction {
270   NOTIMPLEMENTED();
273 - (void)evaluateJavaScript:(NSString*)script
274        stringResultHandler:(web::JavaScriptCompletion)handler {
275   NSString* safeScript = [self scriptByAddingWindowIDCheckForScript:script];
276   web::EvaluateJavaScript(_wkWebView, safeScript, handler);
279 - (web::WebViewType)webViewType {
280   return web::WK_WEB_VIEW_TYPE;
283 - (void)evaluateUserJavaScript:(NSString*)script {
284   [self setUserInteractionRegistered:YES];
285   web::EvaluateJavaScript(_wkWebView, script, nil);
288 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
289 - (void)terminateNetworkActivity {
290   web::CertStore::GetInstance()->RemoveCertsForGroup(self.certGroupID);
291   [super terminateNetworkActivity];
293 #endif  // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
295 - (void)setPageDialogOpenPolicy:(web::PageDialogOpenPolicy)policy {
296   // TODO(eugenebut): implement dialogs/windows suppression using
297   // WKNavigationDelegate methods where possible.
298   [super setPageDialogOpenPolicy:policy];
301 #pragma mark -
302 #pragma mark Testing-Only Methods
304 - (void)injectWebViewContentView:(CRWWebViewContentView*)webViewContentView {
305   [super injectWebViewContentView:webViewContentView];
306   [self setWebView:static_cast<WKWebView*>(webViewContentView.webView)];
309 #pragma mark - Protected property implementations
311 - (UIView*)webView {
312   return _wkWebView.get();
315 - (UIScrollView*)webScrollView {
316   return [_wkWebView scrollView];
319 - (BOOL)ignoreURLVerificationFailures {
320   return NO;
323 - (NSString*)title {
324   return [_wkWebView title];
327 - (NSString*)currentReferrerString {
328   return _currentReferrerString.get();
331 #pragma mark Protected method implementations
333 - (void)ensureWebViewCreated {
334   WKWebViewConfiguration* config =
335       [self webViewConfigurationProvider].GetWebViewConfiguration();
336   [self ensureWebViewCreatedWithConfiguration:config];
339 - (void)resetWebView {
340   [self setWebView:nil];
343 - (GURL)webURLWithTrustLevel:(web::URLVerificationTrustLevel*)trustLevel {
344   DCHECK(trustLevel);
345   *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
346   return _documentURL;
349 - (void)registerUserAgent {
350   // TODO(stuartmorgan): Rename this method, since it works for both.
351   web::BuildAndRegisterUserAgentForUIWebView(
352       self.webStateImpl->GetRequestGroupID(),
353       [self useDesktopUserAgent]);
356 // The core.js cannot pass messages back to obj-c  if it is injected
357 // to |WEB_VIEW_DOCUMENT| because it does not support iframe creation used
358 // by core.js to communicate back. That functionality is only supported
359 // by |WEB_VIEW_HTML_DOCUMENT|. |WEB_VIEW_DOCUMENT| is used when displaying
360 // non-HTML contents (e.g. PDF documents).
361 - (web::WebViewDocumentType)webViewDocumentType {
362   // This happens during tests.
363   if (!_wkWebView) {
364     return web::WEB_VIEW_DOCUMENT_TYPE_GENERIC;
365   }
367   if (!self.documentMIMEType) {
368     return web::WEB_VIEW_DOCUMENT_TYPE_UNKNOWN;
369   }
371   if ([self.documentMIMEType isEqualToString:@"text/html"] ||
372       [self.documentMIMEType isEqualToString:@"application/xhtml+xml"] ||
373       [self.documentMIMEType isEqualToString:@"application/xml"]) {
374     return web::WEB_VIEW_DOCUMENT_TYPE_HTML;
375   }
377   return web::WEB_VIEW_DOCUMENT_TYPE_GENERIC;
380 - (void)loadRequest:(NSMutableURLRequest*)request {
381   [_wkWebView loadRequest:request];
384 - (void)loadWebHTMLString:(NSString*)html forURL:(const GURL&)URL {
385   [_wkWebView loadHTMLString:html baseURL:net::NSURLWithGURL(URL)];
388 - (BOOL)scriptHasBeenInjectedForClass:(Class)jsInjectionManagerClass
389                        presenceBeacon:(NSString*)beacon {
390   return [_injectedScriptManagers containsObject:jsInjectionManagerClass];
393 - (void)injectScript:(NSString*)script forClass:(Class)JSInjectionManagerClass {
394   // Skip evaluation if there's no content (e.g., if what's being injected is
395   // an umbrella manager).
396   if ([script length]) {
397     [super injectScript:script forClass:JSInjectionManagerClass];
398     // Every injection except windowID requires windowID check.
399     if (JSInjectionManagerClass != [CRWJSWindowIdManager class])
400       script = [self scriptByAddingWindowIDCheckForScript:script];
401     web::EvaluateJavaScript(_wkWebView, script, nil);
402   }
403   [_injectedScriptManagers addObject:JSInjectionManagerClass];
406 - (void)willLoadCurrentURLInWebView {
407   // TODO(stuartmorgan): Get a WKWebView version of the request ID verification
408   // code working for debug builds.
412 - (void)setPageChangeProbability:(web::PageChangeProbability)probability {
413   // Nothing to do; no polling timer.
416 - (void)abortWebLoad {
417   [_wkWebView stopLoading];
420 - (void)resetLoadState {
421   // Nothing to do.
424 - (void)setSuppressDialogsWithHelperScript:(NSString*)script {
425   [self evaluateJavaScript:script stringResultHandler:nil];
428 - (void)applyWebViewScrollZoomScaleFromZoomState:
429     (const web::PageZoomState&)zoomState {
430   // After rendering a web page, WKWebView keeps the |minimumZoomScale| and
431   // |maximumZoomScale| properties of its scroll view constant while adjusting
432   // the |zoomScale| property accordingly.  The maximum-scale or minimum-scale
433   // meta tags of a page may have changed since the state was recorded, so clamp
434   // the zoom scale to the current range if necessary.
435   DCHECK(zoomState.IsValid());
436   // Legacy-format scroll states cannot be applied to WKWebViews.
437   if (zoomState.IsLegacyFormat())
438     return;
439   CGFloat zoomScale = zoomState.zoom_scale();
440   if (zoomScale < self.webScrollView.minimumZoomScale)
441     zoomScale = self.webScrollView.minimumZoomScale;
442   if (zoomScale > self.webScrollView.maximumZoomScale)
443     zoomScale = self.webScrollView.maximumZoomScale;
444   self.webScrollView.zoomScale = zoomScale;
447 - (void)handleCancelledError:(NSError*)error {
448   if ([self shouldAbortLoadForCancelledError:error]) {
449     // Do not abort the load for WKWebView, because calling stopLoading may
450     // stop the subsequent provisional load as well.
451     [self loadCancelled];
452     [[self sessionController] discardNonCommittedEntries];
453   }
456 #pragma mark Private methods
458 - (NSString*)documentMIMEType {
459   return _documentMIMEType.get();
462 - (void)setDocumentMIMEType:(NSString*)type {
463   _documentMIMEType.reset([type copy]);
466 - (NSDictionary*)wkWebViewObservers {
467   return @{
468 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
469     @"estimatedProgress" : @"webViewEstimatedProgressDidChange",
470     @"certificateChain" : @"webViewSecurityFeaturesDidChange",
471     @"hasOnlySecureContent" : @"webViewSecurityFeaturesDidChange",
472 #endif
473     @"loading" : @"webViewLoadingStateDidChange",
474     @"title" : @"webViewTitleDidChange",
475     @"URL" : @"webViewURLDidChange",
476   };
479 - (NSString*)activityIndicatorGroupID {
480   return [NSString stringWithFormat:
481       @"WKWebViewWebController.NetworkActivityIndicatorKey.%@",
482           self.webStateImpl->GetRequestGroupID()];
485 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
486 - (int)certGroupID {
487   DCHECK(self.webStateImpl);
488   // Request tracker IDs are used as certificate groups.
489   return self.webStateImpl->GetRequestTracker()->identifier();
491 #endif
493 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider {
494   DCHECK(self.webStateImpl);
495   web::BrowserState* browserState = self.webStateImpl->GetBrowserState();
496   return web::WKWebViewConfigurationProvider::FromBrowserState(browserState);
499 - (void)ensureWebViewCreatedWithConfiguration:(WKWebViewConfiguration*)config {
500   if (!_wkWebView) {
501     // Use a separate userContentController for each web view.
502     // WKUserContentController does not allow adding multiple script message
503     // handlers for the same name, hence userContentController can't be shared
504     // between all web views.
505     config.userContentController = [self createUserContentController];
506     [self setWebView:[self createWebViewWithConfiguration:config]];
507     // Notify super class about created web view. -webViewDidChange is not
508     // called from -setWebView:scriptMessageRouter: as the latter used in unit
509     // tests with fake web view, which cannot be added to view hierarchy.
510     [self webViewDidChange];
511   }
514 - (WKWebView*)createWebViewWithConfiguration:(WKWebViewConfiguration*)config {
515   return [web::CreateWKWebView(
516               CGRectZero,
517               config,
518               self.webStateImpl->GetBrowserState(),
519               self.webStateImpl->GetRequestGroupID(),
520               [self useDesktopUserAgent]) autorelease];
523 - (void)setWebView:(WKWebView*)webView {
524   DCHECK_NE(_wkWebView.get(), webView);
526   // Unwind the old web view.
527   WKUserContentController* oldContentController =
528       [[_wkWebView configuration] userContentController];
529   [oldContentController removeScriptMessageHandlerForName:kScriptMessageName];
530   [oldContentController removeScriptMessageHandlerForName:kScriptImmediateName];
531   [_wkWebView setNavigationDelegate:nil];
532   [_wkWebView setUIDelegate:nil];
533   for (NSString* keyPath in self.wkWebViewObservers) {
534     [_wkWebView removeObserver:self forKeyPath:keyPath];
535   }
536   [self clearActivityIndicatorTasks];
538   _wkWebView.reset([webView retain]);
540   // Set up the new web view.
541   WKUserContentController* newContentController =
542       [[_wkWebView configuration] userContentController];
543   [newContentController addScriptMessageHandler:self name:kScriptMessageName];
544   [newContentController addScriptMessageHandler:self name:kScriptImmediateName];
545   [_wkWebView setNavigationDelegate:self];
546   [_wkWebView setUIDelegate:self];
547   for (NSString* keyPath in self.wkWebViewObservers) {
548     [_wkWebView addObserver:self forKeyPath:keyPath options:0 context:nullptr];
549   }
550   _injectedScriptManagers.reset([[NSMutableSet alloc] init]);
551   _crashDetector.reset([self newCrashDetectorWithWebView:_wkWebView]);
552   _documentURL = [self defaultURL];
555 - (BOOL)isLinkNavigation:(WKNavigationType)navigationType {
556   switch (navigationType) {
557     case WKNavigationTypeLinkActivated:
558       return YES;
559     case WKNavigationTypeOther:
560       return [self userClickedRecently];
561     default:
562       return NO;
563   }
566 - (void)setPendingReferrerString:(NSString*)referrer {
567   _pendingReferrerString.reset([referrer copy]);
570 - (void)updatePendingReferrerFromNavigationAction:(WKNavigationAction*)action {
571   if (action.targetFrame.isMainFrame)
572     [self setPendingReferrerString:GetRefererFromNavigationAction(action)];
575 - (void)commitPendingReferrerString {
576   _currentReferrerString.reset(_pendingReferrerString.release());
579 - (void)discardPendingReferrerString {
580   _pendingReferrerString.reset();
583 - (CRWWKWebViewCrashDetector*)newCrashDetectorWithWebView:(WKWebView*)webView {
584   // iOS9 provides crash detection API.
585   if (!webView || base::ios::IsRunningOnIOS9OrLater())
586     return nil;
588   base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
589   id crashHandler = ^{
590     [weakSelf webViewWebProcessDidCrash];
591   };
592   return [[CRWWKWebViewCrashDetector alloc] initWithWebView:webView
593                                                crashHandler:crashHandler];
596 - (void)webViewWebProcessDidCrash {
597   if ([self.delegate respondsToSelector:
598           @selector(webControllerWebProcessDidCrash:)]) {
599     [self.delegate webControllerWebProcessDidCrash:self];
600   }
603 - (void)queryPageReferrerPolicy:(void(^)(NSString*))responseHandler {
604   DCHECK(responseHandler);
605   [self evaluateJavaScript:@"__gCrWeb.getPageReferrerPolicy()"
606        stringResultHandler:^(NSString* referrer, NSError* error) {
607       DCHECK_NE(error.code, WKErrorJavaScriptExceptionOccurred);
608       responseHandler(!error ? referrer : nil);
609   }];
612 - (void)didBlockPopupWithURL:(GURL)popupURL
613                    sourceURL:(GURL)sourceURL
614               referrerPolicy:(const std::string&)referrerPolicyString {
615   web::ReferrerPolicy referrerPolicy =
616       [self referrerPolicyFromString:referrerPolicyString];
617   web::Referrer referrer(sourceURL, referrerPolicy);
618   NSString* const kWindowName = @"";  // obsoleted
619   base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
620   void(^showPopupHandler)() = ^{
621       // On Desktop cross-window comunication is not supported for unblocked
622       // popups; so it's ok to create a new independent page.
623       CRWWebController* child = [[weakSelf delegate]
624           webPageOrderedOpen:popupURL
625                     referrer:referrer
626                   windowName:kWindowName
627                 inBackground:NO];
628       DCHECK(!child || child.sessionController.openedByDOM);
629   };
631   web::BlockedPopupInfo info(popupURL, referrer, kWindowName, showPopupHandler);
632   [self.delegate webController:self didBlockPopup:info];
635 - (void)didBlockPopupWithURL:(GURL)popupURL sourceURL:(GURL)sourceURL {
636   if (![self.delegate respondsToSelector:
637       @selector(webController:didBlockPopup:)]) {
638     return;
639   }
641   base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
642   dispatch_async(dispatch_get_main_queue(), ^{
643       [self queryPageReferrerPolicy:^(NSString* policy) {
644           [weakSelf didBlockPopupWithURL:popupURL
645                                sourceURL:sourceURL
646                           referrerPolicy:base::SysNSStringToUTF8(policy)];
647       }];
648   });
651 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
652 - (void)handleSSLError:(NSError*)error {
653   DCHECK(web::IsWKWebViewSSLError(error));
655   net::SSLInfo sslInfo;
656   web::GetSSLInfoFromWKWebViewSSLError(error, &sslInfo);
658   web::SSLStatus sslStatus;
659   sslStatus.security_style = web::SECURITY_STYLE_AUTHENTICATION_BROKEN;
660   sslStatus.cert_status = sslInfo.cert_status;
661   sslStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
662       sslInfo.cert.get(), self.certGroupID);
664   [self.delegate presentSSLError:sslInfo
665                     forSSLStatus:sslStatus
666                      recoverable:NO
667                         callback:nullptr];
669 #endif  // #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
671 - (void)addActivityIndicatorTask {
672   [[CRWNetworkActivityIndicatorManager sharedInstance]
673       startNetworkTaskForGroup:[self activityIndicatorGroupID]];
676 - (void)clearActivityIndicatorTasks {
677   [[CRWNetworkActivityIndicatorManager sharedInstance]
678       clearNetworkTasksForGroup:[self activityIndicatorGroupID]];
681 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
682 - (void)updateSSLStatusForCurrentNavigationItem {
683   if ([self isBeingDestroyed])
684     return;
686   DCHECK(self.webStateImpl);
687   web::NavigationItem* item =
688       self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
689   if (!item)
690     return;
692   web::SSLStatus previousSSLStatus = item->GetSSL();
693   web::SSLStatus& SSLStatus = item->GetSSL();
694   if (item->GetURL().SchemeIsCryptographic()) {
695     // TODO(eugenebut): Do not set security style to authenticated once
696     // proceeding with bad ssl cert is implemented.
697     SSLStatus.security_style = web::SECURITY_STYLE_AUTHENTICATED;
698     SSLStatus.content_status = [_wkWebView hasOnlySecureContent]
699                                    ? web::SSLStatus::NORMAL_CONTENT
700                                    : web::SSLStatus::DISPLAYED_INSECURE_CONTENT;
702     if (base::ios::IsRunningOnIOS9OrLater()) {
703       scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(
704           [_wkWebView performSelector:@selector(certificateChain)]));
705       if (cert) {
706         SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
707             cert.get(), self.certGroupID);
708       } else {
709         SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
710         SSLStatus.cert_id = 0;
711       }
712     }
713   } else {
714     SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
715     SSLStatus.cert_id = 0;
716   }
718   if (!previousSSLStatus.Equals(SSLStatus)) {
719     [self didUpdateSSLStatusForCurrentNavigationItem];
720   }
722 #endif  // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
724 - (void)registerLoadRequest:(const GURL&)url {
725   // If load request is registered via WKWebViewWebController, assume transition
726   // is link or client redirect as other transitions will already be registered
727   // by web controller or delegates.
728   // TODO(stuartmorgan): Remove guesswork and replace with information from
729   // decidePolicyForNavigationAction:.
730   ui::PageTransition transition = self.userInteractionRegistered
731                                       ? ui::PAGE_TRANSITION_LINK
732                                       : ui::PAGE_TRANSITION_CLIENT_REDIRECT;
733   // The referrer is not known yet, and will be updated later.
734   const web::Referrer emptyReferrer;
735   [self registerLoadRequest:url referrer:emptyReferrer transition:transition];
738 - (void)URLDidChangeWithoutDocumentChange:(const GURL&)newURL {
739   DCHECK(newURL == net::GURLWithNSURL([_wkWebView URL]));
740   _documentURL = newURL;
741   // If called during window.history.pushState or window.history.replaceState
742   // JavaScript evaluation, only update the document URL. This callback does not
743   // have any information about the state object and cannot create (or edit) the
744   // navigation entry for this page change. Web controller will sync with
745   // history changes when a window.history.didPushState or
746   // window.history.didReplaceState message is received, which should happen in
747   // the next runloop.
748   if (!_changingHistoryState) {
749     [self registerLoadRequest:_documentURL];
750     [self didStartLoadingURL:_documentURL updateHistory:YES];
751     [self didFinishNavigation];
752   }
755 - (WKUserContentController*)createUserContentController {
756   WKUserContentController* result =
757       [[[WKUserContentController alloc] init] autorelease];
758   base::scoped_nsobject<WKUserScript> script([[WKUserScript alloc]
759         initWithSource:web::GetEarlyPageScript(web::WK_WEB_VIEW_TYPE)
760          injectionTime:WKUserScriptInjectionTimeAtDocumentStart
761       forMainFrameOnly:YES]);
762   [result addUserScript:script];
763   return result;
766 - (void)userContentController:(WKUserContentController*)userContentController
767       didReceiveScriptMessage:(WKScriptMessage*)message {
768   // Broken out into separate method to catch errors.
769   // TODO(jyquinn): Evaluate whether this is necessary for WKWebView.
770   if (![self respondToWKScriptMessage:message]) {
771     DLOG(WARNING) << "Message from JS not handled due to invalid format";
772   }
775 - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage {
776   CHECK(scriptMessage.frameInfo.mainFrame);
777   int errorCode = 0;
778   std::string errorMessage;
779   scoped_ptr<base::Value> inputJSONData(
780       base::JSONReader::ReadAndReturnError(
781           base::SysNSStringToUTF8(scriptMessage.body),
782           false,
783           &errorCode,
784           &errorMessage));
785   if (errorCode) {
786     DLOG(WARNING) << "JSON parse error: %s" << errorMessage.c_str();
787     return NO;
788   }
789   base::DictionaryValue* message = nullptr;
790   if (!inputJSONData->GetAsDictionary(&message)) {
791     return NO;
792   }
793   std::string windowID;
794   message->GetString("crwWindowId", &windowID);
795   // Check for correct windowID
796   if (![[self windowId] isEqualToString:base::SysUTF8ToNSString(windowID)]) {
797     DLOG(WARNING) << "Message from JS ignored due to non-matching windowID: "
798                   << [self windowId] << " != "
799                   << base::SysUTF8ToNSString(windowID);
800     return NO;
801   }
802   base::DictionaryValue* command = nullptr;
803   if (!message->GetDictionary("crwCommand", &command)) {
804     return NO;
805   }
806   if ([scriptMessage.name isEqualToString:kScriptImmediateName] ||
807       [scriptMessage.name isEqualToString:kScriptMessageName]) {
808     return [self respondToMessage:command
809                 userIsInteracting:[self userIsInteracting]
810                         originURL:net::GURLWithNSURL([_wkWebView URL])];
811   }
813   NOTREACHED();
814   return NO;
817 - (SEL)selectorToHandleJavaScriptCommand:(const std::string&)command {
818   static std::map<std::string, SEL>* handlers = nullptr;
819   static dispatch_once_t onceToken;
820   dispatch_once(&onceToken, ^{
821     handlers = new std::map<std::string, SEL>();
822     (*handlers)["window.history.didPushState"] =
823         @selector(handleWindowHistoryDidPushStateMessage:context:);
824     (*handlers)["window.history.didReplaceState"] =
825         @selector(handleWindowHistoryDidReplaceStateMessage:context:);
826     (*handlers)["window.history.willChangeState"] =
827         @selector(handleWindowHistoryWillChangeStateMessage:context:);
828   });
829   DCHECK(handlers);
830   auto iter = handlers->find(command);
831   return iter != handlers->end()
832              ? iter->second
833              : [super selectorToHandleJavaScriptCommand:command];
836 - (BOOL)shouldAbortLoadForCancelledError:(NSError*)error {
837   DCHECK_EQ(error.code, NSURLErrorCancelled);
838   // Do not abort the load if it is for an app specific URL, as such errors
839   // are produced during the app specific URL load process.
840   const GURL errorURL =
841       net::GURLWithNSURL(error.userInfo[NSURLErrorFailingURLErrorKey]);
842   if (web::GetWebClient()->IsAppSpecificURL(errorURL))
843     return NO;
844   // Don't abort NSURLErrorCancelled errors originating from navigation, as the
845   // WKWebView will automatically retry these loads.
846   WKWebViewErrorSource source = WKWebViewErrorSourceFromError(error);
847   return source != NAVIGATION;
850 #pragma mark -
851 #pragma mark JavaScript message handlers
853 - (BOOL)handleWindowHistoryWillChangeStateMessage:
854     (base::DictionaryValue*)message
855                                           context:(NSDictionary*)context {
856   _changingHistoryState = YES;
857   return
858       [super handleWindowHistoryWillChangeStateMessage:message context:context];
861 - (BOOL)handleWindowHistoryDidPushStateMessage:(base::DictionaryValue*)message
862                                        context:(NSDictionary*)context {
863   DCHECK(_changingHistoryState);
864   _changingHistoryState = NO;
865   return [super handleWindowHistoryDidPushStateMessage:message context:context];
868 - (BOOL)handleWindowHistoryDidReplaceStateMessage:
869     (base::DictionaryValue*)message
870                                          context:(NSDictionary*)context {
871   DCHECK(_changingHistoryState);
872   _changingHistoryState = NO;
873   return [super handleWindowHistoryDidReplaceStateMessage:message
874                                                   context:context];
877 #pragma mark -
878 #pragma mark WebUI
880 - (void)createWebUIForURL:(const GURL&)URL {
881   [super createWebUIForURL:URL];
882   _webUIManager.reset(
883       [[CRWWebUIManager alloc] initWithWebState:self.webStateImpl]);
886 - (void)clearWebUI {
887   [super clearWebUI];
888   _webUIManager.reset();
891 #pragma mark -
892 #pragma mark KVO Observation
894 - (void)observeValueForKeyPath:(NSString*)keyPath
895                       ofObject:(id)object
896                         change:(NSDictionary*)change
897                        context:(void*)context {
898   NSString* dispatcherSelectorName = self.wkWebViewObservers[keyPath];
899   DCHECK(dispatcherSelectorName);
900   if (dispatcherSelectorName)
901     [self performSelector:NSSelectorFromString(dispatcherSelectorName)];
904 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
905 // TODO(eugenebut): use WKWebView progress even if Chrome net stack is enabled.
906 - (void)webViewEstimatedProgressDidChange {
907   if ([self.delegate respondsToSelector:
908           @selector(webController:didUpdateProgress:)]) {
909     [self.delegate webController:self
910                didUpdateProgress:[_wkWebView estimatedProgress]];
911   }
914 - (void)webViewSecurityFeaturesDidChange {
915   if (self.loadPhase == web::LOAD_REQUESTED) {
916     // Do not update SSL Status for pending load. It will be updated in
917     // |webView:didCommitNavigation:| callback.
918     return;
919   }
920   [self updateSSLStatusForCurrentNavigationItem];
923 #endif  // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
925 - (void)webViewLoadingStateDidChange {
926   if ([_wkWebView isLoading]) {
927     [self addActivityIndicatorTask];
928   } else {
929     [self clearActivityIndicatorTasks];
930   }
933 - (void)webViewTitleDidChange {
934   if ([self.delegate respondsToSelector:
935           @selector(webController:titleDidChange:)]) {
936     DCHECK(self.title);
937     [self.delegate webController:self titleDidChange:self.title];
938   }
941 - (void)webViewURLDidChange {
942   // TODO(stuartmorgan): Determine if there are any cases where this still
943   // happens, and if so whether anything should be done when it does.
944   if (![_wkWebView URL]) {
945     DVLOG(1) << "Received nil URL callback";
946     return;
947   }
948   GURL url(net::GURLWithNSURL([_wkWebView URL]));
949   // URL changes happen at three points:
950   // 1) When a load starts; at this point, the load is provisional, and
951   //    it should be ignored until it's committed, since the document/window
952   //    objects haven't changed yet.
953   // 2) When a non-document-changing URL change happens (hash change,
954   //    history.pushState, etc.). This URL change happens instantly, so should
955   //    be reported.
956   // 3) When a navigation error occurs after provisional navigation starts,
957   //    the URL reverts to the previous URL without triggering a new navigation.
958   //
959   // If |isLoading| is NO, then it must be case 2 or 3. If the last committed
960   // URL (_documentURL) matches the current URL, assume that it is a revert from
961   // navigation failure and do nothing. If the URL does not match, assume it is
962   // a non-document-changing URL change, and handle accordingly.
963   //
964   // If |isLoading| is YES, then it could either be case 1, or it could be
965   // case 2 on a page that hasn't finished loading yet. If the domain of the
966   // new URL matches the last committed URL, then check window.location.href,
967   // and if it matches, trust it. The domain check ensures that if a site
968   // somehow corrupts window.location.href it can't do a redirect to a
969   // slow-loading target page while it is still loading to spoof the domain.
970   // On a document-changing URL change, the window.location.href will match the
971   // previous URL at this stage, not the web view's current URL.
972   if (![_wkWebView isLoading]) {
973     if (_documentURL == url)
974       return;
975     [self URLDidChangeWithoutDocumentChange:url];
976   } else if (!_documentURL.host().empty() &&
977              _documentURL.host() == url.host()) {
978     [_wkWebView evaluateJavaScript:@"window.location.href"
979                  completionHandler:^(id result, NSError* error) {
980                      // If the web view has gone away, or the location
981                      // couldn't be retrieved, abort.
982                      if (!_wkWebView ||
983                          ![result isKindOfClass:[NSString class]]) {
984                        return;
985                      }
986                      GURL jsURL([result UTF8String]);
987                      // Make sure that the URL is as expected, and re-check
988                      // the host to prevent race conditions.
989                      if (jsURL == url && _documentURL.host() == url.host()) {
990                        [self URLDidChangeWithoutDocumentChange:url];
991                      }
992                  }];
993   }
996 #pragma mark -
997 #pragma mark WKNavigationDelegate Methods
999 - (void)webView:(WKWebView *)webView
1000     decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
1001                     decisionHandler:
1002         (void (^)(WKNavigationActionPolicy))decisionHandler {
1003   if (self.isBeingDestroyed) {
1004     decisionHandler(WKNavigationActionPolicyCancel);
1005     return;
1006   }
1008   NSURLRequest* request = navigationAction.request;
1009   GURL url = net::GURLWithNSURL(request.URL);
1011   // The page will not be changed until this navigation is commited, so the
1012   // retrieved referrer will be pending until |didCommitNavigation| callback.
1013   [self updatePendingReferrerFromNavigationAction:navigationAction];
1015   if (navigationAction.sourceFrame.isMainFrame)
1016     self.documentMIMEType = nil;
1018   web::FrameInfo targetFrame(navigationAction.targetFrame.isMainFrame);
1019   BOOL isLinkClick = [self isLinkNavigation:navigationAction.navigationType];
1020   BOOL allowLoad = [self shouldAllowLoadWithRequest:request
1021                                         targetFrame:&targetFrame
1022                                         isLinkClick:isLinkClick];
1023   decisionHandler(allowLoad ? WKNavigationActionPolicyAllow
1024                             : WKNavigationActionPolicyCancel);
1027 - (void)webView:(WKWebView *)webView
1028     decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
1029                       decisionHandler:
1030                           (void (^)(WKNavigationResponsePolicy))handler {
1031   if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
1032     // Create HTTP headers from the response.
1033     // TODO(kkhorimoto): Due to the limited interface of NSHTTPURLResponse, some
1034     // data in the HttpResponseHeaders generated here is inexact.  Once
1035     // UIWebView is no longer supported, update WebState's implementation so
1036     // that the Content-Language and the MIME type can be set without using this
1037     // imperfect conversion.
1038     scoped_refptr<net::HttpResponseHeaders> HTTPHeaders =
1039         net::CreateHeadersFromNSHTTPURLResponse(
1040             static_cast<NSHTTPURLResponse*>(navigationResponse.response));
1041     self.webStateImpl->OnHttpResponseHeadersReceived(
1042         HTTPHeaders.get(), net::GURLWithNSURL(navigationResponse.response.URL));
1043   }
1044   if (navigationResponse.isForMainFrame)
1045     self.documentMIMEType = navigationResponse.response.MIMEType;
1046   handler(navigationResponse.canShowMIMEType ? WKNavigationResponsePolicyAllow :
1047               WKNavigationResponsePolicyCancel);
1050 // TODO(stuartmorgan): Move all the guesswork around these states out of the
1051 // superclass, and wire these up to the remaining methods.
1052 - (void)webView:(WKWebView *)webView
1053     didStartProvisionalNavigation:(WKNavigation *)navigation {
1054   GURL webViewURL = net::GURLWithNSURL(webView.URL);
1055   // Intercept renderer-initiated navigations. If this navigation has not yet
1056   // been registered, do so. loadPhase check is necessary because
1057   // lastRegisteredRequestURL may be the same as the webViewURL on a new tab
1058   // created by window.open (default is about::blank).
1059   // TODO(jyquinn): Audit [CRWWebController loadWithParams] for other tasks that
1060   // should be performed here.
1061   if (self.lastRegisteredRequestURL != webViewURL ||
1062       self.loadPhase != web::LOAD_REQUESTED) {
1063     // Reset current WebUI if one exists.
1064     [self clearWebUI];
1065     // Restart app specific URL loads to properly capture state.
1066     // TODO(jyquinn): Extract necessary tasks for app specific URL navigation
1067     // rather than restarting the load.
1068     if (web::GetWebClient()->IsAppSpecificURL(webViewURL)) {
1069       [self abortWebLoad];
1070       web::WebLoadParams params(webViewURL);
1071       [self loadWithParams:params];
1072       return;
1073     } else {
1074       [self registerLoadRequest:webViewURL];
1075     }
1076   }
1077   // Ensure the URL is registered and loadPhase is as expected.
1078   DCHECK(self.lastRegisteredRequestURL == webViewURL);
1079   DCHECK(self.loadPhase == web::LOAD_REQUESTED);
1082 - (void)webView:(WKWebView *)webView
1083     didReceiveServerRedirectForProvisionalNavigation:
1084         (WKNavigation *)navigation {
1085   [self registerLoadRequest:net::GURLWithNSURL(webView.URL)
1086                    referrer:[self currentReferrer]
1087                  transition:ui::PAGE_TRANSITION_SERVER_REDIRECT];
1090 - (void)webView:(WKWebView *)webView
1091     didFailProvisionalNavigation:(WKNavigation *)navigation
1092                        withError:(NSError *)error {
1093   [self discardPendingReferrerString];
1095   error = WKWebViewErrorWithSource(error, PROVISIONAL_LOAD);
1097 #if defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1098   // For WKWebViews, the underlying errors for errors reported by the net stack
1099   // are not copied over when transferring the errors from the IO thread.  For
1100   // cancelled errors that trigger load abortions, translate the error early to
1101   // trigger |-discardNonCommittedEntries| from |-handleLoadError:inMainFrame:|.
1102   if (error.code == NSURLErrorCancelled &&
1103       [self shouldAbortLoadForCancelledError:error] &&
1104       !error.userInfo[NSUnderlyingErrorKey]) {
1105     error = web::NetErrorFromError(error);
1106   }
1107 #endif  // defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1109 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1110   if (web::IsWKWebViewSSLError(error))
1111     [self handleSSLError:error];
1112   else
1113 #endif
1114     [self handleLoadError:error inMainFrame:YES];
1117 - (void)webView:(WKWebView *)webView
1118     didCommitNavigation:(WKNavigation *)navigation {
1119   DCHECK_EQ(_wkWebView, webView);
1120   // This point should closely approximate the document object change, so reset
1121   // the list of injected scripts to those that are automatically injected.
1122   _injectedScriptManagers.reset([[NSMutableSet alloc] init]);
1123   [self injectWindowID];
1125   // The page has changed; commit the pending referrer.
1126   [self commitPendingReferrerString];
1128   // This is the point where the document's URL has actually changed.
1129   _documentURL = net::GURLWithNSURL([_wkWebView URL]);
1130   DCHECK(_documentURL == self.lastRegisteredRequestURL);
1131   [self webPageChanged];
1133 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
1134   [self updateSSLStatusForCurrentNavigationItem];
1135 #endif
1138 - (void)webView:(WKWebView *)webView
1139     didFinishNavigation:(WKNavigation *)navigation {
1140   DCHECK(!self.isHalted);
1141   // Trigger JavaScript driven post-document-load-completion tasks.
1142   // TODO(jyquinn): Investigate using WKUserScriptInjectionTimeAtDocumentEnd to
1143   // inject this material at the appropriate time rather than invoking here.
1144   web::EvaluateJavaScript(webView,
1145                           @"__gCrWeb.didFinishNavigation()", nil);
1146   [self didFinishNavigation];
1149 - (void)webView:(WKWebView *)webView
1150     didFailNavigation:(WKNavigation *)navigation
1151             withError:(NSError *)error {
1152   [self handleLoadError:WKWebViewErrorWithSource(error, NAVIGATION)
1153             inMainFrame:YES];
1156 - (void)webView:(WKWebView *)webView
1157     didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
1158                     completionHandler:
1159         (void (^)(NSURLSessionAuthChallengeDisposition disposition,
1160                   NSURLCredential *credential))completionHandler {
1161   NOTIMPLEMENTED();
1162   completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace, nil);
1165 - (void)webViewWebContentProcessDidTerminate:(WKWebView*)webView {
1166   [self webViewWebProcessDidCrash];
1169 #pragma mark WKUIDelegate Methods
1171 - (WKWebView*)webView:(WKWebView*)webView
1172     createWebViewWithConfiguration:(WKWebViewConfiguration*)configuration
1173                forNavigationAction:(WKNavigationAction*)navigationAction
1174                     windowFeatures:(WKWindowFeatures*)windowFeatures {
1175   GURL requestURL = net::GURLWithNSURL(navigationAction.request.URL);
1176   NSString* referer = GetRefererFromNavigationAction(navigationAction);
1177   GURL referrerURL = referer ? GURL(base::SysNSStringToUTF8(referer)) :
1178                                [self currentURL];
1180   if (![self userIsInteracting] &&
1181       [self shouldBlockPopupWithURL:requestURL sourceURL:referrerURL]) {
1182     [self didBlockPopupWithURL:requestURL sourceURL:referrerURL];
1183     // Desktop Chrome does not return a window for the blocked popups;
1184     // follow the same approach by returning nil;
1185     return nil;
1186   }
1188   id child = [self createChildWebControllerWithReferrerURL:referrerURL];
1189   // WKWebView requires WKUIDelegate to return a child view created with
1190   // exactly the same |configuration| object (exception is raised if config is
1191   // different). |configuration| param and config returned by
1192   // WKWebViewConfigurationProvider are different objects because WKWebView
1193   // makes a shallow copy of the config inside init, so every WKWebView
1194   // owns a separate shallow copy of WKWebViewConfiguration.
1195   [child ensureWebViewCreatedWithConfiguration:configuration];
1196   return [child webView];
1199 - (void)webView:(WKWebView*)webView
1200     runJavaScriptAlertPanelWithMessage:(NSString*)message
1201                       initiatedByFrame:(WKFrameInfo*)frame
1202                      completionHandler:(void(^)())completionHandler {
1203   SEL alertSelector = @selector(webController:
1204            runJavaScriptAlertPanelWithMessage:
1205                                    requestURL:
1206                             completionHandler:);
1207   if ([self.UIDelegate respondsToSelector:alertSelector]) {
1208     [self.UIDelegate webController:self
1209         runJavaScriptAlertPanelWithMessage:message
1210                                 requestURL:net::GURLWithNSURL(frame.request.URL)
1211                          completionHandler:completionHandler];
1212   } else if (completionHandler) {
1213     completionHandler();
1214   }
1217 - (void)webView:(WKWebView*)webView
1218     runJavaScriptConfirmPanelWithMessage:(NSString*)message
1219                         initiatedByFrame:(WKFrameInfo*)frame
1220                        completionHandler:
1221         (void (^)(BOOL result))completionHandler {
1222   SEL confirmationSelector = @selector(webController:
1223                 runJavaScriptConfirmPanelWithMessage:
1224                                           requestURL:
1225                                    completionHandler:);
1226   if ([self.UIDelegate respondsToSelector:confirmationSelector]) {
1227     [self.UIDelegate webController:self
1228         runJavaScriptConfirmPanelWithMessage:message
1229                                   requestURL:
1230             net::GURLWithNSURL(frame.request.URL)
1231                            completionHandler:completionHandler];
1232   } else if (completionHandler) {
1233     completionHandler(NO);
1234   }
1237 - (void)webView:(WKWebView*)webView
1238     runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
1239                               defaultText:(NSString*)defaultText
1240                          initiatedByFrame:(WKFrameInfo*)frame
1241                         completionHandler:
1242         (void (^)(NSString *result))completionHandler {
1243   SEL textInputSelector = @selector(webController:
1244             runJavaScriptTextInputPanelWithPrompt:
1245                                   placeholderText:
1246                                        requestURL:
1247                                 completionHandler:);
1248   if ([self.UIDelegate respondsToSelector:textInputSelector]) {
1249     [self.UIDelegate webController:self
1250         runJavaScriptTextInputPanelWithPrompt:prompt
1251                               placeholderText:defaultText
1252                                    requestURL:
1253             net::GURLWithNSURL(frame.request.URL)
1254                             completionHandler:completionHandler];
1255   } else if (completionHandler) {
1256     completionHandler(nil);
1257   }
1260 @end