Commit 9617c63d authored by Mohammad Refaat's avatar Mohammad Refaat Committed by Commit Bot

Pass the webstate to be replaced to the pre-render service

There is only 1 pre-render service per BrowserState which makes
the last created BVC the delegate for the the pre-render service
so when there are multiple windows, the last one created will provide
the webState to be replaced to the service and the service will use that
to get the navigation history which will be wrong in cases where the
prerendered webstate is not on the last created window.

As a work around pass the webstate to be replaced to the service

Bug: 1139286
Change-Id: I5a8651ba1c52ecf9e0a9d2ea0a3a18c1e0afdfc5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2486961Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Commit-Queue: Mohammad Refaat <mrefaat@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820297}
parent 8454c40e
...@@ -27,6 +27,7 @@ class FakePrerenderService : public PrerenderService { ...@@ -27,6 +27,7 @@ class FakePrerenderService : public PrerenderService {
void StartPrerender(const GURL& url, void StartPrerender(const GURL& url,
const web::Referrer& referrer, const web::Referrer& referrer,
ui::PageTransition transition, ui::PageTransition transition,
web::WebState* web_state_to_replace,
bool immediately) override; bool immediately) override;
bool MaybeLoadPrerenderedURL(const GURL& url, bool MaybeLoadPrerenderedURL(const GURL& url,
ui::PageTransition transition, ui::PageTransition transition,
......
...@@ -18,6 +18,7 @@ void FakePrerenderService::SetDelegate(id<PreloadControllerDelegate> delegate) { ...@@ -18,6 +18,7 @@ void FakePrerenderService::SetDelegate(id<PreloadControllerDelegate> delegate) {
void FakePrerenderService::StartPrerender(const GURL& url, void FakePrerenderService::StartPrerender(const GURL& url,
const web::Referrer& referrer, const web::Referrer& referrer,
ui::PageTransition transition, ui::PageTransition transition,
web::WebState* web_state_to_replace,
bool immediately) { bool immediately) {
preload_url_ = url; preload_url_ = url;
} }
......
...@@ -48,9 +48,10 @@ class WebState; ...@@ -48,9 +48,10 @@ class WebState;
// Prerenders the given |url| with the given |transition|. Normally, prerender // Prerenders the given |url| with the given |transition|. Normally, prerender
// requests are fulfilled after a short delay, to prevent unnecessary prerenders // requests are fulfilled after a short delay, to prevent unnecessary prerenders
// while the user is typing. If |immediately| is YES, this method starts // while the user is typing. If |immediately| is YES, this method starts
// prerendering immediately, with no delay. |immediately| should be set to YES // prerendering immediately, with no delay. |currentWebState| is used to create
// only when there is a very high confidence that the user will navigate to the // a new WebState for the prerender with the same session. |immediately| should
// given |url|. // be set to YES only when there is a very high confidence that the user will
// navigate to the given |url|.
// //
// If there is already an existing request for |url|, this method does nothing // If there is already an existing request for |url|, this method does nothing
// and does not reset the delay timer. If there is an existing request for a // and does not reset the delay timer. If there is an existing request for a
...@@ -59,6 +60,7 @@ class WebState; ...@@ -59,6 +60,7 @@ class WebState;
- (void)prerenderURL:(const GURL&)url - (void)prerenderURL:(const GURL&)url
referrer:(const web::Referrer&)referrer referrer:(const web::Referrer&)referrer
transition:(ui::PageTransition)transition transition:(ui::PageTransition)transition
currentWebState:(web::WebState*)currentWebState
immediately:(BOOL)immediately; immediately:(BOOL)immediately;
// Cancels any outstanding prerender requests and destroys any prerendered Tabs. // Cancels any outstanding prerender requests and destroys any prerendered Tabs.
......
...@@ -159,6 +159,7 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -159,6 +159,7 @@ const base::Feature kPreloadDelayWebStateReset{
PreloadCancelling> { PreloadCancelling> {
std::unique_ptr<web::WebStateDelegateBridge> _webStateDelegate; std::unique_ptr<web::WebStateDelegateBridge> _webStateDelegate;
std::unique_ptr<web::WebStateObserverBridge> _webStateObserver; std::unique_ptr<web::WebStateObserverBridge> _webStateObserver;
std::unique_ptr<web::WebStateObserverBridge> _webStateToReplaceObserver;
std::unique_ptr<PrefObserverBridge> _observerBridge; std::unique_ptr<PrefObserverBridge> _observerBridge;
std::unique_ptr<ConnectionTypeObserverBridge> _connectionTypeObserver; std::unique_ptr<ConnectionTypeObserverBridge> _connectionTypeObserver;
std::unique_ptr<web::WebStatePolicyDeciderBridge> _policyDeciderBridge; std::unique_ptr<web::WebStatePolicyDeciderBridge> _policyDeciderBridge;
...@@ -174,6 +175,11 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -174,6 +175,11 @@ const base::Feature kPreloadDelayWebStateReset{
// The dialog presenter. // The dialog presenter.
std::unique_ptr<web::JavaScriptDialogPresenter> _dialogPresenter; std::unique_ptr<web::JavaScriptDialogPresenter> _dialogPresenter;
// A weak pointer to the webState that will be replaced with the prerendered
// one. This is needed by |startPrerender| to build the new webstate with the
// same sessions.
web::WebState* _webStateToReplace;
} }
// The ChromeBrowserState passed on initialization. // The ChromeBrowserState passed on initialization.
...@@ -245,6 +251,8 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -245,6 +251,8 @@ const base::Feature kPreloadDelayWebStateReset{
net::NetworkChangeNotifier::GetConnectionType()); net::NetworkChangeNotifier::GetConnectionType());
_webStateDelegate = std::make_unique<web::WebStateDelegateBridge>(self); _webStateDelegate = std::make_unique<web::WebStateDelegateBridge>(self);
_webStateObserver = std::make_unique<web::WebStateObserverBridge>(self); _webStateObserver = std::make_unique<web::WebStateObserverBridge>(self);
_webStateToReplaceObserver =
std::make_unique<web::WebStateObserverBridge>(self);
_observerBridge = std::make_unique<PrefObserverBridge>(self); _observerBridge = std::make_unique<PrefObserverBridge>(self);
_prefChangeRegistrar.Init(_browserState->GetPrefs()); _prefChangeRegistrar.Init(_browserState->GetPrefs());
_observerBridge->ObserveChangesForPreference( _observerBridge->ObserveChangesForPreference(
...@@ -255,7 +263,7 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -255,7 +263,7 @@ const base::Feature kPreloadDelayWebStateReset{
_connectionTypeObserver = _connectionTypeObserver =
std::make_unique<ConnectionTypeObserverBridge>(self); std::make_unique<ConnectionTypeObserverBridge>(self);
} }
_webStateToReplace = nullptr;
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
addObserver:self addObserver:self
selector:@selector(didReceiveMemoryWarning) selector:@selector(didReceiveMemoryWarning)
...@@ -325,6 +333,7 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -325,6 +333,7 @@ const base::Feature kPreloadDelayWebStateReset{
- (void)prerenderURL:(const GURL&)url - (void)prerenderURL:(const GURL&)url
referrer:(const web::Referrer&)referrer referrer:(const web::Referrer&)referrer
transition:(ui::PageTransition)transition transition:(ui::PageTransition)transition
currentWebState:(web::WebState*)currentWebState
immediately:(BOOL)immediately { immediately:(BOOL)immediately {
// TODO(crbug.com/754050): If CanPrerenderURL() returns false, should we // TODO(crbug.com/754050): If CanPrerenderURL() returns false, should we
// cancel any scheduled prerender requests? // cancel any scheduled prerender requests?
...@@ -340,6 +349,12 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -340,6 +349,12 @@ const base::Feature kPreloadDelayWebStateReset{
} }
[self removeScheduledPrerenderRequests]; [self removeScheduledPrerenderRequests];
_webStateToReplace = currentWebState;
// Observing the |_webStateToReplace| to make sure that if it's destructed
// the pre-rendering will be canceled.
if (_webStateToReplace) {
_webStateToReplace->AddObserver(_webStateToReplaceObserver.get());
}
_scheduledRequest = _scheduledRequest =
std::make_unique<PrerenderRequest>(url, transition, referrer); std::make_unique<PrerenderRequest>(url, transition, referrer);
...@@ -452,6 +467,9 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -452,6 +467,9 @@ const base::Feature kPreloadDelayWebStateReset{
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didFinishNavigation:(web::NavigationContext*)navigation { didFinishNavigation:(web::NavigationContext*)navigation {
// the |_webStateToReplace| is observed for destruction event only.
if (_webStateToReplace == webState)
return;
DCHECK_EQ(webState, _webState.get()); DCHECK_EQ(webState, _webState.get());
if ([self shouldCancelPreloadForMimeType:webState->GetContentsMimeType()]) if ([self shouldCancelPreloadForMimeType:webState->GetContentsMimeType()])
[self schedulePrerenderCancel]; [self schedulePrerenderCancel];
...@@ -459,6 +477,10 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -459,6 +477,10 @@ const base::Feature kPreloadDelayWebStateReset{
- (void)webState:(web::WebState*)webState - (void)webState:(web::WebState*)webState
didLoadPageWithSuccess:(BOOL)loadSuccess { didLoadPageWithSuccess:(BOOL)loadSuccess {
// the |_webStateToReplace| is observed for destruction event only.
if (_webStateToReplace == webState)
return;
DCHECK_EQ(webState, _webState.get()); DCHECK_EQ(webState, _webState.get());
// The load should have been cancelled when the navigation finishes, but this // The load should have been cancelled when the navigation finishes, but this
// makes sure that we didn't miss one. // makes sure that we didn't miss one.
...@@ -469,6 +491,14 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -469,6 +491,14 @@ const base::Feature kPreloadDelayWebStateReset{
} }
} }
- (void)webStateDestroyed:(web::WebState*)webState {
if (_webState.get() == webState)
return;
DCHECK_EQ(webState, _webStateToReplace);
// There is no way to create a pre-rendered webState without existing webState
// web state to replace, So cancel the prerender.
[self schedulePrerenderCancel];
}
#pragma mark - CRWWebStatePolicyDecider #pragma mark - CRWWebStatePolicyDecider
- (WebStatePolicyDecider::PolicyDecision) - (WebStatePolicyDecider::PolicyDecision)
...@@ -568,6 +598,10 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -568,6 +598,10 @@ const base::Feature kPreloadDelayWebStateReset{
- (void)removeScheduledPrerenderRequests { - (void)removeScheduledPrerenderRequests {
[NSObject cancelPreviousPerformRequestsWithTarget:self]; [NSObject cancelPreviousPerformRequestsWithTarget:self];
_scheduledRequest = nullptr; _scheduledRequest = nullptr;
if (_webStateToReplace) {
_webStateToReplace->RemoveObserver(_webStateToReplaceObserver.get());
}
_webStateToReplace = nullptr;
} }
#pragma mark - Prerender Helpers #pragma mark - Prerender Helpers
...@@ -577,17 +611,27 @@ const base::Feature kPreloadDelayWebStateReset{ ...@@ -577,17 +611,27 @@ const base::Feature kPreloadDelayWebStateReset{
[self destroyPreviewContents]; [self destroyPreviewContents];
self.prerenderedURL = self.scheduledURL; self.prerenderedURL = self.scheduledURL;
std::unique_ptr<PrerenderRequest> request = std::move(_scheduledRequest); std::unique_ptr<PrerenderRequest> request = std::move(_scheduledRequest);
// No need to observer the destruction of the |_webStateToReplace| anymore
// as it will be used here.
if (_webStateToReplace) {
_webStateToReplace->RemoveObserver(_webStateToReplaceObserver.get());
}
// TODO(crbug.com/1140583): The correct way is to always get the
// webStateToReplace from the delegate. however this is not possible because
// there is only one delegate per browser state.
if (!_webStateToReplace)
_webStateToReplace = [self.delegate webStateToReplace];
web::WebState* webStateToReplace = [self.delegate webStateToReplace]; if (!self.prerenderedURL.is_valid() || !_webStateToReplace) {
if (!self.prerenderedURL.is_valid() || !webStateToReplace) {
[self destroyPreviewContents]; [self destroyPreviewContents];
return; return;
} }
web::WebState::CreateParams createParams(self.browserState); web::WebState::CreateParams createParams(self.browserState);
_webState = web::WebState::CreateWithStorageSession( _webState = web::WebState::CreateWithStorageSession(
createParams, webStateToReplace->BuildSessionStorage()); createParams, _webStateToReplace->BuildSessionStorage());
_webStateToReplace = nullptr;
// Add the preload controller as a policyDecider before other tab helpers, so // Add the preload controller as a policyDecider before other tab helpers, so
// that it can block the navigation if needed before other policy deciders // that it can block the navigation if needed before other policy deciders
// execute thier side effects (eg. AppLauncherTabHelper launching app). // execute thier side effects (eg. AppLauncherTabHelper launching app).
......
...@@ -118,6 +118,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) { ...@@ -118,6 +118,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) {
[controller_ prerenderURL:GURL() [controller_ prerenderURL:GURL()
referrer:kReferrer referrer:kReferrer
transition:kTransition transition:kTransition
currentWebState:nil
immediately:YES]; immediately:YES];
EXPECT_FALSE([controller_ releasePrerenderContents]); EXPECT_FALSE([controller_ releasePrerenderContents]);
...@@ -126,6 +127,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) { ...@@ -126,6 +127,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) {
[controller_ prerenderURL:GURL("chrome://newtab") [controller_ prerenderURL:GURL("chrome://newtab")
referrer:kReferrer referrer:kReferrer
transition:kTransition transition:kTransition
currentWebState:nil
immediately:YES]; immediately:YES];
EXPECT_FALSE([controller_ releasePrerenderContents]); EXPECT_FALSE([controller_ releasePrerenderContents]);
...@@ -134,6 +136,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) { ...@@ -134,6 +136,7 @@ TEST_F(PreloadControllerTest, DontPreloadNonWebURLs) {
[controller_ prerenderURL:GURL("about:flags") [controller_ prerenderURL:GURL("about:flags")
referrer:kReferrer referrer:kReferrer
transition:kTransition transition:kTransition
currentWebState:nil
immediately:YES]; immediately:YES];
EXPECT_FALSE([controller_ releasePrerenderContents]); EXPECT_FALSE([controller_ releasePrerenderContents]);
} }
......
...@@ -29,9 +29,14 @@ class PrerenderService : public KeyedService { ...@@ -29,9 +29,14 @@ class PrerenderService : public KeyedService {
// Prerenders the given |url| with the given |transition|. Normally, // Prerenders the given |url| with the given |transition|. Normally,
// prerender requests are fulfilled after a short delay, to prevent // prerender requests are fulfilled after a short delay, to prevent
// unnecessary prerenders while the user is typing. If |immediately| is YES, // unnecessary prerenders while the user is typing. If |immediately| is YES,
// this method starts prerendering immediately, with no delay. |immediately| // this method starts prerendering immediately, with no delay.
// should be set to YES only when there is a very high confidence that the // |web_state_to_replace| is provided so that the new prerendered web state
// user will navigate to the given |url|. // can have the same session data. |immediately| should be set to YES only
// when there is a very high confidence that the user will navigate to the
// given |url|.
// TODO(crbug.com/1140583): passing |web_state_to_replace| is a workaround for
// not having prerender service per browser, remove it once prerenderService
// is a browser agent.
// //
// If there is already an existing request for |url|, this method does nothing // If there is already an existing request for |url|, this method does nothing
// and does not reset the delay timer. If there is an existing request for a // and does not reset the delay timer. If there is an existing request for a
...@@ -40,6 +45,7 @@ class PrerenderService : public KeyedService { ...@@ -40,6 +45,7 @@ class PrerenderService : public KeyedService {
virtual void StartPrerender(const GURL& url, virtual void StartPrerender(const GURL& url,
const web::Referrer& referrer, const web::Referrer& referrer,
ui::PageTransition transition, ui::PageTransition transition,
web::WebState* web_state_to_replace,
bool immediately) = 0; bool immediately) = 0;
// If |url| is prerendered, loads the prerendered web state into // If |url| is prerendered, loads the prerendered web state into
......
...@@ -26,6 +26,7 @@ class PrerenderServiceImpl : public PrerenderService { ...@@ -26,6 +26,7 @@ class PrerenderServiceImpl : public PrerenderService {
void StartPrerender(const GURL& url, void StartPrerender(const GURL& url,
const web::Referrer& referrer, const web::Referrer& referrer,
ui::PageTransition transition, ui::PageTransition transition,
web::WebState* web_state_to_replace,
bool immediately) override; bool immediately) override;
bool MaybeLoadPrerenderedURL(const GURL& url, bool MaybeLoadPrerenderedURL(const GURL& url,
ui::PageTransition transition, ui::PageTransition transition,
......
...@@ -38,10 +38,12 @@ void PrerenderServiceImpl::SetDelegate(id<PreloadControllerDelegate> delegate) { ...@@ -38,10 +38,12 @@ void PrerenderServiceImpl::SetDelegate(id<PreloadControllerDelegate> delegate) {
void PrerenderServiceImpl::StartPrerender(const GURL& url, void PrerenderServiceImpl::StartPrerender(const GURL& url,
const web::Referrer& referrer, const web::Referrer& referrer,
ui::PageTransition transition, ui::PageTransition transition,
web::WebState* web_state_to_replace,
bool immediately) { bool immediately) {
[controller_ prerenderURL:url [controller_ prerenderURL:url
referrer:referrer referrer:referrer
transition:transition transition:transition
currentWebState:web_state_to_replace
immediately:immediately]; immediately:immediately];
} }
......
...@@ -799,7 +799,7 @@ TEST_P(SafeBrowsingTabHelperTest, UnsafeMainFrameRequestCancelsPrerendering) { ...@@ -799,7 +799,7 @@ TEST_P(SafeBrowsingTabHelperTest, UnsafeMainFrameRequestCancelsPrerendering) {
PrerenderServiceFactory::GetForBrowserState( PrerenderServiceFactory::GetForBrowserState(
ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState())); ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState()));
prerender_service->StartPrerender(unsafe_url, web::Referrer(), prerender_service->StartPrerender(unsafe_url, web::Referrer(),
ui::PAGE_TRANSITION_LINK, ui::PAGE_TRANSITION_LINK, &web_state_,
/*immediately=*/true); /*immediately=*/true);
EXPECT_TRUE(ShouldAllowRequestUrl(unsafe_url).ShouldAllowNavigation()); EXPECT_TRUE(ShouldAllowRequestUrl(unsafe_url).ShouldAllowNavigation());
...@@ -830,7 +830,7 @@ TEST_P(SafeBrowsingTabHelperTest, UnsafeSubframeRequestCancelsPrerendering) { ...@@ -830,7 +830,7 @@ TEST_P(SafeBrowsingTabHelperTest, UnsafeSubframeRequestCancelsPrerendering) {
PrerenderServiceFactory::GetForBrowserState( PrerenderServiceFactory::GetForBrowserState(
ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState())); ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState()));
prerender_service->StartPrerender(main_frame_item->GetURL(), web::Referrer(), prerender_service->StartPrerender(main_frame_item->GetURL(), web::Referrer(),
ui::PAGE_TRANSITION_LINK, ui::PAGE_TRANSITION_LINK, &web_state_,
/*immediately=*/true); /*immediately=*/true);
EXPECT_TRUE(ShouldAllowRequestUrl(unsafe_url, /*for_main_frame=*/false) EXPECT_TRUE(ShouldAllowRequestUrl(unsafe_url, /*for_main_frame=*/false)
...@@ -864,7 +864,7 @@ TEST_P(SafeBrowsingTabHelperTest, ...@@ -864,7 +864,7 @@ TEST_P(SafeBrowsingTabHelperTest,
PrerenderServiceFactory::GetForBrowserState( PrerenderServiceFactory::GetForBrowserState(
ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState())); ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState()));
prerender_service->StartPrerender(safe_url, web::Referrer(), prerender_service->StartPrerender(safe_url, web::Referrer(),
ui::PAGE_TRANSITION_LINK, ui::PAGE_TRANSITION_LINK, &web_state_,
/*immediately=*/true); /*immediately=*/true);
EXPECT_TRUE(ShouldAllowRequestUrl(safe_url).ShouldAllowNavigation()); EXPECT_TRUE(ShouldAllowRequestUrl(safe_url).ShouldAllowNavigation());
...@@ -891,7 +891,7 @@ TEST_P(SafeBrowsingTabHelperTest, ...@@ -891,7 +891,7 @@ TEST_P(SafeBrowsingTabHelperTest,
PrerenderServiceFactory::GetForBrowserState( PrerenderServiceFactory::GetForBrowserState(
ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState())); ChromeBrowserState::FromBrowserState(web_state_.GetBrowserState()));
prerender_service->StartPrerender(main_frame_item->GetURL(), web::Referrer(), prerender_service->StartPrerender(main_frame_item->GetURL(), web::Referrer(),
ui::PAGE_TRANSITION_LINK, ui::PAGE_TRANSITION_LINK, &web_state_,
/*immediately=*/true); /*immediately=*/true);
EXPECT_TRUE(ShouldAllowRequestUrl(safe_url, /*for_main_frame=*/false) EXPECT_TRUE(ShouldAllowRequestUrl(safe_url, /*for_main_frame=*/false)
......
...@@ -159,7 +159,7 @@ void ChromeOmniboxClientIOS::OnResultChanged( ...@@ -159,7 +159,7 @@ void ChromeOmniboxClientIOS::OnResultChanged(
ui::PageTransition transition = ui::PageTransitionFromInt( ui::PageTransition transition = ui::PageTransitionFromInt(
match.transition | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR); match.transition | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
service->StartPrerender(match.destination_url, web::Referrer(), transition, service->StartPrerender(match.destination_url, web::Referrer(), transition,
is_inline_autocomplete); controller_->GetWebState(), is_inline_autocomplete);
} else { } else {
service->CancelPrerender(); service->CancelPrerender();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment