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/web_view_internal_creation_util.h"
7 #import <objc/runtime.h>
8 #import <WebKit/WebKit.h>
10 #include "base/logging.h"
11 #include "base/mac/scoped_nsobject.h"
12 #import "ios/web/alloc_with_zone_interceptor.h"
13 #include "ios/web/public/active_state_manager.h"
14 #include "ios/web/public/browser_state.h"
15 #import "ios/web/public/browsing_data_partition.h"
16 #include "ios/web/public/web_client.h"
17 #import "ios/web/public/web_view_counter.h"
18 #import "ios/web/public/web_view_creation_util.h"
19 #include "ios/web/ui_web_view_util.h"
20 #import "ios/web/weak_nsobject_counter.h"
21 #import "ios/web/web_state/ui/crw_static_file_web_view.h"
22 #import "ios/web/web_state/ui/crw_ui_simple_web_view_controller.h"
23 #import "ios/web/web_state/ui/crw_wk_simple_web_view_controller.h"
24 #import "ios/web/web_state/ui/wk_web_view_configuration_provider.h"
25 #import "ios/web/web_view_counter_impl.h"
28 #import "ios/web/web_state/ui/crw_debug_web_view.h"
34 // Returns the counter of all the active WKWebViews.
35 // DEPRECATED. Please use web::WebViewCounter instead.
36 // TODO(shreyasv): Remove this once all callers have stopped using it.
38 web::WeakNSObjectCounter& GetActiveWKWebViewCounter() {
39 static web::WeakNSObjectCounter active_wk_web_view_counter;
40 return active_wk_web_view_counter;
43 // Decides if web views can be created.
44 bool gAllowWebViewCreation = NO;
46 // Decides if web views are associated with an ActiveStateManager which is
48 bool gWebViewsNeedActiveStateManager = NO;
52 @interface WKWebView (CRWAdditions)
55 @implementation WKWebView (CRWAdditions)
58 id (^allocator)(Class klass, NSZone* zone) = ^id(Class klass, NSZone* zone) {
59 if (web::IsWebViewAllocInitAllowed()) {
60 return NSAllocateObject(klass, 0, zone);
62 // You have hit this because you are trying to create a WKWebView directly.
63 // Please use one of the web::CreateWKWKWebView methods that vend a
68 web::AddAllocWithZoneMethod([WKWebView class], allocator);
73 #endif // !defined(NDEBUG)
79 // Verifies the preconditions for creating a WKWebView. Must be called before
80 // a WKWebView is allocated. Not verifying the preconditions before creating
81 // a WKWebView will lead to undefined behavior.
82 void VerifyWKWebViewCreationPreConditions(
83 BrowserState* browser_state,
84 WKWebViewConfiguration* configuration) {
85 DCHECK(browser_state);
86 DCHECK(configuration);
87 DCHECK(web::BrowsingDataPartition::IsSynchronized());
88 WKWebViewConfigurationProvider& config_provider =
89 WKWebViewConfigurationProvider::FromBrowserState(browser_state);
90 DCHECK_EQ([config_provider.GetWebViewConfiguration() processPool],
91 [configuration processPool]);
94 // Called before a WKWebView is created.
95 void PreWKWebViewCreation(BrowserState* browser_state) {
96 DCHECK(browser_state);
97 DCHECK(GetWebClient());
98 GetWebClient()->PreWebViewCreation();
101 if (IsWebViewAllocInitAllowed() && gWebViewsNeedActiveStateManager) {
102 DCHECK(BrowserState::GetActiveStateManager(browser_state)->IsActive());
107 // Called after the WKWebView |web_view| is created.
108 void PostWKWebViewCreation(WKWebView* web_view, BrowserState* browser_state) {
112 GetActiveWKWebViewCounter().Insert(web_view);
115 WebViewCounterImpl* web_view_counter =
116 WebViewCounterImpl::FromBrowserState(browser_state);
117 DCHECK(web_view_counter);
118 web_view_counter->InsertWKWebView(web_view);
120 // TODO(stuartmorgan): Figure out how to make this work; two different client
121 // methods for the two web view types?
122 // web::GetWebClient()->PostWebViewCreation(result);
127 UIWebView* CreateWebView(CGRect frame,
128 NSString* request_group_id,
129 BOOL use_desktop_user_agent) {
130 web::BuildAndRegisterUserAgentForUIWebView(request_group_id,
131 use_desktop_user_agent);
132 return web::CreateWebView(frame);
135 UIWebView* CreateWebView(CGRect frame) {
136 DCHECK(web::GetWebClient());
137 web::GetWebClient()->PreWebViewCreation();
139 UIWebView* result = nil;
141 result = [[UIWebView alloc] initWithFrame:frame];
143 // TODO(eugenebut): create constant for @"LogJavascript" (crbug.com/391807).
144 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"LogJavascript"])
145 result = [[CRWDebugWebView alloc] initWithFrame:frame];
147 result = [[UIWebView alloc] initWithFrame:frame];
148 #endif // defined(NDEBUG)
150 // Disable data detector types. Safari does the same.
151 [result setDataDetectorTypes:UIDataDetectorTypeNone];
152 [result setScalesPageToFit:YES];
154 // By default UIWebView uses a very sluggish scroll speed. Set it to a more
156 result.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
158 web::GetWebClient()->PostWebViewCreation(result);
163 WKWebView* CreateWKWebView(CGRect frame,
164 WKWebViewConfiguration* configuration,
165 BrowserState* browser_state,
166 NSString* request_group_id,
167 BOOL use_desktop_user_agent) {
168 web::BuildAndRegisterUserAgentForUIWebView(request_group_id,
169 use_desktop_user_agent);
170 return CreateWKWebView(frame, configuration, browser_state);
173 WKWebView* CreateWKWebView(CGRect frame,
174 WKWebViewConfiguration* configuration,
175 BrowserState* browser_state) {
176 VerifyWKWebViewCreationPreConditions(browser_state, configuration);
178 PreWKWebViewCreation(browser_state);
180 bool previous_allow_web_view_creation_value = gAllowWebViewCreation;
181 gAllowWebViewCreation = true;
184 [[WKWebView alloc] initWithFrame:frame configuration:configuration];
186 gAllowWebViewCreation = previous_allow_web_view_creation_value;
188 PostWKWebViewCreation(result, browser_state);
193 NSUInteger GetActiveWKWebViewsCount() {
195 // This should not be used in release builds.
199 return GetActiveWKWebViewCounter().Size();
203 id<CRWSimpleWebViewController> CreateSimpleWebViewController(
205 BrowserState* browser_state,
206 WebViewType web_view_type) {
207 DCHECK(web::BrowsingDataPartition::IsSynchronized());
209 // Transparently return the correct subclass.
210 if (web_view_type == WK_WEB_VIEW_TYPE) {
211 base::scoped_nsobject<WKWebView> web_view(
212 web::CreateWKWebView(frame, browser_state));
213 return [[CRWWKSimpleWebViewController alloc] initWithWKWebView:web_view];
215 base::scoped_nsobject<UIWebView> web_view(web::CreateWebView(frame));
216 return [[CRWUISimpleWebViewController alloc] initWithUIWebView:web_view];
219 id<CRWSimpleWebViewController> CreateStaticFileSimpleWebViewController(
221 BrowserState* browser_state,
222 WebViewType web_view_type) {
223 DCHECK(web::BrowsingDataPartition::IsSynchronized());
225 // Transparently return the correct subclass.
226 if (web_view_type == WK_WEB_VIEW_TYPE) {
227 // TOOD(shreyasv): Create a new util function vending a WKWebView, wrap that
228 // now return the UIWebView version. crbug.com/403634.
230 base::scoped_nsobject<UIWebView> staticFileWebView(
231 CreateStaticFileWebView(frame, browser_state));
232 return [[CRWUISimpleWebViewController alloc]
233 initWithUIWebView:staticFileWebView];
236 UIWebView* CreateStaticFileWebView(CGRect frame, BrowserState* browser_state) {
237 DCHECK(web::GetWebClient());
238 web::GetWebClient()->PreWebViewCreation();
241 [[CRWStaticFileWebView alloc] initWithFrame:frame
242 browserState:browser_state];
244 web::GetWebClient()->PostWebViewCreation(result);
248 UIWebView* CreateStaticFileWebView() {
249 return CreateStaticFileWebView(CGRectZero, nullptr);
253 bool IsWebViewAllocInitAllowed() {
254 static dispatch_once_t once_token = 0;
255 dispatch_once(&once_token, ^{
256 DCHECK(GetWebClient());
257 gAllowWebViewCreation = GetWebClient()->AllowWebViewAllocInit();
258 if (!gAllowWebViewCreation) {
259 gWebViewsNeedActiveStateManager =
260 GetWebClient()->WebViewsNeedActiveStateManager();
263 return gAllowWebViewCreation;