| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import <UIKit/UIKit.h> | 5 #import <UIKit/UIKit.h> |
| 6 | 6 |
| 7 #include "ios/chrome/browser/ui/preload_controller.h" | 7 #include "ios/chrome/browser/ui/preload_controller.h" |
| 8 | 8 |
| 9 #include "base/ios/device_util.h" | 9 #include "base/ios/device_util.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/scoped_nsobject.h" |
| 11 #include "base/metrics/field_trial.h" | 12 #include "base/metrics/field_trial.h" |
| 12 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 13 #include "base/strings/sys_string_conversions.h" | 14 #include "base/strings/sys_string_conversions.h" |
| 14 #include "components/prefs/pref_service.h" | 15 #include "components/prefs/pref_service.h" |
| 15 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" | 16 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 16 #include "ios/chrome/browser/pref_names.h" | 17 #include "ios/chrome/browser/pref_names.h" |
| 18 #import "ios/chrome/browser/tabs/legacy_tab_helper.h" |
| 17 #import "ios/chrome/browser/tabs/tab.h" | 19 #import "ios/chrome/browser/tabs/tab.h" |
| 20 #import "ios/chrome/browser/tabs/tab_helper_util.h" |
| 21 #import "ios/chrome/browser/tabs/tab_private.h" |
| 18 #include "ios/chrome/browser/ui/preload_controller_delegate.h" | 22 #include "ios/chrome/browser/ui/preload_controller_delegate.h" |
| 19 #include "ios/chrome/browser/ui/prerender_final_status.h" | 23 #include "ios/chrome/browser/ui/prerender_final_status.h" |
| 20 #import "ios/web/public/web_state/ui/crw_native_content.h" | 24 #import "ios/web/public/web_state/ui/crw_native_content.h" |
| 21 #include "ios/web/public/web_thread.h" | 25 #include "ios/web/public/web_thread.h" |
| 22 #import "ios/web/web_state/ui/crw_web_controller.h" | 26 #import "ios/web/web_state/ui/crw_web_controller.h" |
| 23 #import "net/base/mac/url_conversions.h" | 27 #import "net/base/mac/url_conversions.h" |
| 24 #include "net/url_request/url_fetcher.h" | 28 #include "net/url_request/url_fetcher.h" |
| 25 #include "net/url_request/url_fetcher_delegate.h" | 29 #include "net/url_request/url_fetcher_delegate.h" |
| 26 #include "ui/base/page_transition_types.h" | 30 #include "ui/base/page_transition_types.h" |
| 27 | 31 |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 public: | 98 public: |
| 95 explicit PrefetchDelegate(PreloadController* owner) : owner_(owner) {} | 99 explicit PrefetchDelegate(PreloadController* owner) : owner_(owner) {} |
| 96 void OnURLFetchComplete(const net::URLFetcher* source) override { | 100 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 97 [owner_ prefetchDidComplete:source]; | 101 [owner_ prefetchDidComplete:source]; |
| 98 } | 102 } |
| 99 | 103 |
| 100 private: | 104 private: |
| 101 PreloadController* owner_; // weak | 105 PreloadController* owner_; // weak |
| 102 }; | 106 }; |
| 103 | 107 |
| 104 @implementation PreloadController | 108 @implementation PreloadController { |
| 109 ios::ChromeBrowserState* browserState_; // Weak. |
| 110 |
| 111 // The WebState used for prerendering. |
| 112 std::unique_ptr<web::WebState> webState_; |
| 113 |
| 114 // The URL that is prerendered in |webState_|. This can be different from |
| 115 // the value returned by WebState last committed navigation item, for example |
| 116 // in cases where there was a redirect. |
| 117 // |
| 118 // When choosing whether or not to use a prerendered Tab, |
| 119 // BrowserViewController compares the URL being loaded by the omnibox with the |
| 120 // URL of the prerendered Tab. Comparing against the Tab's currently URL |
| 121 // could return false negatives in cases of redirect, hence the need to store |
| 122 // the originally prerendered URL. |
| 123 GURL prerenderedURL_; |
| 124 |
| 125 // The URL that is scheduled to be prerendered, its associated transition and |
| 126 // referrer. |scheduledTransition_| and |scheduledReferrer_| are not valid |
| 127 // when |scheduledURL_| is empty. |
| 128 GURL scheduledURL_; |
| 129 ui::PageTransition scheduledTransition_; |
| 130 web::Referrer scheduledReferrer_; |
| 131 |
| 132 // The most-recently prefetched URL, or nil if there have been no prefetched |
| 133 // URLs. |
| 134 GURL prefetchedURL_; |
| 135 |
| 136 // The URLFetcher and associated delegate used to prefetch URLs. The delegate |
| 137 // simply forwards callbacks from URLFetcher back to the PrerenderController. |
| 138 std::unique_ptr<PrefetchDelegate> prefetcherDelegate_; |
| 139 std::unique_ptr<net::URLFetcher> prefetcher_; |
| 140 |
| 141 // Bridge to listen to pref changes. |
| 142 std::unique_ptr<PrefObserverBridge> observerBridge_; |
| 143 // Registrar for pref changes notifications. |
| 144 PrefChangeRegistrar prefChangeRegistrar_; |
| 145 // Observer for the WWAN setting. Contains a valid object only if the |
| 146 // instant setting is set to wifi-only. |
| 147 std::unique_ptr<ConnectionTypeObserverBridge> connectionTypeObserverBridge_; |
| 148 |
| 149 // Whether or not the preference is enabled. |
| 150 BOOL enabled_; |
| 151 // Whether or not prerendering is only when on wifi. |
| 152 BOOL wifiOnly_; |
| 153 // Whether or not the current connection is using WWAN. |
| 154 BOOL usingWWAN_; |
| 155 |
| 156 // Number of successful prerenders (i.e. the user viewed the prerendered page) |
| 157 // during the lifetime of this controller. |
| 158 int successfulPrerendersPerSessionCount_; |
| 159 |
| 160 id<PreloadControllerDelegate> delegate_; // weak |
| 161 } |
| 105 | 162 |
| 106 @synthesize prerenderedURL = prerenderedURL_; | 163 @synthesize prerenderedURL = prerenderedURL_; |
| 107 @synthesize prefetchedURL = prefetchedURL_; | 164 @synthesize prefetchedURL = prefetchedURL_; |
| 108 @synthesize delegate = delegate_; | 165 @synthesize delegate = delegate_; |
| 109 | 166 |
| 110 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState { | 167 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState { |
| 111 DCHECK(browserState); | 168 DCHECK(browserState); |
| 112 DCHECK_CURRENTLY_ON(web::WebThread::UI); | 169 DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| 113 if ((self = [super init])) { | 170 if ((self = [super init])) { |
| 114 browserState_ = browserState; | 171 browserState_ = browserState; |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 | 265 |
| 209 - (void)cancelPrerender { | 266 - (void)cancelPrerender { |
| 210 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_CANCELLED]; | 267 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_CANCELLED]; |
| 211 } | 268 } |
| 212 | 269 |
| 213 - (void)cancelPrerenderForReason:(PrerenderFinalStatus)reason { | 270 - (void)cancelPrerenderForReason:(PrerenderFinalStatus)reason { |
| 214 [self removeScheduledPrerenderRequests]; | 271 [self removeScheduledPrerenderRequests]; |
| 215 [self destroyPreviewContentsForReason:reason]; | 272 [self destroyPreviewContentsForReason:reason]; |
| 216 } | 273 } |
| 217 | 274 |
| 218 - (Tab*)releasePrerenderContents { | 275 - (std::unique_ptr<web::WebState>)releasePrerenderContents { |
| 219 successfulPrerendersPerSessionCount_++; | 276 successfulPrerendersPerSessionCount_++; |
| 220 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, | 277 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, |
| 221 PRERENDER_FINAL_STATUS_USED, | 278 PRERENDER_FINAL_STATUS_USED, |
| 222 PRERENDER_FINAL_STATUS_MAX); | 279 PRERENDER_FINAL_STATUS_MAX); |
| 223 [self removeScheduledPrerenderRequests]; | 280 [self removeScheduledPrerenderRequests]; |
| 224 prerenderedURL_ = GURL(); | 281 prerenderedURL_ = GURL(); |
| 225 [[tab_ webController] setNativeProvider:nil]; | 282 |
| 226 [tab_ setDelegate:nil]; | 283 if (webState_) { |
| 227 return [tab_.release() autorelease]; | 284 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get()); |
| 285 [[tab webController] setNativeProvider:nil]; |
| 286 [tab setDelegate:nil]; |
| 287 } |
| 288 |
| 289 return std::move(webState_); |
| 228 } | 290 } |
| 229 | 291 |
| 230 - (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type { | 292 - (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type { |
| 231 DCHECK_CURRENTLY_ON(web::WebThread::UI); | 293 DCHECK_CURRENTLY_ON(web::WebThread::UI); |
| 232 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(type); | 294 usingWWAN_ = net::NetworkChangeNotifier::IsConnectionCellular(type); |
| 233 if (wifiOnly_ && usingWWAN_) | 295 if (wifiOnly_ && usingWWAN_) |
| 234 [self cancelPrerender]; | 296 [self cancelPrerender]; |
| 235 } | 297 } |
| 236 | 298 |
| 237 - (void)onPreferenceChanged:(const std::string&)preferenceName { | 299 - (void)onPreferenceChanged:(const std::string&)preferenceName { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 265 | 327 |
| 266 - (void)didReceiveMemoryWarning { | 328 - (void)didReceiveMemoryWarning { |
| 267 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED]; | 329 [self cancelPrerenderForReason:PRERENDER_FINAL_STATUS_MEMORY_LIMIT_EXCEEDED]; |
| 268 } | 330 } |
| 269 | 331 |
| 270 #pragma mark - | 332 #pragma mark - |
| 271 #pragma mark CRWNativeContentProvider implementation | 333 #pragma mark CRWNativeContentProvider implementation |
| 272 | 334 |
| 273 // Delegate the call to the original native provider. | 335 // Delegate the call to the original native provider. |
| 274 - (BOOL)hasControllerForURL:(const GURL&)url { | 336 - (BOOL)hasControllerForURL:(const GURL&)url { |
| 275 return [[tab_ webController].nativeProvider hasControllerForURL:url]; | 337 if (!webState_) |
| 338 return NO; |
| 339 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get()); |
| 340 return [[tab webController].nativeProvider hasControllerForURL:url]; |
| 276 } | 341 } |
| 277 | 342 |
| 278 // Override the CRWNativeContentProvider methods to cancel any prerenders that | 343 // Override the CRWNativeContentProvider methods to cancel any prerenders that |
| 279 // require native content. | 344 // require native content. |
| 280 - (id<CRWNativeContent>)controllerForURL:(const GURL&)url | 345 - (id<CRWNativeContent>)controllerForURL:(const GURL&)url |
| 281 webState:(web::WebState*)webState { | 346 webState:(web::WebState*)webState { |
| 282 [self schedulePrerenderCancel]; | 347 [self schedulePrerenderCancel]; |
| 283 return nil; | 348 return nil; |
| 284 } | 349 } |
| 285 | 350 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 316 [self destroyPreviewContents]; | 381 [self destroyPreviewContents]; |
| 317 prerenderedURL_ = scheduledURL_; | 382 prerenderedURL_ = scheduledURL_; |
| 318 scheduledURL_ = GURL(); | 383 scheduledURL_ = GURL(); |
| 319 | 384 |
| 320 DCHECK(prerenderedURL_.is_valid()); | 385 DCHECK(prerenderedURL_.is_valid()); |
| 321 if (!prerenderedURL_.is_valid()) { | 386 if (!prerenderedURL_.is_valid()) { |
| 322 [self destroyPreviewContents]; | 387 [self destroyPreviewContents]; |
| 323 return; | 388 return; |
| 324 } | 389 } |
| 325 | 390 |
| 326 Tab* tab = | 391 web::WebState::CreateParams createParams(browserState_); |
| 327 [Tab preloadingTabWithBrowserState:browserState_ | 392 webState_ = web::WebState::Create(createParams); |
| 328 url:prerenderedURL_ | 393 AttachTabHelpers(webState_.get()); |
| 329 referrer:scheduledReferrer_ | |
| 330 transition:scheduledTransition_ | |
| 331 provider:self | |
| 332 opener:nil | |
| 333 desktopUserAgent:[delegate_ shouldUseDesktopUserAgent] | |
| 334 configuration:^(Tab* tab) { | |
| 335 [tab setIsPrerenderTab:YES]; | |
| 336 [tab setDelegate:self]; | |
| 337 }]; | |
| 338 | 394 |
| 339 // Create and set up the prerender. | 395 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get()); |
| 340 tab_.reset([tab retain]); | 396 DCHECK(tab); |
| 397 |
| 398 [[tab webController] setNativeProvider:self]; |
| 399 [[tab webController] setWebUsageEnabled:YES]; |
| 400 [tab setIsPrerenderTab:YES]; |
| 401 [tab setDelegate:self]; |
| 402 |
| 403 web::NavigationManager::WebLoadParams loadParams(prerenderedURL_); |
| 404 loadParams.referrer = scheduledReferrer_; |
| 405 loadParams.transition_type = scheduledTransition_; |
| 406 if ([delegate_ shouldUseDesktopUserAgent]) { |
| 407 loadParams.user_agent_override_option = |
| 408 web::NavigationManager::UserAgentOverrideOption::DESKTOP; |
| 409 } |
| 410 [[tab webController] loadWithParams:loadParams]; |
| 341 | 411 |
| 342 // Trigger the page to start loading. | 412 // Trigger the page to start loading. |
| 343 [tab_ view]; | 413 [tab view]; |
| 344 } | 414 } |
| 345 | 415 |
| 346 - (const GURL)urlToPrefetchURL:(const GURL&)url { | 416 - (const GURL)urlToPrefetchURL:(const GURL&)url { |
| 347 GURL::Replacements replacements; | 417 GURL::Replacements replacements; |
| 348 | 418 |
| 349 // Add prefetch indicator to query params. | 419 // Add prefetch indicator to query params. |
| 350 std::string query = url.query(); | 420 std::string query = url.query(); |
| 351 if (!query.empty()) { | 421 if (!query.empty()) { |
| 352 query.append("&"); | 422 query.append("&"); |
| 353 } | 423 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 372 << "Prefetching URL got response code " << source->GetResponseCode(); | 442 << "Prefetching URL got response code " << source->GetResponseCode(); |
| 373 } | 443 } |
| 374 prefetcher_.reset(); | 444 prefetcher_.reset(); |
| 375 } | 445 } |
| 376 | 446 |
| 377 - (void)destroyPreviewContents { | 447 - (void)destroyPreviewContents { |
| 378 [self destroyPreviewContentsForReason:PRERENDER_FINAL_STATUS_CANCELLED]; | 448 [self destroyPreviewContentsForReason:PRERENDER_FINAL_STATUS_CANCELLED]; |
| 379 } | 449 } |
| 380 | 450 |
| 381 - (void)destroyPreviewContentsForReason:(PrerenderFinalStatus)reason { | 451 - (void)destroyPreviewContentsForReason:(PrerenderFinalStatus)reason { |
| 382 if (!tab_.get()) | 452 if (!webState_) |
| 383 return; | 453 return; |
| 384 | 454 |
| 385 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason, | 455 UMA_HISTOGRAM_ENUMERATION(kPrerenderFinalStatusHistogramName, reason, |
| 386 PRERENDER_FINAL_STATUS_MAX); | 456 PRERENDER_FINAL_STATUS_MAX); |
| 387 [[tab_ webController] setNativeProvider:nil]; | 457 |
| 388 [tab_ setDelegate:nil]; | 458 Tab* tab = LegacyTabHelper::GetTabForWebState(webState_.get()); |
| 389 [tab_ close]; | 459 [[tab webController] setNativeProvider:nil]; |
| 390 tab_.reset(); | 460 webState_.reset(); |
| 461 |
| 391 prerenderedURL_ = GURL(); | 462 prerenderedURL_ = GURL(); |
| 392 } | 463 } |
| 393 | 464 |
| 394 - (void)schedulePrerenderCancel { | 465 - (void)schedulePrerenderCancel { |
| 395 // TODO(rohitrao): Instead of cancelling the prerender, should we mark it as | 466 // TODO(rohitrao): Instead of cancelling the prerender, should we mark it as |
| 396 // failed instead? That way, subsequent prerender requests for the same URL | 467 // failed instead? That way, subsequent prerender requests for the same URL |
| 397 // will not kick off new prerenders. b/5944421 | 468 // will not kick off new prerenders. b/5944421 |
| 398 [self removeScheduledPrerenderRequests]; | 469 [self removeScheduledPrerenderRequests]; |
| 399 [self performSelector:@selector(cancelPrerender) withObject:nil afterDelay:0]; | 470 [self performSelector:@selector(cancelPrerender) withObject:nil afterDelay:0]; |
| 400 } | 471 } |
| 401 | 472 |
| 402 - (void)removeScheduledPrerenderRequests { | 473 - (void)removeScheduledPrerenderRequests { |
| 403 [NSObject cancelPreviousPerformRequestsWithTarget:self]; | 474 [NSObject cancelPreviousPerformRequestsWithTarget:self]; |
| 404 scheduledURL_ = GURL(); | 475 scheduledURL_ = GURL(); |
| 405 } | 476 } |
| 406 | 477 |
| 407 #pragma mark - TabDelegate | 478 #pragma mark - TabDelegate |
| 408 | 479 |
| 409 - (void)discardPrerender { | 480 - (void)discardPrerender { |
| 410 [self schedulePrerenderCancel]; | 481 [self schedulePrerenderCancel]; |
| 411 } | 482 } |
| 412 | 483 |
| 413 @end | 484 @end |
| OLD | NEW |