blob: 7febc8d97833cf08ec112ce2f8b1627463585e2e [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"
mrefaat4aec47b2019-03-29 18:44:2119#include "ios/chrome/browser/crash_report/crash_report_helper.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3120#import "ios/chrome/browser/geolocation/omnibox_geolocation_controller.h"
Sylvain Defresnef5b8a8e2017-10-24 02:54:4421#import "ios/chrome/browser/history/history_tab_helper.h"
mrefaat3ad5e412018-08-02 20:11:3422#import "ios/chrome/browser/itunes_urls/itunes_urls_handler_tab_helper.h"
sdefresned9217bc2016-12-19 13:58:3223#include "ios/chrome/browser/pref_names.h"
Rohit Rao44f204302017-08-10 14:49:5424#include "ios/chrome/browser/prerender/preload_controller_delegate.h"
edchincd32fdf2017-10-25 12:45:4525#import "ios/chrome/browser/signin/account_consistency_service_factory.h"
sdefresne2c600c52017-04-04 16:49:5926#import "ios/chrome/browser/tabs/tab_helper_util.h"
Mark Cogan7ed77ac2019-05-28 19:01:0827#import "ios/web/public/deprecated/crw_native_content.h"
28#import "ios/web/public/deprecated/crw_native_content_holder.h"
Mohammad Refaate92aff52019-06-26 17:32:2929#import "ios/web/public/deprecated/crw_web_controller_util.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"
Yi Sua98c08fb2019-06-13 09:03:5632#include "ios/web/public/thread/web_thread.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#import "ios/web/web_state/ui/crw_web_controller.h"
37#import "net/base/mac/url_conversions.h"
sdefresned9217bc2016-12-19 13:58:3238#include "ui/base/page_transition_types.h"
39
stkhapugine757b63a2017-04-10 12:31:2740#if !defined(__has_feature) || !__has_feature(objc_arc)
41#error "This file requires ARC support."
42#endif
43
mrefaat3ad5e412018-08-02 20:11:3444using web::WebStatePolicyDecider;
45
sdefresned9217bc2016-12-19 13:58:3246namespace {
Gauthier Ambarda2d683a2019-04-16 08:56:0947
48// PrerenderFinalStatus values are used in the "Prerender.FinalStatus" histogram
49// and new values needs to be kept in sync with histogram.xml.
50enum PrerenderFinalStatus {
51 PRERENDER_FINAL_STATUS_USED = 0,
52 PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED = 12,
53 PRERENDER_FINAL_STATUS_CANCELLED = 32,
54 PRERENDER_FINAL_STATUS_MAX = 52,
55};
56
sdefresned9217bc2016-12-19 13:58:3257// Delay before starting to prerender a URL.
58const NSTimeInterval kPrerenderDelay = 0.5;
59
Rohit Rao9a8c39b2017-08-14 17:52:3060// The finch experiment to turn off prerendering as a field trial.
sdefresned9217bc2016-12-19 13:58:3261const char kTabEvictionFieldTrialName[] = "TabEviction";
62// The associated group.
63const char kPrerenderTabEvictionTrialGroup[] = "NoPrerendering";
64// The name of the histogram for recording final status (e.g. used/cancelled)
65// of prerender requests.
66const char kPrerenderFinalStatusHistogramName[] = "Prerender.FinalStatus";
Julien Brianceaub7e590ac2017-08-01 17:30:2267// The name of the histogram for recording the number of successful prerenders.
sdefresned9217bc2016-12-19 13:58:3268const char kPrerendersPerSessionCountHistogramName[] =
69 "Prerender.PrerendersPerSessionCount";
Justin Cohen118fee6c2018-12-06 19:36:5970// The name of the histogram for recording time until a successful prerender.
71const char kPrerenderStartToReleaseContentsTime[] =
72 "Prerender.PrerenderStartToReleaseContentsTime";
sdefresned9217bc2016-12-19 13:58:3273
74// Is this install selected for this particular experiment.
75bool IsPrerenderTabEvictionExperimentalGroup() {
76 base::FieldTrial* trial =
77 base::FieldTrialList::Find(kTabEvictionFieldTrialName);
78 return trial && trial->group_name() == kPrerenderTabEvictionTrialGroup;
79}
80
81} // namespace
82
Sylvain Defresne949367262018-03-02 22:33:1683@interface PreloadController (PrivateMethods)
sdefresned9217bc2016-12-19 13:58:3284
85// Returns YES if prerendering is enabled.
86- (BOOL)isPrerenderingEnabled;
87
Rohit Rao9a8c39b2017-08-14 17:52:3088// Returns YES if the |url| is valid for prerendering.
sdefresned9217bc2016-12-19 13:58:3289- (BOOL)shouldPreloadURL:(const GURL&)url;
90
91// Called to start any scheduled prerendering requests.
92- (void)startPrerender;
93
94// Destroys the preview Tab and resets |prerenderURL_| to the empty URL.
95- (void)destroyPreviewContents;
96
97// Schedules the current prerender to be cancelled during the next run of the
98// event loop.
99- (void)schedulePrerenderCancel;
100
101// Removes any scheduled prerender requests and resets |scheduledURL| to the
102// empty URL.
103- (void)removeScheduledPrerenderRequests;
104
Justin Cohen118fee6c2018-12-06 19:36:59105// Records metric on a successful prerender.
106- (void)recordReleaseMetrics;
107
sdefresned9217bc2016-12-19 13:58:32108@end
109
mrefaat308f0222018-08-02 03:22:39110@interface PreloadController ()<CRWWebStateObserver,
mrefaat3ad5e412018-08-02 20:11:34111 CRWWebStatePolicyDecider,
Moe Ahmadi7431d0b2018-04-13 17:32:20112 ManageAccountsDelegate,
113 PrefObserverDelegate>
Sylvain Defresne949367262018-03-02 22:33:16114@end
115
sdefresne2c600c52017-04-04 16:49:59116@implementation PreloadController {
117 ios::ChromeBrowserState* browserState_; // Weak.
118
119 // The WebState used for prerendering.
120 std::unique_ptr<web::WebState> webState_;
121
Sylvain Defresnef5d2d952017-11-14 11:15:31122 // The WebStateDelegateBridge used to register self as a CRWWebStateDelegate
123 // with the pre-rendered WebState.
124 std::unique_ptr<web::WebStateDelegateBridge> webStateDelegate_;
125
Sylvain Defresne949367262018-03-02 22:33:16126 // The WebStateObserverBridge used to register self as a WebStateObserver
127 // with the pre-rendered WebState.
128 std::unique_ptr<web::WebStateObserverBridge> webStateObserver_;
129
sdefresne2c600c52017-04-04 16:49:59130 // The URL that is prerendered in |webState_|. This can be different from
131 // the value returned by WebState last committed navigation item, for example
132 // in cases where there was a redirect.
133 //
134 // When choosing whether or not to use a prerendered Tab,
135 // BrowserViewController compares the URL being loaded by the omnibox with the
136 // URL of the prerendered Tab. Comparing against the Tab's currently URL
137 // could return false negatives in cases of redirect, hence the need to store
138 // the originally prerendered URL.
139 GURL prerenderedURL_;
140
141 // The URL that is scheduled to be prerendered, its associated transition and
142 // referrer. |scheduledTransition_| and |scheduledReferrer_| are not valid
143 // when |scheduledURL_| is empty.
144 GURL scheduledURL_;
145 ui::PageTransition scheduledTransition_;
146 web::Referrer scheduledReferrer_;
147
sdefresne2c600c52017-04-04 16:49:59148 // Bridge to listen to pref changes.
149 std::unique_ptr<PrefObserverBridge> observerBridge_;
150 // Registrar for pref changes notifications.
151 PrefChangeRegistrar prefChangeRegistrar_;
152 // Observer for the WWAN setting. Contains a valid object only if the
153 // instant setting is set to wifi-only.
154 std::unique_ptr<ConnectionTypeObserverBridge> connectionTypeObserverBridge_;
155
156 // Whether or not the preference is enabled.
157 BOOL enabled_;
158 // Whether or not prerendering is only when on wifi.
159 BOOL wifiOnly_;
160 // Whether or not the current connection is using WWAN.
161 BOOL usingWWAN_;
162
163 // Number of successful prerenders (i.e. the user viewed the prerendered page)
164 // during the lifetime of this controller.
165 int successfulPrerendersPerSessionCount_;
mrefaat3ad5e412018-08-02 20:11:34166
Justin Cohen118fee6c2018-12-06 19:36:59167 // Tracks the last time of the last attempt to load a |prerenderedURL_|. Used
168 // for UMA reporting of load durations.
169 base::TimeTicks startTime_;
170
mrefaat3ad5e412018-08-02 20:11:34171 // Bridge to provide navigation policies for |webState_|.
172 std::unique_ptr<web::WebStatePolicyDeciderBridge> policyDeciderBridge_;
sdefresne2c600c52017-04-04 16:49:59173}
sdefresned9217bc2016-12-19 13:58:32174
175@synthesize prerenderedURL = prerenderedURL_;
sdefresned9217bc2016-12-19 13:58:32176@synthesize delegate = delegate_;
177
178- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
179 DCHECK(browserState);
180 DCHECK_CURRENTLY_ON(web::WebThread::UI);
181 if ((self = [super init])) {
182 browserState_ = browserState;
183 enabled_ =
184 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
185 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
186 prefs::kNetworkPredictionWifiOnly);
187 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
188 net::NetworkChangeNotifier::GetConnectionType());
Sylvain Defresne949367262018-03-02 22:33:16189 webStateDelegate_ = std::make_unique<web::WebStateDelegateBridge>(self);
190 webStateObserver_ = std::make_unique<web::WebStateObserverBridge>(self);
191 observerBridge_ = std::make_unique<PrefObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32192 prefChangeRegistrar_.Init(browserState_->GetPrefs());
193 observerBridge_->ObserveChangesForPreference(
194 prefs::kNetworkPredictionEnabled, &prefChangeRegistrar_);
195 observerBridge_->ObserveChangesForPreference(
196 prefs::kNetworkPredictionWifiOnly, &prefChangeRegistrar_);
197 if (enabled_ && wifiOnly_) {
Sylvain Defresne949367262018-03-02 22:33:16198 connectionTypeObserverBridge_ =
199 std::make_unique<ConnectionTypeObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32200 }
201
202 [[NSNotificationCenter defaultCenter]
203 addObserver:self
204 selector:@selector(didReceiveMemoryWarning)
205 name:UIApplicationDidReceiveMemoryWarningNotification
206 object:nil];
207 }
208 return self;
209}
210
michaeldobc2f42e2017-01-12 19:04:47211- (void)browserStateDestroyed {
212 [self cancelPrerender];
213 connectionTypeObserverBridge_.reset();
214}
215
sdefresned9217bc2016-12-19 13:58:32216- (void)dealloc {
Steven Holte95922222018-09-14 20:06:23217 UMA_HISTOGRAM_COUNTS_1M(kPrerendersPerSessionCountHistogramName,
218 successfulPrerendersPerSessionCount_);
sdefresned9217bc2016-12-19 13:58:32219 [self cancelPrerender];
sdefresned9217bc2016-12-19 13:58:32220}
221
222- (void)prerenderURL:(const GURL&)url
223 referrer:(const web::Referrer&)referrer
224 transition:(ui::PageTransition)transition
225 immediately:(BOOL)immediately {
Rohit Rao44f204302017-08-10 14:49:54226 // TODO(crbug.com/754050): If shouldPrerenderURL returns false, should we
227 // cancel any scheduled prerender requests?
sdefresned9217bc2016-12-19 13:58:32228 if (![self isPrerenderingEnabled] || ![self shouldPreloadURL:url])
229 return;
230
231 // Ignore this request if there is already a scheduled request for the same
232 // URL; or, if there is no scheduled request, but the currently prerendered
233 // page matches this URL.
234 if (url == scheduledURL_ ||
235 (scheduledURL_.is_empty() && url == prerenderedURL_)) {
236 return;
237 }
238
239 [self removeScheduledPrerenderRequests];
240 scheduledURL_ = url;
241 scheduledTransition_ = transition;
242 scheduledReferrer_ = referrer;
243
244 NSTimeInterval delay = immediately ? 0.0 : kPrerenderDelay;
245 [self performSelector:@selector(startPrerender)
246 withObject:nil
247 afterDelay:delay];
248}
249
sdefresned9217bc2016-12-19 13:58:32250- (void)cancelPrerender {
251 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_CANCELLED];
252}
253
254- (void)cancelPrerenderForReason:(PrerenderFinalStatus)reason {
255 [self removeScheduledPrerenderRequests];
256 [self destroyPreviewContentsForReason:reason];
257}
258
Sylvain Defresnef5d2d952017-11-14 11:15:31259- (BOOL)isWebStatePrerendered:(web::WebState*)webState {
260 return webState && webState_.get() == webState;
261}
262
sdefresne2c600c52017-04-04 16:49:59263- (std::unique_ptr<web::WebState>)releasePrerenderContents {
sdefresned9217bc2016-12-19 13:58:32264 successfulPrerendersPerSessionCount_++;
Justin Cohen118fee6c2018-12-06 19:36:59265 [self recordReleaseMetrics];
sdefresned9217bc2016-12-19 13:58:32266 [self removeScheduledPrerenderRequests];
267 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59268 startTime_ = base::TimeTicks();
Sylvain Defresnef5d2d952017-11-14 11:15:31269 if (!webState_)
270 return nullptr;
Sylvain Defresnef5b8a8e2017-10-24 02:54:44271
Sylvain Defresnef5d2d952017-11-14 11:15:31272 // Move the pre-rendered WebState to a local variable so that it will no
273 // longer be considered as pre-rendering (otherwise tab helpers may early
274 // exist when invoked).
275 std::unique_ptr<web::WebState> webState = std::move(webState_);
276 DCHECK(![self isWebStatePrerendered:webState.get()]);
edchincd32fdf2017-10-25 12:45:45277
Mohammad Refaate92aff52019-06-26 17:32:29278 web_deprecated::SetNativeProvider(webState.get(), nil);
Sylvain Defresne949367262018-03-02 22:33:16279 webState->RemoveObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21280 breakpad::StopMonitoringURLsForWebState(webState.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31281 webState->SetDelegate(nullptr);
mrefaat3ad5e412018-08-02 20:11:34282 policyDeciderBridge_.reset();
Gauthier Ambard5144bfbd2017-11-15 10:07:52283 HistoryTabHelper::FromWebState(webState.get())
Sylvain Defresnef5d2d952017-11-14 11:15:31284 ->SetDelayHistoryServiceNotification(false);
285
286 if (AccountConsistencyService* accountConsistencyService =
287 ios::AccountConsistencyServiceFactory::GetForBrowserState(
288 browserState_)) {
Gauthier Ambard5144bfbd2017-11-15 10:07:52289 accountConsistencyService->RemoveWebStateHandler(webState.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31290 }
291
mrefaata4bdb95b2018-12-04 18:36:10292 if (!webState->IsLoading()) {
mrefaatc977e372019-02-04 22:19:18293 [[OmniboxGeolocationController sharedInstance]
294 finishPageLoadForWebState:webState.get()
295 loadSuccess:YES];
sdefresne2c600c52017-04-04 16:49:59296 }
297
Sylvain Defresnef5d2d952017-11-14 11:15:31298 return webState;
sdefresned9217bc2016-12-19 13:58:32299}
300
301- (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type {
302 DCHECK_CURRENTLY_ON(web::WebThread::UI);
303 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(type);
304 if (wifiOnly_ && usingWWAN_)
305 [self cancelPrerender];
306}
307
308- (void)onPreferenceChanged:(const std::string&)preferenceName {
309 if (preferenceName == prefs::kNetworkPredictionEnabled ||
310 preferenceName == prefs::kNetworkPredictionWifiOnly) {
311 DCHECK_CURRENTLY_ON(web::WebThread::UI);
312 // The logic is simpler if both preferences changes are handled equally.
313 enabled_ =
314 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
315 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
316 prefs::kNetworkPredictionWifiOnly);
317
318 if (wifiOnly_ && enabled_) {
319 if (!connectionTypeObserverBridge_.get()) {
320 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
321 net::NetworkChangeNotifier::GetConnectionType());
322 connectionTypeObserverBridge_.reset(
323 new ConnectionTypeObserverBridge(self));
324 }
325 if (usingWWAN_) {
326 [self cancelPrerender];
327 }
328 } else if (enabled_) {
329 connectionTypeObserverBridge_.reset();
330 } else {
331 [self cancelPrerender];
332 connectionTypeObserverBridge_.reset();
333 }
334 }
335}
336
337- (void)didReceiveMemoryWarning {
338 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED];
339}
340
341#pragma mark -
342#pragma mark CRWNativeContentProvider implementation
343
sdefresned9217bc2016-12-19 13:58:32344- (BOOL)hasControllerForURL:(const GURL&)url {
sdefresne2c600c52017-04-04 16:49:59345 if (!webState_)
346 return NO;
rohitraoeeb5293b2017-06-15 14:40:02347
348 return [delegate_ preloadHasNativeControllerForURL:url];
sdefresned9217bc2016-12-19 13:58:32349}
350
351// Override the CRWNativeContentProvider methods to cancel any prerenders that
352// require native content.
olivierrobind43eecb2017-01-27 20:35:26353- (id<CRWNativeContent>)controllerForURL:(const GURL&)url
354 webState:(web::WebState*)webState {
sdefresned9217bc2016-12-19 13:58:32355 [self schedulePrerenderCancel];
356 return nil;
357}
358
Kurt Horimotoa36505e2018-12-22 04:03:10359- (UIEdgeInsets)nativeContentInsetForWebState:(web::WebState*)webState {
360 // |-controllerForURL:webState:| short-circuits the native controller
361 // presentation flow, so the insets are never used.
362 return UIEdgeInsetsZero;
Eugene But00fcaa82018-04-13 20:03:57363}
364
sdefresned9217bc2016-12-19 13:58:32365#pragma mark -
366#pragma mark Private Methods
367
368- (BOOL)isPrerenderingEnabled {
369 DCHECK_CURRENTLY_ON(web::WebThread::UI);
370 return !IsPrerenderTabEvictionExperimentalGroup() && enabled_ &&
371 !ios::device_util::IsSingleCoreDevice() &&
372 ios::device_util::RamIsAtLeast512Mb() && (!wifiOnly_ || !usingWWAN_);
373}
374
sdefresned9217bc2016-12-19 13:58:32375- (BOOL)shouldPreloadURL:(const GURL&)url {
376 return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme);
377}
378
379- (void)startPrerender {
380 // Destroy any existing prerenders before starting a new one.
381 [self destroyPreviewContents];
382 prerenderedURL_ = scheduledURL_;
383 scheduledURL_ = GURL();
384
385 DCHECK(prerenderedURL_.is_valid());
386 if (!prerenderedURL_.is_valid()) {
387 [self destroyPreviewContents];
388 return;
389 }
390
sdefresne2c600c52017-04-04 16:49:59391 web::WebState::CreateParams createParams(browserState_);
392 webState_ = web::WebState::Create(createParams);
mrefaat3ad5e412018-08-02 20:11:34393 // Add the preload controller as a policyDecider before other tab helpers, so
394 // that it can block the navigation if needed before other policy deciders
395 // execute thier side effects (eg. AppLauncherTabHelper launching app).
396 policyDeciderBridge_ =
397 std::make_unique<web::WebStatePolicyDeciderBridge>(webState_.get(), self);
Sylvain Defresne17b8aa42017-12-21 16:17:17398 AttachTabHelpers(webState_.get(), /*for_prerender=*/true);
sdefresned9217bc2016-12-19 13:58:32399
Mohammad Refaate92aff52019-06-26 17:32:29400 web_deprecated::SetNativeProvider(webState_.get(), nil);
Sylvain Defresne949367262018-03-02 22:33:16401
Sylvain Defresnef5d2d952017-11-14 11:15:31402 webState_->SetDelegate(webStateDelegate_.get());
Sylvain Defresne949367262018-03-02 22:33:16403 webState_->AddObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21404 breakpad::MonitorURLsForWebState(webState_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31405 webState_->SetWebUsageEnabled(true);
Sylvain Defresne949367262018-03-02 22:33:16406
edchincd32fdf2017-10-25 12:45:45407 if (AccountConsistencyService* accountConsistencyService =
408 ios::AccountConsistencyServiceFactory::GetForBrowserState(
409 browserState_)) {
410 accountConsistencyService->SetWebStateHandler(webState_.get(), self);
411 }
sdefresne2c600c52017-04-04 16:49:59412
Sylvain Defresnef5b8a8e2017-10-24 02:54:44413 HistoryTabHelper::FromWebState(webState_.get())
414 ->SetDelayHistoryServiceNotification(true);
415
sdefresne2c600c52017-04-04 16:49:59416 web::NavigationManager::WebLoadParams loadParams(prerenderedURL_);
417 loadParams.referrer = scheduledReferrer_;
418 loadParams.transition_type = scheduledTransition_;
rohitraoeeb5293b2017-06-15 14:40:02419 if ([delegate_ preloadShouldUseDesktopUserAgent]) {
sdefresne2c600c52017-04-04 16:49:59420 loadParams.user_agent_override_option =
421 web::NavigationManager::UserAgentOverrideOption::DESKTOP;
422 }
Justin Cohen43fcdec2019-03-27 11:58:45423 webState_->SetKeepRenderProcessAlive(true);
sdefresne7d699dd2017-04-05 13:05:23424 webState_->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresned9217bc2016-12-19 13:58:32425
Sylvain Defresne83c32cb2018-03-01 11:03:35426 // LoadIfNecessary is needed because the view is not created (but needed) when
427 // loading the page. TODO(crbug.com/705819): Remove this call.
428 webState_->GetNavigationManager()->LoadIfNecessary();
Justin Cohen118fee6c2018-12-06 19:36:59429
430 startTime_ = base::TimeTicks::Now();
sdefresned9217bc2016-12-19 13:58:32431}
432
sdefresned9217bc2016-12-19 13:58:32433- (void)destroyPreviewContents {
434 [self destroyPreviewContentsForReason:PRERENDER_FINAL_STATUS_CANCELLED];
435}
436
437- (void)destroyPreviewContentsForReason:(PrerenderFinalStatus)reason {
sdefresne2c600c52017-04-04 16:49:59438 if (!webState_)
sdefresned9217bc2016-12-19 13:58:32439 return;
440
441 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason,
442 PRERENDER_FINAL_STATUS_MAX);
sdefresne2c600c52017-04-04 16:49:59443
Mohammad Refaate92aff52019-06-26 17:32:29444 web_deprecated::SetNativeProvider(webState_.get(), nil);
Sylvain Defresne949367262018-03-02 22:33:16445 webState_->RemoveObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21446 breakpad::StopMonitoringURLsForWebState(webState_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31447 webState_->SetDelegate(nullptr);
sdefresne2c600c52017-04-04 16:49:59448 webState_.reset();
449
sdefresned9217bc2016-12-19 13:58:32450 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59451 startTime_ = base::TimeTicks();
sdefresned9217bc2016-12-19 13:58:32452}
453
454- (void)schedulePrerenderCancel {
Rohit Rao44f204302017-08-10 14:49:54455 // TODO(crbug.com/228550): Instead of cancelling the prerender, should we mark
456 // it as failed instead? That way, subsequent prerender requests for the same
457 // URL will not kick off new prerenders.
sdefresned9217bc2016-12-19 13:58:32458 [self removeScheduledPrerenderRequests];
459 [self performSelector:@selector(cancelPrerender) withObject:nil afterDelay:0];
460}
461
462- (void)removeScheduledPrerenderRequests {
463 [NSObject cancelPreviousPerformRequestsWithTarget:self];
464 scheduledURL_ = GURL();
465}
466
Sylvain Defresnef5d2d952017-11-14 11:15:31467#pragma mark - CRWWebStateDelegate
468
Eugene But6c41f5e2018-11-09 00:39:32469- (web::WebState*)webState:(web::WebState*)webState
470 createNewWebStateForURL:(const GURL&)URL
471 openerURL:(const GURL&)openerURL
472 initiatedByUser:(BOOL)initiatedByUser {
473 DCHECK([self isWebStatePrerendered:webState]);
474 [self schedulePrerenderCancel];
475 return nil;
476}
477
478- (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
479 (web::WebState*)webState {
480 DCHECK([self isWebStatePrerendered:webState]);
481 [self schedulePrerenderCancel];
482 return nullptr;
483}
484
Sylvain Defresnef5d2d952017-11-14 11:15:31485- (void)webState:(web::WebState*)webState
486 didRequestHTTPAuthForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
487 proposedCredential:(NSURLCredential*)proposedCredential
488 completionHandler:(void (^)(NSString* username,
489 NSString* password))handler {
490 DCHECK([self isWebStatePrerendered:webState]);
491 [self schedulePrerenderCancel];
492 if (handler) {
493 handler(nil, nil);
494 }
495}
496
Justin Cohen118fee6c2018-12-06 19:36:59497- (void)recordReleaseMetrics {
498 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName,
499 PRERENDER_FINAL_STATUS_USED,
500 PRERENDER_FINAL_STATUS_MAX);
501
502 DCHECK_NE(base::TimeTicks(), startTime_);
503 UMA_HISTOGRAM_TIMES(kPrerenderStartToReleaseContentsTime,
504 base::TimeTicks::Now() - startTime_);
505}
506
Sylvain Defresne949367262018-03-02 22:33:16507#pragma mark - CRWWebStateObserver
sdefresned9217bc2016-12-19 13:58:32508
Sylvain Defresne949367262018-03-02 22:33:16509- (void)webState:(web::WebState*)webState
Sylvain Defresne949367262018-03-02 22:33:16510 didLoadPageWithSuccess:(BOOL)loadSuccess {
511 DCHECK_EQ(webState, webState_.get());
512 // Cancel prerendering if response is "application/octet-stream". It can be a
513 // video file which should not be played from preload tab. See issue at
514 // http://crbug.com/436813 for more details.
515 const std::string& mimeType = webState->GetContentsMimeType();
516 if (mimeType == "application/octet-stream")
517 [self schedulePrerenderCancel];
518}
519
edchincd32fdf2017-10-25 12:45:45520#pragma mark - ManageAccountsDelegate
521
522- (void)onManageAccounts {
Sylvain Defresne949367262018-03-02 22:33:16523 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45524}
525
526- (void)onAddAccount {
Sylvain Defresne949367262018-03-02 22:33:16527 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45528}
529
530- (void)onGoIncognito:(const GURL&)url {
Sylvain Defresne949367262018-03-02 22:33:16531 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45532}
533
mrefaat3ad5e412018-08-02 20:11:34534#pragma mark - CRWWebStatePolicyDecider
535
536- (BOOL)shouldAllowRequest:(NSURLRequest*)request
537 requestInfo:(const WebStatePolicyDecider::RequestInfo&)info {
538 GURL requestURL = net::GURLWithNSURL(request.URL);
539 // Don't allow preloading for requests that are handled by opening another
540 // application or by presenting a native UI.
541 if (AppLauncherTabHelper::IsAppUrl(requestURL) ||
542 ITunesUrlsHandlerTabHelper::CanHandleUrl(requestURL)) {
543 [self schedulePrerenderCancel];
544 return NO;
545 }
546 return YES;
547}
sdefresned9217bc2016-12-19 13:58:32548@end