Commit ba9f7b9f authored by trevordixon's avatar trevordixon Committed by Commit bot

predictors: Mark before_first_contentful_paint for resources fetched before fcp.

BUG=631966
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2755093002
Cr-Commit-Position: refs/heads/master@{#468621}
parent f0759345
...@@ -31,13 +31,16 @@ ResourcePrefetchPredictorPageLoadMetricsObserver::CreateIfNeeded( ...@@ -31,13 +31,16 @@ ResourcePrefetchPredictorPageLoadMetricsObserver::CreateIfNeeded(
if (!predictor) if (!predictor)
return nullptr; return nullptr;
return base::MakeUnique<ResourcePrefetchPredictorPageLoadMetricsObserver>( return base::MakeUnique<ResourcePrefetchPredictorPageLoadMetricsObserver>(
predictor); predictor, web_contents);
} }
ResourcePrefetchPredictorPageLoadMetricsObserver:: ResourcePrefetchPredictorPageLoadMetricsObserver::
ResourcePrefetchPredictorPageLoadMetricsObserver( ResourcePrefetchPredictorPageLoadMetricsObserver(
predictors::ResourcePrefetchPredictor* predictor) predictors::ResourcePrefetchPredictor* predictor,
: predictor_(predictor) { content::WebContents* web_contents)
: predictor_(predictor),
web_contents_(web_contents),
record_histograms_(false) {
DCHECK(predictor_); DCHECK(predictor_);
} }
...@@ -49,31 +52,42 @@ ResourcePrefetchPredictorPageLoadMetricsObserver::OnStart( ...@@ -49,31 +52,42 @@ ResourcePrefetchPredictorPageLoadMetricsObserver::OnStart(
content::NavigationHandle* navigation_handle, content::NavigationHandle* navigation_handle,
const GURL& currently_commited_url, const GURL& currently_commited_url,
bool started_in_foreground) { bool started_in_foreground) {
return (started_in_foreground && record_histograms_ =
predictor_->IsUrlPrefetchable(navigation_handle->GetURL())) started_in_foreground &&
? CONTINUE_OBSERVING predictor_->IsUrlPrefetchable(navigation_handle->GetURL());
: STOP_OBSERVING;
return CONTINUE_OBSERVING;
} }
page_load_metrics::PageLoadMetricsObserver::ObservePolicy page_load_metrics::PageLoadMetricsObserver::ObservePolicy
ResourcePrefetchPredictorPageLoadMetricsObserver::OnHidden( ResourcePrefetchPredictorPageLoadMetricsObserver::OnHidden(
const page_load_metrics::PageLoadTiming& timing, const page_load_metrics::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& extra_info) { const page_load_metrics::PageLoadExtraInfo& extra_info) {
return STOP_OBSERVING; record_histograms_ = false;
return CONTINUE_OBSERVING;
} }
void ResourcePrefetchPredictorPageLoadMetricsObserver::OnFirstContentfulPaint( void ResourcePrefetchPredictorPageLoadMetricsObserver::OnFirstContentfulPaint(
const page_load_metrics::PageLoadTiming& timing, const page_load_metrics::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& extra_info) { const page_load_metrics::PageLoadExtraInfo& extra_info) {
PAGE_LOAD_HISTOGRAM( predictors::NavigationID navigation_id(web_contents_);
internal::kHistogramResourcePrefetchPredictorFirstContentfulPaint,
timing.paint_timing.first_contentful_paint.value()); predictor_->RecordFirstContentfulPaint(
navigation_id, extra_info.navigation_start +
timing.paint_timing.first_contentful_paint.value());
if (record_histograms_) {
PAGE_LOAD_HISTOGRAM(
internal::kHistogramResourcePrefetchPredictorFirstContentfulPaint,
timing.paint_timing.first_contentful_paint.value());
}
} }
void ResourcePrefetchPredictorPageLoadMetricsObserver::OnFirstMeaningfulPaint( void ResourcePrefetchPredictorPageLoadMetricsObserver::OnFirstMeaningfulPaint(
const page_load_metrics::PageLoadTiming& timing, const page_load_metrics::PageLoadTiming& timing,
const page_load_metrics::PageLoadExtraInfo& extra_info) { const page_load_metrics::PageLoadExtraInfo& extra_info) {
PAGE_LOAD_HISTOGRAM( if (record_histograms_) {
internal::kHistogramResourcePrefetchPredictorFirstMeaningfulPaint, PAGE_LOAD_HISTOGRAM(
timing.paint_timing.first_meaningful_paint.value()); internal::kHistogramResourcePrefetchPredictorFirstMeaningfulPaint,
timing.paint_timing.first_meaningful_paint.value());
}
} }
...@@ -35,7 +35,8 @@ class ResourcePrefetchPredictorPageLoadMetricsObserver ...@@ -35,7 +35,8 @@ class ResourcePrefetchPredictorPageLoadMetricsObserver
// Public for testing. Normally one should use CreateIfNeeded. Predictor must // Public for testing. Normally one should use CreateIfNeeded. Predictor must
// outlive this observer. // outlive this observer.
explicit ResourcePrefetchPredictorPageLoadMetricsObserver( explicit ResourcePrefetchPredictorPageLoadMetricsObserver(
predictors::ResourcePrefetchPredictor* predictor); predictors::ResourcePrefetchPredictor* predictor,
content::WebContents* web_contents);
~ResourcePrefetchPredictorPageLoadMetricsObserver() override; ~ResourcePrefetchPredictorPageLoadMetricsObserver() override;
...@@ -55,6 +56,8 @@ class ResourcePrefetchPredictorPageLoadMetricsObserver ...@@ -55,6 +56,8 @@ class ResourcePrefetchPredictorPageLoadMetricsObserver
private: private:
predictors::ResourcePrefetchPredictor* predictor_; predictors::ResourcePrefetchPredictor* predictor_;
content::WebContents* web_contents_;
bool record_histograms_;
DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorPageLoadMetricsObserver); DISALLOW_COPY_AND_ASSIGN(ResourcePrefetchPredictorPageLoadMetricsObserver);
}; };
......
...@@ -48,7 +48,7 @@ class ResourcePrefetchPredictorPageLoadMetricsObserverTest ...@@ -48,7 +48,7 @@ class ResourcePrefetchPredictorPageLoadMetricsObserverTest
void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override { void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
tracker->AddObserver( tracker->AddObserver(
base::MakeUnique<ResourcePrefetchPredictorPageLoadMetricsObserver>( base::MakeUnique<ResourcePrefetchPredictorPageLoadMetricsObserver>(
predictor_.get())); predictor_.get(), web_contents()));
} }
std::unique_ptr<testing::StrictMock<MockResourcePrefetchPredictor>> std::unique_ptr<testing::StrictMock<MockResourcePrefetchPredictor>>
......
...@@ -469,6 +469,7 @@ ResourcePrefetchPredictor::OriginRequestSummary::~OriginRequestSummary() {} ...@@ -469,6 +469,7 @@ ResourcePrefetchPredictor::OriginRequestSummary::~OriginRequestSummary() {}
ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary() ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary()
: resource_type(content::RESOURCE_TYPE_LAST_TYPE), : resource_type(content::RESOURCE_TYPE_LAST_TYPE),
priority(net::IDLE), priority(net::IDLE),
before_first_contentful_paint(false),
was_cached(false), was_cached(false),
has_validators(false), has_validators(false),
always_revalidate(false), always_revalidate(false),
...@@ -490,6 +491,14 @@ bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse( ...@@ -490,6 +491,14 @@ bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse(
if (!request_info) if (!request_info)
return false; return false;
// This method is called when the response is started, so this field reflects
// the time at which the response began, not when it finished, as would
// arguably be ideal. This means if firstContentfulPaint happens after the
// response has started, but before it's finished, we will erroneously mark
// the resource as having been loaded before firstContentfulPaint. This is
// a rare and insignificant enough occurrence that we opt to record the time
// here for the sake of simplicity.
summary->response_time = base::TimeTicks::Now();
summary->resource_url = request.original_url(); summary->resource_url = request.original_url();
summary->request_url = request.url(); summary->request_url = request.url();
content::ResourceType resource_type_from_request = content::ResourceType resource_type_from_request =
...@@ -517,7 +526,9 @@ bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse( ...@@ -517,7 +526,9 @@ bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse(
ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary( ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary(
const GURL& i_main_frame_url) const GURL& i_main_frame_url)
: main_frame_url(i_main_frame_url), initial_url(i_main_frame_url) {} : main_frame_url(i_main_frame_url),
initial_url(i_main_frame_url),
first_contentful_paint(base::TimeTicks::Max()) {}
ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary( ResourcePrefetchPredictor::PageRequestSummary::PageRequestSummary(
const PageRequestSummary& other) = default; const PageRequestSummary& other) = default;
...@@ -645,6 +656,18 @@ void ResourcePrefetchPredictor::RecordMainFrameLoadComplete( ...@@ -645,6 +656,18 @@ void ResourcePrefetchPredictor::RecordMainFrameLoadComplete(
} }
} }
void ResourcePrefetchPredictor::RecordFirstContentfulPaint(
const NavigationID& navigation_id,
const base::TimeTicks& first_contentful_paint) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (initialization_state_ != INITIALIZED)
return;
NavigationMap::iterator nav_it = inflight_navigations_.find(navigation_id);
if (nav_it != inflight_navigations_.end())
nav_it->second->first_contentful_paint = first_contentful_paint;
}
void ResourcePrefetchPredictor::StartPrefetching(const GURL& url, void ResourcePrefetchPredictor::StartPrefetching(const GURL& url,
PrefetchOrigin origin) { PrefetchOrigin origin) {
TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StartPrefetching", "url", TRACE_EVENT1("browser", "ResourcePrefetchPredictor::StartPrefetching", "url",
...@@ -844,6 +867,12 @@ void ResourcePrefetchPredictor::OnNavigationComplete( ...@@ -844,6 +867,12 @@ void ResourcePrefetchPredictor::OnNavigationComplete(
std::unique_ptr<PageRequestSummary> summary = std::move(nav_it->second); std::unique_ptr<PageRequestSummary> summary = std::move(nav_it->second);
inflight_navigations_.erase(nav_it); inflight_navigations_.erase(nav_it);
// Set before_first_contentful paint for each resource.
for (auto& request_summary : summary->subresource_requests) {
request_summary.before_first_contentful_paint =
request_summary.response_time < summary->first_contentful_paint;
}
const GURL& initial_url = summary->initial_url; const GURL& initial_url = summary->initial_url;
ResourcePrefetchPredictor::Prediction prediction; ResourcePrefetchPredictor::Prediction prediction;
bool has_data = GetPrefetchData(initial_url, &prediction); bool has_data = GetPrefetchData(initial_url, &prediction);
...@@ -1359,6 +1388,8 @@ void ResourcePrefetchPredictor::LearnNavigation( ...@@ -1359,6 +1388,8 @@ void ResourcePrefetchPredictor::LearnNavigation(
resource_to_add->set_average_position(i + 1); resource_to_add->set_average_position(i + 1);
resource_to_add->set_priority( resource_to_add->set_priority(
static_cast<ResourceData::Priority>(summary.priority)); static_cast<ResourceData::Priority>(summary.priority));
resource_to_add->set_before_first_contentful_paint(
summary.before_first_contentful_paint);
resource_to_add->set_has_validators(summary.has_validators); resource_to_add->set_has_validators(summary.has_validators);
resource_to_add->set_always_revalidate(summary.always_revalidate); resource_to_add->set_always_revalidate(summary.always_revalidate);
...@@ -1408,6 +1439,8 @@ void ResourcePrefetchPredictor::LearnNavigation( ...@@ -1408,6 +1439,8 @@ void ResourcePrefetchPredictor::LearnNavigation(
old_resource->set_priority( old_resource->set_priority(
static_cast<ResourceData::Priority>(new_summary.priority)); static_cast<ResourceData::Priority>(new_summary.priority));
old_resource->set_before_first_contentful_paint(
new_summary.before_first_contentful_paint);
int position = new_index[resource_url] + 1; int position = new_index[resource_url] + 1;
int total = int total =
...@@ -1435,6 +1468,8 @@ void ResourcePrefetchPredictor::LearnNavigation( ...@@ -1435,6 +1468,8 @@ void ResourcePrefetchPredictor::LearnNavigation(
resource_to_add->set_average_position(i + 1); resource_to_add->set_average_position(i + 1);
resource_to_add->set_priority( resource_to_add->set_priority(
static_cast<ResourceData::Priority>(summary.priority)); static_cast<ResourceData::Priority>(summary.priority));
resource_to_add->set_before_first_contentful_paint(
summary.before_first_contentful_paint);
resource_to_add->set_has_validators(new_resources[i].has_validators); resource_to_add->set_has_validators(new_resources[i].has_validators);
resource_to_add->set_always_revalidate( resource_to_add->set_always_revalidate(
new_resources[i].always_revalidate); new_resources[i].always_revalidate);
......
...@@ -132,6 +132,8 @@ class ResourcePrefetchPredictor ...@@ -132,6 +132,8 @@ class ResourcePrefetchPredictor
GURL request_url; // URL after all redirects. GURL request_url; // URL after all redirects.
content::ResourceType resource_type; content::ResourceType resource_type;
net::RequestPriority priority; net::RequestPriority priority;
base::TimeTicks response_time;
bool before_first_contentful_paint;
// Only for responses. // Only for responses.
std::string mime_type; std::string mime_type;
...@@ -158,6 +160,7 @@ class ResourcePrefetchPredictor ...@@ -158,6 +160,7 @@ class ResourcePrefetchPredictor
GURL main_frame_url; GURL main_frame_url;
GURL initial_url; GURL initial_url;
base::TimeTicks first_contentful_paint;
// Stores all subresource requests within a single navigation, from initial // Stores all subresource requests within a single navigation, from initial
// main frame request to navigation completion. // main frame request to navigation completion.
...@@ -228,6 +231,11 @@ class ResourcePrefetchPredictor ...@@ -228,6 +231,11 @@ class ResourcePrefetchPredictor
// Called when the main frame of a page completes loading. // Called when the main frame of a page completes loading.
void RecordMainFrameLoadComplete(const NavigationID& navigation_id); void RecordMainFrameLoadComplete(const NavigationID& navigation_id);
// Called after the main frame's first contentful paint.
void RecordFirstContentfulPaint(
const NavigationID& navigation_id,
const base::TimeTicks& first_contentful_paint);
// Starts prefetching if it is enabled for |origin| and prefetching data // Starts prefetching if it is enabled for |origin| and prefetching data
// exists for the |main_frame_url| either at the URL or at the host level. // exists for the |main_frame_url| either at the URL or at the host level.
void StartPrefetching(const GURL& main_frame_url, PrefetchOrigin origin); void StartPrefetching(const GURL& main_frame_url, PrefetchOrigin origin);
...@@ -297,6 +305,8 @@ class ResourcePrefetchPredictor ...@@ -297,6 +305,8 @@ class ResourcePrefetchPredictor
TestPrecisionRecallHistograms); TestPrecisionRecallHistograms);
FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest, FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
TestPrefetchingDurationHistogram); TestPrefetchingDurationHistogram);
FRIEND_TEST_ALL_PREFIXES(ResourcePrefetchPredictorTest,
TestRecordFirstContentfulPaint);
enum InitializationState { enum InitializationState {
NOT_INITIALIZED = 0, NOT_INITIALIZED = 0,
......
...@@ -57,6 +57,7 @@ message ResourceData { ...@@ -57,6 +57,7 @@ message ResourceData {
optional Priority priority = 7; optional Priority priority = 7;
optional bool has_validators = 8; optional bool has_validators = 8;
optional bool always_revalidate = 9; optional bool always_revalidate = 9;
optional bool before_first_contentful_paint = 10;
} }
// Represents a collection of subresources associated with URL or host. // Represents a collection of subresources associated with URL or host.
......
...@@ -72,6 +72,7 @@ const char kScriptXHRPath[] = "/predictors/xhr.js"; ...@@ -72,6 +72,7 @@ const char kScriptXHRPath[] = "/predictors/xhr.js";
const char kHtmlIframePath[] = "/predictors/html_iframe.html"; const char kHtmlIframePath[] = "/predictors/html_iframe.html";
const char kHtmlJavascriptRedirectPath[] = const char kHtmlJavascriptRedirectPath[] =
"/predictors/javascript_redirect.html"; "/predictors/javascript_redirect.html";
const char kHtmlFcpOrderPath[] = "/predictors/subresource_fcp_order.html";
// Host, path. // Host, path.
const char* kScript[2] = {kFooHost, kScriptPath}; const char* kScript[2] = {kFooHost, kScriptPath};
...@@ -85,7 +86,9 @@ struct ResourceSummary { ...@@ -85,7 +86,9 @@ struct ResourceSummary {
is_no_store(false), is_no_store(false),
is_external(false), is_external(false),
is_observable(true), is_observable(true),
is_prohibited(false) {} is_prohibited(false) {
request.before_first_contentful_paint = true;
}
ResourcePrefetchPredictor::URLRequestSummary request; ResourcePrefetchPredictor::URLRequestSummary request;
// Allows to update HTTP ETag. // Allows to update HTTP ETag.
...@@ -99,6 +102,9 @@ struct ResourceSummary { ...@@ -99,6 +102,9 @@ struct ResourceSummary {
// A request with |is_prohibited| set to true makes the test that originates // A request with |is_prohibited| set to true makes the test that originates
// the request fail. // the request fail.
bool is_prohibited; bool is_prohibited;
// If set, the HTTP request handler will sleep for this long before
// responding.
base::TimeDelta delay;
}; };
struct RedirectEdge { struct RedirectEdge {
...@@ -170,9 +176,12 @@ void SetValidNavigationID(NavigationID* navigation_id) { ...@@ -170,9 +176,12 @@ void SetValidNavigationID(NavigationID* navigation_id) {
} }
void ModifySubresourceForComparison(URLRequestSummary* subresource, void ModifySubresourceForComparison(URLRequestSummary* subresource,
bool match_navigation_id) { bool match_navigation_id,
bool match_before_first_contentful_paint) {
if (!match_navigation_id) if (!match_navigation_id)
SetValidNavigationID(&subresource->navigation_id); SetValidNavigationID(&subresource->navigation_id);
if (!match_before_first_contentful_paint)
subresource->before_first_contentful_paint = true;
if (subresource->resource_type == content::RESOURCE_TYPE_IMAGE && if (subresource->resource_type == content::RESOURCE_TYPE_IMAGE &&
subresource->priority == net::LOWEST) { subresource->priority == net::LOWEST) {
// Fuzzy comparison for images because an image priority can be // Fuzzy comparison for images because an image priority can be
...@@ -186,15 +195,18 @@ void ModifySubresourceForComparison(URLRequestSummary* subresource, ...@@ -186,15 +195,18 @@ void ModifySubresourceForComparison(URLRequestSummary* subresource,
// and fail the test if the expectation is not met. // and fail the test if the expectation is not met.
void CompareSubresources(std::vector<URLRequestSummary> actual_subresources, void CompareSubresources(std::vector<URLRequestSummary> actual_subresources,
std::vector<URLRequestSummary> expected_subresources, std::vector<URLRequestSummary> expected_subresources,
bool match_navigation_id) { bool match_navigation_id,
bool match_before_first_contentful_paint) {
// Duplicate resources can be observed in a single navigation but // Duplicate resources can be observed in a single navigation but
// ResourcePrefetchPredictor only cares about the first occurrence of each. // ResourcePrefetchPredictor only cares about the first occurrence of each.
RemoveDuplicateSubresources(&actual_subresources); RemoveDuplicateSubresources(&actual_subresources);
for (auto& subresource : actual_subresources) for (auto& subresource : actual_subresources)
ModifySubresourceForComparison(&subresource, match_navigation_id); ModifySubresourceForComparison(&subresource, match_navigation_id,
match_before_first_contentful_paint);
for (auto& subresource : expected_subresources) for (auto& subresource : expected_subresources)
ModifySubresourceForComparison(&subresource, match_navigation_id); ModifySubresourceForComparison(&subresource, match_navigation_id,
match_before_first_contentful_paint);
EXPECT_THAT(actual_subresources, EXPECT_THAT(actual_subresources,
testing::UnorderedElementsAreArray(expected_subresources)); testing::UnorderedElementsAreArray(expected_subresources));
...@@ -234,11 +246,14 @@ class LearningObserver : public TestObserver { ...@@ -234,11 +246,14 @@ class LearningObserver : public TestObserver {
LearningObserver(ResourcePrefetchPredictor* predictor, LearningObserver(ResourcePrefetchPredictor* predictor,
const size_t expected_url_visit_count, const size_t expected_url_visit_count,
const PageRequestSummary& expected_summary, const PageRequestSummary& expected_summary,
bool match_navigation_id) bool match_navigation_id,
bool match_before_first_contentful_paint)
: TestObserver(predictor), : TestObserver(predictor),
url_visit_count_(expected_url_visit_count), url_visit_count_(expected_url_visit_count),
summary_(expected_summary), summary_(expected_summary),
match_navigation_id_(match_navigation_id) {} match_navigation_id_(match_navigation_id),
match_before_first_contentful_paint_(
match_before_first_contentful_paint) {}
// TestObserver: // TestObserver:
void OnNavigationLearned(size_t url_visit_count, void OnNavigationLearned(size_t url_visit_count,
...@@ -249,7 +264,8 @@ class LearningObserver : public TestObserver { ...@@ -249,7 +264,8 @@ class LearningObserver : public TestObserver {
for (const auto& resource : summary.subresource_requests) for (const auto& resource : summary.subresource_requests)
current_navigation_ids_.insert(resource.navigation_id); current_navigation_ids_.insert(resource.navigation_id);
CompareSubresources(summary.subresource_requests, CompareSubresources(summary.subresource_requests,
summary_.subresource_requests, match_navigation_id_); summary_.subresource_requests, match_navigation_id_,
match_before_first_contentful_paint_);
run_loop_.Quit(); run_loop_.Quit();
} }
...@@ -264,6 +280,7 @@ class LearningObserver : public TestObserver { ...@@ -264,6 +280,7 @@ class LearningObserver : public TestObserver {
size_t url_visit_count_; size_t url_visit_count_;
PageRequestSummary summary_; PageRequestSummary summary_;
bool match_navigation_id_; bool match_navigation_id_;
bool match_before_first_contentful_paint_;
std::set<NavigationID> current_navigation_ids_; std::set<NavigationID> current_navigation_ids_;
DISALLOW_COPY_AND_ASSIGN(LearningObserver); DISALLOW_COPY_AND_ASSIGN(LearningObserver);
...@@ -361,12 +378,18 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest { ...@@ -361,12 +378,18 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest {
ResourcePrefetchPredictor::SetAllowPortInUrlsForTesting(false); ResourcePrefetchPredictor::SetAllowPortInUrlsForTesting(false);
} }
void TestLearningAndPrefetching(const GURL& main_frame_url) { void TestLearningAndPrefetching(
const GURL& main_frame_url,
bool match_before_first_contentful_paint = false) {
// Navigate to |main_frame_url| and check all the expectations. // Navigate to |main_frame_url| and check all the expectations.
NavigateToURLAndCheckSubresources(main_frame_url); NavigateToURLAndCheckSubresources(main_frame_url,
WindowOpenDisposition::CURRENT_TAB,
match_before_first_contentful_paint);
ClearCache(); ClearCache();
// It is needed to have at least two resource hits to trigger prefetch. // It is needed to have at least two resource hits to trigger prefetch.
NavigateToURLAndCheckSubresources(main_frame_url); NavigateToURLAndCheckSubresources(main_frame_url,
WindowOpenDisposition::CURRENT_TAB,
match_before_first_contentful_paint);
ClearCache(); ClearCache();
// Prefetch all needed resources and change expectations so that all // Prefetch all needed resources and change expectations so that all
// cacheable resources should be served from cache next navigation. // cacheable resources should be served from cache next navigation.
...@@ -390,7 +413,8 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest { ...@@ -390,7 +413,8 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest {
void NavigateToURLAndCheckSubresources( void NavigateToURLAndCheckSubresources(
const GURL& navigation_url, const GURL& navigation_url,
WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB) { WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB,
bool match_before_first_contentful_paint = false) {
GURL initial_url = GetLastClientSideRedirectEndpoint(navigation_url); GURL initial_url = GetLastClientSideRedirectEndpoint(navigation_url);
GURL main_frame_url = GetRedirectEndpoint(navigation_url); GURL main_frame_url = GetRedirectEndpoint(navigation_url);
std::vector<URLRequestSummary> url_request_summaries; std::vector<URLRequestSummary> url_request_summaries;
...@@ -408,7 +432,7 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest { ...@@ -408,7 +432,7 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest {
predictor_, UpdateAndGetVisitCount(initial_url), predictor_, UpdateAndGetVisitCount(initial_url),
CreatePageRequestSummary(main_frame_url.spec(), initial_url.spec(), CreatePageRequestSummary(main_frame_url.spec(), initial_url.spec(),
url_request_summaries), url_request_summaries),
match_navigation_id); match_navigation_id, match_before_first_contentful_paint);
ui_test_utils::NavigateToURLWithDisposition( ui_test_utils::NavigateToURLWithDisposition(
browser(), navigation_url, disposition, browser(), navigation_url, disposition,
ui_test_utils::BROWSER_TEST_NONE); ui_test_utils::BROWSER_TEST_NONE);
...@@ -674,6 +698,9 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest { ...@@ -674,6 +698,9 @@ class ResourcePrefetchPredictorBrowserTest : public InProcessBrowserTest {
// 0kB. // 0kB.
http_response->set_content(std::string(1024, ' ')); http_response->set_content(std::string(1024, ' '));
if (!summary.delay.is_zero())
base::PlatformThread::Sleep(summary.delay);
return std::move(http_response); return std::move(http_response);
} }
...@@ -744,6 +771,22 @@ IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Simple) { ...@@ -744,6 +771,22 @@ IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Simple) {
internal::kResourcePrefetchPredictorPrefetchHitsSize, 4, 1); internal::kResourcePrefetchPredictorPrefetchHitsSize, 4, 1);
} }
IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest,
SubresourceFcpOrder) {
AddResource(GetURL(kStylePath), content::RESOURCE_TYPE_STYLESHEET,
net::HIGHEST);
AddResource(GetURL(kScriptPath), content::RESOURCE_TYPE_SCRIPT, net::MEDIUM);
ResourceSummary* image = AddResource(
GetURL(kImagePath), content::RESOURCE_TYPE_IMAGE, net::LOWEST);
// Delay HTTP response to ensure enough time to receive notice of
// firstContentfulPaint.
image->delay = base::TimeDelta::FromMilliseconds(1500);
image->request.before_first_contentful_paint = false;
TestLearningAndPrefetching(GetURL(kHtmlFcpOrderPath), true);
}
IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Redirect) { IN_PROC_BROWSER_TEST_F(ResourcePrefetchPredictorBrowserTest, Redirect) {
GURL initial_url = GetURLWithHost(kFooHost, kRedirectPath); GURL initial_url = GetURLWithHost(kFooHost, kRedirectPath);
GURL redirected_url = GURL redirected_url =
......
...@@ -139,7 +139,7 @@ class ResourcePrefetchPredictorTables : public PredictorTableBase { ...@@ -139,7 +139,7 @@ class ResourcePrefetchPredictorTables : public PredictorTableBase {
// Database version. Always increment it when any change is made to the data // Database version. Always increment it when any change is made to the data
// schema (including the .proto). // schema (including the .proto).
static constexpr int kDatabaseVersion = 8; static constexpr int kDatabaseVersion = 9;
// PredictorTableBase: // PredictorTableBase:
void CreateTableIfNonExistent() override; void CreateTableIfNonExistent() override;
......
...@@ -37,6 +37,7 @@ void InitializeResourceData(ResourceData* resource, ...@@ -37,6 +37,7 @@ void InitializeResourceData(ResourceData* resource,
resource->set_consecutive_misses(consecutive_misses); resource->set_consecutive_misses(consecutive_misses);
resource->set_average_position(average_position); resource->set_average_position(average_position);
resource->set_priority(static_cast<ResourceData::Priority>(priority)); resource->set_priority(static_cast<ResourceData::Priority>(priority));
resource->set_before_first_contentful_paint(true);
resource->set_has_validators(has_validators); resource->set_has_validators(has_validators);
resource->set_always_revalidate(always_revalidate); resource->set_always_revalidate(always_revalidate);
} }
...@@ -163,6 +164,7 @@ URLRequestSummary CreateURLRequestSummary(SessionID::id_type tab_id, ...@@ -163,6 +164,7 @@ URLRequestSummary CreateURLRequestSummary(SessionID::id_type tab_id,
summary.request_url = summary.resource_url; summary.request_url = summary.resource_url;
summary.resource_type = resource_type; summary.resource_type = resource_type;
summary.priority = priority; summary.priority = priority;
summary.before_first_contentful_paint = true;
summary.mime_type = mime_type; summary.mime_type = mime_type;
summary.was_cached = was_cached; summary.was_cached = was_cached;
if (!redirect_url.empty()) if (!redirect_url.empty())
...@@ -188,6 +190,7 @@ std::ostream& operator<<(std::ostream& os, const ResourceData& resource) { ...@@ -188,6 +190,7 @@ std::ostream& operator<<(std::ostream& os, const ResourceData& resource) {
<< resource.number_of_misses() << "," << resource.number_of_misses() << ","
<< resource.consecutive_misses() << "," << resource.consecutive_misses() << ","
<< resource.average_position() << "," << resource.priority() << "," << resource.average_position() << "," << resource.priority() << ","
<< resource.before_first_contentful_paint() << ","
<< resource.has_validators() << "," << resource.always_revalidate() << resource.has_validators() << "," << resource.always_revalidate()
<< "]"; << "]";
} }
...@@ -232,9 +235,10 @@ std::ostream& operator<<(std::ostream& os, const PageRequestSummary& summary) { ...@@ -232,9 +235,10 @@ std::ostream& operator<<(std::ostream& os, const PageRequestSummary& summary) {
std::ostream& operator<<(std::ostream& os, const URLRequestSummary& summary) { std::ostream& operator<<(std::ostream& os, const URLRequestSummary& summary) {
return os << "[" << summary.navigation_id << "," << summary.resource_url return os << "[" << summary.navigation_id << "," << summary.resource_url
<< "," << summary.resource_type << "," << summary.priority << "," << "," << summary.resource_type << "," << summary.priority << ","
<< summary.mime_type << "," << summary.was_cached << "," << summary.before_first_contentful_paint << "," << summary.mime_type
<< summary.redirect_url << "," << summary.has_validators << "," << "," << summary.was_cached << "," << summary.redirect_url << ","
<< summary.always_revalidate << "]"; << summary.has_validators << "," << summary.always_revalidate
<< "]";
} }
std::ostream& operator<<(std::ostream& os, const NavigationID& navigation_id) { std::ostream& operator<<(std::ostream& os, const NavigationID& navigation_id) {
...@@ -262,6 +266,8 @@ bool operator==(const ResourceData& lhs, const ResourceData& rhs) { ...@@ -262,6 +266,8 @@ bool operator==(const ResourceData& lhs, const ResourceData& rhs) {
lhs.consecutive_misses() == rhs.consecutive_misses() && lhs.consecutive_misses() == rhs.consecutive_misses() &&
AlmostEqual(lhs.average_position(), rhs.average_position()) && AlmostEqual(lhs.average_position(), rhs.average_position()) &&
lhs.priority() == rhs.priority() && lhs.priority() == rhs.priority() &&
lhs.before_first_contentful_paint() ==
rhs.before_first_contentful_paint() &&
lhs.has_validators() == rhs.has_validators() && lhs.has_validators() == rhs.has_validators() &&
lhs.always_revalidate() == rhs.always_revalidate(); lhs.always_revalidate() == rhs.always_revalidate();
} }
...@@ -297,6 +303,8 @@ bool operator==(const URLRequestSummary& lhs, const URLRequestSummary& rhs) { ...@@ -297,6 +303,8 @@ bool operator==(const URLRequestSummary& lhs, const URLRequestSummary& rhs) {
lhs.resource_url == rhs.resource_url && lhs.resource_url == rhs.resource_url &&
lhs.resource_type == rhs.resource_type && lhs.resource_type == rhs.resource_type &&
lhs.priority == rhs.priority && lhs.mime_type == rhs.mime_type && lhs.priority == rhs.priority && lhs.mime_type == rhs.mime_type &&
lhs.before_first_contentful_paint ==
rhs.before_first_contentful_paint &&
lhs.was_cached == rhs.was_cached && lhs.was_cached == rhs.was_cached &&
lhs.redirect_url == rhs.redirect_url && lhs.redirect_url == rhs.redirect_url &&
lhs.has_validators == rhs.has_validators && lhs.has_validators == rhs.has_validators &&
......
...@@ -2117,4 +2117,57 @@ TEST_F(ResourcePrefetchPredictorTest, TestPrefetchingDurationHistogram) { ...@@ -2117,4 +2117,57 @@ TEST_F(ResourcePrefetchPredictorTest, TestPrefetchingDurationHistogram) {
internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 1); internal::kResourcePrefetchPredictorPrefetchingDurationHistogram, 1);
} }
TEST_F(ResourcePrefetchPredictorTest, TestRecordFirstContentfulPaint) {
using testing::_;
EXPECT_CALL(*mock_tables_.get(), UpdateRedirectData(_, _));
EXPECT_CALL(*mock_tables_.get(), UpdateOriginData(_));
auto res1_time = base::TimeTicks::FromInternalValue(1);
auto res2_time = base::TimeTicks::FromInternalValue(2);
auto fcp_time = base::TimeTicks::FromInternalValue(3);
auto res3_time = base::TimeTicks::FromInternalValue(4);
URLRequestSummary main_frame =
CreateURLRequestSummary(1, "http://www.google.com");
predictor_->RecordURLRequest(main_frame);
EXPECT_EQ(1U, predictor_->inflight_navigations_.size());
URLRequestSummary resource1 = CreateURLRequestSummary(
1, "http://www.google.com", "http://google.com/style1.css",
content::RESOURCE_TYPE_STYLESHEET, net::MEDIUM, "text/css", false);
resource1.response_time = res1_time;
predictor_->RecordURLResponse(resource1);
URLRequestSummary resource2 = CreateURLRequestSummary(
1, "http://www.google.com", "http://google.com/script1.js",
content::RESOURCE_TYPE_SCRIPT, net::MEDIUM, "text/javascript", false);
resource2.response_time = res2_time;
predictor_->RecordURLResponse(resource2);
URLRequestSummary resource3 = CreateURLRequestSummary(
1, "http://www.google.com", "http://google.com/script2.js",
content::RESOURCE_TYPE_SCRIPT, net::MEDIUM, "text/javascript", false);
resource3.response_time = res3_time;
predictor_->RecordURLResponse(resource3);
predictor_->RecordFirstContentfulPaint(main_frame.navigation_id, fcp_time);
PrefetchData host_data = CreatePrefetchData("www.google.com");
InitializeResourceData(host_data.add_resources(),
"http://google.com/style1.css",
content::RESOURCE_TYPE_STYLESHEET, 1, 0, 0, 1.0,
net::MEDIUM, false, false);
InitializeResourceData(
host_data.add_resources(), "http://google.com/script1.js",
content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, 2.0, net::MEDIUM, false, false);
ResourceData* resource3_rd = host_data.add_resources();
InitializeResourceData(resource3_rd, "http://google.com/script2.js",
content::RESOURCE_TYPE_SCRIPT, 1, 0, 0, 3.0,
net::MEDIUM, false, false);
resource3_rd->set_before_first_contentful_paint(false);
EXPECT_CALL(*mock_tables_.get(),
UpdateResourceData(host_data, PREFETCH_KEY_TYPE_HOST));
predictor_->RecordMainFrameLoadComplete(main_frame.navigation_id);
profile_->BlockUntilHistoryProcessesPendingRequests();
}
} // namespace predictors } // namespace predictors
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<th>Consec Misses</th> <th>Consec Misses</th>
<th>Average Position</th> <th>Average Position</th>
<th>Score</th> <th>Score</th>
<th>Before FCP</th>
</tr> </tr>
</thead> </thead>
<tbody id="rpp_url_body"> <tbody id="rpp_url_body">
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
<th>Consec Misses</th> <th>Consec Misses</th>
<th>Average Position</th> <th>Average Position</th>
<th>Score</th> <th>Score</th>
<th>Before FCP</th>
</tr> </tr>
</thead> </thead>
<tbody id="rpp_host_body"> <tbody id="rpp_host_body">
......
...@@ -92,6 +92,8 @@ function renderCacheData(body, database) { ...@@ -92,6 +92,8 @@ function renderCacheData(body, database) {
resource.position; resource.position;
row.appendChild(document.createElement('td')).textContent = row.appendChild(document.createElement('td')).textContent =
resource.score; resource.score;
row.appendChild(document.createElement('td')).textContent =
resource.before_first_contentful_paint;
body.appendChild(row); body.appendChild(row);
} }
} }
......
...@@ -132,6 +132,8 @@ void PredictorsHandler::AddPrefetchDataMapToListValue( ...@@ -132,6 +132,8 @@ void PredictorsHandler::AddPrefetchDataMapToListValue(
resource->SetDouble("position", r.average_position()); resource->SetDouble("position", r.average_position());
resource->SetDouble( resource->SetDouble(
"score", ResourcePrefetchPredictorTables::ComputeResourceScore(r)); "score", ResourcePrefetchPredictorTables::ComputeResourceScore(r));
resource->SetBoolean("before_first_contentful_paint",
r.before_first_contentful_paint());
resource->SetBoolean( resource->SetBoolean(
"is_prefetchable", "is_prefetchable",
resource_prefetch_predictor_->IsResourcePrefetchable(r)); resource_prefetch_predictor_->IsResourcePrefetchable(r));
......
<!DOCTYPE html>
<html>
<head>
<title>Subresources collection</title>
<link rel="stylesheet" href="/handled-by-test/style.css">
<script src="/handled-by-test/script.js"></script>
</head>
<body>
<p>Hello world!</p>
<!--
The server will wait long enough for firstContentfulPaint to happen before
responding with this image.
-->
<img src="/handled-by-test/image.png">
</body>
</html>
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