Index: ios/web/web_state/ui/wk_web_view_configuration_provider.mm |
diff --git a/ios/web/web_state/ui/wk_web_view_configuration_provider.mm b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..535f8f1b0a4ebfaddba239f28bca4b29a28fc02b |
--- /dev/null |
+++ b/ios/web/web_state/ui/wk_web_view_configuration_provider.mm |
@@ -0,0 +1,113 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#import "ios/web/web_state/ui/wk_web_view_configuration_provider.h" |
+ |
+#import <Foundation/Foundation.h> |
+#import <WebKit/WebKit.h> |
+ |
+#import "base/ios/weak_nsobject.h" |
+#import "base/logging.h" |
+#include "ios/web/public/browser_state.h" |
+#import "ios/web/web_state/js/page_script_util.h" |
+#import "ios/web/web_state/ui/crw_wk_script_message_router.h" |
+ |
+// TODO(eugenebut): swizzle [[WKWebViewConfiguration alloc] init] to make sure |
+// that WKWebViewConfiguration is obtained only from provider and can not be |
+// created by alloc/init call. |
+ |
+namespace web { |
+ |
+namespace { |
+// A key used to associate a WKWebViewConfigurationProvider with a BrowserState. |
+const char kWKWebViewConfigProviderKeyName[] = "wk_web_view_config_provider"; |
+ |
+// Returns an autoreleased instance of WKUserScript to be added to |
+// configuration's userContentController. |
+WKUserScript* GetEarlyPageScript() { |
+ // Content of WKUserScript can be injected into the same page multiple times |
+ // without notifying WKNavigationDelegate (e.g. after window.document.write |
+ // JavaScript call). Injecting the script one more time will invalidate |
+ // __gCrWeb.windowId variable and will break the ability to send messages from |
+ // JS to the native code. Wrapping injected script into "if (!injected)" check |
+ // prevents multiple injections into the same page. |
+ NSString* const kSourceTemplate = |
+ @"if (!window.__gCrWKUserScriptInjected) {" |
+ " window.__gCrWKUserScriptInjected = true; %@" |
+ "}"; |
+ NSString* earlyScript = GetEarlyPageScript(WK_WEB_VIEW_TYPE); |
+ NSString* source = [NSString stringWithFormat:kSourceTemplate, earlyScript]; |
+ return [[[WKUserScript alloc] |
+ initWithSource:source |
+ injectionTime:WKUserScriptInjectionTimeAtDocumentStart |
+ forMainFrameOnly:YES] autorelease]; |
+} |
+ |
+} // namespace |
+ |
+// static |
+WKWebViewConfigurationProvider& |
+WKWebViewConfigurationProvider::FromBrowserState(BrowserState* browser_state) { |
+ DCHECK([NSThread isMainThread]); |
+ DCHECK(browser_state); |
+ if (!browser_state->GetUserData(kWKWebViewConfigProviderKeyName)) { |
+ browser_state->SetUserData(kWKWebViewConfigProviderKeyName, |
+ new WKWebViewConfigurationProvider()); |
+ } |
+ return *(static_cast<WKWebViewConfigurationProvider*>( |
+ browser_state->GetUserData(kWKWebViewConfigProviderKeyName))); |
+} |
+ |
+WKWebViewConfigurationProvider::WKWebViewConfigurationProvider() { |
+} |
+ |
+WKWebViewConfigurationProvider::~WKWebViewConfigurationProvider() { |
+} |
+ |
+WKWebViewConfiguration* |
+WKWebViewConfigurationProvider::GetWebViewConfiguration() { |
+ DCHECK([NSThread isMainThread]); |
+ if (!configuration_) { |
+ configuration_.reset([[WKWebViewConfiguration alloc] init]); |
+ // setJavaScriptCanOpenWindowsAutomatically is required to support popups. |
+ [[configuration_ preferences] setJavaScriptCanOpenWindowsAutomatically:YES]; |
+ [[configuration_ userContentController] addUserScript:GetEarlyPageScript()]; |
+ } |
+ // Prevent callers from changing the internals of configuration. |
+ return [[configuration_ copy] autorelease]; |
+} |
+ |
+CRWWKScriptMessageRouter* |
+WKWebViewConfigurationProvider::GetScriptMessageRouter() { |
+ DCHECK([NSThread isMainThread]); |
+ if (!router_) { |
+ WKUserContentController* userContentController = |
+ [GetWebViewConfiguration() userContentController]; |
+ router_.reset([[CRWWKScriptMessageRouter alloc] |
+ initWithUserContentController:userContentController]); |
+ } |
+ return router_; |
+} |
+ |
+bool WKWebViewConfigurationProvider::HasWebViewConfiguration() const { |
+ DCHECK([NSThread isMainThread]); |
+ return configuration_; |
+} |
+ |
+void WKWebViewConfigurationProvider::Purge() { |
+ DCHECK([NSThread isMainThread]); |
+#if !defined(NDEBUG) || !defined(DCHECK_ALWAYS_ON) // Matches DCHECK_IS_ON. |
+ base::WeakNSObject<id> weak_configuration(configuration_); |
+ base::WeakNSObject<id> weak_router(router_); |
+ base::WeakNSObject<id> weak_process_pool([configuration_ processPool]); |
+#endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) |
+ configuration_.reset(); |
+ router_.reset(); |
+ // Make sure that no one retains configuration, router, processPool. |
+ DCHECK(!weak_configuration); |
+ DCHECK(!weak_router); |
+ DCHECK(!weak_process_pool); |
+} |
+ |
+} // namespace web |