blob: d68a65d58a443d8b6bd78a826a793fb07609f632 [file] [log] [blame]
sdefresned9217bc2016-12-19 13:58:321// Copyright 2012 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.
4
5#import <UIKit/UIKit.h>
6
Rohit Rao44f204302017-08-10 14:49:547#include "ios/chrome/browser/prerender/preload_controller.h"
sdefresned9217bc2016-12-19 13:58:328
9#include "base/ios/device_util.h"
10#include "base/logging.h"
11#include "base/metrics/field_trial.h"
asvitkinef1899e32017-01-27 16:30:2912#include "base/metrics/histogram_macros.h"
sdefresned9217bc2016-12-19 13:58:3213#include "base/strings/sys_string_conversions.h"
Moe Ahmadi7431d0b2018-04-13 17:32:2014#import "components/prefs/ios/pref_observer_bridge.h"
sdefresned9217bc2016-12-19 13:58:3215#include "components/prefs/pref_service.h"
edchincd32fdf2017-10-25 12:45:4516#import "components/signin/ios/browser/account_consistency_service.h"
mrefaat3ad5e412018-08-02 20:11:3417#import "ios/chrome/browser/app_launcher/app_launcher_tab_helper.h"
sdefresned9217bc2016-12-19 13:58:3218#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3119#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
Sylvain Defresnef5b8a8e2017-10-24 02:54:4420#import "ios/chrome/browser/history/history_tab_helper.h"
mrefaat3ad5e412018-08-02 20:11:3421#import "ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper.h"
sdefresned9217bc2016-12-19 13:58:3222#include "ios/chrome/browser/pref_names.h"
Rohit Rao44f204302017-08-10 14:49:5423#include "ios/chrome/browser/prerender/preload_controller_delegate.h"
edchincd32fdf2017-10-25 12:45:4524#import "ios/chrome/browser/signin/account_consistency_service_factory.h"
sdefresne2c600c52017-04-04 16:49:5925#import "ios/chrome/browser/tabs/legacy_tab_helper.h"
sdefresned9217bc2016-12-19 13:58:3226#import "ios/chrome/browser/tabs/tab.h"
sdefresne2c600c52017-04-04 16:49:5927#import "ios/chrome/browser/tabs/tab_helper_util.h"
28#import "ios/chrome/browser/tabs/tab_private.h"
sdefresned9217bc2016-12-19 13:58:3229#include "ios/chrome/browser/ui/prerender_final_status.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3130#import "ios/web/public/navigation_item.h"
Gregory Chatzinoff0c2d0a62017-08-25 01:54:3731#import "ios/web/public/navigation_manager.h"
sdefresned9217bc2016-12-19 13:58:3232#import "ios/web/public/web_state/ui/crw_native_content.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3133#import "ios/web/public/web_state/web_state.h"
Sylvain Defresne949367262018-03-02 22:33:1634#include "ios/web/public/web_state/web_state_observer_bridge.h"
mrefaat3ad5e412018-08-02 20:11:3435#import "ios/web/public/web_state/web_state_policy_decider_bridge.h"
sdefresned9217bc2016-12-19 13:58:3236#include "ios/web/public/web_thread.h"
37#import "ios/web/web_state/ui/crw_web_controller.h"
38#import "net/base/mac/url_conversions.h"
sdefresned9217bc2016-12-19 13:58:3239#include "ui/base/page_transition_types.h"
40
stkhapugine757b63a2017-04-10 12:31:2741#if !defined(__has_feature) || !__has_feature(objc_arc)
42#error "This file requires ARC support."
43#endif
44
mrefaat3ad5e412018-08-02 20:11:3445using web::WebStatePolicyDecider;
46
sdefresned9217bc2016-12-19 13:58:3247namespace {
48// Delay before starting to prerender a URL.
49const NSTimeInterval kPrerenderDelay = 0.5;
50
Rohit Rao9a8c39b2017-08-14 17:52:3051// The finch experiment to turn off prerendering as a field trial.
sdefresned9217bc2016-12-19 13:58:3252const char kTabEvictionFieldTrialName[] = "TabEviction";
53// The associated group.
54const char kPrerenderTabEvictionTrialGroup[] = "NoPrerendering";
55// The name of the histogram for recording final status (e.g. used/cancelled)
56// of prerender requests.
57const char kPrerenderFinalStatusHistogramName[] = "Prerender.FinalStatus";
Julien Brianceaub7e590ac2017-08-01 17:30:2258// The name of the histogram for recording the number of successful prerenders.
sdefresned9217bc2016-12-19 13:58:3259const char kPrerendersPerSessionCountHistogramName[] =
60 "Prerender.PrerendersPerSessionCount";
Justin Cohen118fee6c2018-12-06 19:36:5961// The name of the histogram for recording time until a successful prerender.
62const char kPrerenderStartToReleaseContentsTime[] =
63 "Prerender.PrerenderStartToReleaseContentsTime";
sdefresned9217bc2016-12-19 13:58:3264
65// Is this install selected for this particular experiment.
66bool IsPrerenderTabEvictionExperimentalGroup() {
67 base::FieldTrial* trial =
68 base::FieldTrialList::Find(kTabEvictionFieldTrialName);
69 return trial && trial->group_name() == kPrerenderTabEvictionTrialGroup;
70}
71
72} // namespace
73
Sylvain Defresne949367262018-03-02 22:33:1674@interface PreloadController (PrivateMethods)
sdefresned9217bc2016-12-19 13:58:3275
76// Returns YES if prerendering is enabled.
77- (BOOL)isPrerenderingEnabled;
78
Rohit Rao9a8c39b2017-08-14 17:52:3079// Returns YES if the |url| is valid for prerendering.
sdefresned9217bc2016-12-19 13:58:3280- (BOOL)shouldPreloadURL:(const GURL&)url;
81
82// Called to start any scheduled prerendering requests.
83- (void)startPrerender;
84
85// Destroys the preview Tab and resets |prerenderURL_| to the empty URL.
86- (void)destroyPreviewContents;
87
88// Schedules the current prerender to be cancelled during the next run of the
89// event loop.
90- (void)schedulePrerenderCancel;
91
92// Removes any scheduled prerender requests and resets |scheduledURL| to the
93// empty URL.
94- (void)removeScheduledPrerenderRequests;
95
Justin Cohen118fee6c2018-12-06 19:36:5996// Records metric on a successful prerender.
97- (void)recordReleaseMetrics;
98
sdefresned9217bc2016-12-19 13:58:3299@end
100
mrefaat308f0222018-08-02 03:22:39101@interface PreloadController ()<CRWWebStateObserver,
mrefaat3ad5e412018-08-02 20:11:34102 CRWWebStatePolicyDecider,
Moe Ahmadi7431d0b2018-04-13 17:32:20103 ManageAccountsDelegate,
104 PrefObserverDelegate>
Sylvain Defresne949367262018-03-02 22:33:16105@end
106
sdefresne2c600c52017-04-04 16:49:59107@implementation PreloadController {
108 ios::ChromeBrowserState* browserState_; // Weak.
109
110 // The WebState used for prerendering.
111 std::unique_ptr<web::WebState> webState_;
112
Sylvain Defresnef5d2d952017-11-14 11:15:31113 // The WebStateDelegateBridge used to register self as a CRWWebStateDelegate
114 // with the pre-rendered WebState.
115 std::unique_ptr<web::WebStateDelegateBridge> webStateDelegate_;
116
Sylvain Defresne949367262018-03-02 22:33:16117 // The WebStateObserverBridge used to register self as a WebStateObserver
118 // with the pre-rendered WebState.
119 std::unique_ptr<web::WebStateObserverBridge> webStateObserver_;
120
sdefresne2c600c52017-04-04 16:49:59121 // The URL that is prerendered in |webState_|. This can be different from
122 // the value returned by WebState last committed navigation item, for example
123 // in cases where there was a redirect.
124 //
125 // When choosing whether or not to use a prerendered Tab,
126 // BrowserViewController compares the URL being loaded by the omnibox with the
127 // URL of the prerendered Tab. Comparing against the Tab's currently URL
128 // could return false negatives in cases of redirect, hence the need to store
129 // the originally prerendered URL.
130 GURL prerenderedURL_;
131
132 // The URL that is scheduled to be prerendered, its associated transition and
133 // referrer. |scheduledTransition_| and |scheduledReferrer_| are not valid
134 // when |scheduledURL_| is empty.
135 GURL scheduledURL_;
136 ui::PageTransition scheduledTransition_;
137 web::Referrer scheduledReferrer_;
138
sdefresne2c600c52017-04-04 16:49:59139 // Bridge to listen to pref changes.
140 std::unique_ptr<PrefObserverBridge> observerBridge_;
141 // Registrar for pref changes notifications.
142 PrefChangeRegistrar prefChangeRegistrar_;
143 // Observer for the WWAN setting. Contains a valid object only if the
144 // instant setting is set to wifi-only.
145 std::unique_ptr<ConnectionTypeObserverBridge> connectionTypeObserverBridge_;
146
147 // Whether or not the preference is enabled.
148 BOOL enabled_;
149 // Whether or not prerendering is only when on wifi.
150 BOOL wifiOnly_;
151 // Whether or not the current connection is using WWAN.
152 BOOL usingWWAN_;
153
154 // Number of successful prerenders (i.e. the user viewed the prerendered page)
155 // during the lifetime of this controller.
156 int successfulPrerendersPerSessionCount_;
mrefaat3ad5e412018-08-02 20:11:34157
Justin Cohen118fee6c2018-12-06 19:36:59158 // Tracks the last time of the last attempt to load a |prerenderedURL_|. Used
159 // for UMA reporting of load durations.
160 base::TimeTicks startTime_;
161
mrefaat3ad5e412018-08-02 20:11:34162 // Bridge to provide navigation policies for |webState_|.
163 std::unique_ptr<web::WebStatePolicyDeciderBridge> policyDeciderBridge_;
sdefresne2c600c52017-04-04 16:49:59164}
sdefresned9217bc2016-12-19 13:58:32165
166@synthesize prerenderedURL = prerenderedURL_;
sdefresned9217bc2016-12-19 13:58:32167@synthesize delegate = delegate_;
168
169- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
170 DCHECK(browserState);
171 DCHECK_CURRENTLY_ON(web::WebThread::UI);
172 if ((self = [super init])) {
173 browserState_ = browserState;
174 enabled_ =
175 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
176 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
177 prefs::kNetworkPredictionWifiOnly);
178 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
179 net::NetworkChangeNotifier::GetConnectionType());
Sylvain Defresne949367262018-03-02 22:33:16180 webStateDelegate_ = std::make_unique<web::WebStateDelegateBridge>(self);
181 webStateObserver_ = std::make_unique<web::WebStateObserverBridge>(self);
182 observerBridge_ = std::make_unique<PrefObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32183 prefChangeRegistrar_.Init(browserState_->GetPrefs());
184 observerBridge_->ObserveChangesForPreference(
185 prefs::kNetworkPredictionEnabled, &prefChangeRegistrar_);
186 observerBridge_->ObserveChangesForPreference(
187 prefs::kNetworkPredictionWifiOnly, &prefChangeRegistrar_);
188 if (enabled_ && wifiOnly_) {
Sylvain Defresne949367262018-03-02 22:33:16189 connectionTypeObserverBridge_ =
190 std::make_unique<ConnectionTypeObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32191 }
192
193 [[NSNotificationCenter defaultCenter]
194 addObserver:self
195 selector:@selector(didReceiveMemoryWarning)
196 name:UIApplicationDidReceiveMemoryWarningNotification
197 object:nil];
198 }
199 return self;
200}
201
michaeldobc2f42e2017-01-12 19:04:47202- (void)browserStateDestroyed {
203 [self cancelPrerender];
204 connectionTypeObserverBridge_.reset();
205}
206
sdefresned9217bc2016-12-19 13:58:32207- (void)dealloc {
Steven Holte95922222018-09-14 20:06:23208 UMA_HISTOGRAM_COUNTS_1M(kPrerendersPerSessionCountHistogramName,
209 successfulPrerendersPerSessionCount_);
sdefresned9217bc2016-12-19 13:58:32210 [self cancelPrerender];
sdefresned9217bc2016-12-19 13:58:32211}
212
213- (void)prerenderURL:(const GURL&)url
214 referrer:(const web::Referrer&)referrer
215 transition:(ui::PageTransition)transition
216 immediately:(BOOL)immediately {
Rohit Rao44f204302017-08-10 14:49:54217 // TODO(crbug.com/754050): If shouldPrerenderURL returns false, should we
218 // cancel any scheduled prerender requests?
sdefresned9217bc2016-12-19 13:58:32219 if (![self isPrerenderingEnabled] || ![self shouldPreloadURL:url])
220 return;
221
222 // Ignore this request if there is already a scheduled request for the same
223 // URL; or, if there is no scheduled request, but the currently prerendered
224 // page matches this URL.
225 if (url == scheduledURL_ ||
226 (scheduledURL_.is_empty() && url == prerenderedURL_)) {
227 return;
228 }
229
230 [self removeScheduledPrerenderRequests];
231 scheduledURL_ = url;
232 scheduledTransition_ = transition;
233 scheduledReferrer_ = referrer;
234
235 NSTimeInterval delay = immediately ? 0.0 : kPrerenderDelay;
236 [self performSelector:@selector(startPrerender)
237 withObject:nil
238 afterDelay:delay];
239}
240
sdefresned9217bc2016-12-19 13:58:32241- (void)cancelPrerender {
242 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_CANCELLED];
243}
244
245- (void)cancelPrerenderForReason:(PrerenderFinalStatus)reason {
246 [self removeScheduledPrerenderRequests];
247 [self destroyPreviewContentsForReason:reason];
248}
249
Sylvain Defresnef5d2d952017-11-14 11:15:31250- (BOOL)isWebStatePrerendered:(web::WebState*)webState {
251 return webState && webState_.get() == webState;
252}
253
sdefresne2c600c52017-04-04 16:49:59254- (std::unique_ptr<web::WebState>)releasePrerenderContents {
sdefresned9217bc2016-12-19 13:58:32255 successfulPrerendersPerSessionCount_++;
Justin Cohen118fee6c2018-12-06 19:36:59256 [self recordReleaseMetrics];
sdefresned9217bc2016-12-19 13:58:32257 [self removeScheduledPrerenderRequests];
258 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59259 startTime_ = base::TimeTicks();
Sylvain Defresnef5d2d952017-11-14 11:15:31260 if (!webState_)
261 return nullptr;
Sylvain Defresnef5b8a8e2017-10-24 02:54:44262
Sylvain Defresnef5d2d952017-11-14 11:15:31263 // Move the pre-rendered WebState to a local variable so that it will no
264 // longer be considered as pre-rendering (otherwise tab helpers may early
265 // exist when invoked).
266 std::unique_ptr<web::WebState> webState = std::move(webState_);
267 DCHECK(![self isWebStatePrerendered:webState.get()]);
edchincd32fdf2017-10-25 12:45:45268
Sylvain Defresnef5d2d952017-11-14 11:15:31269 Tab* tab = LegacyTabHelper::GetTabForWebState(webState.get());
270 [[tab webController] setNativeProvider:nil];
Sylvain Defresne949367262018-03-02 22:33:16271
Sylvain Defresne949367262018-03-02 22:33:16272 webState->RemoveObserver(webStateObserver_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31273 webState->SetDelegate(nullptr);
mrefaat3ad5e412018-08-02 20:11:34274 policyDeciderBridge_.reset();
Gauthier Ambard5144bfbd2017-11-15 10:07:52275 HistoryTabHelper::FromWebState(webState.get())
Sylvain Defresnef5d2d952017-11-14 11:15:31276 ->SetDelayHistoryServiceNotification(false);
277
278 if (AccountConsistencyService* accountConsistencyService =
279 ios::AccountConsistencyServiceFactory::GetForBrowserState(
280 browserState_)) {
Gauthier Ambard5144bfbd2017-11-15 10:07:52281 accountConsistencyService->RemoveWebStateHandler(webState.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31282 }
283
mrefaata4bdb95b2018-12-04 18:36:10284 if (!webState->IsLoading()) {
mrefaatc977e372019-02-04 22:19:18285 [[OmniboxGeolocationController sharedInstance]
286 finishPageLoadForWebState:webState.get()
287 loadSuccess:YES];
sdefresne2c600c52017-04-04 16:49:59288 }
289
Sylvain Defresnef5d2d952017-11-14 11:15:31290 return webState;
sdefresned9217bc2016-12-19 13:58:32291}
292
293- (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type {
294 DCHECK_CURRENTLY_ON(web::WebThread::UI);
295 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(type);
296 if (wifiOnly_ && usingWWAN_)
297 [self cancelPrerender];
298}
299
300- (void)onPreferenceChanged:(const std::string&)preferenceName {
301 if (preferenceName == prefs::kNetworkPredictionEnabled ||
302 preferenceName == prefs::kNetworkPredictionWifiOnly) {
303 DCHECK_CURRENTLY_ON(web::WebThread::UI);
304 // The logic is simpler if both preferences changes are handled equally.
305 enabled_ =
306 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
307 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
308 prefs::kNetworkPredictionWifiOnly);
309
310 if (wifiOnly_ && enabled_) {
311 if (!connectionTypeObserverBridge_.get()) {
312 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
313 net::NetworkChangeNotifier::GetConnectionType());
314 connectionTypeObserverBridge_.reset(
315 new ConnectionTypeObserverBridge(self));
316 }
317 if (usingWWAN_) {
318 [self cancelPrerender];
319 }
320 } else if (enabled_) {
321 connectionTypeObserverBridge_.reset();
322 } else {
323 [self cancelPrerender];
324 connectionTypeObserverBridge_.reset();
325 }
326 }
327}
328
329- (void)didReceiveMemoryWarning {
330 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED];
331}
332
333#pragma mark -
334#pragma mark CRWNativeContentProvider implementation
335
sdefresned9217bc2016-12-19 13:58:32336- (BOOL)hasControllerForURL:(const GURL&)url {
sdefresne2c600c52017-04-04 16:49:59337 if (!webState_)
338 return NO;
rohitraoeeb5293b2017-06-15 14:40:02339
340 return [delegate_ preloadHasNativeControllerForURL:url];
sdefresned9217bc2016-12-19 13:58:32341}
342
343// Override the CRWNativeContentProvider methods to cancel any prerenders that
344// require native content.
olivierrobind43eecb2017-01-27 20:35:26345- (id<CRWNativeContent>)controllerForURL:(const GURL&)url
346 webState:(web::WebState*)webState {
sdefresned9217bc2016-12-19 13:58:32347 [self schedulePrerenderCancel];
348 return nil;
349}
350
Kurt Horimotoa36505e2018-12-22 04:03:10351- (UIEdgeInsets)nativeContentInsetForWebState:(web::WebState*)webState {
352 // |-controllerForURL:webState:| short-circuits the native controller
353 // presentation flow, so the insets are never used.
354 return UIEdgeInsetsZero;
Eugene But00fcaa82018-04-13 20:03:57355}
356
sdefresned9217bc2016-12-19 13:58:32357#pragma mark -
358#pragma mark Private Methods
359
360- (BOOL)isPrerenderingEnabled {
361 DCHECK_CURRENTLY_ON(web::WebThread::UI);
362 return !IsPrerenderTabEvictionExperimentalGroup() && enabled_ &&
363 !ios::device_util::IsSingleCoreDevice() &&
364 ios::device_util::RamIsAtLeast512Mb() && (!wifiOnly_ || !usingWWAN_);
365}
366
sdefresned9217bc2016-12-19 13:58:32367- (BOOL)shouldPreloadURL:(const GURL&)url {
368 return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme);
369}
370
371- (void)startPrerender {
372 // Destroy any existing prerenders before starting a new one.
373 [self destroyPreviewContents];
374 prerenderedURL_ = scheduledURL_;
375 scheduledURL_ = GURL();
376
377 DCHECK(prerenderedURL_.is_valid());
378 if (!prerenderedURL_.is_valid()) {
379 [self destroyPreviewContents];
380 return;
381 }
382
sdefresne2c600c52017-04-04 16:49:59383 web::WebState::CreateParams createParams(browserState_);
384 webState_ = web::WebState::Create(createParams);
mrefaat3ad5e412018-08-02 20:11:34385 // Add the preload controller as a policyDecider before other tab helpers, so
386 // that it can block the navigation if needed before other policy deciders
387 // execute thier side effects (eg. AppLauncherTabHelper launching app).
388 policyDeciderBridge_ =
389 std::make_unique<web::WebStatePolicyDeciderBridge>(webState_.get(), self);
Sylvain Defresne17b8aa42017-12-21 16:17:17390 AttachTabHelpers(webState_.get(), /*for_prerender=*/true);
sdefresned9217bc2016-12-19 13:58:32391
sdefresne2c600c52017-04-04 16:49:59392 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
393 DCHECK(tab);
394
395 [[tab webController] setNativeProvider:self];
Sylvain Defresne949367262018-03-02 22:33:16396
Sylvain Defresnef5d2d952017-11-14 11:15:31397 webState_->SetDelegate(webStateDelegate_.get());
Sylvain Defresne949367262018-03-02 22:33:16398 webState_->AddObserver(webStateObserver_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31399 webState_->SetWebUsageEnabled(true);
Sylvain Defresne949367262018-03-02 22:33:16400
edchincd32fdf2017-10-25 12:45:45401 if (AccountConsistencyService* accountConsistencyService =
402 ios::AccountConsistencyServiceFactory::GetForBrowserState(
403 browserState_)) {
404 accountConsistencyService->SetWebStateHandler(webState_.get(), self);
405 }
sdefresne2c600c52017-04-04 16:49:59406
Sylvain Defresnef5b8a8e2017-10-24 02:54:44407 HistoryTabHelper::FromWebState(webState_.get())
408 ->SetDelayHistoryServiceNotification(true);
409
sdefresne2c600c52017-04-04 16:49:59410 web::NavigationManager::WebLoadParams loadParams(prerenderedURL_);
411 loadParams.referrer = scheduledReferrer_;
412 loadParams.transition_type = scheduledTransition_;
rohitraoeeb5293b2017-06-15 14:40:02413 if ([delegate_ preloadShouldUseDesktopUserAgent]) {
sdefresne2c600c52017-04-04 16:49:59414 loadParams.user_agent_override_option =
415 web::NavigationManager::UserAgentOverrideOption::DESKTOP;
416 }
Justin Cohen43fcdec2019-03-27 11:58:45417 webState_->SetKeepRenderProcessAlive(true);
sdefresne7d699dd2017-04-05 13:05:23418 webState_->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresned9217bc2016-12-19 13:58:32419
Sylvain Defresne83c32cb2018-03-01 11:03:35420 // LoadIfNecessary is needed because the view is not created (but needed) when
421 // loading the page. TODO(crbug.com/705819): Remove this call.
422 webState_->GetNavigationManager()->LoadIfNecessary();
Justin Cohen118fee6c2018-12-06 19:36:59423
424 startTime_ = base::TimeTicks::Now();
sdefresned9217bc2016-12-19 13:58:32425}
426
sdefresned9217bc2016-12-19 13:58:32427- (void)destroyPreviewContents {
428 [self destroyPreviewContentsForReason:PRERENDER_FINAL_STATUS_CANCELLED];
429}
430
431- (void)destroyPreviewContentsForReason:(PrerenderFinalStatus)reason {
sdefresne2c600c52017-04-04 16:49:59432 if (!webState_)
sdefresned9217bc2016-12-19 13:58:32433 return;
434
435 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason,
436 PRERENDER_FINAL_STATUS_MAX);
sdefresne2c600c52017-04-04 16:49:59437
438 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
439 [[tab webController] setNativeProvider:nil];
Sylvain Defresne949367262018-03-02 22:33:16440 webState_->RemoveObserver(webStateObserver_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31441 webState_->SetDelegate(nullptr);
sdefresne2c600c52017-04-04 16:49:59442 webState_.reset();
443
sdefresned9217bc2016-12-19 13:58:32444 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59445 startTime_ = base::TimeTicks();
sdefresned9217bc2016-12-19 13:58:32446}
447
448- (void)schedulePrerenderCancel {
Rohit Rao44f204302017-08-10 14:49:54449 // TODO(crbug.com/228550): Instead of cancelling the prerender, should we mark
450 // it as failed instead? That way, subsequent prerender requests for the same
451 // URL will not kick off new prerenders.
sdefresned9217bc2016-12-19 13:58:32452 [self removeScheduledPrerenderRequests];
453 [self performSelector:@selector(cancelPrerender) withObject:nil afterDelay:0];
454}
455
456- (void)removeScheduledPrerenderRequests {
457 [NSObject cancelPreviousPerformRequestsWithTarget:self];
458 scheduledURL_ = GURL();
459}
460
Sylvain Defresnef5d2d952017-11-14 11:15:31461#pragma mark - CRWWebStateDelegate
462
Eugene But6c41f5e2018-11-09 00:39:32463- (web::WebState*)webState:(web::WebState*)webState
464 createNewWebStateForURL:(const GURL&)URL
465 openerURL:(const GURL&)openerURL
466 initiatedByUser:(BOOL)initiatedByUser {
467 DCHECK([self isWebStatePrerendered:webState]);
468 [self schedulePrerenderCancel];
469 return nil;
470}
471
472- (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
473 (web::WebState*)webState {
474 DCHECK([self isWebStatePrerendered:webState]);
475 [self schedulePrerenderCancel];
476 return nullptr;
477}
478
Sylvain Defresnef5d2d952017-11-14 11:15:31479- (void)webState:(web::WebState*)webState
480 didRequestHTTPAuthForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
481 proposedCredential:(NSURLCredential*)proposedCredential
482 completionHandler:(void (^)(NSString* username,
483 NSString* password))handler {
484 DCHECK([self isWebStatePrerendered:webState]);
485 [self schedulePrerenderCancel];
486 if (handler) {
487 handler(nil, nil);
488 }
489}
490
Justin Cohen118fee6c2018-12-06 19:36:59491- (void)recordReleaseMetrics {
492 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName,
493 PRERENDER_FINAL_STATUS_USED,
494 PRERENDER_FINAL_STATUS_MAX);
495
496 DCHECK_NE(base::TimeTicks(), startTime_);
497 UMA_HISTOGRAM_TIMES(kPrerenderStartToReleaseContentsTime,
498 base::TimeTicks::Now() - startTime_);
499}
500
Sylvain Defresne949367262018-03-02 22:33:16501#pragma mark - CRWWebStateObserver
sdefresned9217bc2016-12-19 13:58:32502
Sylvain Defresne949367262018-03-02 22:33:16503- (void)webState:(web::WebState*)webState
Sylvain Defresne949367262018-03-02 22:33:16504 didLoadPageWithSuccess:(BOOL)loadSuccess {
505 DCHECK_EQ(webState, webState_.get());
506 // Cancel prerendering if response is "application/octet-stream". It can be a
507 // video file which should not be played from preload tab. See issue at
508 // http://crbug.com/436813 for more details.
509 const std::string& mimeType = webState->GetContentsMimeType();
510 if (mimeType == "application/octet-stream")
511 [self schedulePrerenderCancel];
512}
513
edchincd32fdf2017-10-25 12:45:45514#pragma mark - ManageAccountsDelegate
515
516- (void)onManageAccounts {
Sylvain Defresne949367262018-03-02 22:33:16517 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45518}
519
520- (void)onAddAccount {
Sylvain Defresne949367262018-03-02 22:33:16521 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45522}
523
524- (void)onGoIncognito:(const GURL&)url {
Sylvain Defresne949367262018-03-02 22:33:16525 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45526}
527
mrefaat3ad5e412018-08-02 20:11:34528#pragma mark - CRWWebStatePolicyDecider
529
530- (BOOL)shouldAllowRequest:(NSURLRequest*)request
531 requestInfo:(const WebStatePolicyDecider::RequestInfo&)info {
532 GURL requestURL = net::GURLWithNSURL(request.URL);
533 // Don't allow preloading for requests that are handled by opening another
534 // application or by presenting a native UI.
535 if (AppLauncherTabHelper::IsAppUrl(requestURL) ||
536 ITunesUrlsHandlerTabHelper::CanHandleUrl(requestURL)) {
537 [self schedulePrerenderCancel];
538 return NO;
539 }
540 return YES;
541}
sdefresned9217bc2016-12-19 13:58:32542@end