1 // Copyright 2015 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/wk_web_view_configuration_provider.h"
7 #import <Foundation/Foundation.h>
8 #import <objc/runtime.h>
9 #import <WebKit/WebKit.h>
11 #include "base/ios/ios_util.h"
12 #import "base/ios/weak_nsobject.h"
13 #import "base/logging.h"
14 #import "ios/web/alloc_with_zone_interceptor.h"
15 #include "ios/web/public/browser_state.h"
16 #import "ios/web/web_state/js/page_script_util.h"
17 #import "ios/web/web_state/web_view_internal_creation_util.h"
22 BOOL gAllowWKProcessPoolCreation = NO;
24 // TODO(eugenebut): Cleanup this macro, once all bots switched to iOS9 SDK
25 // (crbug.com/523365).
26 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
27 // By default WKProcessPool creation is not allowed by embedder to prevent
28 // issues with browsing data clearing. However some iOS system methods do create
29 // WKProcessPool inside, which is perfectly fine and should be allowed. This
30 // method whitelists given |klass| with given |selector|, so creation of
31 // WKProcessPool is allowed inside that selector call. This function currently
32 // supports Objective-C methods with up to 4 arguments and needs to be updated
33 // if support for more arguments is required.
34 void AllowWKProcessPoolCreation(Class klass, SEL selector) {
35 Method method = class_getInstanceMethod(klass, selector);
36 IMP originalImp = method_getImplementation(method);
37 IMP safeImp = imp_implementationWithBlock(
38 ^(id self, id arg1, id arg2, id arg3, id arg4) {
39 BOOL oldAllowWKProcessPoolCreation = gAllowWKProcessPoolCreation;
40 gAllowWKProcessPoolCreation = YES;
41 id result = originalImp(self, selector, arg1, arg2, arg3, arg4);
42 gAllowWKProcessPoolCreation = oldAllowWKProcessPoolCreation;
46 method_setImplementation(method, safeImp);
48 #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
51 @interface WKProcessPool (CRWAdditions)
54 @implementation WKProcessPool (CRWAdditions)
57 id (^allocator)(Class klass, NSZone* zone) = ^id(Class klass, NSZone* zone) {
58 if (gAllowWKProcessPoolCreation || web::IsWebViewAllocInitAllowed()) {
59 return NSAllocateObject(klass, 0, zone);
61 // You have hit this because you are trying to create a WKProcessPool
62 // directly or indirectly (f.e. by creating WKWebViewConfiguration
63 // manually). Please use GetWebViewConfiguration() to get
64 // WKWebViewConfiguration object.
68 web::AddAllocWithZoneMethod([WKProcessPool class], allocator);
70 // TODO(eugenebut): Cleanup this macro, once all bots switched to iOS9 SDK
71 // (crbug.com/523365).
72 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
73 if (!base::ios::IsRunningOnIOS9OrLater())
76 // Make sure that WKWebsiteDataStore is allowed to create WKProcessPool for
77 // internal implementation purposes.
78 AllowWKProcessPoolCreation(
79 [WKWebsiteDataStore class],
80 @selector(fetchDataRecordsOfTypes:completionHandler:completionHandler:));
81 AllowWKProcessPoolCreation(
82 [WKWebsiteDataStore class],
83 @selector(removeDataOfTypes:forDataRecords:completionHandler:));
84 AllowWKProcessPoolCreation(
85 [WKWebsiteDataStore class],
86 @selector(removeDataOfTypes:modifiedSince:completionHandler:));
87 #endif // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
92 #endif // !defined(NDEBUG)
97 // A key used to associate a WKWebViewConfigurationProvider with a BrowserState.
98 const char kWKWebViewConfigProviderKeyName[] = "wk_web_view_config_provider";
100 // Returns an autoreleased instance of WKUserScript to be added to
101 // configuration's userContentController.
102 WKUserScript* GetEarlyPageScript() {
103 return [[[WKUserScript alloc]
104 initWithSource:GetEarlyPageScript(WK_WEB_VIEW_TYPE)
105 injectionTime:WKUserScriptInjectionTimeAtDocumentStart
106 forMainFrameOnly:YES] autorelease];
112 WKWebViewConfigurationProvider&
113 WKWebViewConfigurationProvider::FromBrowserState(BrowserState* browser_state) {
114 DCHECK([NSThread isMainThread]);
115 DCHECK(browser_state);
116 if (!browser_state->GetUserData(kWKWebViewConfigProviderKeyName)) {
117 bool is_off_the_record = browser_state->IsOffTheRecord();
118 browser_state->SetUserData(
119 kWKWebViewConfigProviderKeyName,
120 new WKWebViewConfigurationProvider(is_off_the_record));
122 return *(static_cast<WKWebViewConfigurationProvider*>(
123 browser_state->GetUserData(kWKWebViewConfigProviderKeyName)));
126 WKWebViewConfigurationProvider::WKWebViewConfigurationProvider(
127 bool is_off_the_record)
128 : is_off_the_record_(is_off_the_record) {}
130 WKWebViewConfigurationProvider::~WKWebViewConfigurationProvider() {
133 WKWebViewConfiguration*
134 WKWebViewConfigurationProvider::GetWebViewConfiguration() {
135 DCHECK([NSThread isMainThread]);
136 if (!configuration_) {
137 configuration_.reset([[WKWebViewConfiguration alloc] init]);
138 // TODO(eugenebut): Cleanup this macro, once all bots switched to iOS9 SDK
139 // (crbug.com/523365).
140 #if defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
141 if (is_off_the_record_ && base::ios::IsRunningOnIOS9OrLater()) {
142 // WKWebsiteDataStore is iOS9 only.
144 setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
146 #endif // defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >=
148 // setJavaScriptCanOpenWindowsAutomatically is required to support popups.
149 [[configuration_ preferences] setJavaScriptCanOpenWindowsAutomatically:YES];
150 [[configuration_ userContentController] addUserScript:GetEarlyPageScript()];
152 // Lazily load WKProcessPool. -[[WKProcessPool alloc] init] call is not
153 // allowed except when creating config object inside this class.
154 // Unmanaged creation of WKProcessPool may lead to issues with cookie
155 // clearing and Browsing Data Partitioning implementation.
156 gAllowWKProcessPoolCreation = YES;
157 CHECK([configuration_ processPool]);
158 gAllowWKProcessPoolCreation = NO;
159 #endif // !defined(NDEBUG)
161 // Prevent callers from changing the internals of configuration.
162 return [[configuration_ copy] autorelease];
165 void WKWebViewConfigurationProvider::Purge() {
166 DCHECK([NSThread isMainThread]);
167 #if !defined(NDEBUG) || !defined(DCHECK_ALWAYS_ON) // Matches DCHECK_IS_ON.
168 base::WeakNSObject<id> weak_configuration(configuration_);
169 base::WeakNSObject<id> weak_process_pool([configuration_ processPool]);
170 #endif // !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
171 configuration_.reset();
172 // Make sure that no one retains configuration and processPool.
173 DCHECK(!weak_configuration);
174 // TODO(shreyasv): Enable this DCHECK (crbug.com/522672).
175 // DCHECK(!weak_process_pool);