Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ios / web / web_state / ui / wk_web_view_configuration_provider.mm
blobd50a6252c2224016b6d4f9cbd4ecaf99b23924d4
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"
19 #if !defined(NDEBUG)
21 namespace {
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;
43         return result;
44       });
46   method_setImplementation(method, safeImp);
48 #endif  // __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
51 @interface WKProcessPool (CRWAdditions)
52 @end
54 @implementation WKProcessPool (CRWAdditions)
56 + (void)load {
57   id (^allocator)(Class klass, NSZone* zone) = ^id(Class klass, NSZone* zone) {
58     if (gAllowWKProcessPoolCreation || web::IsWebViewAllocInitAllowed()) {
59       return NSAllocateObject(klass, 0, zone);
60     }
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.
65     NOTREACHED();
66     return nil;
67   };
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())
74     return;
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
90 @end
92 #endif  // !defined(NDEBUG)
94 namespace web {
96 namespace {
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];
109 }  // namespace
111 // static
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));
121   }
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.
143       [configuration_
144           setWebsiteDataStore:[WKWebsiteDataStore nonPersistentDataStore]];
145     }
146 #endif  // defined(__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >=
147         // __IPHONE_9_0
148     // setJavaScriptCanOpenWindowsAutomatically is required to support popups.
149     [[configuration_ preferences] setJavaScriptCanOpenWindowsAutomatically:YES];
150     [[configuration_ userContentController] addUserScript:GetEarlyPageScript()];
151 #if !defined(NDEBUG)
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)
160   }
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);
178 }  // namespace web