blob: d5046709dab26a1544aa1f3eb4d5f65686b75f40 [file] [log] [blame]
// Copyright 2015 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.
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/check_op.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/no_state_prefetch_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h"
#include "chrome/browser/page_load_metrics/page_load_metrics_initialize.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_histograms.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/prerender/prerender_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_restore_test_helper.h"
#include "chrome/browser/sessions/session_service_factory.h"
#include "chrome/browser/sessions/session_service_test_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/page_load_metrics/browser/observers/core_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "components/page_load_metrics/browser/page_load_tracker.h"
#include "components/prefs/pref_service.h"
#include "components/prerender/common/prerender_origin.h"
#include "components/sessions/content/content_test_helper.h"
#include "components/sessions/core/serialized_navigation_entry.h"
#include "components/sessions/core/serialized_navigation_entry_test_helper.h"
#include "components/ukm/test_ukm_recorder.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/common/content_features.h"
#include "content/public/common/referrer.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/navigation_handle_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/base/net_errors.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/controllable_http_response.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/network/public/cpp/features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/use_counter/css_property_id.mojom.h"
#include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
using page_load_metrics::PageLoadMetricsTestWaiter;
using TimingField = page_load_metrics::PageLoadMetricsTestWaiter::TimingField;
using WebFeature = blink::mojom::WebFeature;
using testing::UnorderedElementsAre;
using NoStatePrefetch = ukm::builders::NoStatePrefetch;
namespace {
constexpr char kCacheablePathPrefix[] = "/cacheable";
std::unique_ptr<net::test_server::HttpResponse> HandleCachableRequestHandler(
const net::test_server::HttpRequest& request) {
if (!base::StartsWith(request.relative_url, kCacheablePathPrefix,
base::CompareCase::SENSITIVE)) {
return nullptr;
}
if (request.headers.find("If-None-Match") != request.headers.end()) {
return std::make_unique<net::test_server::RawHttpResponse>(
"HTTP/1.1 304 Not Modified", "");
}
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
response->set_code(net::HTTP_OK);
response->set_content_type("text/html");
response->AddCustomHeader("cache-control", "max-age=60");
response->AddCustomHeader("etag", "foobar");
response->set_content("hi");
return std::move(response);
}
} // namespace
class PageLoadMetricsBrowserTest : public InProcessBrowserTest {
public:
PageLoadMetricsBrowserTest() {
scoped_feature_list_.InitWithFeatures(
{ukm::kUkmFeature, blink::features::kPortals,
blink::features::kPortalsCrossOrigin},
{});
}
~PageLoadMetricsBrowserTest() override {}
protected:
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
embedded_test_server()->RegisterRequestHandler(
base::BindRepeating(&HandleCachableRequestHandler));
}
void PreRunTestOnMainThread() override {
InProcessBrowserTest::PreRunTestOnMainThread();
histogram_tester_ = std::make_unique<base::HistogramTester>();
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
}
// Force navigation to a new page, so the currently tracked page load runs its
// OnComplete callback. You should prefer to use PageLoadMetricsTestWaiter,
// and only use NavigateToUntrackedUrl for cases where the waiter isn't
// sufficient.
void NavigateToUntrackedUrl() {
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
}
void MakeComponentFullscreen(const std::string& id) {
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"document.getElementById(\"" + id + "\").webkitRequestFullscreen();"));
}
std::string GetRecordedPageLoadMetricNames() {
auto entries = histogram_tester_->GetTotalCountsForPrefix("PageLoad.");
std::vector<std::string> names;
std::transform(
entries.begin(), entries.end(), std::back_inserter(names),
[](const std::pair<std::string, base::HistogramBase::Count>& entry) {
return entry.first;
});
return base::JoinString(names, ",");
}
bool NoPageLoadMetricsRecorded() {
// Determine whether any 'public' page load metrics are recorded. We exclude
// 'internal' metrics as these may be recorded for debugging purposes.
size_t total_pageload_histograms =
histogram_tester_->GetTotalCountsForPrefix("PageLoad.").size();
size_t total_internal_histograms =
histogram_tester_->GetTotalCountsForPrefix("PageLoad.Internal.").size();
DCHECK_GE(total_pageload_histograms, total_internal_histograms);
return total_pageload_histograms - total_internal_histograms == 0;
}
std::unique_ptr<PageLoadMetricsTestWaiter> CreatePageLoadMetricsTestWaiter() {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
return std::make_unique<PageLoadMetricsTestWaiter>(web_contents);
}
// Triggers nostate prefetch of |url|.
void TriggerNoStatePrefetch(const GURL& url) {
prerender::PrerenderManager* prerender_manager =
prerender::PrerenderManagerFactory::GetForBrowserContext(
browser()->profile());
ASSERT_TRUE(prerender_manager);
prerender::test_utils::TestPrerenderContentsFactory*
prerender_contents_factory =
new prerender::test_utils::TestPrerenderContentsFactory();
prerender_manager->SetPrerenderContentsFactoryForTest(
prerender_contents_factory);
content::SessionStorageNamespace* storage_namespace =
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetController()
.GetDefaultSessionStorageNamespace();
ASSERT_TRUE(storage_namespace);
std::unique_ptr<prerender::test_utils::TestPrerender> test_prerender =
prerender_contents_factory->ExpectPrerenderContents(
prerender::FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
std::unique_ptr<prerender::PrerenderHandle> prerender_handle =
prerender_manager->AddPrerenderFromOmnibox(url, storage_namespace,
gfx::Size(640, 480));
ASSERT_EQ(prerender_handle->contents(), test_prerender->contents());
// The final status may be either FINAL_STATUS_NOSTATE_PREFETCH_FINISHED or
// FINAL_STATUS_RECENTLY_VISITED.
test_prerender->contents()->set_skip_final_checks(true);
}
void VerifyBasicPageLoadUkms(const GURL& expected_source_url) {
using PageLoad = ukm::builders::PageLoad;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(kv.second.get(),
expected_source_url);
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::
kDocumentTiming_NavigationToDOMContentLoadedEventFiredName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kDocumentTiming_NavigationToLoadEventFiredName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), PageLoad::kPaintTiming_NavigationToFirstPaintName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), PageLoad::kMainFrameResource_SocketReusedName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), PageLoad::kMainFrameResource_DNSDelayName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), PageLoad::kMainFrameResource_ConnectDelayName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kMainFrameResource_RequestStartToSendStartName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kMainFrameResource_SendStartToReceiveHeadersEndName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kMainFrameResource_RequestStartToReceiveHeadersEndName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kMainFrameResource_NavigationStartToRequestStartName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(),
PageLoad::kMainFrameResource_HttpProtocolSchemeName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), PageLoad::kSiteEngagementScoreName));
}
}
void VerifyNavigationMetrics(std::vector<GURL> expected_source_urls) {
int expected_count = expected_source_urls.size();
// Verify if the elapsed time from the navigation start are recorded.
histogram_tester_->ExpectTotalCount(
internal::kHistogramNavigationTimingNavigationStartToFirstRequestStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::kHistogramNavigationTimingNavigationStartToFirstResponseStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingNavigationStartToFirstLoaderCallback,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::kHistogramNavigationTimingNavigationStartToFinalRequestStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::kHistogramNavigationTimingNavigationStartToFinalResponseStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingNavigationStartToFinalLoaderCallback,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingNavigationStartToNavigationCommitSent,
expected_count);
// Verify if the intervals between adjacent milestones are recorded.
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingFirstRequestStartToFirstResponseStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingFirstResponseStartToFirstLoaderCallback,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingFinalRequestStartToFinalResponseStart,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingFinalResponseStartToFinalLoaderCallback,
expected_count);
histogram_tester_->ExpectTotalCount(
internal::
kHistogramNavigationTimingFinalLoaderCallbackToNavigationCommitSent,
expected_count);
using ukm::builders::NavigationTiming;
const std::vector<const char*> metrics = {
NavigationTiming::kFirstRequestStartName,
NavigationTiming::kFirstResponseStartName,
NavigationTiming::kFirstLoaderCallbackName,
NavigationTiming::kFinalRequestStartName,
NavigationTiming::kFinalResponseStartName,
NavigationTiming::kFinalLoaderCallbackName,
NavigationTiming::kNavigationCommitSentName};
const auto& entries = test_ukm_recorder_->GetMergedEntriesByName(
NavigationTiming::kEntryName);
ASSERT_EQ(expected_source_urls.size(), entries.size());
int i = 0;
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(kv.second.get(),
expected_source_urls[i++]);
// Verify if the elapsed times from the navigation start are recorded.
for (const char* metric : metrics) {
EXPECT_TRUE(
test_ukm_recorder_->EntryHasMetric(kv.second.get(), metric));
}
}
}
enum class EarlyHintsPolicy {
// Navigation will receive no early hints response.
NoHints,
// Navigation will receive one early hints response.
OneHint,
// Navigation will receive multiple early hints responses.
MultipleHints,
};
content::NavigationHandleTiming NavigateWithEarlyHints(
EarlyHintsPolicy policy) {
auto response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page.html",
/*relative_url_is_prefix=*/true);
EXPECT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/mock_page.html");
content::NavigationHandleObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), url);
browser()->OpenURL(content::OpenURLParams(
url, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
response->WaitForRequest();
// Serve the Early Hints responses.
const char kHttpResponseHeader[] =
"HTTP/1.1 103 Early Hints\r\n"
"\r\n";
switch (policy) {
case EarlyHintsPolicy::NoHints:
break;
case EarlyHintsPolicy::OneHint:
response->Send(kHttpResponseHeader);
break;
case EarlyHintsPolicy::MultipleHints:
response->Send(kHttpResponseHeader);
response->Send(kHttpResponseHeader);
response->Send(kHttpResponseHeader);
break;
}
// Serve the main response.
const char kMainHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
response->Send(kMainHttpResponseHeader);
response->Done();
// Wait until the navigation completes.
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->Wait();
EXPECT_TRUE(observer.has_committed());
// Force navigation to another page, which should force logging of
// histograms persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
VerifyNavigationMetrics({url});
return observer.navigation_handle_timing();
}
content::NavigationHandleTiming RedirectWithEarlyHints(
EarlyHintsPolicy policy_for_first_request,
EarlyHintsPolicy policy_for_final_request) {
auto response1 =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page1.html",
/*relative_url_is_prefix=*/true);
auto response2 =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page2.html",
/*relative_url_is_prefix=*/true);
EXPECT_TRUE(embedded_test_server()->Start());
GURL url1 = embedded_test_server()->GetURL("/mock_page1.html");
GURL url2 = embedded_test_server()->GetURL("/mock_page2.html");
content::NavigationHandleObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), url1);
browser()->OpenURL(content::OpenURLParams(
url1, content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
response1->WaitForRequest();
// Serve the Early Hints responses for the first request.
const char kHttpResponseHeader[] =
"HTTP/1.1 103 Early Hints\r\n"
"\r\n";
switch (policy_for_first_request) {
case EarlyHintsPolicy::NoHints:
break;
case EarlyHintsPolicy::OneHint:
response1->Send(kHttpResponseHeader);
break;
case EarlyHintsPolicy::MultipleHints:
response1->Send(kHttpResponseHeader);
response1->Send(kHttpResponseHeader);
response1->Send(kHttpResponseHeader);
break;
}
// Serve the redirection response.
const char kHttpRedirectResponseHeader[] =
"HTTP/1.1 307 Temporary Redirect\r\n"
"Location: /mock_page2.html\r\n"
"\r\n";
response1->Send(kHttpRedirectResponseHeader);
// Wait for the redirected request.
response1->Done();
response2->WaitForRequest();
// Serve the Early Hints responses for the redirected request.
switch (policy_for_final_request) {
case EarlyHintsPolicy::NoHints:
break;
case EarlyHintsPolicy::OneHint:
response2->Send(kHttpResponseHeader);
break;
case EarlyHintsPolicy::MultipleHints:
response2->Send(kHttpResponseHeader);
response2->Send(kHttpResponseHeader);
response2->Send(kHttpResponseHeader);
break;
}
// Serve the main response.
const char kMainHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
response2->Send(kMainHttpResponseHeader);
response2->Done();
// Wait until the navigation completes.
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->Wait();
EXPECT_TRUE(observer.has_committed());
// Force navigation to another page, which should force logging of
// histograms persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
VerifyNavigationMetrics({url2});
return observer.navigation_handle_timing();
}
content::RenderFrameHost* RenderFrameHost() const {
return browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
}
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<base::HistogramTester> histogram_tester_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
private:
DISALLOW_COPY_AND_ASSIGN(PageLoadMetricsBrowserTest);
};
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NewPage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/title1.html");
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstPaint);
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramParseDuration, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseBlockedOnScriptLoad, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseBlockedOnScriptExecution, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 1);
VerifyBasicPageLoadUkms(url);
const auto& nostate_prefetch_entries =
test_ukm_recorder_->GetMergedEntriesByName(NoStatePrefetch::kEntryName);
EXPECT_EQ(0u, nostate_prefetch_entries.size());
VerifyNavigationMetrics({url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, Redirect) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL final_url = embedded_test_server()->GetURL("/title1.html");
GURL first_url =
embedded_test_server()->GetURL("/server-redirect?" + final_url.spec());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstPaint);
ui_test_utils::NavigateToURL(browser(), first_url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramParseDuration, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseBlockedOnScriptLoad, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseBlockedOnScriptExecution, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 1);
VerifyBasicPageLoadUkms(final_url);
const auto& nostate_prefetch_entries =
test_ukm_recorder_->GetMergedEntriesByName(NoStatePrefetch::kEntryName);
EXPECT_EQ(0u, nostate_prefetch_entries.size());
VerifyNavigationMetrics({final_url});
}
// Triggers nostate prefetch, and verifies that the UKM metrics related to
// nostate prefetch are recorded correctly.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoStatePrefetchMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/title1.html");
TriggerNoStatePrefetch(url);
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstPaint);
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 1);
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NoStatePrefetch::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(kv.second.get(), url);
// UKM metrics related to attempted nostate prefetch should be recorded.
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NoStatePrefetch::kPrefetchedRecently_FinalStatusName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NoStatePrefetch::kPrefetchedRecently_OriginName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NoStatePrefetch::kPrefetchedRecently_PrefetchAgeName));
}
VerifyNavigationMetrics({url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, CachedPage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL(kCacheablePathPrefix);
// Navigate to the |url| to cache the main resource.
ui_test_utils::NavigateToURL(browser(), url);
NavigateToUntrackedUrl();
using PageLoad = ukm::builders::PageLoad;
auto entries =
test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
auto* const uncached_load_entry = kv.second.get();
test_ukm_recorder_->ExpectEntrySourceHasUrl(uncached_load_entry, url);
EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(uncached_load_entry,
PageLoad::kWasCachedName));
}
VerifyNavigationMetrics({url});
// Reset the recorders so it would only contain the cached pageload.
histogram_tester_ = std::make_unique<base::HistogramTester>();
test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
// Second navigation to the |url| should hit cache.
ui_test_utils::NavigateToURL(browser(), url);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
entries = test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
auto* const cached_load_entry = kv.second.get();
test_ukm_recorder_->ExpectEntrySourceHasUrl(cached_load_entry, url);
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(cached_load_entry,
PageLoad::kWasCachedName));
}
VerifyNavigationMetrics({url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NewPageInNewForegroundTab) {
ASSERT_TRUE(embedded_test_server()->Start());
NavigateParams params(browser(),
embedded_test_server()->GetURL("/title1.html"),
ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(&params);
auto waiter = std::make_unique<PageLoadMetricsTestWaiter>(
params.navigated_or_inserted_contents);
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->Wait();
// Due to crbug.com/725347, with browser side navigation enabled, navigations
// in new tabs were recorded as starting in the background. Here we verify
// that navigations initiated in a new tab are recorded as happening in the
// foreground.
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kBackgroundHistogramLoad, 0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoPaintForEmptyDocument) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/empty.html"));
waiter->Wait();
EXPECT_FALSE(waiter->DidObserveInPage(TimingField::kFirstPaint));
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
0);
}
// TODO(crbug.com/986642): Flaky on Win and Linux.
#if defined(OS_WIN) || defined(OS_LINUX)
#define MAYBE_NoPaintForEmptyDocumentInChildFrame \
DISABLED_NoPaintForEmptyDocumentInChildFrame
#else
#define MAYBE_NoPaintForEmptyDocumentInChildFrame \
NoPaintForEmptyDocumentInChildFrame
#endif
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MAYBE_NoPaintForEmptyDocumentInChildFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL a_url(
embedded_test_server()->GetURL("/page_load_metrics/empty_iframe.html"));
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddSubFrameExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(browser(), a_url);
waiter->Wait();
EXPECT_FALSE(waiter->DidObserveInPage(TimingField::kFirstPaint));
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PaintInChildFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL a_url(embedded_test_server()->GetURL("/page_load_metrics/iframe.html"));
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstPaint);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(), a_url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PaintInDynamicChildFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstPaint);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/page_load_metrics/dynamic_iframe.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PaintInMultipleChildFrames) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL a_url(embedded_test_server()->GetURL("/page_load_metrics/iframes.html"));
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddSubFrameExpectation(TimingField::kFirstPaint);
waiter->AddPageExpectation(TimingField::kFirstPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstContentfulPaint);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(), a_url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PaintInMainAndChildFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL a_url(embedded_test_server()->GetURL(
"/page_load_metrics/main_frame_with_iframe.html"));
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstPaint);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstPaint);
waiter->AddSubFrameExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(), a_url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, SameDocumentNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
GURL url = embedded_test_server()->GetURL("/title1.html");
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
// Perform a same-document navigation. No additional metrics should be logged.
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html#hash"));
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
VerifyNavigationMetrics({url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, SameUrlNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
GURL url = embedded_test_server()->GetURL("/title1.html");
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 1);
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
VerifyNavigationMetrics({url});
// We expect one histogram sample for each navigation to title1.html.
histogram_tester_->ExpectTotalCount(internal::kHistogramDomContentLoaded, 2);
histogram_tester_->ExpectTotalCount(internal::kHistogramLoad, 2);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
VerifyNavigationMetrics({url, url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
DocWriteAbortsSubframeNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/doc_write_aborts_subframe.html"));
waiter->AddMinimumCompleteResourcesExpectation(4);
waiter->Wait();
EXPECT_FALSE(waiter->DidObserveInPage(TimingField::kFirstPaint));
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NonHtmlMainResource) {
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/circle.svg"));
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NonHttpOrHttpsUrl) {
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIVersionURL));
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, HttpErrorPage) {
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/page_load_metrics/404.html"));
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, ChromeErrorPage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/title1.html");
// By shutting down the server, we ensure a failure.
ASSERT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
content::NavigationHandleObserver observer(
browser()->tab_strip_model()->GetActiveWebContents(), url);
ui_test_utils::NavigateToURL(browser(), url);
ASSERT_TRUE(observer.is_error());
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, Ignore204Pages) {
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/page204.html"));
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, IgnoreDownloads) {
ASSERT_TRUE(embedded_test_server()->Start());
content::DownloadTestObserverTerminal downloads_observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
1, // == wait_count (only waiting for "download-test3.gif").
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/download-test3.gif"));
downloads_observer.WaitForFinished();
NavigateToUntrackedUrl();
EXPECT_TRUE(NoPageLoadMetricsRecorded())
<< "Recorded metrics: " << GetRecordedPageLoadMetricNames();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoDocumentWrite) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteBlock) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_script_block.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteReload) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_script_block.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
1);
// Reload should not log the histogram as the script is not blocked.
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kDocumentWriteBlockReload);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_script_block.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockReloadCount, 1);
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kDocumentWriteBlockReload);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_script_block.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockReloadCount, 2);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteAsync) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_async_script.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteSameDomain) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_external_script.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoDocumentWriteScript) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/document_write_no_script.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
0);
}
// TODO(crbug.com/712935): Flaky on Linux dbg.
// TODO(crbug.com/738235): Now flaky on Win and Mac.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DISABLED_BadXhtml) {
ASSERT_TRUE(embedded_test_server()->Start());
// When an XHTML page contains invalid XML, it causes a paint of the error
// message without a layout. Page load metrics currently treats this as an
// error. Eventually, we'll fix this by special casing the handling of
// documents with non-well-formed XML on the blink side. See crbug.com/627607
// for more.
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/page_load_metrics/badxml.xhtml"));
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 0);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kErrorEvents,
page_load_metrics::ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
histogram_tester_->ExpectTotalCount(
page_load_metrics::internal::kPageLoadTimingStatus, 1);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadTimingStatus,
page_load_metrics::internal::INVALID_ORDER_PARSE_START_FIRST_PAINT, 1);
}
// Test code that aborts provisional navigations.
// TODO(csharrison): Move these to unit tests once the navigation API in content
// properly calls NavigationHandle/NavigationThrottle methods.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, AbortNewNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
content::TestNavigationManager manager(
browser()->tab_strip_model()->GetActiveWebContents(), url);
Navigate(&params);
EXPECT_TRUE(manager.WaitForRequestStart());
GURL url2(embedded_test_server()->GetURL("/title2.html"));
NavigateParams params2(browser(), url2, ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
Navigate(&params2);
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramAbortNewNavigationBeforeCommit, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, AbortReload) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
content::TestNavigationManager manager(
browser()->tab_strip_model()->GetActiveWebContents(), url);
Navigate(&params);
EXPECT_TRUE(manager.WaitForRequestStart());
NavigateParams params2(browser(), url, ui::PAGE_TRANSITION_RELOAD);
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
Navigate(&params2);
waiter->Wait();
histogram_tester_->ExpectTotalCount(
internal::kHistogramAbortReloadBeforeCommit, 1);
}
// TODO(crbug.com/675061): Flaky on Win7 dbg.
#if defined(OS_WIN)
#define MAYBE_AbortClose DISABLED_AbortClose
#else
#define MAYBE_AbortClose AbortClose
#endif
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, MAYBE_AbortClose) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
content::TestNavigationManager manager(
browser()->tab_strip_model()->GetActiveWebContents(), url);
Navigate(&params);
EXPECT_TRUE(manager.WaitForRequestStart());
browser()->tab_strip_model()->GetActiveWebContents()->Close();
manager.WaitForNavigationFinished();
histogram_tester_->ExpectTotalCount(
internal::kHistogramAbortCloseBeforeCommit, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, AbortMultiple) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/title1.html"));
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
content::TestNavigationManager manager(
browser()->tab_strip_model()->GetActiveWebContents(), url);
Navigate(&params);
EXPECT_TRUE(manager.WaitForRequestStart());
GURL url2(embedded_test_server()->GetURL("/title2.html"));
NavigateParams params2(browser(), url2, ui::PAGE_TRANSITION_TYPED);
content::TestNavigationManager manager2(
browser()->tab_strip_model()->GetActiveWebContents(), url2);
Navigate(&params2);
EXPECT_TRUE(manager2.WaitForRequestStart());
manager.WaitForNavigationFinished();
GURL url3(embedded_test_server()->GetURL("/title3.html"));
NavigateParams params3(browser(), url3, ui::PAGE_TRANSITION_TYPED);
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
Navigate(&params3);
waiter->Wait();
manager2.WaitForNavigationFinished();
histogram_tester_->ExpectTotalCount(
internal::kHistogramAbortNewNavigationBeforeCommit, 2);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
NoAbortMetricsOnClientRedirect) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL first_url(embedded_test_server()->GetURL("/title1.html"));
ui_test_utils::NavigateToURL(browser(), first_url);
GURL second_url(embedded_test_server()->GetURL("/title2.html"));
NavigateParams params(browser(), second_url, ui::PAGE_TRANSITION_LINK);
content::TestNavigationManager manager(
browser()->tab_strip_model()->GetActiveWebContents(), second_url);
Navigate(&params);
EXPECT_TRUE(manager.WaitForRequestStart());
{
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(),
"window.location.reload();"));
waiter->Wait();
}
manager.WaitForNavigationFinished();
EXPECT_TRUE(
histogram_tester_
->GetTotalCountsForPrefix("PageLoad.Experimental.AbortTiming.")
.empty());
}
// TODO(crbug.com/1009885): Flaky on Linux MSan builds.
#if defined(MEMORY_SANITIZER) && defined(OS_LINUX)
#define MAYBE_FirstMeaningfulPaintRecorded DISABLED_FirstMeaningfulPaintRecorded
#else
#define MAYBE_FirstMeaningfulPaintRecorded FirstMeaningfulPaintRecorded
#endif
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MAYBE_FirstMeaningfulPaintRecorded) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstMeaningfulPaint);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
waiter->Wait();
histogram_tester_->ExpectUniqueSample(
internal::kHistogramFirstMeaningfulPaintStatus,
internal::FIRST_MEANINGFUL_PAINT_RECORDED, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstMeaningfulPaint,
1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseStartToFirstMeaningfulPaint, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
FirstMeaningfulPaintNotRecorded) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/page_with_active_connections.html"));
waiter->Wait();
// Navigate away before a FMP is reported.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
1);
histogram_tester_->ExpectUniqueSample(
internal::kHistogramFirstMeaningfulPaintStatus,
internal::FIRST_MEANINGFUL_PAINT_DID_NOT_REACH_NETWORK_STABLE, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstMeaningfulPaint,
0);
histogram_tester_->ExpectTotalCount(
internal::kHistogramParseStartToFirstMeaningfulPaint, 0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
NoStatePrefetchObserverCacheable) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
"Prerender.none_PrefetchTTFCP.Reference.NoStore.Visible", 0);
histogram_tester_->ExpectTotalCount(
"Prerender.none_PrefetchTTFCP.Reference.Cacheable.Visible", 1);
histogram_tester_->ExpectTotalCount(
"PageLoad.PaintTiming.ParseStartToFirstContentfulPaint", 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
NoStatePrefetchObserverNoStore) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/nostore.html"));
waiter->Wait();
histogram_tester_->ExpectTotalCount(
"Prerender.none_PrefetchTTFCP.Reference.NoStore.Visible", 1);
histogram_tester_->ExpectTotalCount(
"Prerender.none_PrefetchTTFCP.Reference.Cacheable.Visible", 0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PayloadSize) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
"/page_load_metrics/large.html"));
waiter->Wait();
// Payload histograms are only logged when a page load terminates, so force
// navigation to another page.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
1);
// Verify that there is a single sample recorded in the 10kB bucket (the size
// of the main HTML response).
histogram_tester_->ExpectBucketCount(internal::kHistogramPageLoadTotalBytes,
10, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PayloadSizeChildFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/page_load_metrics/large_iframe.html"));
waiter->Wait();
// Payload histograms are only logged when a page load terminates, so force
// navigation to another page.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
1);
// Verify that there is a single sample recorded in the 10kB bucket (the size
// of the iframe response).
histogram_tester_->ExpectBucketCount(internal::kHistogramPageLoadTotalBytes,
10, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
PayloadSizeIgnoresDownloads) {
ASSERT_TRUE(embedded_test_server()->Start());
content::DownloadTestObserverTerminal downloads_observer(
content::BrowserContext::GetDownloadManager(browser()->profile()),
1, // == wait_count (only waiting for "download-test1.lib").
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/download_anchor_click.html"));
downloads_observer.WaitForFinished();
NavigateToUntrackedUrl();
histogram_tester_->ExpectUniqueSample(internal::kHistogramPageLoadTotalBytes,
0, 1);
}
// Test UseCounter Features observed in the main frame are recorded, exactly
// once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterFeaturesInMainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(
WebFeature::kApplicationCacheManifestSelectSecureOrigin),
1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(
WebFeature::kApplicationCacheManifestSelectSecureOrigin),
1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterCSSPropertiesInMainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kFontFamily
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 6,
1);
// CSSPropertyID::kFontSize
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 7,
1);
histogram_tester_->ExpectBucketCount(
internal::kCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterAnimatedCSSPropertiesInMainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kWidth
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 161, 1);
// CSSPropertyID::kMarginLeft
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 91, 1);
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
class PageLoadMetricsBrowserTestWithAutoupgradesDisabled
: public PageLoadMetricsBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
PageLoadMetricsBrowserTest::SetUpCommandLine(command_line);
feature_list.InitAndDisableFeature(
blink::features::kMixedContentAutoupgrade);
}
private:
base::test::ScopedFeatureList feature_list;
};
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAutoupgradesDisabled,
UseCounterFeaturesMixedContent) {
// UseCounterFeaturesInMainFrame loads the test file on a loopback
// address. Loopback is treated as a secure origin in most ways, but it
// doesn't count as mixed content when it loads http://
// subresources. Therefore, this test loads the test file on a real HTTPS
// server.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
https_server.GetURL("/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kMixedContentAudio), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kMixedContentImage), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kMixedContentVideo), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAutoupgradesDisabled,
UseCounterCSSPropertiesMixedContent) {
// UseCounterCSSPropertiesInMainFrame loads the test file on a loopback
// address. Loopback is treated as a secure origin in most ways, but it
// doesn't count as mixed content when it loads http://
// subresources. Therefore, this test loads the test file on a real HTTPS
// server.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
https_server.GetURL("/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kFontFamily
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 6,
1);
// CSSPropertyID::kFontSize
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 7,
1);
histogram_tester_->ExpectBucketCount(
internal::kCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAutoupgradesDisabled,
UseCounterAnimatedCSSPropertiesMixedContent) {
// UseCounterCSSPropertiesInMainFrame loads the test file on a loopback
// address. Loopback is treated as a secure origin in most ways, but it
// doesn't count as mixed content when it loads http://
// subresources. Therefore, this test loads the test file on a real HTTPS
// server.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
https_server.GetURL("/page_load_metrics/use_counter_features.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kWidth
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 161, 1);
// CSSPropertyID::kMarginLeft
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 91, 1);
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterFeaturesInNonSecureMainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"non-secure.test", "/page_load_metrics/use_counter_features.html"));
MakeComponentFullscreen("testvideo");
waiter->Wait();
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kFullscreenInsecureOrigin), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kFullscreenInsecureOrigin), 1);
}
// Test UseCounter UKM features observed.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterUkmFeaturesLogged) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
GURL url = embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features.html");
ui_test_utils::NavigateToURL(browser(), url);
MakeComponentFullscreen("testvideo");
waiter->Wait();
NavigateToUntrackedUrl();
const auto& entries = test_ukm_recorder_->GetEntriesByName(
ukm::builders::Blink_UseCounter::kEntryName);
EXPECT_EQ(4u, entries.size());
std::vector<int64_t> ukm_features;
for (const auto* entry : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, url);
test_ukm_recorder_->ExpectEntryMetric(
entry, ukm::builders::Blink_UseCounter::kIsMainFrameFeatureName, 1);
const auto* metric = test_ukm_recorder_->GetEntryMetric(
entry, ukm::builders::Blink_UseCounter::kFeatureName);
DCHECK(metric);
ukm_features.push_back(*metric);
}
EXPECT_THAT(
ukm_features,
UnorderedElementsAre(
static_cast<int64_t>(WebFeature::kPageVisits),
static_cast<int64_t>(WebFeature::kFullscreenSecureOrigin),
static_cast<int64_t>(WebFeature::kNavigatorVibrate),
static_cast<int64_t>(
WebFeature::kApplicationCacheManifestSelectSecureOrigin)));
}
// Test UseCounter UKM mixed content features observed.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAutoupgradesDisabled,
UseCounterUkmMixedContentFeaturesLogged) {
// As with UseCounterFeaturesMixedContent, load on a real HTTPS server to
// trigger mixed content.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
GURL url =
https_server.GetURL("/page_load_metrics/use_counter_features.html");
ui_test_utils::NavigateToURL(browser(), url);
MakeComponentFullscreen("testvideo");
waiter->Wait();
NavigateToUntrackedUrl();
const auto& entries = test_ukm_recorder_->GetEntriesByName(
ukm::builders::Blink_UseCounter::kEntryName);
EXPECT_EQ(7u, entries.size());
std::vector<int64_t> ukm_features;
for (const auto* entry : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(entry, url);
test_ukm_recorder_->ExpectEntryMetric(
entry, ukm::builders::Blink_UseCounter::kIsMainFrameFeatureName, 1);
const auto* metric = test_ukm_recorder_->GetEntryMetric(
entry, ukm::builders::Blink_UseCounter::kFeatureName);
DCHECK(metric);
ukm_features.push_back(*metric);
}
EXPECT_THAT(ukm_features,
UnorderedElementsAre(
static_cast<int64_t>(WebFeature::kPageVisits),
static_cast<int64_t>(WebFeature::kFullscreenSecureOrigin),
static_cast<int64_t>(WebFeature::kNavigatorVibrate),
static_cast<int64_t>(
WebFeature::kApplicationCacheManifestSelectSecureOrigin),
static_cast<int64_t>(WebFeature::kMixedContentImage),
static_cast<int64_t>(WebFeature::kMixedContentAudio),
static_cast<int64_t>(WebFeature::kMixedContentVideo)));
}
// Test UseCounter Features observed in a child frame are recorded, exactly
// once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, UseCounterFeaturesInIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframe.html"));
waiter->Wait();
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
// No feature but page visits should get counted.
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kTextWholeText), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
}
// Test UseCounter Features observed in multiple child frames are recorded,
// exactly once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterFeaturesInIframes) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframes.html"));
waiter->Wait();
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kTextWholeText), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
// No feature but page visits should get counted.
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kTextWholeText), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kV8Element_Animate_Method), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kNavigatorVibrate), 0);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramMainFrameName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
}
// Test UseCounter CSS properties observed in a child frame are recorded,
// exactly once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterCSSPropertiesInIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframe.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kFontFamily
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 6,
1);
// CSSPropertyID::kFontSize
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 7,
1);
histogram_tester_->ExpectBucketCount(
internal::kCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
// Test UseCounter CSS Properties observed in multiple child frames are
// recorded, exactly once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterCSSPropertiesInIframes) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframes.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kFontFamily
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 6,
1);
// CSSPropertyID::kFontSize
histogram_tester_->ExpectBucketCount(internal::kCssPropertiesHistogramName, 7,
1);
histogram_tester_->ExpectBucketCount(
internal::kCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
// Test UseCounter CSS properties observed in a child frame are recorded,
// exactly once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterAnimatedCSSPropertiesInIframe) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframe.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kWidth
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 161, 1);
// CSSPropertyID::kMarginLeft
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 91, 1);
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
// Test UseCounter CSS Properties observed in multiple child frames are
// recorded, exactly once per feature.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterAnimatedCSSPropertiesInIframes) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/use_counter_features_in_iframes.html"));
waiter->Wait();
NavigateToUntrackedUrl();
// CSSPropertyID::kWidth
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 161, 1);
// CSSPropertyID::kMarginLeft
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName, 91, 1);
histogram_tester_->ExpectBucketCount(
internal::kAnimatedCssPropertiesHistogramName,
blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
}
// Test UseCounter Features observed for SVG pages.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
UseCounterObserveSVGImagePage) {
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
"/page_load_metrics/circle.svg"));
NavigateToUntrackedUrl();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kPageVisits), 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, LoadingMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadTimingInfo);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
// Waits until nonzero loading metrics are seen.
waiter->Wait();
}
class SessionRestorePageLoadMetricsBrowserTest
: public PageLoadMetricsBrowserTest {
public:
SessionRestorePageLoadMetricsBrowserTest() {}
// PageLoadMetricsBrowserTest:
void SetUpOnMainThread() override {
PageLoadMetricsBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
}
Browser* QuitBrowserAndRestore(Browser* browser) {
Profile* profile = browser->profile();
SessionStartupPref::SetStartupPref(
profile, SessionStartupPref(SessionStartupPref::LAST));
#if defined(OS_CHROMEOS)
SessionServiceTestHelper helper(
SessionServiceFactory::GetForProfile(profile));
helper.SetForceBrowserNotAliveWithNoWindows(true);
helper.ReleaseService();
#endif
std::unique_ptr<ScopedKeepAlive> keep_alive(new ScopedKeepAlive(
KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED));
CloseBrowserSynchronously(browser);
// Create a new window, which should trigger session restore.
chrome::NewEmptyWindow(profile);
SessionRestoreTestHelper().Wait();
return BrowserList::GetInstance()->GetLastActive();
}
void WaitForTabsToLoad(Browser* browser) {
for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
content::WebContents* contents =
browser->tab_strip_model()->GetWebContentsAt(i);
contents->GetController().LoadIfNecessary();
ASSERT_TRUE(content::WaitForLoadStop(contents));
}
}
// The PageLoadMetricsTestWaiter can observe first meaningful paints on these
// test pages while not on other simple pages such as /title1.html.
GURL GetTestURL() const {
return embedded_test_server()->GetURL(
"/page_load_metrics/main_frame_with_iframe.html");
}
GURL GetTestURL2() const {
return embedded_test_server()->GetURL("/title2.html");
}
void ExpectFirstPaintMetricsTotalCount(int expected_total_count) const {
histogram_tester_->ExpectTotalCount(
internal::kHistogramSessionRestoreForegroundTabFirstPaint,
expected_total_count);
histogram_tester_->ExpectTotalCount(
internal::kHistogramSessionRestoreForegroundTabFirstContentfulPaint,
expected_total_count);
histogram_tester_->ExpectTotalCount(
internal::kHistogramSessionRestoreForegroundTabFirstMeaningfulPaint,
expected_total_count);
}
private:
DISALLOW_COPY_AND_ASSIGN(SessionRestorePageLoadMetricsBrowserTest);
};
class SessionRestorePaintWaiter : public SessionRestoreObserver {
public:
SessionRestorePaintWaiter() { SessionRestore::AddObserver(this); }
~SessionRestorePaintWaiter() { SessionRestore::RemoveObserver(this); }
// SessionRestoreObserver implementation:
void OnWillRestoreTab(content::WebContents* contents) override {
chrome::InitializePageLoadMetricsForWebContents(contents);
auto waiter = std::make_unique<PageLoadMetricsTestWaiter>(contents);
waiter->AddPageExpectation(TimingField::kFirstPaint);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->AddPageExpectation(TimingField::kFirstMeaningfulPaint);
waiters_[contents] = std::move(waiter);
}
// First meaningful paints occur only on foreground tabs.
void WaitForForegroundTabs(size_t num_expected_foreground_tabs) {
size_t num_actual_foreground_tabs = 0;
for (auto iter = waiters_.begin(); iter != waiters_.end(); ++iter) {
if (iter->first->GetVisibility() == content::Visibility::HIDDEN)
continue;
iter->second->Wait();
++num_actual_foreground_tabs;
}
EXPECT_EQ(num_expected_foreground_tabs, num_actual_foreground_tabs);
}
private:
std::unordered_map<content::WebContents*,
std::unique_ptr<PageLoadMetricsTestWaiter>>
waiters_;
DISALLOW_COPY_AND_ASSIGN(SessionRestorePaintWaiter);
};
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
InitialVisibilityOfSingleRestoredTab) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
histogram_tester_->ExpectTotalCount(
page_load_metrics::internal::kPageLoadStartedInForeground, 1);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadStartedInForeground, true, 1);
Browser* new_browser = QuitBrowserAndRestore(browser());
ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
histogram_tester_->ExpectTotalCount(
page_load_metrics::internal::kPageLoadStartedInForeground, 2);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadStartedInForeground, true, 2);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
InitialVisibilityOfMultipleRestoredTabs) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
ui_test_utils::NavigateToURLWithDisposition(
browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
histogram_tester_->ExpectTotalCount(
page_load_metrics::internal::kPageLoadStartedInForeground, 2);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadStartedInForeground, false, 1);
Browser* new_browser = QuitBrowserAndRestore(browser());
ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
TabStripModel* tab_strip = new_browser->tab_strip_model();
ASSERT_TRUE(tab_strip);
ASSERT_EQ(2, tab_strip->count());
histogram_tester_->ExpectTotalCount(
page_load_metrics::internal::kPageLoadStartedInForeground, 4);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadStartedInForeground, true, 2);
histogram_tester_->ExpectBucketCount(
page_load_metrics::internal::kPageLoadStartedInForeground, false, 2);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
NoSessionRestore) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
ExpectFirstPaintMetricsTotalCount(0);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
SingleTabSessionRestore) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
SessionRestorePaintWaiter session_restore_paint_waiter;
QuitBrowserAndRestore(browser());
session_restore_paint_waiter.WaitForForegroundTabs(1);
ExpectFirstPaintMetricsTotalCount(1);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
MultipleTabsSessionRestore) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
ui_test_utils::NavigateToURLWithDisposition(
browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
SessionRestorePaintWaiter session_restore_paint_waiter;
Browser* new_browser = QuitBrowserAndRestore(browser());
TabStripModel* tab_strip = new_browser->tab_strip_model();
ASSERT_TRUE(tab_strip);
ASSERT_EQ(2, tab_strip->count());
// Only metrics of the initial foreground tab are recorded.
session_restore_paint_waiter.WaitForForegroundTabs(1);
ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
ExpectFirstPaintMetricsTotalCount(1);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
NavigationDuringSessionRestore) {
NavigateToUntrackedUrl();
Browser* new_browser = QuitBrowserAndRestore(browser());
auto waiter = std::make_unique<PageLoadMetricsTestWaiter>(
new_browser->tab_strip_model()->GetActiveWebContents());
waiter->AddPageExpectation(TimingField::kFirstMeaningfulPaint);
ui_test_utils::NavigateToURL(new_browser, GetTestURL());
waiter->Wait();
// No metrics recorded for the second navigation because the tab navigated
// away during session restore.
ExpectFirstPaintMetricsTotalCount(0);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
LoadingAfterSessionRestore) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
Browser* new_browser = nullptr;
{
SessionRestorePaintWaiter session_restore_paint_waiter;
new_browser = QuitBrowserAndRestore(browser());
session_restore_paint_waiter.WaitForForegroundTabs(1);
ExpectFirstPaintMetricsTotalCount(1);
}
// Load a new page after session restore.
auto waiter = std::make_unique<PageLoadMetricsTestWaiter>(
new_browser->tab_strip_model()->GetActiveWebContents());
waiter->AddPageExpectation(TimingField::kFirstMeaningfulPaint);
ui_test_utils::NavigateToURL(new_browser, GetTestURL());
waiter->Wait();
// No more metrics because the navigation is after session restore.
ExpectFirstPaintMetricsTotalCount(1);
}
#if defined(OS_WIN) || defined(OS_LINUX)
#define MAYBE_InitialForegroundTabChanged DISABLED_InitialForegroundTabChanged
#else
#define MAYBE_InitialForegroundTabChanged InitialForegroundTabChanged
#endif
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
MAYBE_InitialForegroundTabChanged) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
ui_test_utils::NavigateToURLWithDisposition(
browser(), GetTestURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
SessionRestorePaintWaiter session_restore_paint_waiter;
Browser* new_browser = QuitBrowserAndRestore(browser());
// Change the foreground tab before the first meaningful paint.
TabStripModel* tab_strip = new_browser->tab_strip_model();
ASSERT_TRUE(tab_strip);
ASSERT_EQ(2, tab_strip->count());
ASSERT_EQ(0, tab_strip->active_index());
tab_strip->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
session_restore_paint_waiter.WaitForForegroundTabs(1);
// No metrics were recorded because initial foreground tab was switched away.
ExpectFirstPaintMetricsTotalCount(0);
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
MultipleSessionRestores) {
ui_test_utils::NavigateToURL(browser(), GetTestURL());
Browser* current_browser = browser();
const int num_session_restores = 3;
for (int i = 1; i <= num_session_restores; ++i) {
SessionRestorePaintWaiter session_restore_paint_waiter;
current_browser = QuitBrowserAndRestore(current_browser);
session_restore_paint_waiter.WaitForForegroundTabs(1);
ExpectFirstPaintMetricsTotalCount(i);
}
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
RestoreForeignTab) {
sessions::SessionTab tab;
tab.tab_visual_index = 0;
tab.current_navigation_index = 1;
tab.navigations.push_back(sessions::ContentTestHelper::CreateNavigation(
GetTestURL().spec(), "one"));
tab.navigations.back().set_encoded_page_state("");
ASSERT_EQ(1, browser()->tab_strip_model()->count());
// Restore in the current tab.
content::WebContents* tab_contents = nullptr;
{
SessionRestorePaintWaiter session_restore_paint_waiter;
tab_contents = SessionRestore::RestoreForeignSessionTab(
browser()->tab_strip_model()->GetActiveWebContents(), tab,
WindowOpenDisposition::CURRENT_TAB);
ASSERT_EQ(1, browser()->tab_strip_model()->count());
ASSERT_TRUE(tab_contents);
ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
session_restore_paint_waiter.WaitForForegroundTabs(1);
ExpectFirstPaintMetricsTotalCount(1);
}
// Restore in a new foreground tab.
{
SessionRestorePaintWaiter session_restore_paint_waiter;
tab_contents = SessionRestore::RestoreForeignSessionTab(
browser()->tab_strip_model()->GetActiveWebContents(), tab,
WindowOpenDisposition::NEW_FOREGROUND_TAB);
ASSERT_EQ(2, browser()->tab_strip_model()->count());
ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
ASSERT_TRUE(tab_contents);
ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
session_restore_paint_waiter.WaitForForegroundTabs(1);
ExpectFirstPaintMetricsTotalCount(2);
}
// Restore in a new background tab.
{
tab_contents = SessionRestore::RestoreForeignSessionTab(
browser()->tab_strip_model()->GetActiveWebContents(), tab,
WindowOpenDisposition::NEW_BACKGROUND_TAB);
ASSERT_EQ(3, browser()->tab_strip_model()->count());
ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
ASSERT_TRUE(tab_contents);
ASSERT_EQ(GetTestURL(), tab_contents->GetURL());
ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(browser()));
// Do not record timings of initially background tabs.
ExpectFirstPaintMetricsTotalCount(2);
}
}
IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest,
RestoreForeignSession) {
Profile* profile = browser()->profile();
// Set up the restore data: one window with two tabs.
std::vector<const sessions::SessionWindow*> session;
sessions::SessionWindow window;
{
auto tab1 = std::make_unique<sessions::SessionTab>();
tab1->tab_visual_index = 0;
tab1->current_navigation_index = 0;
tab1->pinned = true;
tab1->navigations.push_back(sessions::ContentTestHelper::CreateNavigation(
GetTestURL().spec(), "one"));
tab1->navigations.back().set_encoded_page_state("");
window.tabs.push_back(std::move(tab1));
}
{
auto tab2 = std::make_unique<sessions::SessionTab>();
tab2->tab_visual_index = 1;
tab2->current_navigation_index = 0;
tab2->pinned = false;
tab2->navigations.push_back(sessions::ContentTestHelper::CreateNavigation(
GetTestURL2().spec(), "two"));
tab2->navigations.back().set_encoded_page_state("");
window.tabs.push_back(std::move(tab2));
}
// Restore the session window with 2 tabs.
session.push_back(static_cast<const sessions::SessionWindow*>(&window));
SessionRestorePaintWaiter session_restore_paint_waiter;
SessionRestore::RestoreForeignSessionWindows(profile, session.begin(),
session.end());
session_restore_paint_waiter.WaitForForegroundTabs(1);
Browser* new_browser = BrowserList::GetInstance()->GetLastActive();
ASSERT_TRUE(new_browser);
ASSERT_EQ(2, new_browser->tab_strip_model()->count());
ASSERT_NO_FATAL_FAILURE(WaitForTabsToLoad(new_browser));
ExpectFirstPaintMetricsTotalCount(1);
}
// TODO(crbug.com/882077) Disabled due to flaky timeouts on all platforms.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
DISABLED_ReceivedAggregateResourceDataLength) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"foo.com", "/cross_site_iframe_factory.html?foo"));
waiter->Wait();
int64_t one_frame_page_size = waiter->current_network_bytes();
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"a.com", "/cross_site_iframe_factory.html?a(b,c,d(e,f,g))"));
// Verify that 7 iframes are fetched, with some amount of tolerance since
// favicon is fetched only once.
waiter->AddMinimumNetworkBytesExpectation(7 * (one_frame_page_size - 100));
waiter->Wait();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
ChunkedResponse_OverheadDoesNotCountForBodyBytes) {
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n";
const int kChunkSize = 5;
const int kNumChunks = 5;
auto main_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page.html",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
main_response->WaitForRequest();
main_response->Send(kHttpResponseHeader);
for (int i = 0; i < kNumChunks; i++) {
main_response->Send(std::to_string(kChunkSize));
main_response->Send("\r\n");
main_response->Send(std::string(kChunkSize, '*'));
main_response->Send("\r\n");
}
main_response->Done();
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->Wait();
// Verify that overheads for each chunk are not reported as body bytes.
EXPECT_EQ(waiter->current_network_body_bytes(), kChunkSize * kNumChunks);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, ReceivedCompleteResources) {
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"\r\n";
auto main_html_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/mock_page.html",
true /*relative_url_is_prefix*/);
auto script_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/script.js",
true /*relative_url_is_prefix*/);
auto iframe_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/iframe.html",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
main_html_response->WaitForRequest();
main_html_response->Send(kHttpResponseHeader);
main_html_response->Send(
"<html><body></body><script src=\"script.js\"></script></html>");
main_html_response->Send(std::string(1000, ' '));
main_html_response->Done();
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->AddMinimumNetworkBytesExpectation(1000);
waiter->Wait();
script_response->WaitForRequest();
script_response->Send(kHttpResponseHeader);
script_response->Send(
"var iframe = document.createElement(\"iframe\");"
"iframe.src =\"iframe.html\";"
"document.body.appendChild(iframe);");
script_response->Send(std::string(1000, ' '));
// Data received but resource not complete
waiter->AddMinimumCompleteResourcesExpectation(1);
waiter->AddMinimumNetworkBytesExpectation(2000);
waiter->Wait();
script_response->Done();
waiter->AddMinimumCompleteResourcesExpectation(2);
waiter->Wait();
// Make sure main resources are loaded correctly
iframe_response->WaitForRequest();
iframe_response->Send(kHttpResponseHeader);
iframe_response->Send(std::string(2000, ' '));
iframe_response->Done();
waiter->AddMinimumCompleteResourcesExpectation(3);
waiter->AddMinimumNetworkBytesExpectation(4000);
waiter->Wait();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MemoryCacheResource_Recorded) {
const char kHttpResponseHeader[] =
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Cache-Control: max-age=60\r\n"
"\r\n";
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
auto cached_response =
std::make_unique<net::test_server::ControllableHttpResponse>(
embedded_test_server(), "/cachetime",
true /*relative_url_is_prefix*/);
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/page_with_cached_subresource.html"),
content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
// Load a resource large enough to record a nonzero number of kilobytes.
cached_response->WaitForRequest();
cached_response->Send(kHttpResponseHeader);
cached_response->Send(std::string(10 * 1024, ' '));
cached_response->Done();
waiter->AddMinimumCompleteResourcesExpectation(3);
waiter->Wait();
// Re-navigate the page to the same url with a different query string so the
// main resource is not loaded from the disk cache. The subresource will be
// loaded from the memory cache.
waiter = CreatePageLoadMetricsTestWaiter();
browser()->OpenURL(content::OpenURLParams(
embedded_test_server()->GetURL("/page_with_cached_subresource.html?xyz"),
content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false));
// Favicon is not fetched this time.
waiter->AddMinimumCompleteResourcesExpectation(2);
waiter->Wait();
// Verify no resources were cached for the first load.
histogram_tester_->ExpectBucketCount(
internal::kHistogramCacheCompletedResources, 0, 1);
histogram_tester_->ExpectBucketCount(internal::kHistogramPageLoadCacheBytes,
0, 1);
// Force histograms to record.
NavigateToUntrackedUrl();
// Verify that the cached resource from the memory cache is recorded
// correctly.
histogram_tester_->ExpectBucketCount(
internal::kHistogramCacheCompletedResources, 1, 1);
histogram_tester_->ExpectBucketCount(internal::kHistogramPageLoadCacheBytes,
10, 1);
}
// Verifies that css image resources shared across document do not cause a
// crash, and are only counted once per context. https://crbug.com/979459.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MemoryCacheResources_RecordedOncePerContext) {
embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/document_with_css_image_sharing.html"));
waiter->AddMinimumCompleteResourcesExpectation(7);
waiter->Wait();
// Force histograms to record.
NavigateToUntrackedUrl();
// Verify that cached resources are only reported once per context.
histogram_tester_->ExpectBucketCount(
internal::kHistogramCacheCompletedResources, 2, 1);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, InputEventsForClick) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
GURL url = embedded_test_server()->GetURL("/page_load_metrics/link.html");
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
content::SimulateMouseClickAt(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft, gfx::Point(100, 100));
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToNavigation, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToNavigationLinkClick, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToFirstPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToFirstContentfulPaint, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
// Navigation should record the metrics twice because of the initial pageload
// and the second pageload ("/title1.html") initiated by the link click.
VerifyNavigationMetrics(
{url, embedded_test_server()->GetURL("/title1.html")});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, InputEventsForOmniboxMatch) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
GURL url = embedded_test_server()->GetURL("/title1.html");
ui_test_utils::SendToOmniboxAndSubmit(browser(), url.spec(),
base::TimeTicks::Now());
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToNavigation, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToFirstPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToFirstContentfulPaint, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
VerifyNavigationMetrics({url});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
InputEventsForJavaScriptHref) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
GURL url =
embedded_test_server()->GetURL("/page_load_metrics/javascript_href.html");
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
waiter = CreatePageLoadMetricsTestWaiter();
content::SimulateMouseClickAt(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft, gfx::Point(100, 100));
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToNavigation, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToNavigationLinkClick, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToFirstPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToFirstContentfulPaint, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
// Navigation should record the metrics twice because of the initial pageload
// and the second pageload ("/title1.html") initiated by the link click.
VerifyNavigationMetrics(
{url, embedded_test_server()->GetURL("/title1.html")});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
InputEventsForJavaScriptWindowOpen) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
GURL url = embedded_test_server()->GetURL(
"/page_load_metrics/javascript_window_open.html");
ui_test_utils::NavigateToURL(browser(), url);
waiter->Wait();
content::WebContentsAddedObserver web_contents_added_observer;
content::SimulateMouseClickAt(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft, gfx::Point(100, 100));
// Wait for new window to open.
auto* web_contents = web_contents_added_observer.GetWebContents();
waiter = std::make_unique<PageLoadMetricsTestWaiter>(web_contents);
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToNavigation, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToNavigationLinkClick, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramInputToFirstPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramInputToFirstContentfulPaint, 1);
// Close all pages, which should force logging of histograms persisted at the
// end of the page load lifetime.
browser()->tab_strip_model()->CloseAllTabs();
// Navigation should record the metrics twice because of the initial pageload
// and the second pageload ("/title1.html") initiated by the link click.
VerifyNavigationMetrics(
{url, embedded_test_server()->GetURL("/title1.html")});
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, FirstInputFromScroll) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL("/page_load_metrics/scroll.html"));
waiter->Wait();
content::SimulateGestureScrollSequence(
browser()->tab_strip_model()->GetActiveWebContents(),
gfx::Point(100, 100), gfx::Vector2dF(0, 15));
NavigateToUntrackedUrl();
// First Input Delay should not be reported from a scroll!
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputTimestamp,
0);
}
// Does a navigation to a page controlled by a service worker and verifies
// that service worker page load metrics are logged.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, ServiceWorkerMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstPaint);
// Load a page that registers a service worker.
GURL url = embedded_test_server()->GetURL(
"/service_worker/create_service_worker.html");
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ("DONE", EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
"register('fetch_event_pass_through.js');"));
waiter->Wait();
// The first load was not controlled, so service worker metrics should not be
// logged.
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramServiceWorkerFirstPaint, 0);
waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kFirstPaint);
// Load a controlled page.
GURL controlled_url = url;
ui_test_utils::NavigateToURL(browser(), controlled_url);
waiter->Wait();
// Service worker metrics should be logged.
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstPaint, 2);
histogram_tester_->ExpectTotalCount(
internal::kHistogramServiceWorkerFirstPaint, 1);
// Force navigation to another page, which should force logging of histograms
// persisted at the end of the page load lifetime.
NavigateToUntrackedUrl();
// Navigation should record the metrics twice because of the initial pageload
// to register a service worker and the page load controlled by the service
// worker.
VerifyNavigationMetrics({url, controlled_url});
}
// Does a navigation to a page which records a WebFeature before commit.
// Regression test for https://crbug.com/1043018.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PreCommitWebFeature) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
ui_test_utils::NavigateToURL(browser(),
embedded_test_server()->GetURL("/title1.html"));
waiter->Wait();
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kSecureContextCheckPassed), 1);
histogram_tester_->ExpectBucketCount(
internal::kFeaturesHistogramName,
static_cast<int32_t>(WebFeature::kSecureContextCheckFailed), 0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionsMainFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Evaluate the height and width of the page as the browser_test can
// vary the dimensions.
int document_height =
EvalJs(web_contents, "document.body.scrollHeight").ExtractInt();
int document_width =
EvalJs(web_contents, "document.body.scrollWidth").ExtractInt();
// Expectation is before NavigateToUrl for this test as the expectation can be
// met after NavigateToUrl and before the Wait.
waiter->AddMainFrameIntersectionExpectation(
gfx::Rect(0, 0, document_width,
document_height)); // Initial main frame rect.
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
waiter->Wait();
// Create a |document_width|x|document_height| frame at 100,100, increasing
// the page width and height by 100.
waiter->AddMainFrameIntersectionExpectation(
gfx::Rect(0, 0, document_width + 100, document_height + 100));
EXPECT_TRUE(ExecJs(
web_contents,
content::JsReplace("createIframeAtRect(\"test\", 100, 100, $1, $2);",
document_width, document_height)));
waiter->Wait();
}
// Creates a single frame within the main frame and verifies the intersection
// with the main frame.
// TODO(https://crbug/1085175): Main frame document intersections need to be
// transformed into the main frame documents coordinate system.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionSingleFrame) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 200, 200));
// Create a 200x200 iframe at 100,100.
EXPECT_TRUE(ExecJs(web_contents,
"createIframeAtRect(\"test\", 100, 100, 200, 200);"));
waiter->Wait();
}
// Creates a set of nested frames within the main frame and verifies
// their intersections with the main frame.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionSameOrigin) {
EXPECT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 200, 200));
// Create a 200x200 iframe at 100,100.
EXPECT_TRUE(ExecJs(web_contents,
"createIframeAtRect(\"test\", 100, 100, 200, 200);"));
waiter->Wait();
NavigateIframeToURL(
web_contents, "test",
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
// Creates the grandchild iframe within the child frame at 10, 10 with
// dimensions 300x300. This frame is clipped by 110 pixels in the bottom and
// right. This translates to an intersection of 110, 110, 190, 190 with the
// main frame.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(110, 110, 190, 190));
content::RenderFrameHost* child_frame =
content::ChildFrameAt(web_contents->GetMainFrame(), 0);
EXPECT_TRUE(
ExecJs(child_frame, "createIframeAtRect(\"test2\", 10, 10, 300, 300);"));
waiter->Wait();
}
// Creates a set of nested frames, with a cross origin subframe, within the
// main frame and verifies their intersections with the main frame.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionCrossOrigin) {
EXPECT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 200, 200));
// Create a 200x200 iframe at 100,100.
EXPECT_TRUE(ExecJs(web_contents,
"createIframeAtRect(\"test\", 100, 100, 200, 200);"));
NavigateIframeToURL(
web_contents, "test",
embedded_test_server()->GetURL(
"b.com",
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
// Wait for the main frame intersection after we have navigated the frame
// to a cross-origin url.
waiter->Wait();
// Change the size of the frame to 150, 150. This tests the cross origin
// code path as the previous wait can flakily pass due to receiving the
// correct intersection before the frame transitions to cross-origin without
// checking that the final computation is consistent.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 150, 150));
EXPECT_TRUE(ExecJs(web_contents,
"let frame = document.getElementById('test'); "
"frame.width = 150; "
"frame.height = 150; "));
waiter->Wait();
// Creates the grandchild iframe within the child frame at 10, 10 with
// dimensions 300x300. This frame is clipped by 110 pixels in the bottom and
// right. This translates to an intersection of 110, 110, 190, 190 with the
// main frame.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(110, 110, 140, 140));
content::RenderFrameHost* child_frame =
content::ChildFrameAt(web_contents->GetMainFrame(), 0);
EXPECT_TRUE(
ExecJs(child_frame, "createIframeAtRect(\"test2\", 10, 10, 300, 300);"));
waiter->Wait();
}
// Creates a set of nested frames, with a cross origin subframe that is out of
// view within the main frame and verifies their intersections with the main
// frame.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionCrossOriginOutOfView) {
EXPECT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 200, 200));
// Create a 200x200 iframe at 100,100.
EXPECT_TRUE(ExecJs(web_contents,
"createIframeAtRect(\"test\", 100, 100, 200, 200);"));
NavigateIframeToURL(
web_contents, "test",
embedded_test_server()->GetURL(
"b.com",
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
// Wait for the main frame intersection after we have navigated the frame
// to a cross-origin url.
waiter->Wait();
// Creates the grandchild iframe within the child frame outside the parent
// frame's viewport.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(0, 0, 0, 0));
content::RenderFrameHost* child_frame =
content::ChildFrameAt(web_contents->GetMainFrame(), 0);
EXPECT_TRUE(ExecJs(child_frame,
"createIframeAtRect(\"test2\", 5000, 5000, 190, 190);"));
waiter->Wait();
}
// Creates a set of nested frames, with a cross origin subframe that is out of
// view within the main frame and verifies their intersections with the main
// frame. The out of view frame is then scrolled back into view and the
// intersection is verified.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
MainFrameIntersectionCrossOriginScrolled) {
EXPECT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
ui_test_utils::NavigateToURL(
browser(),
embedded_test_server()->GetURL(
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(100, 100, 200, 200));
// Create a 200x200 iframe at 100,100.
EXPECT_TRUE(ExecJs(web_contents,
"createIframeAtRect(\"test\", 100, 100, 200, 200);"));
NavigateIframeToURL(
web_contents, "test",
embedded_test_server()->GetURL(
"b.com",
"/page_load_metrics/blank_with_positioned_iframe_writer.html"));
// Wait for the main frame intersection after we have navigated the frame
// to a cross-origin url.
waiter->Wait();
// Creates the grandchild iframe within the child frame outside the parent
// frame's viewport.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(0, 0, 0, 0));
content::RenderFrameHost* child_frame =
content::ChildFrameAt(web_contents->GetMainFrame(), 0);
EXPECT_TRUE(ExecJs(child_frame,
"createIframeAtRect(\"test2\", 5000, 5000, 190, 190);"));
waiter->Wait();
// Scroll the child frame and verify the grandchild frame's intersection.
// The parent frame is at position 100,100 with dimensions 200x200. The
// child frame after scrolling is positioned at 100,100 within the parent
// frame and is clipped to 100x100. The grand child's main frame document
// position is then 200,200 after the child frame is scrolled.
waiter->AddMainFrameIntersectionExpectation(gfx::Rect(200, 200, 100, 100));
EXPECT_TRUE(ExecJs(child_frame, "window.scroll(4900, 4900); "));
waiter->Wait();
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PageLCPStopsUponInput) {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
content::SetupCrossSiteRedirector(embedded_test_server());
ASSERT_TRUE(embedded_test_server()->Start());
// Waiter to ensure main content is loaded.
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter->AddPageExpectation(TimingField::kLargestContentfulPaint);
// Waiter to ensure that iframe content is loaded.
auto waiter2 = CreatePageLoadMetricsTestWaiter();
waiter2->AddPageExpectation(TimingField::kLoadEvent);
waiter2->AddSubFrameExpectation(TimingField::kLoadEvent);
waiter2->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter2->AddSubFrameExpectation(TimingField::kFirstContentfulPaint);
waiter2->AddPageExpectation(TimingField::kLargestContentfulPaint);
waiter2->AddSubFrameExpectation(TimingField::kLargestContentfulPaint);
waiter2->AddPageExpectation(TimingField::kFirstInputOrScroll);
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/page_load_metrics/click_to_create_iframe.html"));
waiter->Wait();
// Tap in the middle of the button.
ASSERT_TRUE(content::ExecuteScriptWithoutUserGesture(
RenderFrameHost(),
"var submitRect = document.getElementById('button')"
".getBoundingClientRect();"));
double y;
ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractDouble(
RenderFrameHost(),
"window.domAutomationController.send((submitRect.top +"
"submitRect.bottom) / 2);",
&y));
double x;
EXPECT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractDouble(
RenderFrameHost(),
"window.domAutomationController.send((submitRect.left + submitRect.right)"
"/ 2);",
&x));
content::SimulateMouseClickAt(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft,
gfx::Point(static_cast<int>(x), static_cast<int>(y)));
waiter2->Wait();
// LCP is collected only at the end of the page lifecycle. Navigate to flush.
NavigateToUntrackedUrl();
histogram_tester_->ExpectTotalCount(
internal::kHistogramLargestContentfulPaint, 1);
auto all_frames_value =
histogram_tester_
->GetAllSamples(internal::kHistogramLargestContentfulPaint)[0]
.min;
histogram_tester_->ExpectTotalCount(
internal::kHistogramLargestContentfulPaintMainFrame, 1);
auto main_frame_value =
histogram_tester_
->GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrame)[0]
.min;
// Even though the content on the iframe is larger, the all_frames LCP value
// should match the main frame value because the iframe content was created
// after input in the main frame.
ASSERT_EQ(all_frames_value, main_frame_value);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, FirstInputDelayFromClick) {
ASSERT_TRUE(embedded_test_server()->Start());
auto waiter = CreatePageLoadMetricsTestWaiter();
waiter->AddPageExpectation(TimingField::kLoadEvent);
waiter->AddPageExpectation(TimingField::kFirstContentfulPaint);
auto waiter2 = CreatePageLoadMetricsTestWaiter();
waiter2->AddPageExpectation(TimingField::kLoadEvent);
waiter2->AddPageExpectation(TimingField::kFirstContentfulPaint);
waiter2->AddPageExpectation(TimingField::kFirstInputDelay);
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(
"/page_load_metrics/click.html"));
waiter->Wait();
content::SimulateMouseClickAt(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft, gfx::Point(100, 100));
waiter2->Wait();
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 1);
histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputTimestamp,
1);
}
// Tests that a portal activation records metrics.
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, PortalActivation) {
// We only record metrics for portals when the time is consistent across
// processes.
if (!base::TimeTicks::IsConsistentAcrossProcesses())
return;
ASSERT_TRUE(embedded_test_server()->Start());
ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("portal.test", "/title1.html"));
content::WebContents* outer_contents =
browser()->tab_strip_model()->GetActiveWebContents();
// Create a portal to a.com.
GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
std::string script = R"(
var portal = document.createElement('portal');
portal.src = '%s';
document.body.appendChild(portal);
)";
content::WebContentsAddedObserver contents_observer;
content::TestNavigationObserver portal_nav_observer(a_url);
portal_nav_observer.StartWatchingNewWebContents();
EXPECT_TRUE(ExecJs(outer_contents,
base::StringPrintf(script.c_str(), a_url.spec().c_str())));
portal_nav_observer.WaitForNavigationFinished();
content::WebContents* portal_contents = contents_observer.GetWebContents();
{
// The portal is not activated, so nothing should be recorded.
auto entries = test_ukm_recorder_->GetMergedEntriesByName(
ukm::builders::PageLoad::kEntryName);
EXPECT_EQ(0u, entries.size());
}
// Activate the portal.
std::string activated_listener = R"(
activatePromise = new Promise(resolve => {
window.addEventListener('portalactivate', resolve(true));
});
)";
EXPECT_TRUE(ExecJs(portal_contents, activated_listener));
EXPECT_TRUE(
ExecJs(outer_contents, "document.querySelector('portal').activate()"));
EXPECT_EQ(true,
EvalJsWithManualReply(portal_contents,
"activatePromise.then(r => { "
" window.domAutomationController.send(r);"
"});"));
// The activated portal contents should be the currently active contents.
EXPECT_EQ(portal_contents,
browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_NE(portal_contents, outer_contents);
{
// The portal is activated, so there should be a PageLoad entry.
auto entries = test_ukm_recorder_->GetMergedEntriesByName(
ukm::builders::PageLoad::kEntryName);
EXPECT_EQ(1u, entries.size());
}
{
// The portal is activated, also check the portal entry.
auto entries = test_ukm_recorder_->GetMergedEntriesByName(
ukm::builders::Portal_Activate::kEntryName);
EXPECT_EQ(1u, entries.size());
}
}
class PageLoadMetricsBrowserTestWithBackForwardCache
: public PageLoadMetricsBrowserTest {
public:
void SetUpCommandLine(base::CommandLine* command_line) override {
PageLoadMetricsBrowserTest::SetUpCommandLine(command_line);
feature_list.InitAndEnableFeatureWithParameters(
features::kBackForwardCache,
// Set a very long TTL before expiration (longer than the test timeout)
// so tests that are expecting deletion don't pass when they shouldn't.
//
// TODO(hajimehoshi): This value is used in various places. Define a
// constant and use it.
//
// Some features like the outstanding network requests are expected to
// appear in almost any output. Filter them out to make the tests
// simpler.
{{"TimeToLiveInBackForwardCacheInSeconds", "3600"},
{"ignore_outstanding_network_request_for_testing", "true"}});
}
private:
base::test::ScopedFeatureList feature_list;
};
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithBackForwardCache,
BackForwardCacheEvent) {
ASSERT_TRUE(embedded_test_server()->Start());
auto url1 = embedded_test_server()->GetURL("a.com", "/title1.html");
auto url2 = embedded_test_server()->GetURL("b.com", "/title1.html");
// Go to URL1.
ui_test_utils::NavigateToURL(browser(), url1);
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kEnterBackForwardCache, 0);
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kRestoreFromBackForwardCache, 0);
// Go to URL2. The previous page (URL1) is put into the back-forward cache.
ui_test_utils::NavigateToURL(browser(), url2);
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kEnterBackForwardCache, 1);
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kRestoreFromBackForwardCache, 0);
// Go back to URL1. The previous page (URL2) is put into the back-forward
// cache.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
web_contents->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(web_contents));
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kEnterBackForwardCache, 2);
// For now CorePageLoadMetricsObserver::OnEnterBackForwardCache returns
// STOP_OBSERVING, OnRestoreFromBackForward is never reached.
//
// TODO(hajimehoshi): Update this when the CorePageLoadMetricsObserver
// continues to observe after entering to back-forward cache.
histogram_tester_->ExpectBucketCount(
internal::kHistogramBackForwardCacheEvent,
internal::PageLoadBackForwardCacheEvent::kRestoreFromBackForwardCache, 0);
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, EarlyHints) {
content::NavigationHandleTiming timing =
NavigateWithEarlyHints(EarlyHintsPolicy::OneHint);
// There were no redirections, so the first request should be the same as the
// final request.
EXPECT_FALSE(timing.early_hints_for_first_request_time.is_null());
EXPECT_FALSE(timing.early_hints_for_final_request_time.is_null());
EXPECT_EQ(timing.early_hints_for_first_request_time,
timing.early_hints_for_final_request_time);
// The timings of the Early Hints response should be recorded.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 1);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page.html"));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, EarlyHints_NoHints) {
content::NavigationHandleTiming timing =
NavigateWithEarlyHints(EarlyHintsPolicy::NoHints);
// No Early Hints responses were received.
EXPECT_TRUE(timing.early_hints_for_first_request_time.is_null());
EXPECT_TRUE(timing.early_hints_for_final_request_time.is_null());
// The timings of the Early Hints response should not be recorded.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 0);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 0);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 0);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page.html"));
EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, EarlyHints_MultipleHints) {
content::NavigationHandleTiming timing =
NavigateWithEarlyHints(EarlyHintsPolicy::MultipleHints);
// There were no redirections, so the first request should be the same as the
// final request.
EXPECT_FALSE(timing.early_hints_for_first_request_time.is_null());
EXPECT_FALSE(timing.early_hints_for_final_request_time.is_null());
EXPECT_EQ(timing.early_hints_for_first_request_time,
timing.early_hints_for_final_request_time);
// The timings of the Early Hints responses should be recorded only one time.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 1);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page.html"));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
EarlyHints_HintsBeforeRedirection) {
content::NavigationHandleTiming timing = RedirectWithEarlyHints(
EarlyHintsPolicy::MultipleHints, EarlyHintsPolicy::NoHints);
// The early hints were served for the first request, but not for the
// redirected request.
EXPECT_FALSE(timing.early_hints_for_first_request_time.is_null());
EXPECT_TRUE(timing.early_hints_for_final_request_time.is_null());
// The timings of the Early Hints response should be recorded only for the
// first request.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 0);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 0);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page2.html"));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
EarlyHints_HintsAfterRedirection) {
content::NavigationHandleTiming timing = RedirectWithEarlyHints(
EarlyHintsPolicy::NoHints, EarlyHintsPolicy::MultipleHints);
// The early hints were served for the redirected request, but not for the
// first request.
EXPECT_TRUE(timing.early_hints_for_first_request_time.is_null());
EXPECT_FALSE(timing.early_hints_for_final_request_time.is_null());
// The timings of the Early Hints response should be recorded only for the
// redirected request.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 0);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 1);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page2.html"));
EXPECT_FALSE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}
IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest,
EarlyHints_HintsBeforeAndAfterRedirection) {
content::NavigationHandleTiming timing = RedirectWithEarlyHints(
EarlyHintsPolicy::MultipleHints, EarlyHintsPolicy::MultipleHints);
// The early hints were served for both the first request and the redirected
// request.
EXPECT_FALSE(timing.early_hints_for_first_request_time.is_null());
EXPECT_FALSE(timing.early_hints_for_final_request_time.is_null());
EXPECT_LT(timing.early_hints_for_first_request_time,
timing.early_hints_for_final_request_time);
// The timings of the Early Hints response should be recorded.
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFirstRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsFinalRequestStartToEarlyHints, 1);
histogram_tester_->ExpectTotalCount(
internal::kHistogramEarlyHintsEarlyHintsToFinalResponseStart, 1);
using NavigationTiming = ukm::builders::NavigationTiming;
const auto& entries =
test_ukm_recorder_->GetMergedEntriesByName(NavigationTiming::kEntryName);
EXPECT_EQ(1u, entries.size());
for (const auto& kv : entries) {
test_ukm_recorder_->ExpectEntrySourceHasUrl(
kv.second.get(), embedded_test_server()->GetURL("/mock_page2.html"));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFirstRequestName));
EXPECT_TRUE(test_ukm_recorder_->EntryHasMetric(
kv.second.get(), NavigationTiming::kEarlyHintsForFinalRequestName));
}
}