blob: 992122671660345ee1846b31d61ec632b8a27ff1 [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/legacy_tab_helper.h"
sdefresned9217bc2016-12-19 13:58:3227#import "ios/chrome/browser/tabs/tab.h"
sdefresne2c600c52017-04-04 16:49:5928#import "ios/chrome/browser/tabs/tab_helper_util.h"
29#import "ios/chrome/browser/tabs/tab_private.h"
sdefresned9217bc2016-12-19 13:58:3230#include "ios/chrome/browser/ui/prerender_final_status.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3131#import "ios/web/public/navigation_item.h"
Gregory Chatzinoff0c2d0a62017-08-25 01:54:3732#import "ios/web/public/navigation_manager.h"
sdefresned9217bc2016-12-19 13:58:3233#import "ios/web/public/web_state/ui/crw_native_content.h"
Sylvain Defresnef5d2d952017-11-14 11:15:3134#import "ios/web/public/web_state/web_state.h"
Sylvain Defresne949367262018-03-02 22:33:1635#include "ios/web/public/web_state/web_state_observer_bridge.h"
mrefaat3ad5e412018-08-02 20:11:3436#import "ios/web/public/web_state/web_state_policy_decider_bridge.h"
sdefresned9217bc2016-12-19 13:58:3237#include "ios/web/public/web_thread.h"
38#import "ios/web/web_state/ui/crw_web_controller.h"
39#import "net/base/mac/url_conversions.h"
sdefresned9217bc2016-12-19 13:58:3240#include "ui/base/page_transition_types.h"
41
stkhapugine757b63a2017-04-10 12:31:2742#if !defined(__has_feature) || !__has_feature(objc_arc)
43#error "This file requires ARC support."
44#endif
45
mrefaat3ad5e412018-08-02 20:11:3446using web::WebStatePolicyDecider;
47
sdefresned9217bc2016-12-19 13:58:3248namespace {
49// Delay before starting to prerender a URL.
50const NSTimeInterval kPrerenderDelay = 0.5;
51
Rohit Rao9a8c39b2017-08-14 17:52:3052// The finch experiment to turn off prerendering as a field trial.
sdefresned9217bc2016-12-19 13:58:3253const char kTabEvictionFieldTrialName[] = "TabEviction";
54// The associated group.
55const char kPrerenderTabEvictionTrialGroup[] = "NoPrerendering";
56// The name of the histogram for recording final status (e.g. used/cancelled)
57// of prerender requests.
58const char kPrerenderFinalStatusHistogramName[] = "Prerender.FinalStatus";
Julien Brianceaub7e590ac2017-08-01 17:30:2259// The name of the histogram for recording the number of successful prerenders.
sdefresned9217bc2016-12-19 13:58:3260const char kPrerendersPerSessionCountHistogramName[] =
61 "Prerender.PrerendersPerSessionCount";
Justin Cohen118fee6c2018-12-06 19:36:5962// The name of the histogram for recording time until a successful prerender.
63const char kPrerenderStartToReleaseContentsTime[] =
64 "Prerender.PrerenderStartToReleaseContentsTime";
sdefresned9217bc2016-12-19 13:58:3265
66// Is this install selected for this particular experiment.
67bool IsPrerenderTabEvictionExperimentalGroup() {
68 base::FieldTrial* trial =
69 base::FieldTrialList::Find(kTabEvictionFieldTrialName);
70 return trial && trial->group_name() == kPrerenderTabEvictionTrialGroup;
71}
72
73} // namespace
74
Sylvain Defresne949367262018-03-02 22:33:1675@interface PreloadController (PrivateMethods)
sdefresned9217bc2016-12-19 13:58:3276
77// Returns YES if prerendering is enabled.
78- (BOOL)isPrerenderingEnabled;
79
Rohit Rao9a8c39b2017-08-14 17:52:3080// Returns YES if the |url| is valid for prerendering.
sdefresned9217bc2016-12-19 13:58:3281- (BOOL)shouldPreloadURL:(const GURL&)url;
82
83// Called to start any scheduled prerendering requests.
84- (void)startPrerender;
85
86// Destroys the preview Tab and resets |prerenderURL_| to the empty URL.
87- (void)destroyPreviewContents;
88
89// Schedules the current prerender to be cancelled during the next run of the
90// event loop.
91- (void)schedulePrerenderCancel;
92
93// Removes any scheduled prerender requests and resets |scheduledURL| to the
94// empty URL.
95- (void)removeScheduledPrerenderRequests;
96
Justin Cohen118fee6c2018-12-06 19:36:5997// Records metric on a successful prerender.
98- (void)recordReleaseMetrics;
99
sdefresned9217bc2016-12-19 13:58:32100@end
101
mrefaat308f0222018-08-02 03:22:39102@interface PreloadController ()<CRWWebStateObserver,
mrefaat3ad5e412018-08-02 20:11:34103 CRWWebStatePolicyDecider,
Moe Ahmadi7431d0b2018-04-13 17:32:20104 ManageAccountsDelegate,
105 PrefObserverDelegate>
Sylvain Defresne949367262018-03-02 22:33:16106@end
107
sdefresne2c600c52017-04-04 16:49:59108@implementation PreloadController {
109 ios::ChromeBrowserState* browserState_; // Weak.
110
111 // The WebState used for prerendering.
112 std::unique_ptr<web::WebState> webState_;
113
Sylvain Defresnef5d2d952017-11-14 11:15:31114 // The WebStateDelegateBridge used to register self as a CRWWebStateDelegate
115 // with the pre-rendered WebState.
116 std::unique_ptr<web::WebStateDelegateBridge> webStateDelegate_;
117
Sylvain Defresne949367262018-03-02 22:33:16118 // The WebStateObserverBridge used to register self as a WebStateObserver
119 // with the pre-rendered WebState.
120 std::unique_ptr<web::WebStateObserverBridge> webStateObserver_;
121
sdefresne2c600c52017-04-04 16:49:59122 // The URL that is prerendered in |webState_|. This can be different from
123 // the value returned by WebState last committed navigation item, for example
124 // in cases where there was a redirect.
125 //
126 // When choosing whether or not to use a prerendered Tab,
127 // BrowserViewController compares the URL being loaded by the omnibox with the
128 // URL of the prerendered Tab. Comparing against the Tab's currently URL
129 // could return false negatives in cases of redirect, hence the need to store
130 // the originally prerendered URL.
131 GURL prerenderedURL_;
132
133 // The URL that is scheduled to be prerendered, its associated transition and
134 // referrer. |scheduledTransition_| and |scheduledReferrer_| are not valid
135 // when |scheduledURL_| is empty.
136 GURL scheduledURL_;
137 ui::PageTransition scheduledTransition_;
138 web::Referrer scheduledReferrer_;
139
sdefresne2c600c52017-04-04 16:49:59140 // Bridge to listen to pref changes.
141 std::unique_ptr<PrefObserverBridge> observerBridge_;
142 // Registrar for pref changes notifications.
143 PrefChangeRegistrar prefChangeRegistrar_;
144 // Observer for the WWAN setting. Contains a valid object only if the
145 // instant setting is set to wifi-only.
146 std::unique_ptr<ConnectionTypeObserverBridge> connectionTypeObserverBridge_;
147
148 // Whether or not the preference is enabled.
149 BOOL enabled_;
150 // Whether or not prerendering is only when on wifi.
151 BOOL wifiOnly_;
152 // Whether or not the current connection is using WWAN.
153 BOOL usingWWAN_;
154
155 // Number of successful prerenders (i.e. the user viewed the prerendered page)
156 // during the lifetime of this controller.
157 int successfulPrerendersPerSessionCount_;
mrefaat3ad5e412018-08-02 20:11:34158
Justin Cohen118fee6c2018-12-06 19:36:59159 // Tracks the last time of the last attempt to load a |prerenderedURL_|. Used
160 // for UMA reporting of load durations.
161 base::TimeTicks startTime_;
162
mrefaat3ad5e412018-08-02 20:11:34163 // Bridge to provide navigation policies for |webState_|.
164 std::unique_ptr<web::WebStatePolicyDeciderBridge> policyDeciderBridge_;
sdefresne2c600c52017-04-04 16:49:59165}
sdefresned9217bc2016-12-19 13:58:32166
167@synthesize prerenderedURL = prerenderedURL_;
sdefresned9217bc2016-12-19 13:58:32168@synthesize delegate = delegate_;
169
170- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState {
171 DCHECK(browserState);
172 DCHECK_CURRENTLY_ON(web::WebThread::UI);
173 if ((self = [super init])) {
174 browserState_ = browserState;
175 enabled_ =
176 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
177 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
178 prefs::kNetworkPredictionWifiOnly);
179 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
180 net::NetworkChangeNotifier::GetConnectionType());
Sylvain Defresne949367262018-03-02 22:33:16181 webStateDelegate_ = std::make_unique<web::WebStateDelegateBridge>(self);
182 webStateObserver_ = std::make_unique<web::WebStateObserverBridge>(self);
183 observerBridge_ = std::make_unique<PrefObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32184 prefChangeRegistrar_.Init(browserState_->GetPrefs());
185 observerBridge_->ObserveChangesForPreference(
186 prefs::kNetworkPredictionEnabled, &prefChangeRegistrar_);
187 observerBridge_->ObserveChangesForPreference(
188 prefs::kNetworkPredictionWifiOnly, &prefChangeRegistrar_);
189 if (enabled_ && wifiOnly_) {
Sylvain Defresne949367262018-03-02 22:33:16190 connectionTypeObserverBridge_ =
191 std::make_unique<ConnectionTypeObserverBridge>(self);
sdefresned9217bc2016-12-19 13:58:32192 }
193
194 [[NSNotificationCenter defaultCenter]
195 addObserver:self
196 selector:@selector(didReceiveMemoryWarning)
197 name:UIApplicationDidReceiveMemoryWarningNotification
198 object:nil];
199 }
200 return self;
201}
202
michaeldobc2f42e2017-01-12 19:04:47203- (void)browserStateDestroyed {
204 [self cancelPrerender];
205 connectionTypeObserverBridge_.reset();
206}
207
sdefresned9217bc2016-12-19 13:58:32208- (void)dealloc {
Steven Holte95922222018-09-14 20:06:23209 UMA_HISTOGRAM_COUNTS_1M(kPrerendersPerSessionCountHistogramName,
210 successfulPrerendersPerSessionCount_);
sdefresned9217bc2016-12-19 13:58:32211 [self cancelPrerender];
sdefresned9217bc2016-12-19 13:58:32212}
213
214- (void)prerenderURL:(const GURL&)url
215 referrer:(const web::Referrer&)referrer
216 transition:(ui::PageTransition)transition
217 immediately:(BOOL)immediately {
Rohit Rao44f204302017-08-10 14:49:54218 // TODO(crbug.com/754050): If shouldPrerenderURL returns false, should we
219 // cancel any scheduled prerender requests?
sdefresned9217bc2016-12-19 13:58:32220 if (![self isPrerenderingEnabled] || ![self shouldPreloadURL:url])
221 return;
222
223 // Ignore this request if there is already a scheduled request for the same
224 // URL; or, if there is no scheduled request, but the currently prerendered
225 // page matches this URL.
226 if (url == scheduledURL_ ||
227 (scheduledURL_.is_empty() && url == prerenderedURL_)) {
228 return;
229 }
230
231 [self removeScheduledPrerenderRequests];
232 scheduledURL_ = url;
233 scheduledTransition_ = transition;
234 scheduledReferrer_ = referrer;
235
236 NSTimeInterval delay = immediately ? 0.0 : kPrerenderDelay;
237 [self performSelector:@selector(startPrerender)
238 withObject:nil
239 afterDelay:delay];
240}
241
sdefresned9217bc2016-12-19 13:58:32242- (void)cancelPrerender {
243 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_CANCELLED];
244}
245
246- (void)cancelPrerenderForReason:(PrerenderFinalStatus)reason {
247 [self removeScheduledPrerenderRequests];
248 [self destroyPreviewContentsForReason:reason];
249}
250
Sylvain Defresnef5d2d952017-11-14 11:15:31251- (BOOL)isWebStatePrerendered:(web::WebState*)webState {
252 return webState && webState_.get() == webState;
253}
254
sdefresne2c600c52017-04-04 16:49:59255- (std::unique_ptr<web::WebState>)releasePrerenderContents {
sdefresned9217bc2016-12-19 13:58:32256 successfulPrerendersPerSessionCount_++;
Justin Cohen118fee6c2018-12-06 19:36:59257 [self recordReleaseMetrics];
sdefresned9217bc2016-12-19 13:58:32258 [self removeScheduledPrerenderRequests];
259 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59260 startTime_ = base::TimeTicks();
Sylvain Defresnef5d2d952017-11-14 11:15:31261 if (!webState_)
262 return nullptr;
Sylvain Defresnef5b8a8e2017-10-24 02:54:44263
Sylvain Defresnef5d2d952017-11-14 11:15:31264 // Move the pre-rendered WebState to a local variable so that it will no
265 // longer be considered as pre-rendering (otherwise tab helpers may early
266 // exist when invoked).
267 std::unique_ptr<web::WebState> webState = std::move(webState_);
268 DCHECK(![self isWebStatePrerendered:webState.get()]);
edchincd32fdf2017-10-25 12:45:45269
Sylvain Defresnef5d2d952017-11-14 11:15:31270 Tab* tab = LegacyTabHelper::GetTabForWebState(webState.get());
271 [[tab webController] setNativeProvider:nil];
Sylvain Defresne949367262018-03-02 22:33:16272
Sylvain Defresne949367262018-03-02 22:33:16273 webState->RemoveObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21274 breakpad::StopMonitoringURLsForWebState(webState.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31275 webState->SetDelegate(nullptr);
mrefaat3ad5e412018-08-02 20:11:34276 policyDeciderBridge_.reset();
Gauthier Ambard5144bfbd2017-11-15 10:07:52277 HistoryTabHelper::FromWebState(webState.get())
Sylvain Defresnef5d2d952017-11-14 11:15:31278 ->SetDelayHistoryServiceNotification(false);
279
280 if (AccountConsistencyService* accountConsistencyService =
281 ios::AccountConsistencyServiceFactory::GetForBrowserState(
282 browserState_)) {
Gauthier Ambard5144bfbd2017-11-15 10:07:52283 accountConsistencyService->RemoveWebStateHandler(webState.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31284 }
285
mrefaata4bdb95b2018-12-04 18:36:10286 if (!webState->IsLoading()) {
mrefaatc977e372019-02-04 22:19:18287 [[OmniboxGeolocationController sharedInstance]
288 finishPageLoadForWebState:webState.get()
289 loadSuccess:YES];
sdefresne2c600c52017-04-04 16:49:59290 }
291
Sylvain Defresnef5d2d952017-11-14 11:15:31292 return webState;
sdefresned9217bc2016-12-19 13:58:32293}
294
295- (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type {
296 DCHECK_CURRENTLY_ON(web::WebThread::UI);
297 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(type);
298 if (wifiOnly_ && usingWWAN_)
299 [self cancelPrerender];
300}
301
302- (void)onPreferenceChanged:(const std::string&)preferenceName {
303 if (preferenceName == prefs::kNetworkPredictionEnabled ||
304 preferenceName == prefs::kNetworkPredictionWifiOnly) {
305 DCHECK_CURRENTLY_ON(web::WebThread::UI);
306 // The logic is simpler if both preferences changes are handled equally.
307 enabled_ =
308 browserState_->GetPrefs()->GetBoolean(prefs::kNetworkPredictionEnabled);
309 wifiOnly_ = browserState_->GetPrefs()->GetBoolean(
310 prefs::kNetworkPredictionWifiOnly);
311
312 if (wifiOnly_ && enabled_) {
313 if (!connectionTypeObserverBridge_.get()) {
314 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(
315 net::NetworkChangeNotifier::GetConnectionType());
316 connectionTypeObserverBridge_.reset(
317 new ConnectionTypeObserverBridge(self));
318 }
319 if (usingWWAN_) {
320 [self cancelPrerender];
321 }
322 } else if (enabled_) {
323 connectionTypeObserverBridge_.reset();
324 } else {
325 [self cancelPrerender];
326 connectionTypeObserverBridge_.reset();
327 }
328 }
329}
330
331- (void)didReceiveMemoryWarning {
332 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED];
333}
334
335#pragma mark -
336#pragma mark CRWNativeContentProvider implementation
337
sdefresned9217bc2016-12-19 13:58:32338- (BOOL)hasControllerForURL:(const GURL&)url {
sdefresne2c600c52017-04-04 16:49:59339 if (!webState_)
340 return NO;
rohitraoeeb5293b2017-06-15 14:40:02341
342 return [delegate_ preloadHasNativeControllerForURL:url];
sdefresned9217bc2016-12-19 13:58:32343}
344
345// Override the CRWNativeContentProvider methods to cancel any prerenders that
346// require native content.
olivierrobind43eecb2017-01-27 20:35:26347- (id<CRWNativeContent>)controllerForURL:(const GURL&)url
348 webState:(web::WebState*)webState {
sdefresned9217bc2016-12-19 13:58:32349 [self schedulePrerenderCancel];
350 return nil;
351}
352
Kurt Horimotoa36505e2018-12-22 04:03:10353- (UIEdgeInsets)nativeContentInsetForWebState:(web::WebState*)webState {
354 // |-controllerForURL:webState:| short-circuits the native controller
355 // presentation flow, so the insets are never used.
356 return UIEdgeInsetsZero;
Eugene But00fcaa82018-04-13 20:03:57357}
358
sdefresned9217bc2016-12-19 13:58:32359#pragma mark -
360#pragma mark Private Methods
361
362- (BOOL)isPrerenderingEnabled {
363 DCHECK_CURRENTLY_ON(web::WebThread::UI);
364 return !IsPrerenderTabEvictionExperimentalGroup() && enabled_ &&
365 !ios::device_util::IsSingleCoreDevice() &&
366 ios::device_util::RamIsAtLeast512Mb() && (!wifiOnly_ || !usingWWAN_);
367}
368
sdefresned9217bc2016-12-19 13:58:32369- (BOOL)shouldPreloadURL:(const GURL&)url {
370 return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme);
371}
372
373- (void)startPrerender {
374 // Destroy any existing prerenders before starting a new one.
375 [self destroyPreviewContents];
376 prerenderedURL_ = scheduledURL_;
377 scheduledURL_ = GURL();
378
379 DCHECK(prerenderedURL_.is_valid());
380 if (!prerenderedURL_.is_valid()) {
381 [self destroyPreviewContents];
382 return;
383 }
384
sdefresne2c600c52017-04-04 16:49:59385 web::WebState::CreateParams createParams(browserState_);
386 webState_ = web::WebState::Create(createParams);
mrefaat3ad5e412018-08-02 20:11:34387 // Add the preload controller as a policyDecider before other tab helpers, so
388 // that it can block the navigation if needed before other policy deciders
389 // execute thier side effects (eg. AppLauncherTabHelper launching app).
390 policyDeciderBridge_ =
391 std::make_unique<web::WebStatePolicyDeciderBridge>(webState_.get(), self);
Sylvain Defresne17b8aa42017-12-21 16:17:17392 AttachTabHelpers(webState_.get(), /*for_prerender=*/true);
sdefresned9217bc2016-12-19 13:58:32393
sdefresne2c600c52017-04-04 16:49:59394 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
395 DCHECK(tab);
396
397 [[tab webController] setNativeProvider:self];
Sylvain Defresne949367262018-03-02 22:33:16398
Sylvain Defresnef5d2d952017-11-14 11:15:31399 webState_->SetDelegate(webStateDelegate_.get());
Sylvain Defresne949367262018-03-02 22:33:16400 webState_->AddObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21401 breakpad::MonitorURLsForWebState(webState_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31402 webState_->SetWebUsageEnabled(true);
Sylvain Defresne949367262018-03-02 22:33:16403
edchincd32fdf2017-10-25 12:45:45404 if (AccountConsistencyService* accountConsistencyService =
405 ios::AccountConsistencyServiceFactory::GetForBrowserState(
406 browserState_)) {
407 accountConsistencyService->SetWebStateHandler(webState_.get(), self);
408 }
sdefresne2c600c52017-04-04 16:49:59409
Sylvain Defresnef5b8a8e2017-10-24 02:54:44410 HistoryTabHelper::FromWebState(webState_.get())
411 ->SetDelayHistoryServiceNotification(true);
412
sdefresne2c600c52017-04-04 16:49:59413 web::NavigationManager::WebLoadParams loadParams(prerenderedURL_);
414 loadParams.referrer = scheduledReferrer_;
415 loadParams.transition_type = scheduledTransition_;
rohitraoeeb5293b2017-06-15 14:40:02416 if ([delegate_ preloadShouldUseDesktopUserAgent]) {
sdefresne2c600c52017-04-04 16:49:59417 loadParams.user_agent_override_option =
418 web::NavigationManager::UserAgentOverrideOption::DESKTOP;
419 }
Justin Cohen43fcdec2019-03-27 11:58:45420 webState_->SetKeepRenderProcessAlive(true);
sdefresne7d699dd2017-04-05 13:05:23421 webState_->GetNavigationManager()->LoadURLWithParams(loadParams);
sdefresned9217bc2016-12-19 13:58:32422
Sylvain Defresne83c32cb2018-03-01 11:03:35423 // LoadIfNecessary is needed because the view is not created (but needed) when
424 // loading the page. TODO(crbug.com/705819): Remove this call.
425 webState_->GetNavigationManager()->LoadIfNecessary();
Justin Cohen118fee6c2018-12-06 19:36:59426
427 startTime_ = base::TimeTicks::Now();
sdefresned9217bc2016-12-19 13:58:32428}
429
sdefresned9217bc2016-12-19 13:58:32430- (void)destroyPreviewContents {
431 [self destroyPreviewContentsForReason:PRERENDER_FINAL_STATUS_CANCELLED];
432}
433
434- (void)destroyPreviewContentsForReason:(PrerenderFinalStatus)reason {
sdefresne2c600c52017-04-04 16:49:59435 if (!webState_)
sdefresned9217bc2016-12-19 13:58:32436 return;
437
438 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason,
439 PRERENDER_FINAL_STATUS_MAX);
sdefresne2c600c52017-04-04 16:49:59440
441 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get());
442 [[tab webController] setNativeProvider:nil];
Sylvain Defresne949367262018-03-02 22:33:16443 webState_->RemoveObserver(webStateObserver_.get());
mrefaat4aec47b2019-03-29 18:44:21444 breakpad::StopMonitoringURLsForWebState(webState_.get());
Sylvain Defresnef5d2d952017-11-14 11:15:31445 webState_->SetDelegate(nullptr);
sdefresne2c600c52017-04-04 16:49:59446 webState_.reset();
447
sdefresned9217bc2016-12-19 13:58:32448 prerenderedURL_ = GURL();
Justin Cohen118fee6c2018-12-06 19:36:59449 startTime_ = base::TimeTicks();
sdefresned9217bc2016-12-19 13:58:32450}
451
452- (void)schedulePrerenderCancel {
Rohit Rao44f204302017-08-10 14:49:54453 // TODO(crbug.com/228550): Instead of cancelling the prerender, should we mark
454 // it as failed instead? That way, subsequent prerender requests for the same
455 // URL will not kick off new prerenders.
sdefresned9217bc2016-12-19 13:58:32456 [self removeScheduledPrerenderRequests];
457 [self performSelector:@selector(cancelPrerender) withObject:nil afterDelay:0];
458}
459
460- (void)removeScheduledPrerenderRequests {
461 [NSObject cancelPreviousPerformRequestsWithTarget:self];
462 scheduledURL_ = GURL();
463}
464
Sylvain Defresnef5d2d952017-11-14 11:15:31465#pragma mark - CRWWebStateDelegate
466
Eugene But6c41f5e2018-11-09 00:39:32467- (web::WebState*)webState:(web::WebState*)webState
468 createNewWebStateForURL:(const GURL&)URL
469 openerURL:(const GURL&)openerURL
470 initiatedByUser:(BOOL)initiatedByUser {
471 DCHECK([self isWebStatePrerendered:webState]);
472 [self schedulePrerenderCancel];
473 return nil;
474}
475
476- (web::JavaScriptDialogPresenter*)javaScriptDialogPresenterForWebState:
477 (web::WebState*)webState {
478 DCHECK([self isWebStatePrerendered:webState]);
479 [self schedulePrerenderCancel];
480 return nullptr;
481}
482
Sylvain Defresnef5d2d952017-11-14 11:15:31483- (void)webState:(web::WebState*)webState
484 didRequestHTTPAuthForProtectionSpace:(NSURLProtectionSpace*)protectionSpace
485 proposedCredential:(NSURLCredential*)proposedCredential
486 completionHandler:(void (^)(NSString* username,
487 NSString* password))handler {
488 DCHECK([self isWebStatePrerendered:webState]);
489 [self schedulePrerenderCancel];
490 if (handler) {
491 handler(nil, nil);
492 }
493}
494
Justin Cohen118fee6c2018-12-06 19:36:59495- (void)recordReleaseMetrics {
496 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName,
497 PRERENDER_FINAL_STATUS_USED,
498 PRERENDER_FINAL_STATUS_MAX);
499
500 DCHECK_NE(base::TimeTicks(), startTime_);
501 UMA_HISTOGRAM_TIMES(kPrerenderStartToReleaseContentsTime,
502 base::TimeTicks::Now() - startTime_);
503}
504
Sylvain Defresne949367262018-03-02 22:33:16505#pragma mark - CRWWebStateObserver
sdefresned9217bc2016-12-19 13:58:32506
Sylvain Defresne949367262018-03-02 22:33:16507- (void)webState:(web::WebState*)webState
Sylvain Defresne949367262018-03-02 22:33:16508 didLoadPageWithSuccess:(BOOL)loadSuccess {
509 DCHECK_EQ(webState, webState_.get());
510 // Cancel prerendering if response is "application/octet-stream". It can be a
511 // video file which should not be played from preload tab. See issue at
512 // http://crbug.com/436813 for more details.
513 const std::string& mimeType = webState->GetContentsMimeType();
514 if (mimeType == "application/octet-stream")
515 [self schedulePrerenderCancel];
516}
517
edchincd32fdf2017-10-25 12:45:45518#pragma mark - ManageAccountsDelegate
519
520- (void)onManageAccounts {
Sylvain Defresne949367262018-03-02 22:33:16521 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45522}
523
524- (void)onAddAccount {
Sylvain Defresne949367262018-03-02 22:33:16525 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45526}
527
528- (void)onGoIncognito:(const GURL&)url {
Sylvain Defresne949367262018-03-02 22:33:16529 [self schedulePrerenderCancel];
edchincd32fdf2017-10-25 12:45:45530}
531
mrefaat3ad5e412018-08-02 20:11:34532#pragma mark - CRWWebStatePolicyDecider
533
534- (BOOL)shouldAllowRequest:(NSURLRequest*)request
535 requestInfo:(const WebStatePolicyDecider::RequestInfo&)info {
536 GURL requestURL = net::GURLWithNSURL(request.URL);
537 // Don't allow preloading for requests that are handled by opening another
538 // application or by presenting a native UI.
539 if (AppLauncherTabHelper::IsAppUrl(requestURL) ||
540 ITunesUrlsHandlerTabHelper::CanHandleUrl(requestURL)) {
541 [self schedulePrerenderCancel];
542 return NO;
543 }
544 return YES;
545}
sdefresned9217bc2016-12-19 13:58:32546@end