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"
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
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) {
68 base::scoped_nsobject<NSMutableDictionary> userInfo(
69 [error.userInfo mutableCopy]);
70 [userInfo setObject:@(source) forKey:kWKWebViewErrorSourceKey];
71 return [NSError errorWithDomain:error.domain
75 WKWebViewErrorSource WKWebViewErrorSourceFromError(NSError* error) {
77 return static_cast<WKWebViewErrorSource>(
78 [error.userInfo[kWKWebViewErrorSourceKey] integerValue]);
83 @interface CRWWKWebViewWebController () <WKNavigationDelegate,
84 WKScriptMessageHandler,
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.
98 // A set of script managers whose scripts have been injected into the current
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
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
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
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
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;
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;
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
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;
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.
269 - (void)setKeyboardDisplayRequiresUserAction:(BOOL)requiresUserAction {
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];
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
312 return _wkWebView.get();
315 - (UIScrollView*)webScrollView {
316 return [_wkWebView scrollView];
319 - (BOOL)ignoreURLVerificationFailures {
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 {
345 *trustLevel = web::URLVerificationTrustLevel::kAbsolute;
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.
364 return web::WEB_VIEW_DOCUMENT_TYPE_GENERIC;
367 if (!self.documentMIMEType) {
368 return web::WEB_VIEW_DOCUMENT_TYPE_UNKNOWN;
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;
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);
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 {
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())
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];
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 {
468 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
469 @"estimatedProgress" : @"webViewEstimatedProgressDidChange",
470 @"certificateChain" : @"webViewSecurityFeaturesDidChange",
471 @"hasOnlySecureContent" : @"webViewSecurityFeaturesDidChange",
473 @"loading" : @"webViewLoadingStateDidChange",
474 @"title" : @"webViewTitleDidChange",
475 @"URL" : @"webViewURLDidChange",
479 - (NSString*)activityIndicatorGroupID {
480 return [NSString stringWithFormat:
481 @"WKWebViewWebController.NetworkActivityIndicatorKey.%@",
482 self.webStateImpl->GetRequestGroupID()];
485 #if !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
487 DCHECK(self.webStateImpl);
488 // Request tracker IDs are used as certificate groups.
489 return self.webStateImpl->GetRequestTracker()->identifier();
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 {
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];
514 - (WKWebView*)createWebViewWithConfiguration:(WKWebViewConfiguration*)config {
515 return [web::CreateWKWebView(
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];
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];
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:
559 case WKNavigationTypeOther:
560 return [self userClickedRecently];
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())
588 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
590 [weakSelf webViewWebProcessDidCrash];
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];
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);
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
626 windowName:kWindowName
628 DCHECK(!child || child.sessionController.openedByDOM);
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:)]) {
641 base::WeakNSObject<CRWWKWebViewWebController> weakSelf(self);
642 dispatch_async(dispatch_get_main_queue(), ^{
643 [self queryPageReferrerPolicy:^(NSString* policy) {
644 [weakSelf didBlockPopupWithURL:popupURL
646 referrerPolicy:base::SysNSStringToUTF8(policy)];
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
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])
686 DCHECK(self.webStateImpl);
687 web::NavigationItem* item =
688 self.webStateImpl->GetNavigationManagerImpl().GetLastCommittedItem();
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)]));
706 SSLStatus.cert_id = web::CertStore::GetInstance()->StoreCert(
707 cert.get(), self.certGroupID);
709 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
710 SSLStatus.cert_id = 0;
714 SSLStatus.security_style = web::SECURITY_STYLE_UNAUTHENTICATED;
715 SSLStatus.cert_id = 0;
718 if (!previousSSLStatus.Equals(SSLStatus)) {
719 [self didUpdateSSLStatusForCurrentNavigationItem];
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
748 if (!_changingHistoryState) {
749 [self registerLoadRequest:_documentURL];
750 [self didStartLoadingURL:_documentURL updateHistory:YES];
751 [self didFinishNavigation];
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];
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";
775 - (BOOL)respondToWKScriptMessage:(WKScriptMessage*)scriptMessage {
776 CHECK(scriptMessage.frameInfo.mainFrame);
778 std::string errorMessage;
779 scoped_ptr<base::Value> inputJSONData(
780 base::JSONReader::ReadAndReturnError(
781 base::SysNSStringToUTF8(scriptMessage.body),
786 DLOG(WARNING) << "JSON parse error: %s" << errorMessage.c_str();
789 base::DictionaryValue* message = nullptr;
790 if (!inputJSONData->GetAsDictionary(&message)) {
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);
802 base::DictionaryValue* command = nullptr;
803 if (!message->GetDictionary("crwCommand", &command)) {
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])];
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:);
830 auto iter = handlers->find(command);
831 return iter != handlers->end()
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))
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;
851 #pragma mark JavaScript message handlers
853 - (BOOL)handleWindowHistoryWillChangeStateMessage:
854 (base::DictionaryValue*)message
855 context:(NSDictionary*)context {
856 _changingHistoryState = YES;
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
880 - (void)createWebUIForURL:(const GURL&)URL {
881 [super createWebUIForURL:URL];
883 [[CRWWebUIManager alloc] initWithWebState:self.webStateImpl]);
888 _webUIManager.reset();
892 #pragma mark KVO Observation
894 - (void)observeValueForKeyPath:(NSString*)keyPath
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]];
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.
920 [self updateSSLStatusForCurrentNavigationItem];
923 #endif // !defined(ENABLE_CHROME_NET_STACK_FOR_WKWEBVIEW)
925 - (void)webViewLoadingStateDidChange {
926 if ([_wkWebView isLoading]) {
927 [self addActivityIndicatorTask];
929 [self clearActivityIndicatorTasks];
933 - (void)webViewTitleDidChange {
934 if ([self.delegate respondsToSelector:
935 @selector(webController:titleDidChange:)]) {
937 [self.delegate webController:self titleDidChange:self.title];
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";
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
956 // 3) When a navigation error occurs after provisional navigation starts,
957 // the URL reverts to the previous URL without triggering a new navigation.
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.
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)
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.
983 ![result isKindOfClass:[NSString class]]) {
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];
997 #pragma mark WKNavigationDelegate Methods
999 - (void)webView:(WKWebView *)webView
1000 decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
1002 (void (^)(WKNavigationActionPolicy))decisionHandler {
1003 if (self.isBeingDestroyed) {
1004 decisionHandler(WKNavigationActionPolicyCancel);
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
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));
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.
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];
1074 [self registerLoadRequest:webViewURL];
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);
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];
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];
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)
1156 - (void)webView:(WKWebView *)webView
1157 didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
1159 (void (^)(NSURLSessionAuthChallengeDisposition disposition,
1160 NSURLCredential *credential))completionHandler {
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)) :
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;
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:
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();
1217 - (void)webView:(WKWebView*)webView
1218 runJavaScriptConfirmPanelWithMessage:(NSString*)message
1219 initiatedByFrame:(WKFrameInfo*)frame
1221 (void (^)(BOOL result))completionHandler {
1222 SEL confirmationSelector = @selector(webController:
1223 runJavaScriptConfirmPanelWithMessage:
1225 completionHandler:);
1226 if ([self.UIDelegate respondsToSelector:confirmationSelector]) {
1227 [self.UIDelegate webController:self
1228 runJavaScriptConfirmPanelWithMessage:message
1230 net::GURLWithNSURL(frame.request.URL)
1231 completionHandler:completionHandler];
1232 } else if (completionHandler) {
1233 completionHandler(NO);
1237 - (void)webView:(WKWebView*)webView
1238 runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt
1239 defaultText:(NSString*)defaultText
1240 initiatedByFrame:(WKFrameInfo*)frame
1242 (void (^)(NSString *result))completionHandler {
1243 SEL textInputSelector = @selector(webController:
1244 runJavaScriptTextInputPanelWithPrompt:
1247 completionHandler:);
1248 if ([self.UIDelegate respondsToSelector:textInputSelector]) {
1249 [self.UIDelegate webController:self
1250 runJavaScriptTextInputPanelWithPrompt:prompt
1251 placeholderText:defaultText
1253 net::GURLWithNSURL(frame.request.URL)
1254 completionHandler:completionHandler];
1255 } else if (completionHandler) {
1256 completionHandler(nil);