Commit 44fe7d8c authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Refactor NavigationPredictor to use and expose its WebContents

NavigationPredictor is currently tied to a RFH via mojo channels but
the lifetime of a RFH is difficult to reason about, especially during a
navigation to a different origin. This CL moves NavigationPredictor to
a WebContents-centric approach with the added benefits of easier
lifetime management and easier use in dependent code.

Of important note:
* The mojo channels that manage the lifetime of a NavigationPredictor
are still tied to the main frame RFH since that maps to how renderers
actually work.
* A NavigationPredictor can enter a "lame duck" state where it is
observing a WebContents that has been destroyed, but has yet to be
destroyed itself by mojo. Thankfully, this is easily detected since
a WebContentsObserver's web_contents() method returns nullptr when this
occurs. Both places where web_contents() is used are now guarded by
this check.
* Multiple RFHs may belong to one WebContents, but only one
NavigationPredictor will be created for each WebContents because the
RFH must be the single mainframe to construct a NavigationPredictor.

Bug: 1023485, 1041828
Change-Id: I6234225132048c70539395a3d1869602e7b0a0ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2008775
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734090}
parent 8cc25398
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/search_engines/template_url_service_factory.h"
#include "components/search_engines/template_url_service.h" #include "components/search_engines/template_url_service.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/site_instance.h" #include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/message.h" #include "mojo/public/cpp/bindings/message.h"
...@@ -42,14 +41,6 @@ namespace { ...@@ -42,14 +41,6 @@ namespace {
const base::Feature kNavigationPredictorMultiplePrerenders{ const base::Feature kNavigationPredictorMultiplePrerenders{
"NavigationPredictorMultiplePrerenders", base::FEATURE_ENABLED_BY_DEFAULT}; "NavigationPredictorMultiplePrerenders", base::FEATURE_ENABLED_BY_DEFAULT};
bool IsMainFrame(content::RenderFrameHost* rfh) {
// Don't use rfh->GetRenderViewHost()->GetMainFrame() here because
// RenderViewHost is being deprecated and because in OOPIF,
// RenderViewHost::GetMainFrame() returns nullptr for child frames hosted in a
// different process from the main frame.
return rfh->GetParent() == nullptr;
}
std::string GetURLWithoutRefParams(const GURL& gurl) { std::string GetURLWithoutRefParams(const GURL& gurl) {
url::Replacements<char> replacements; url::Replacements<char> replacements;
replacements.ClearRef(); replacements.ClearRef();
...@@ -121,10 +112,8 @@ struct NavigationPredictor::NavigationScore { ...@@ -121,10 +112,8 @@ struct NavigationPredictor::NavigationScore {
base::Optional<size_t> score_rank; base::Optional<size_t> score_rank;
}; };
NavigationPredictor::NavigationPredictor( NavigationPredictor::NavigationPredictor(content::WebContents* web_contents)
content::RenderFrameHost* render_frame_host) : browser_context_(web_contents->GetBrowserContext()),
: browser_context_(
render_frame_host->GetSiteInstance()->GetBrowserContext()),
ratio_area_scale_(base::GetFieldTrialParamByFeatureAsInt( ratio_area_scale_(base::GetFieldTrialParamByFeatureAsInt(
blink::features::kNavigationPredictor, blink::features::kNavigationPredictor,
"ratio_area_scale", "ratio_area_scale",
...@@ -210,22 +199,15 @@ NavigationPredictor::NavigationPredictor( ...@@ -210,22 +199,15 @@ NavigationPredictor::NavigationPredictor(
normalize_navigation_scores_(base::GetFieldTrialParamByFeatureAsBool( normalize_navigation_scores_(base::GetFieldTrialParamByFeatureAsBool(
blink::features::kNavigationPredictor, blink::features::kNavigationPredictor,
"normalize_scores", "normalize_scores",
true)), true)) {
render_frame_host_(render_frame_host) {
DCHECK(browser_context_); DCHECK(browser_context_);
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
DCHECK(render_frame_host_);
if (!IsMainFrame(render_frame_host))
return;
if (browser_context_->IsOffTheRecord()) if (browser_context_->IsOffTheRecord())
return; return;
ukm_recorder_ = ukm::UkmRecorder::Get(); ukm_recorder_ = ukm::UkmRecorder::Get();
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
current_visibility_ = web_contents->GetVisibility(); current_visibility_ = web_contents->GetVisibility();
ukm_source_id_ = web_contents->GetLastCommittedSourceId(); ukm_source_id_ = web_contents->GetLastCommittedSourceId();
Observe(web_contents); Observe(web_contents);
...@@ -250,9 +232,13 @@ void NavigationPredictor::Create( ...@@ -250,9 +232,13 @@ void NavigationPredictor::Create(
if (render_frame_host->GetParent()) if (render_frame_host->GetParent())
return; return;
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
if (!web_contents)
return;
mojo::MakeSelfOwnedReceiver( mojo::MakeSelfOwnedReceiver(
std::make_unique<NavigationPredictor>(render_frame_host), std::make_unique<NavigationPredictor>(web_contents), std::move(receiver));
std::move(receiver));
} }
bool NavigationPredictor::IsValidMetricFromRenderer( bool NavigationPredictor::IsValidMetricFromRenderer(
...@@ -261,7 +247,6 @@ bool NavigationPredictor::IsValidMetricFromRenderer( ...@@ -261,7 +247,6 @@ bool NavigationPredictor::IsValidMetricFromRenderer(
metric.source_url.SchemeIsHTTPOrHTTPS(); metric.source_url.SchemeIsHTTPOrHTTPS();
} }
void NavigationPredictor::RecordTimingOnClick() { void NavigationPredictor::RecordTimingOnClick() {
base::TimeTicks current_timing = base::TimeTicks::Now(); base::TimeTicks current_timing = base::TimeTicks::Now();
...@@ -948,6 +933,13 @@ double NavigationPredictor::GetPageMetricsScore() const { ...@@ -948,6 +933,13 @@ double NavigationPredictor::GetPageMetricsScore() const {
void NavigationPredictor::NotifyPredictionUpdated( void NavigationPredictor::NotifyPredictionUpdated(
const std::vector<std::unique_ptr<NavigationScore>>& const std::vector<std::unique_ptr<NavigationScore>>&
sorted_navigation_scores) { sorted_navigation_scores) {
// It is possible for this class to still exist while its WebContents and
// RenderFrameHost are being destroyed. This can be detected by checking
// |web_contents()| which will be nullptr if the WebContents has been
// destroyed.
if (!web_contents())
return;
NavigationPredictorKeyedService* service = NavigationPredictorKeyedService* service =
NavigationPredictorKeyedServiceFactory::GetForProfile( NavigationPredictorKeyedServiceFactory::GetForProfile(
Profile::FromBrowserContext(browser_context_)); Profile::FromBrowserContext(browser_context_));
...@@ -957,7 +949,7 @@ void NavigationPredictor::NotifyPredictionUpdated( ...@@ -957,7 +949,7 @@ void NavigationPredictor::NotifyPredictionUpdated(
for (const auto& nav_score : sorted_navigation_scores) { for (const auto& nav_score : sorted_navigation_scores) {
top_urls.push_back(nav_score->url); top_urls.push_back(nav_score->url);
} }
service->OnPredictionUpdated(render_frame_host_, document_url_, top_urls); service->OnPredictionUpdated(web_contents(), document_url_, top_urls);
} }
void NavigationPredictor::MaybeTakeActionOnLoad( void NavigationPredictor::MaybeTakeActionOnLoad(
...@@ -1008,6 +1000,13 @@ void NavigationPredictor::Prefetch( ...@@ -1008,6 +1000,13 @@ void NavigationPredictor::Prefetch(
DCHECK(!prerender_handle_); DCHECK(!prerender_handle_);
DCHECK(!prefetch_url_); DCHECK(!prefetch_url_);
// It is possible for this class to still exist while its WebContents and
// RenderFrameHost are being destroyed. This can be detected by checking
// |web_contents()| which will be nullptr if the WebContents has been
// destroyed.
if (!web_contents())
return;
content::SessionStorageNamespace* session_storage_namespace = content::SessionStorageNamespace* session_storage_namespace =
web_contents()->GetController().GetDefaultSessionStorageNamespace(); web_contents()->GetController().GetDefaultSessionStorageNamespace();
gfx::Size size = web_contents()->GetContainerBounds().size(); gfx::Size size = web_contents()->GetContainerBounds().size();
......
...@@ -29,7 +29,7 @@ namespace content { ...@@ -29,7 +29,7 @@ namespace content {
class BrowserContext; class BrowserContext;
class NavigationHandle; class NavigationHandle;
class RenderFrameHost; class RenderFrameHost;
} } // namespace content
namespace prerender { namespace prerender {
class PrerenderManager; class PrerenderManager;
...@@ -44,9 +44,7 @@ class NavigationPredictor : public blink::mojom::AnchorElementMetricsHost, ...@@ -44,9 +44,7 @@ class NavigationPredictor : public blink::mojom::AnchorElementMetricsHost,
public content::WebContentsObserver, public content::WebContentsObserver,
public prerender::PrerenderHandle::Observer { public prerender::PrerenderHandle::Observer {
public: public:
// |render_frame_host| is the host associated with the render frame. It is explicit NavigationPredictor(content::WebContents* web_contents);
// used to retrieve metrics at the browser side.
explicit NavigationPredictor(content::RenderFrameHost* render_frame_host);
~NavigationPredictor() override; ~NavigationPredictor() override;
// Create and bind NavigationPredictor. // Create and bind NavigationPredictor.
...@@ -327,8 +325,8 @@ class NavigationPredictor : public blink::mojom::AnchorElementMetricsHost, ...@@ -327,8 +325,8 @@ class NavigationPredictor : public blink::mojom::AnchorElementMetricsHost,
// The URL of the current page. // The URL of the current page.
GURL document_url_; GURL document_url_;
// Render frame host of the current page. // WebContents of the current page.
const content::RenderFrameHost* render_frame_host_; const content::WebContents* web_contents_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
......
...@@ -7,17 +7,15 @@ ...@@ -7,17 +7,15 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
NavigationPredictorKeyedService::Prediction::Prediction( NavigationPredictorKeyedService::Prediction::Prediction(
const content::RenderFrameHost* render_frame_host, const content::WebContents* web_contents,
const GURL& source_document_url, const GURL& source_document_url,
const std::vector<GURL>& sorted_predicted_urls) const std::vector<GURL>& sorted_predicted_urls)
: render_frame_host_(render_frame_host), : web_contents_(web_contents),
source_document_url_(source_document_url), source_document_url_(source_document_url),
sorted_predicted_urls_(sorted_predicted_urls) { sorted_predicted_urls_(sorted_predicted_urls) {}
// |render_frame_host_| will be used by consumers in future.
ALLOW_UNUSED_LOCAL(render_frame_host_);
}
NavigationPredictorKeyedService::Prediction::Prediction( NavigationPredictorKeyedService::Prediction::Prediction(
const NavigationPredictorKeyedService::Prediction& other) = default; const NavigationPredictorKeyedService::Prediction& other) = default;
...@@ -37,6 +35,11 @@ NavigationPredictorKeyedService::Prediction::sorted_predicted_urls() const { ...@@ -37,6 +35,11 @@ NavigationPredictorKeyedService::Prediction::sorted_predicted_urls() const {
return sorted_predicted_urls_; return sorted_predicted_urls_;
} }
const content::WebContents*
NavigationPredictorKeyedService::Prediction::web_contents() const {
return web_contents_;
}
NavigationPredictorKeyedService::NavigationPredictorKeyedService( NavigationPredictorKeyedService::NavigationPredictorKeyedService(
content::BrowserContext* browser_context) content::BrowserContext* browser_context)
: search_engine_preconnector_(browser_context) { : search_engine_preconnector_(browser_context) {
...@@ -52,12 +55,12 @@ NavigationPredictorKeyedService::~NavigationPredictorKeyedService() { ...@@ -52,12 +55,12 @@ NavigationPredictorKeyedService::~NavigationPredictorKeyedService() {
} }
void NavigationPredictorKeyedService::OnPredictionUpdated( void NavigationPredictorKeyedService::OnPredictionUpdated(
const content::RenderFrameHost* render_frame_host, const content::WebContents* web_contents,
const GURL& document_url, const GURL& document_url,
const std::vector<GURL>& sorted_predicted_urls) { const std::vector<GURL>& sorted_predicted_urls) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
last_prediction_ = last_prediction_ =
Prediction(render_frame_host, document_url, sorted_predicted_urls); Prediction(web_contents, document_url, sorted_predicted_urls);
for (auto& observer : observer_list_) { for (auto& observer : observer_list_) {
observer.OnPredictionUpdated(last_prediction_); observer.OnPredictionUpdated(last_prediction_);
} }
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
namespace content { namespace content {
class BrowserContext; class BrowserContext;
class RenderFrameHost; class WebContents;
} // namespace content } // namespace content
// Keyed service that can be used to receive notifications about the URLs for // Keyed service that can be used to receive notifications about the URLs for
...@@ -27,7 +27,7 @@ class NavigationPredictorKeyedService : public KeyedService { ...@@ -27,7 +27,7 @@ class NavigationPredictorKeyedService : public KeyedService {
// Stores the next set of URLs that the user is expected to navigate to. // Stores the next set of URLs that the user is expected to navigate to.
class Prediction { class Prediction {
public: public:
Prediction(const content::RenderFrameHost* render_frame_host, Prediction(const content::WebContents* web_contents,
const GURL& source_document_url, const GURL& source_document_url,
const std::vector<GURL>& sorted_predicted_urls); const std::vector<GURL>& sorted_predicted_urls);
Prediction(const Prediction& other); Prediction(const Prediction& other);
...@@ -35,11 +35,13 @@ class NavigationPredictorKeyedService : public KeyedService { ...@@ -35,11 +35,13 @@ class NavigationPredictorKeyedService : public KeyedService {
~Prediction(); ~Prediction();
GURL source_document_url() const; GURL source_document_url() const;
std::vector<GURL> sorted_predicted_urls() const; std::vector<GURL> sorted_predicted_urls() const;
const content::WebContents* web_contents() const;
private: private:
// |render_frame_host_| from where the navigation may happen. May be // The WebContents from where the navigation may happen. Do not use this
// nullptr. // pointer outside the observer's call stack unless its destruction is also
const content::RenderFrameHost* render_frame_host_; // observed.
const content::WebContents* web_contents_;
// Current URL of the document from where the navigtion may happen. // Current URL of the document from where the navigtion may happen.
GURL source_document_url_; GURL source_document_url_;
...@@ -72,7 +74,7 @@ class NavigationPredictorKeyedService : public KeyedService { ...@@ -72,7 +74,7 @@ class NavigationPredictorKeyedService : public KeyedService {
SearchEnginePreconnector* SearchEnginePreconnectorForTesting(); SearchEnginePreconnector* SearchEnginePreconnectorForTesting();
// |document_url| may be invalid. Called by navigation predictor. // |document_url| may be invalid. Called by navigation predictor.
void OnPredictionUpdated(const content::RenderFrameHost* render_frame_host, void OnPredictionUpdated(const content::WebContents* web_contents,
const GURL& document_url, const GURL& document_url,
const std::vector<GURL>& sorted_predicted_urls); const std::vector<GURL>& sorted_predicted_urls);
......
...@@ -33,9 +33,9 @@ class TestNavigationPredictor : public NavigationPredictor { ...@@ -33,9 +33,9 @@ class TestNavigationPredictor : public NavigationPredictor {
public: public:
TestNavigationPredictor( TestNavigationPredictor(
mojo::PendingReceiver<AnchorElementMetricsHost> receiver, mojo::PendingReceiver<AnchorElementMetricsHost> receiver,
content::RenderFrameHost* render_frame_host, content::WebContents* web_contents,
bool init_feature_list) bool init_feature_list)
: NavigationPredictor(render_frame_host), : NavigationPredictor(web_contents),
receiver_(this, std::move(receiver)) { receiver_(this, std::move(receiver)) {
if (init_feature_list) { if (init_feature_list) {
const std::vector<base::Feature> features = { const std::vector<base::Feature> features = {
...@@ -83,10 +83,10 @@ class TestNavigationPredictorBasedOnScroll : public TestNavigationPredictor { ...@@ -83,10 +83,10 @@ class TestNavigationPredictorBasedOnScroll : public TestNavigationPredictor {
public: public:
TestNavigationPredictorBasedOnScroll( TestNavigationPredictorBasedOnScroll(
mojo::PendingReceiver<AnchorElementMetricsHost> receiver, mojo::PendingReceiver<AnchorElementMetricsHost> receiver,
content::RenderFrameHost* render_frame_host, content::WebContents* web_contents,
bool init_feature_list) bool init_feature_list)
: TestNavigationPredictor(std::move(receiver), : TestNavigationPredictor(std::move(receiver),
render_frame_host, web_contents,
init_feature_list) {} init_feature_list) {}
~TestNavigationPredictorBasedOnScroll() override {} ~TestNavigationPredictorBasedOnScroll() override {}
...@@ -139,7 +139,7 @@ class NavigationPredictorTest : public ChromeRenderViewHostTestHarness { ...@@ -139,7 +139,7 @@ class NavigationPredictorTest : public ChromeRenderViewHostTestHarness {
void SetUp() override { void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp(); ChromeRenderViewHostTestHarness::SetUp();
predictor_service_helper_ = std::make_unique<TestNavigationPredictor>( predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
predictor_service_.BindNewPipeAndPassReceiver(), main_rfh(), predictor_service_.BindNewPipeAndPassReceiver(), web_contents(),
!field_trial_initiated_); !field_trial_initiated_);
} }
...@@ -509,7 +509,7 @@ class NavigationPredictorSendUkmMetricsEnabledTest ...@@ -509,7 +509,7 @@ class NavigationPredictorSendUkmMetricsEnabledTest
void SetUp() override { void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp(); ChromeRenderViewHostTestHarness::SetUp();
predictor_service_helper_ = std::make_unique<TestNavigationPredictor>( predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
predictor_service_.BindNewPipeAndPassReceiver(), main_rfh(), false); predictor_service_.BindNewPipeAndPassReceiver(), web_contents(), false);
} }
struct TestMetrics { struct TestMetrics {
...@@ -744,7 +744,8 @@ class NavigationPredictorPrefetchAfterPreconnectEnabledTest ...@@ -744,7 +744,8 @@ class NavigationPredictorPrefetchAfterPreconnectEnabledTest
ChromeRenderViewHostTestHarness::SetUp(); ChromeRenderViewHostTestHarness::SetUp();
predictor_service_helper_ = predictor_service_helper_ =
std::make_unique<TestNavigationPredictorBasedOnScroll>( std::make_unique<TestNavigationPredictorBasedOnScroll>(
predictor_service_.BindNewPipeAndPassReceiver(), main_rfh(), false); predictor_service_.BindNewPipeAndPassReceiver(), web_contents(),
false);
} }
blink::mojom::AnchorElementMetricsPtr CreateMetricsPtrWithRatioDistance( blink::mojom::AnchorElementMetricsPtr CreateMetricsPtrWithRatioDistance(
...@@ -803,7 +804,7 @@ class NavigationPredictorPrefetchDisabledTest : public NavigationPredictorTest { ...@@ -803,7 +804,7 @@ class NavigationPredictorPrefetchDisabledTest : public NavigationPredictorTest {
void SetUp() override { void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp(); ChromeRenderViewHostTestHarness::SetUp();
predictor_service_helper_ = std::make_unique<TestNavigationPredictor>( predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
predictor_service_.BindNewPipeAndPassReceiver(), main_rfh(), false); predictor_service_.BindNewPipeAndPassReceiver(), web_contents(), false);
} }
}; };
...@@ -886,7 +887,7 @@ class NavigationPredictorPreconnectPrefetchDisabledTest ...@@ -886,7 +887,7 @@ class NavigationPredictorPreconnectPrefetchDisabledTest
void SetUp() override { void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp(); ChromeRenderViewHostTestHarness::SetUp();
predictor_service_helper_ = std::make_unique<TestNavigationPredictor>( predictor_service_helper_ = std::make_unique<TestNavigationPredictor>(
predictor_service_.BindNewPipeAndPassReceiver(), main_rfh(), false); predictor_service_.BindNewPipeAndPassReceiver(), web_contents(), false);
} }
}; };
......
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