| // 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/file_util.h" |
| #include "base/files/scoped_temp_dir.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/strings/stringprintf.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 "build/chromeos_buildflags.h" |
| #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h" |
| #include "chrome/browser/page_load_metrics/observers/core/ukm_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/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/page_load_metrics_initialize.h" |
| #include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h" |
| #include "chrome/browser/prefetch/no_state_prefetch/prerender_test_utils.h" |
| #include "chrome/browser/prefs/session_startup_pref.h" |
| #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" |
| #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.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.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/no_state_prefetch/browser/no_state_prefetch_handle.h" |
| #include "components/no_state_prefetch/browser/no_state_prefetch_manager.h" |
| #include "components/no_state_prefetch/browser/prerender_histograms.h" |
| #include "components/no_state_prefetch/common/prerender_origin.h" |
| #include "components/page_load_metrics/browser/observers/core/uma_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/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/back_forward_cache.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/content_paths.h" |
| #include "content/public/common/content_switches.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/prerender_test_util.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::PageEndReason; |
| using page_load_metrics::PageLoadMetricsTestWaiter; |
| using TimingField = page_load_metrics::PageLoadMetricsTestWaiter::TimingField; |
| using WebFeature = blink::mojom::WebFeature; |
| using testing::SizeIs; |
| using testing::UnorderedElementsAre; |
| using NoStatePrefetch = ukm::builders::NoStatePrefetch; |
| using PageLoad = ukm::builders::PageLoad; |
| using HistoryNavigation = ukm::builders::HistoryNavigation; |
| |
| 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(const PageLoadMetricsBrowserTest&) = delete; |
| PageLoadMetricsBrowserTest& operator=(const PageLoadMetricsBrowserTest&) = |
| delete; |
| |
| ~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() { |
| ASSERT_TRUE( |
| ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL))); |
| } |
| |
| int64_t GetUKMPageLoadMetric(std::string metric_name) { |
| std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries = |
| test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName); |
| |
| EXPECT_EQ(1ul, merged_entries.size()); |
| const auto& kv = merged_entries.begin(); |
| const int64_t* recorded = |
| ukm::TestUkmRecorder::GetEntryMetric(kv->second.get(), metric_name); |
| EXPECT_TRUE(recorded != nullptr); |
| return (*recorded); |
| } |
| |
| 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::NoStatePrefetchManager* no_state_prefetch_manager = |
| prerender::NoStatePrefetchManagerFactory::GetForBrowserContext( |
| browser()->profile()); |
| ASSERT_TRUE(no_state_prefetch_manager); |
| |
| prerender::test_utils::TestNoStatePrefetchContentsFactory* |
| no_state_prefetch_contents_factory = |
| new prerender::test_utils::TestNoStatePrefetchContentsFactory(); |
| no_state_prefetch_manager->SetNoStatePrefetchContentsFactoryForTest( |
| no_state_prefetch_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 = |
| no_state_prefetch_contents_factory->ExpectNoStatePrefetchContents( |
| prerender::FINAL_STATUS_NOSTATE_PREFETCH_FINISHED); |
| |
| std::unique_ptr<prerender::NoStatePrefetchHandle> no_state_prefetch_handle = |
| no_state_prefetch_manager->StartPrefetchingFromOmnibox( |
| url, storage_namespace, gfx::Size(640, 480)); |
| ASSERT_EQ(no_state_prefetch_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) { |
| 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)); |
| } |
| } |
| } |
| |
| content::WebContents* web_contents() const { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| content::RenderFrameHost* RenderFrameHost() const { |
| return web_contents()->GetMainFrame(); |
| } |
| base::test::ScopedFeatureList scoped_feature_list_; |
| std::unique_ptr<base::HistogramTester> histogram_tester_; |
| std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_; |
| }; |
| |
| class PageLoadMetricsBrowserTestAnimatedLCP |
| : public PageLoadMetricsBrowserTest { |
| protected: |
| void test_animated_image_lcp(bool smaller, bool animated) { |
| // Waiter to ensure main content is loaded. |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| waiter->AddPageExpectation(TimingField::kLargestContentfulPaint); |
| |
| const char kHtmlHttpResponseHeader[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Type: text/html; charset=utf-8\r\n" |
| "\r\n"; |
| const char kImgHttpResponseHeader[] = |
| "HTTP/1.1 200 OK\r\n" |
| "Content-Type: image/png\r\n" |
| "\r\n"; |
| auto main_html_response = |
| std::make_unique<net::test_server::ControllableHttpResponse>( |
| embedded_test_server(), "/mock_page.html", |
| false /*relative_url_is_prefix*/); |
| auto img_response = |
| std::make_unique<net::test_server::ControllableHttpResponse>( |
| embedded_test_server(), |
| animated ? "/images/animated-delayed.png" : "/images/delayed.jpg", |
| false /*relative_url_is_prefix*/); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // File is under content/test/data/ |
| const std::string file_name_string = |
| animated ? "animated.png" : "single_face.jpg"; |
| std::string file_contents; |
| // The first_frame_size number for the animated case (262), represents the |
| // first frame of the animated PNG + an extra chunk enabling the decoder to |
| // understand the first frame is done and decode it. |
| // For the non-animated case (5000), it's an arbitrary number that |
| // represents a part of the JPEG's frame. |
| const unsigned first_frame_size = animated ? 262 : 5000; |
| |
| // Read the animated image into two frames. |
| { |
| base::ScopedAllowBlockingForTesting allow_io; |
| base::FilePath test_dir; |
| ASSERT_TRUE(base::PathService::Get(content::DIR_TEST_DATA, &test_dir)); |
| base::FilePath file_name = test_dir.AppendASCII(file_name_string); |
| ASSERT_TRUE(base::ReadFileToString(file_name, &file_contents)); |
| } |
| // Split the contents into 2 frames |
| std::string first_frame = file_contents.substr(0, first_frame_size); |
| std::string second_frame = file_contents.substr(first_frame_size); |
| |
| 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(kHtmlHttpResponseHeader); |
| main_html_response->Send( |
| animated ? "<html><body></body><img " |
| "src=\"/images/animated-delayed.png\"></script></html>" |
| : "<html><body></body><img " |
| "src=\"/images/delayed.jpg\"></script></html>"); |
| main_html_response->Done(); |
| |
| img_response->WaitForRequest(); |
| img_response->Send(kImgHttpResponseHeader); |
| img_response->Send(first_frame); |
| |
| // Trigger a double rAF and wait a bit, then take a timestamp that's after |
| // the presentation time of the first frame. |
| // Then wait some more to ensure the timestamp is not too close to the point |
| // where the second frame is sent. |
| content::EvalJsResult result = |
| EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), R"( |
| (async () => { |
| const double_raf = () => { |
| return new Promise(r => { |
| requestAnimationFrame(()=>requestAnimationFrame(r)); |
| }) |
| }; |
| await double_raf(); |
| await new Promise(r => setTimeout(r, 50)); |
| const timestamp = performance.now(); |
| await new Promise(r => setTimeout(r, 50)); |
| return timestamp; |
| })();)"); |
| EXPECT_EQ("", result.error); |
| double timestamp = result.ExtractDouble(); |
| |
| img_response->Send(second_frame); |
| img_response->Done(); |
| |
| // Wait on an LCP entry to make sure we have one to report when navigating |
| // away. |
| content::EvalJsResult result2 = |
| EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), R"( |
| (async () => { |
| await new Promise(resolve => { |
| (new PerformanceObserver(list => { |
| const entries = list.getEntries(); |
| for (let entry of entries) { |
| if (entry.url.includes('images')) {resolve()} |
| } |
| })) |
| .observe({type: 'largest-contentful-paint', buffered: true}); |
| })})())"); |
| EXPECT_EQ("", result2.error); |
| waiter->Wait(); |
| |
| // LCP is collected only at the end of the page lifecycle. Navigate to |
| // flush. |
| NavigateToUntrackedUrl(); |
| |
| int64_t value = GetUKMPageLoadMetric( |
| PageLoad::kPaintTiming_NavigationToLargestContentfulPaint2Name); |
| |
| if (smaller) { |
| ASSERT_LT(value, timestamp); |
| } else { |
| ASSERT_GT(value, timestamp); |
| } |
| } |
| }; |
| |
| class PageLoadMetricsBrowserTestWithAnimatedLCPFlag |
| : public PageLoadMetricsBrowserTestAnimatedLCP { |
| public: |
| PageLoadMetricsBrowserTestWithAnimatedLCPFlag() { |
| scoped_feature_list_.Reset(); |
| scoped_feature_list_.InitWithFeatures( |
| {blink::features::kLCPAnimatedImagesReporting}, {}); |
| } |
| }; |
| |
| class PageLoadMetricsBrowserTestWithRuntimeAnimatedLCPFlag |
| : public PageLoadMetricsBrowserTestAnimatedLCP { |
| public: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures, |
| "LCPAnimatedImagesWebExposed"); |
| } |
| }; |
| |
| 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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| NavigateToUntrackedUrl(); |
| |
| 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. |
| ASSERT_TRUE(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(¶ms); |
| 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); |
| ASSERT_TRUE(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 BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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"); |
| ASSERT_TRUE(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. |
| ASSERT_TRUE(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"); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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(); |
| ASSERT_TRUE(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()); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/circle.svg"))); |
| NavigateToUntrackedUrl(); |
| EXPECT_TRUE(NoPageLoadMetricsRecorded()) |
| << "Recorded metrics: " << GetRecordedPageLoadMetricNames(); |
| } |
| |
| // TODO(crbug.com/1223288): Test flakes on Chrome OS. |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #define MAYBE_NonHttpOrHttpsUrl DISABLED_NonHttpOrHttpsUrl |
| #else |
| #define MAYBE_NonHttpOrHttpsUrl NonHttpOrHttpsUrl |
| #endif |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, MAYBE_NonHttpOrHttpsUrl) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| ASSERT_TRUE(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()); |
| |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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()); |
| |
| ASSERT_TRUE(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( |
| browser()->profile()->GetDownloadManager(), |
| 1, // == wait_count (only waiting for "download-test3.gif"). |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| |
| ASSERT_TRUE(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); |
| |
| ASSERT_TRUE(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteBlock) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| |
| ASSERT_TRUE(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteReload) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(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); |
| |
| // Reload should not log the histogram as the script is not blocked. |
| waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL( |
| "/page_load_metrics/document_write_script_block.html"))); |
| waiter->Wait(); |
| |
| waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteAsync) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DocumentWriteSameDomain) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(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); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, NoDocumentWriteScript) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kFirstContentfulPaint); |
| ASSERT_TRUE(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); |
| } |
| |
| // 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. |
| ASSERT_TRUE(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(¶ms); |
| 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(¶ms2); |
| 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(¶ms); |
| EXPECT_TRUE(manager.WaitForRequestStart()); |
| |
| NavigateParams params2(browser(), url, ui::PAGE_TRANSITION_RELOAD); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| Navigate(¶ms2); |
| waiter->Wait(); |
| |
| histogram_tester_->ExpectTotalCount( |
| internal::kHistogramAbortReloadBeforeCommit, 1); |
| } |
| |
| // TODO(crbug.com/675061): Flaky on Win7 dbg. |
| #if BUILDFLAG(IS_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(¶ms); |
| 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(¶ms); |
| 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(¶ms2); |
| |
| 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(¶ms3); |
| 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")); |
| ASSERT_TRUE(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(¶ms); |
| 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) && (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) |
| #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); |
| ASSERT_TRUE(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); |
| |
| ASSERT_TRUE(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, PayloadSize) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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( |
| browser()->profile()->GetDownloadManager(), |
| 1, // == wait_count (only waiting for "download-test1.lib"). |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL); |
| |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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::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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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()); |
| |
| // Ensure that the previous page won't be stored in the back/forward cache, so |
| // that the histogram will be recorded when the previous page is unloaded. |
| // UKM/UMA logging after BFCache eviction is checked by |
| // PageLoadMetricsBrowserTestWithBackForwardCache's |
| // UseCounterUkmFeaturesLoggedOnBFCacheEviction test. |
| browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetController() |
| .GetBackForwardCache() |
| .DisableForTesting(content::BackForwardCache::TEST_ASSUMES_NO_CACHING); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| GURL url = embedded_test_server()->GetURL( |
| "/page_load_metrics/use_counter_features.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| MakeComponentFullscreen("testvideo"); |
| waiter->Wait(); |
| NavigateToUntrackedUrl(); |
| |
| const auto& entries = test_ukm_recorder_->GetEntriesByName( |
| ukm::builders::Blink_UseCounter::kEntryName); |
| EXPECT_THAT(entries, SizeIs(3)); |
| 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))); |
| } |
| |
| // 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()); |
| |
| // Ensure that the previous page won't be stored in the back/forward cache, so |
| // that the histogram will be recorded when the previous page is unloaded. |
| // UKM/UMA logging after BFCache eviction is checked by |
| // PageLoadMetricsBrowserTestWithBackForwardCache's |
| // UseCounterUkmFeaturesLoggedOnBFCacheEviction test. |
| browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetController() |
| .GetBackForwardCache() |
| .DisableForTesting(content::BackForwardCache::TEST_ASSUMES_NO_CACHING); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| GURL url = |
| https_server.GetURL("/page_load_metrics/use_counter_features.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| MakeComponentFullscreen("testvideo"); |
| waiter->Wait(); |
| NavigateToUntrackedUrl(); |
| |
| const auto& entries = test_ukm_recorder_->GetEntriesByName( |
| ukm::builders::Blink_UseCounter::kEntryName); |
| EXPECT_THAT(entries, SizeIs(6)); |
| 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::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); |
| ASSERT_TRUE(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, |
| UseCounterFeaturesInMultipleIframes) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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, |
| UseCounterCSSPropertiesInMultipleIframes) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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, |
| UseCounterAnimatedCSSPropertiesInMultipleIframes) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| ASSERT_TRUE(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()); |
| |
| ASSERT_TRUE(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); |
| } |
| |
| // Test UseCounter Permissions Policy Usages in main frame. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, |
| UseCounterPermissionsPolicyUsageInMainFrame) { |
| auto test_feature = static_cast<blink::UseCounterFeature::EnumValue>( |
| blink::mojom::PermissionsPolicyFeature::kFullscreen); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddUseCounterFeatureExpectation({ |
| blink::mojom::UseCounterFeatureType::kPermissionsPolicyViolationEnforce, |
| test_feature, |
| }); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL( |
| "/page_load_metrics/use_counter_features.html"))); |
| waiter->Wait(); |
| NavigateToUntrackedUrl(); |
| |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyViolationHistogramName, test_feature, 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyHeaderHistogramName, test_feature, 1); |
| } |
| |
| // Test UseCounter Permissions Policy Usages observed in child frame |
| // are recorded, exactly once per feature. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, |
| UseCounterPermissionsPolicyUsageInIframe) { |
| auto test_feature = static_cast<blink::UseCounterFeature::EnumValue>( |
| blink::mojom::PermissionsPolicyFeature::kFullscreen); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddUseCounterFeatureExpectation({ |
| blink::mojom::UseCounterFeatureType::kPermissionsPolicyViolationEnforce, |
| test_feature, |
| }); |
| ASSERT_TRUE(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::kPermissionsPolicyViolationHistogramName, test_feature, 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyHeaderHistogramName, test_feature, 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyIframeAttributeHistogramName, test_feature, |
| 1); |
| } |
| |
| // Test UseCounter Permissions Policy Usages observed in multiple child frames |
| // are recorded, exactly once per feature. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, |
| UseCounterPermissionsPolicyUsageInMultipleIframes) { |
| auto test_feature = static_cast<blink::UseCounterFeature::EnumValue>( |
| blink::mojom::PermissionsPolicyFeature::kFullscreen); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddUseCounterFeatureExpectation({ |
| blink::mojom::UseCounterFeatureType::kPermissionsPolicyViolationEnforce, |
| test_feature, |
| }); |
| ASSERT_TRUE(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::kPermissionsPolicyViolationHistogramName, test_feature, 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyHeaderHistogramName, test_feature, 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kPermissionsPolicyIframeAttributeHistogramName, test_feature, |
| 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, LoadingMetrics) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadTimingInfo); |
| ASSERT_TRUE(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() {} |
| |
| SessionRestorePageLoadMetricsBrowserTest( |
| const SessionRestorePageLoadMetricsBrowserTest&) = delete; |
| SessionRestorePageLoadMetricsBrowserTest& operator=( |
| const SessionRestorePageLoadMetricsBrowserTest&) = delete; |
| |
| // 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 BUILDFLAG(IS_CHROMEOS_ASH) |
| SessionServiceTestHelper helper(profile); |
| helper.SetForceBrowserNotAliveWithNoWindows(true); |
| #endif |
| |
| auto keep_alive = std::make_unique<ScopedKeepAlive>( |
| KeepAliveOrigin::SESSION_RESTORE, KeepAliveRestartOption::DISABLED); |
| auto profile_keep_alive = std::make_unique<ScopedProfileKeepAlive>( |
| profile, ProfileKeepAliveOrigin::kBrowserWindow); |
| 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); |
| } |
| }; |
| |
| class SessionRestorePaintWaiter : public SessionRestoreObserver { |
| public: |
| SessionRestorePaintWaiter() { SessionRestore::AddObserver(this); } |
| |
| SessionRestorePaintWaiter(const SessionRestorePaintWaiter&) = delete; |
| SessionRestorePaintWaiter& operator=(const SessionRestorePaintWaiter&) = |
| delete; |
| |
| ~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_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest, |
| InitialVisibilityOfSingleRestoredTab) { |
| ASSERT_TRUE(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) { |
| ASSERT_TRUE(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) { |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetTestURL())); |
| ExpectFirstPaintMetricsTotalCount(0); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest, |
| SingleTabSessionRestore) { |
| ASSERT_TRUE(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) { |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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) { |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(new_browser, GetTestURL())); |
| waiter->Wait(); |
| |
| // No more metrics because the navigation is after session restore. |
| ExpectFirstPaintMetricsTotalCount(1); |
| } |
| |
| #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) |
| #define MAYBE_InitialForegroundTabChanged DISABLED_InitialForegroundTabChanged |
| #else |
| #define MAYBE_InitialForegroundTabChanged InitialForegroundTabChanged |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest, |
| MAYBE_InitialForegroundTabChanged) { |
| ASSERT_TRUE(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) { |
| ASSERT_TRUE(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); |
| } |
| } |
| |
| // TODO(crbug.com/1242284): Flaky on Linux. |
| #if BUILDFLAG(IS_LINUX) |
| #define MAYBE_RestoreForeignSession DISABLED_RestoreForeignSession |
| #else |
| #define MAYBE_RestoreForeignSession RestoreForeignSession |
| #endif |
| IN_PROC_BROWSER_TEST_F(SessionRestorePageLoadMetricsBrowserTest, |
| MAYBE_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); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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::kHistogramPageLoadCacheBytes, |
| 0, 1); |
| |
| // Force histograms to record. |
| NavigateToUntrackedUrl(); |
| |
| // Verify that the cached resource from the memory cache is recorded |
| // correctly. |
| histogram_tester_->ExpectBucketCount(internal::kHistogramPageLoadCacheBytes, |
| 10, 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"); |
| ASSERT_TRUE(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"); |
| ASSERT_TRUE(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"); |
| ASSERT_TRUE(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); |
| ASSERT_TRUE(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"); |
| ASSERT_TRUE(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; |
| ASSERT_TRUE(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); |
| |
| ASSERT_TRUE(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. |
| |
| ASSERT_TRUE(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. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, |
| MainFrameIntersectionSingleFrame) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| ASSERT_TRUE(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(); |
| ASSERT_TRUE(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(); |
| ASSERT_TRUE(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(); |
| ASSERT_TRUE(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(); |
| ASSERT_TRUE(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); |
| |
| ASSERT_TRUE(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. |
| content::SimulateMouseClickOrTapElementWithId(web_contents(), "button"); |
| 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); |
| } |
| |
| #if BUILDFLAG(IS_MAC) // crbug.com/1277391 |
| #define MAYBE_PageLCPAnimatedImage DISABLED_PageLCPAnimatedImage |
| #else |
| #define MAYBE_PageLCPAnimatedImage PageLCPAnimatedImage |
| #endif |
| // Tests that an animated image's reported LCP values are smaller than its load |
| // times, when the feature flag for animated image reporting is enabled. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAnimatedLCPFlag, |
| MAYBE_PageLCPAnimatedImage) { |
| test_animated_image_lcp(/*smaller=*/true, /*animated=*/true); |
| } |
| |
| // Tests that an animated image's reported LCP values are larger than its load |
| // times, when only the feature flag for animated image web exposure is enabled. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithRuntimeAnimatedLCPFlag, |
| PageLCPAnimatedImageOnlyRuntimeFlag) { |
| test_animated_image_lcp(/*smaller=*/false, /*animated=*/true); |
| } |
| |
| // Tests that a non-animated image's reported LCP values are larger than its |
| // load times, when the feature flag for animated image reporting is enabled. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithAnimatedLCPFlag, |
| PageLCPNonAnimatedImage) { |
| test_animated_image_lcp(/*smaller=*/false, /*animated=*/false); |
| } |
| |
| 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); |
| ASSERT_TRUE(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); |
| } |
| |
| // Flaky on all platforms: https://crbug.com/1211028. |
| // Tests that a portal activation records metrics. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTest, DISABLED_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()); |
| ASSERT_TRUE(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 no page end metrics should be recorded |
| // (although the outer contents may have recorded FCP). |
| auto entries = test_ukm_recorder_->GetMergedEntriesByName( |
| ukm::builders::PageLoad::kEntryName); |
| for (const auto& kv : entries) { |
| EXPECT_FALSE(ukm::TestUkmRecorder::EntryHasMetric( |
| kv.second.get(), |
| ukm::builders::PageLoad::kNavigation_PageEndReason3Name)); |
| } |
| } |
| |
| // 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, content::EvalJs(portal_contents, |
| "activatePromise.then(r => { " |
| " window.domAutomationController.send(r);" |
| "});", |
| content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)); |
| |
| // 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 showing |
| // that the outer contents was closed. |
| auto entries = test_ukm_recorder_->GetMergedEntriesByName( |
| ukm::builders::PageLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| for (const auto& kv : entries) { |
| ukm::TestUkmRecorder::ExpectEntryMetric( |
| kv.second.get(), |
| ukm::builders::PageLoad::kNavigation_PageEndReason3Name, |
| PageEndReason::END_CLOSE); |
| } |
| } |
| { |
| // 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.InitWithFeaturesAndParameters( |
| {{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"}}}}, |
| // Allow BackForwardCache for all devices regardless of their memory. |
| {features::kBackForwardCacheMemoryControls}); |
| } |
| |
| 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. |
| ASSERT_TRUE(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. |
| ASSERT_TRUE(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 UmaPageLoadMetricsObserver::OnEnterBackForwardCache returns |
| // STOP_OBSERVING, OnRestoreFromBackForward is never reached. |
| // |
| // TODO(hajimehoshi): Update this when the UmaPageLoadMetricsObserver |
| // continues to observe after entering to back-forward cache. |
| histogram_tester_->ExpectBucketCount( |
| internal::kHistogramBackForwardCacheEvent, |
| internal::PageLoadBackForwardCacheEvent::kRestoreFromBackForwardCache, 0); |
| } |
| |
| // Test UseCounter UKM features observed when a page is in the BFCache and is |
| // evicted from it. |
| IN_PROC_BROWSER_TEST_F(PageLoadMetricsBrowserTestWithBackForwardCache, |
| UseCounterUkmFeaturesLoggedOnBFCacheEviction) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/page_load_metrics/use_counter_features.html"); |
| { |
| auto waiter = CreatePageLoadMetricsTestWaiter(); |
| waiter->AddPageExpectation(TimingField::kLoadEvent); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); |
| MakeComponentFullscreen("testvideo"); |
| waiter->Wait(); |
| } |
| NavigateToUntrackedUrl(); |
| |
| // Force the BFCache to evict all entries. This should cause the |
| // UseCounter histograms to be logged. |
| browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetController() |
| .GetBackForwardCache() |
| .Flush(); |
| |
| // Navigate to a new URL. This gives the various page load tracking |
| // mechanisms time to process the BFCache evictions. |
| auto url1 = embedded_test_server()->GetURL("a.com", "/title1.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1)); |
| |
| const auto& entries = test_ukm_recorder_->GetEntriesByName( |
| ukm::builders::Blink_UseCounter::kEntryName); |
| EXPECT_THAT(entries, SizeIs(4)); |
| std::vector<int64_t> ukm_features; |
| for (const auto* entry : entries) { |
| 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::kPageVisits))); |
| |
| // Check histogram counts. |
| histogram_tester_->ExpectBucketCount( |
| internal::kFeaturesHistogramName, |
| static_cast<int32_t>(WebFeature::kPageVisits), 2); |
| histogram_tester_->ExpectBucketCount( |
| internal::kFeaturesHistogramName, |
| static_cast<int32_t>(WebFeature::kFullscreenSecureOrigin), 1); |
| histogram_tester_->ExpectBucketCount( |
| internal::kFeaturesHistogramName, |
| static_cast<int32_t>(WebFeature::kNavigatorVibrate), 1); |
| } |
| |
| class NavigationPageLoadMetricsBrowserTest |
| : public PageLoadMetricsBrowserTest, |
| public ::testing::WithParamInterface<std::string> { |
| public: |
| NavigationPageLoadMetricsBrowserTest() = default; |
| ~NavigationPageLoadMetricsBrowserTest() override = default; |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // TODO(crbug.com/1224780): This test used an experiment param (which no |
| // longer exists) to suppress the metrics send timer. If and when the test |
| // is re-enabled, it should be updated to use a different mechanism. |
| PageLoadMetricsBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| // Flaky. See https://crbug.com/1224780. |
| IN_PROC_BROWSER_TEST_P(NavigationPageLoadMetricsBrowserTest, |
| DISABLED_FirstInputDelay) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url2(embedded_test_server()->GetURL( |
| (GetParam() == "SameSite") ? "a.com" : "b.com", "/title2.html")); |
| |
| EXPECT_THAT(histogram_tester_->GetAllSamples( |
| internal::kHistogramFirstContentfulPaint), |
| testing::IsEmpty()); |
| |
| // 1) Navigate to url1. |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url1)); |
| content::RenderFrameHost* rfh_a = RenderFrameHost(); |
| content::RenderProcessHost* rfh_a_process = rfh_a->GetProcess(); |
| |
| // Simulate mouse click. FirstInputDelay won't get updated immediately. |
| content::SimulateMouseClickAt(web_contents(), 0, |
| blink::WebMouseEvent::Button::kLeft, |
| gfx::Point(100, 100)); |
| // Run arbitrary script and run tasks in the brwoser to ensure the input is |
| // processed in the renderer. |
| EXPECT_TRUE(content::ExecJs(rfh_a, "var foo = 42;")); |
| base::RunLoop().RunUntilIdle(); |
| content::FetchHistogramsFromChildProcesses(); |
| histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 0); |
| |
| // 2) Immediately navigate to url2. |
| if (GetParam() == "CrossSiteRendererInitiated") { |
| EXPECT_TRUE(content::NavigateToURLFromRenderer(web_contents(), url2)); |
| } else { |
| EXPECT_TRUE(content::NavigateToURL(web_contents(), url2)); |
| } |
| |
| content::FetchHistogramsFromChildProcesses(); |
| if (GetParam() != "CrossSiteBrowserInitiated" || |
| rfh_a_process == RenderFrameHost()->GetProcess()) { |
| // - For "SameSite" case, since the old and new RenderFrame either share a |
| // process (with RenderDocument/back-forward cache) or the RenderFrame is |
| // reused the metrics update will be sent to the browser during commit and |
| // won't get ignored, successfully updating the FirstInputDelay histogram. |
| // - For "CrossSiteRendererInitiated" case, FirstInputDelay was sent when |
| // the renderer-initiated navigation started on the old frame. |
| // - For "CrossSiteBrowserInitiated" case, if the old and new RenderFrame |
| // share a process, the metrics update will be sent to the browser during |
| // commit and won't get ignored, successfully updating the histogram. |
| histogram_tester_->ExpectTotalCount(internal::kHistogramFirstInputDelay, 1); |
| } else { |
| // Note that in some cases the metrics might flakily get updated in time, |
| // before the browser changed the current RFH. So, we can neither expect it |
| // to be 0 all the time or 1 all the time. |
| // TODO(crbug.com/1150242): Support updating metrics consistently on |
| // cross-RFH cross-process navigations. |
| } |
| } |
| |
| std::vector<std::string> NavigationPageLoadMetricsBrowserTestTestValues() { |
| return {"SameSite", "CrossSiteRendererInitiated", |
| "CrossSiteBrowserInitiated"}; |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| NavigationPageLoadMetricsBrowserTest, |
| testing::ValuesIn(NavigationPageLoadMetricsBrowserTestTestValues())); |
| |
| class PrerenderPageLoadMetricsBrowserTest : public PageLoadMetricsBrowserTest { |
| public: |
| PrerenderPageLoadMetricsBrowserTest() |
| : prerender_helper_(base::BindRepeating( |
| &PrerenderPageLoadMetricsBrowserTest::web_contents, |
| base::Unretained(this))) { |
| feature_list_.InitAndEnableFeature(blink::features::kPrerender2); |
| } |
| |
| void SetUp() override { |
| prerender_helper_.SetUp(embedded_test_server()); |
| PageLoadMetricsBrowserTest::SetUp(); |
| } |
| |
| protected: |
| content::test::PrerenderTestHelper prerender_helper_; |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(PrerenderPageLoadMetricsBrowserTest, PrerenderEvent) { |
| using page_load_metrics::internal::kPageLoadPrerender2Event; |
| using page_load_metrics::internal::PageLoadPrerenderEvent; |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Navigate to an initial page. |
| auto initial_url = embedded_test_server()->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kNavigationInPrerenderedMainFrame, 0); |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kPrerenderActivationNavigation, 0); |
| |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, true, 1); |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, false, 0); |
| |
| // Start a prerender. |
| GURL prerender_url = embedded_test_server()->GetURL("/title2.html"); |
| prerender_helper_.AddPrerender(prerender_url); |
| |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kNavigationInPrerenderedMainFrame, 1); |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kPrerenderActivationNavigation, 0); |
| |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, true, 1); |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, false, 1); |
| |
| // Activate. |
| prerender_helper_.NavigatePrimaryPage(prerender_url); |
| |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kNavigationInPrerenderedMainFrame, 1); |
| histogram_tester_->ExpectBucketCount( |
| kPageLoadPrerender2Event, |
| PageLoadPrerenderEvent::kPrerenderActivationNavigation, 1); |
| |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, true, 1); |
| histogram_tester_->ExpectBucketCount( |
| page_load_metrics::internal::kPageLoadStartedInForeground, false, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(PrerenderPageLoadMetricsBrowserTest, |
| PrerenderingDoNotRecordUKM) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Navigate to an initial page. |
| auto initial_url = embedded_test_server()->GetURL("/empty.html"); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); |
| |
| // Load a page in the prerender. |
| GURL prerender_url = embedded_test_server()->GetURL("/title2.html"); |
| int host_id = prerender_helper_.AddPrerender(prerender_url); |
| content::test::PrerenderHostObserver host_observer(*web_contents(), host_id); |
| EXPECT_FALSE(host_observer.was_activated()); |
| auto entries = |
| test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName); |
| EXPECT_EQ(0u, entries.size()); |
| |
| // Activate. |
| prerender_helper_.NavigatePrimaryPage(prerender_url); |
| EXPECT_TRUE(host_observer.was_activated()); |
| entries = test_ukm_recorder_->GetMergedEntriesByName(PageLoad::kEntryName); |
| EXPECT_EQ(1u, entries.size()); |
| } |
| |
| enum BackForwardCacheStatus { kDisabled = 0, kEnabled = 1 }; |
| |
| class PageLoadMetricsBackForwardCacheBrowserTest |
| : public PageLoadMetricsBrowserTest, |
| public testing::WithParamInterface<BackForwardCacheStatus> { |
| public: |
| PageLoadMetricsBackForwardCacheBrowserTest() { |
| if (GetParam() == BackForwardCacheStatus::kEnabled) { |
| // Enable BackForwardCache. |
| feature_list_.InitWithFeaturesAndParameters( |
| {{features::kBackForwardCache, {{"enable_same_site", "true"}}}}, |
| // Allow BackForwardCache for all devices regardless of their memory. |
| {features::kBackForwardCacheMemoryControls}); |
| } else { |
| feature_list_.InitAndDisableFeature(features::kBackForwardCache); |
| DCHECK(!content::BackForwardCache::IsBackForwardCacheFeatureEnabled()); |
| } |
| } |
| |
| static std::string DescribeParams( |
| const testing::TestParamInfo<ParamType>& info) { |
| return info.param ? "BFCacheEnabled" : "BFCacheDisabled"; |
| } |
| |
| void VerifyPageEndReasons(const std::vector<PageEndReason>& reasons, |
| const GURL& url, |
| bool is_bfcache_enabled); |
| int64_t CountForMetricForURL(base::StringPiece entry_name, |
| base::StringPiece metric_name, |
| const GURL& url); |
| void ExpectNewForegroundDuration(const GURL& url, bool expect_bfcache); |
| |
| private: |
| int64_t expected_page_load_foreground_durations_ = 0; |
| int64_t expected_bfcache_foreground_durations_ = 0; |
| |
| base::test::ScopedFeatureList feature_list_; |
| }; |
| |
| // Verifies the page end reasons are as we expect. This means that the first |
| // page end reason is always recorded in Navigation.PageEndReason3, and |
| // subsequent reasons are recorded in HistoryNavigation.PageEndReason if bfcache |
| // is enabled, or Navigation.PageEndReason3 if not. |
| void PageLoadMetricsBackForwardCacheBrowserTest::VerifyPageEndReasons( |
| const std::vector<PageEndReason>& reasons, |
| const GURL& url, |
| bool is_bfcache_enabled) { |
| unsigned int reason_index = 0; |
| for (auto* entry : |
| test_ukm_recorder_->GetEntriesByName(PageLoad::kEntryName)) { |
| auto* source = test_ukm_recorder_->GetSourceForSourceId(entry->source_id); |
| if (source->url() != url) |
| continue; |
| if (test_ukm_recorder_->EntryHasMetric( |
| entry, PageLoad::kNavigation_PageEndReason3Name)) { |
| if (is_bfcache_enabled) { |
| // If bfcache is on then only one of these should exist, so the index |
| // should be zero. |
| EXPECT_EQ(reason_index, 0U); |
| } |
| ASSERT_LT(reason_index, reasons.size()); |
| test_ukm_recorder_->ExpectEntryMetric( |
| entry, PageLoad::kNavigation_PageEndReason3Name, |
| reasons[reason_index++]); |
| } |
| } |
| if (is_bfcache_enabled) { |
| EXPECT_EQ(reason_index, 1U); |
| } else { |
| EXPECT_EQ(reason_index, reasons.size()); |
| } |
| for (auto* entry : |
| test_ukm_recorder_->GetEntriesByName(HistoryNavigation::kEntryName)) { |
| auto* source = test_ukm_recorder_->GetSourceForSourceId(entry->source_id); |
| if (source->url() != url) |
| continue; |
| if (test_ukm_recorder_->EntryHasMetric( |
| entry, HistoryNavigation:: |
| kPageEndReasonAfterBackForwardCacheRestoreName)) { |
| EXPECT_TRUE(is_bfcache_enabled); |
| ASSERT_LT(reason_index, reasons.size()); |
| test_ukm_recorder_->ExpectEntryMetric( |
| entry, |
| HistoryNavigation::kPageEndReasonAfterBackForwardCacheRestoreName, |
| reasons[reason_index++]); |
| } |
| } |
| // Should have been through all the reasons. |
| EXPECT_EQ(reason_index, reasons.size()); |
| } |
| |
| int64_t PageLoadMetricsBackForwardCacheBrowserTest::CountForMetricForURL( |
| base::StringPiece entry_name, |
| base::StringPiece metric_name, |
| const GURL& url) { |
| int64_t count = 0; |
| for (auto* entry : test_ukm_recorder_->GetEntriesByName(entry_name)) { |
| auto* source = test_ukm_recorder_->GetSourceForSourceId(entry->source_id); |
| if (source->url() != url) |
| continue; |
| if (test_ukm_recorder_->EntryHasMetric(entry, metric_name)) { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| IN_PROC_BROWSER_TEST_P(PageLoadMetricsBackForwardCacheBrowserTest, |
| LogsPageEndReasons) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| bool back_forward_cache_enabled = GetParam() == kEnabled; |
| // Navigate to A. |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a)); |
| content::RenderFrameHostWrapper rfh_a(web_contents()->GetMainFrame()); |
| |
| // Navigate to B. |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b)); |
| if (back_forward_cache_enabled) { |
| ASSERT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| } |
| |
| std::vector<PageEndReason> expected_reasons_a; |
| expected_reasons_a.push_back(page_load_metrics::END_NEW_NAVIGATION); |
| VerifyPageEndReasons(expected_reasons_a, url_a, back_forward_cache_enabled); |
| |
| // Go back to A, restoring it from the back-forward cache (if enabled) |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Navigate to B again - this should trigger the |
| // BackForwardCachePageLoadMetricsObserver for A (if enabled) |
| EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b)); |
| expected_reasons_a.push_back(page_load_metrics::END_NEW_NAVIGATION); |
| VerifyPageEndReasons(expected_reasons_a, url_a, back_forward_cache_enabled); |
| |
| // Go back to A, restoring it from the back-forward cache (again) |
| web_contents()->GetController().GoBack(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Navigate to B using GoForward() to verify the correct page end reason |
| // is stored for A. |
| web_contents()->GetController().GoForward(); |
| EXPECT_TRUE(WaitForLoadStop(web_contents())); |
| expected_reasons_a.push_back(page_load_metrics::END_FORWARD_BACK); |
| VerifyPageEndReasons(expected_reasons_a, url_a, back_forward_cache_enabled); |
| } |
| |
| void PageLoadMetricsBackForwardCacheBrowserTest::ExpectNewForegroundDuration( |
| const GURL& url, |
| bool expect_bfcache) { |
| if (expect_bfcache) { |
| expected_bfcache_foreground_durations_++; |
| } else { |
| expected_page_load_foreground_durations_++; |
| } |
| int64_t bf_count = CountForMetricForURL( |
| HistoryNavigation::kEntryName, |
| HistoryNavigation::kForegroundDurationAfterBackForwardCacheRestoreName, |
| url); |
| int64_t pl_count = CountForMetricForURL( |
| PageLoad::kEntryName, PageLoad::kPageTiming_ForegroundDurationName, url); |
| EXPECT_EQ(bf_count, expected_bfcache_foreground_durations_); |
| EXPECT_EQ(pl_count, expected_page_load_foreground_durations_); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(PageLoadMetricsBackForwardCacheBrowserTest, |
| LogsBasicPageForegroundDuration) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| bool back_forward_cache_enabled = GetParam() == kEnabled; |
| // Navigate to A. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a)); |
| content::RenderFrameHostWrapper rfh_a(web_contents()->GetMainFrame()); |
| |
| // Navigate to B. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b)); |
| if (back_forward_cache_enabled) { |
| ASSERT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| } |
| |
| // Verify a new foreground duration - this one shouldn't be logged by the |
| // bfcache metrics regardless of bfcache being enabled or not. |
| ExpectNewForegroundDuration(url_a, /*expect_bfcache=*/false); |
| |
| // Go back to A, restoring it from the back-forward cache (if enabled) |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Navigate to B again - this should trigger the |
| // BackForwardCachePageLoadMetricsObserver for A (if enabled) |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b)); |
| |
| ExpectNewForegroundDuration(url_a, back_forward_cache_enabled); |
| |
| // Go back to A, restoring it from the back-forward cache (again) |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| web_contents()->GetController().GoForward(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Verify another foreground duration was logged. |
| ExpectNewForegroundDuration(url_a, back_forward_cache_enabled); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(PageLoadMetricsBackForwardCacheBrowserTest, |
| LogsPageForegroundDurationOnHide) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); |
| GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); |
| |
| bool back_forward_cache_enabled = GetParam() == kEnabled; |
| // Navigate to A. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_a)); |
| content::RenderFrameHostWrapper rfh_a(web_contents()->GetMainFrame()); |
| |
| // Navigate to B. |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b)); |
| if (back_forward_cache_enabled) { |
| ASSERT_EQ(rfh_a->GetLifecycleState(), |
| content::RenderFrameHost::LifecycleState::kInBackForwardCache); |
| } |
| |
| // Verify a new foreground duration - this one shouldn't be logged by the |
| // bfcache metrics regardless of bfcache being enabled or not. |
| ExpectNewForegroundDuration(url_a, /*expect_bfcache=*/false); |
| |
| // Go back to A, restoring it from the back-forward cache (if enabled) |
| web_contents()->GetController().GoBack(); |
| ASSERT_TRUE(WaitForLoadStop(web_contents())); |
| |
| // Open and move to a new tab. This hides A, which should log a foreground |
| // duration. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url_b, WindowOpenDisposition::NEW_FOREGROUND_TAB, |
| ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); |
| // The new tab opening should cause a foreground duration for the original |
| // tab, since it's been hidden. |
| ExpectNewForegroundDuration(url_a, back_forward_cache_enabled); |
| |
| // From this point no more foreground durations are expected to be logged, so |
| // stash the current counts. |
| int64_t bf_count = CountForMetricForURL( |
| HistoryNavigation::kEntryName, |
| HistoryNavigation::kForegroundDurationAfterBackForwardCacheRestoreName, |
| url_a); |
| int64_t pl_count = |
| CountForMetricForURL(PageLoad::kEntryName, |
| PageLoad::kPageTiming_ForegroundDurationName, url_a); |
| |
| // Switch back to the tab for url_a. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url_a, WindowOpenDisposition::SWITCH_TO_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| |
| // And then switch back to url_b's tab. This should call OnHidden for the |
| // url_a tab again, but no new foreground duration should be logged. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url_b, WindowOpenDisposition::SWITCH_TO_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| |
| int64_t bf_count_after_switch = CountForMetricForURL( |
| HistoryNavigation::kEntryName, |
| HistoryNavigation::kForegroundDurationAfterBackForwardCacheRestoreName, |
| url_a); |
| int64_t pl_count_after_switch = |
| CountForMetricForURL(PageLoad::kEntryName, |
| PageLoad::kPageTiming_ForegroundDurationName, url_a); |
| EXPECT_EQ(bf_count, bf_count_after_switch); |
| EXPECT_EQ(pl_count, pl_count_after_switch); |
| |
| // Switch back to the tab for url_a, then close the browser. This should cause |
| // OnComplete to be called on the BFCache observer, but this should not cause |
| // a new foreground duration to be logged. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), url_a, WindowOpenDisposition::SWITCH_TO_TAB, |
| ui_test_utils::BROWSER_TEST_NONE); |
| CloseBrowserSynchronously(browser()); |
| |
| // Neither of the metrics for url_a should have moved. |
| int64_t bf_count_after_close = CountForMetricForURL( |
| HistoryNavigation::kEntryName, |
| HistoryNavigation::kForegroundDurationAfterBackForwardCacheRestoreName, |
| url_a); |
| int64_t pl_count_after_close = |
| CountForMetricForURL(PageLoad::kEntryName, |
| PageLoad::kPageTiming_ForegroundDurationName, url_a); |
| EXPECT_EQ(bf_count, bf_count_after_close); |
| EXPECT_EQ(pl_count, pl_count_after_close); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| All, |
| PageLoadMetricsBackForwardCacheBrowserTest, |
| testing::ValuesIn({BackForwardCacheStatus::kDisabled, |
| BackForwardCacheStatus::kEnabled}), |
| PageLoadMetricsBackForwardCacheBrowserTest::DescribeParams); |