| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/prerender/prerender_service_impl.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #import "ios/chrome/browser/main/browser.h" |
| #import "ios/chrome/browser/prerender/preload_controller.h" |
| #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h" |
| #import "ios/chrome/browser/ui/ntp/ntp_util.h" |
| #import "ios/chrome/browser/web/load_timing_tab_helper.h" |
| #import "ios/chrome/browser/web_state_list/web_state_list.h" |
| #import "ios/web/public/navigation/navigation_manager.h" |
| #include "ios/web/public/web_client.h" |
| #import "ios/web/public/web_state.h" |
| #include "ui/base/page_transition_types.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| PrerenderServiceImpl::PrerenderServiceImpl(ChromeBrowserState* browser_state) |
| : controller_( |
| [[PreloadController alloc] initWithBrowserState:browser_state]) {} |
| |
| PrerenderServiceImpl::~PrerenderServiceImpl() = default; |
| |
| void PrerenderServiceImpl::Shutdown() { |
| [controller_ browserStateDestroyed]; |
| controller_ = nil; |
| } |
| |
| void PrerenderServiceImpl::SetDelegate(id<PreloadControllerDelegate> delegate) { |
| controller_.delegate = delegate; |
| } |
| |
| void PrerenderServiceImpl::StartPrerender(const GURL& url, |
| const web::Referrer& referrer, |
| ui::PageTransition transition, |
| web::WebState* web_state_to_replace, |
| bool immediately) { |
| [controller_ prerenderURL:url |
| referrer:referrer |
| transition:transition |
| currentWebState:web_state_to_replace |
| immediately:immediately]; |
| } |
| |
| bool PrerenderServiceImpl::MaybeLoadPrerenderedURL( |
| const GURL& url, |
| ui::PageTransition transition, |
| Browser* browser) { |
| if (!HasPrerenderForUrl(url)) { |
| CancelPrerender(); |
| return false; |
| } |
| |
| std::unique_ptr<web::WebState> new_web_state = |
| [controller_ releasePrerenderContents]; |
| if (!new_web_state) { |
| CancelPrerender(); |
| return false; |
| } |
| |
| WebStateList* web_state_list = browser->GetWebStateList(); |
| |
| // Due to some security workarounds inside ios/web, sometimes a restored |
| // webState may mark new navigations as renderer initiated instead of browser |
| // initiated. As a result 'visible url' of preloaded web state will be |
| // 'last committed url', and not 'url typed by the user'. As these |
| // navigations are uncommitted, and make the omnibox (or NTP) look strange, |
| // simply drop them. See crbug.com/1020497 for the strange UI, and |
| // crbug.com/1010765 for the triggering security fixes. |
| if (web_state_list->GetActiveWebState()->GetVisibleURL() == |
| new_web_state->GetVisibleURL()) { |
| CancelPrerender(); |
| return false; |
| } |
| |
| DCHECK_NE(WebStateList::kInvalidIndex, web_state_list->active_index()); |
| |
| web::NavigationManager* active_navigation_manager = |
| web_state_list->GetActiveWebState()->GetNavigationManager(); |
| int lastIndex = active_navigation_manager->GetLastCommittedItemIndex(); |
| UMA_HISTOGRAM_COUNTS_100("Prerender.PrerenderLoadedOnIndex", lastIndex); |
| |
| BOOL onFirstNTP = |
| IsVisibleURLNewTabPage(web_state_list->GetActiveWebState()) && |
| lastIndex == 0; |
| UMA_HISTOGRAM_BOOLEAN("Prerender.PrerenderLoadedOnFirstNTP", onFirstNTP); |
| |
| loading_prerender_ = true; |
| web_state_list->ReplaceWebStateAt(web_state_list->active_index(), |
| std::move(new_web_state)); |
| loading_prerender_ = false; |
| // new_web_state is now null after the std::move, so grab a new pointer to |
| // it for further updates. |
| web::WebState* active_web_state = web_state_list->GetActiveWebState(); |
| |
| bool typed_or_generated_transition = |
| PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) || |
| PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_GENERATED); |
| if (typed_or_generated_transition) { |
| LoadTimingTabHelper::FromWebState(active_web_state) |
| ->DidPromotePrerenderTab(); |
| } |
| SessionRestorationBrowserAgent::FromBrowser(browser)->SaveSession( |
| /*immediately=*/false); |
| return true; |
| } |
| |
| bool PrerenderServiceImpl::IsLoadingPrerender() { |
| return loading_prerender_; |
| } |
| |
| void PrerenderServiceImpl::CancelPrerender() { |
| [controller_ cancelPrerender]; |
| } |
| |
| bool PrerenderServiceImpl::HasPrerenderForUrl(const GURL& url) { |
| return url == controller_.prerenderedURL; |
| } |
| |
| bool PrerenderServiceImpl::IsWebStatePrerendered(web::WebState* web_state) { |
| return [controller_ isWebStatePrerendered:web_state]; |
| } |