blob: f24cc96b65a312fcd24f2b31ea773e3a5db5ba81 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
#include <memory>
#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
#include "components/page_load_metrics/browser/page_load_metrics_util.h"
#include "components/page_load_metrics/browser/page_load_tracker.h"
#include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/platform/web_mouse_event.h"
using content::NavigationSimulator;
using content::RenderFrameHost;
using content::RenderFrameHostTester;
using LargestContentType =
page_load_metrics::PageLoadMetricsObserver::LargestContentType;
namespace {
const char kDefaultTestUrl[] = "https://google.com";
const char kDefaultTestUrlAnchor[] = "https://google.com#samepage";
const char kDefaultTestUrl2[] = "https://whatever.com";
} // namespace
class CorePageLoadMetricsObserverTest
: public page_load_metrics::PageLoadMetricsObserverTestHarness {
protected:
void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
tracker->AddObserver(std::make_unique<CorePageLoadMetricsObserver>());
}
void SetUp() override {
page_load_metrics::PageLoadMetricsObserverTestHarness::SetUp();
page_load_metrics::LargestContentfulPaintHandler::SetTestMode(true);
}
};
TEST_F(CorePageLoadMetricsObserverTest, NoMetrics) {
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest,
SameDocumentNoTriggerUntilTrueNavCommit) {
base::TimeDelta first_layout = base::TimeDelta::FromMilliseconds(1);
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->first_layout = first_layout;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
NavigateAndCommit(GURL(kDefaultTestUrlAnchor));
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, SingleMetricAfterCommit) {
base::TimeDelta first_layout = base::TimeDelta::FromMilliseconds(1);
base::TimeDelta parse_start = base::TimeDelta::FromMilliseconds(1);
base::TimeDelta parse_stop = base::TimeDelta::FromMilliseconds(5);
base::TimeDelta parse_script_load_duration =
base::TimeDelta::FromMilliseconds(3);
base::TimeDelta parse_script_exec_duration =
base::TimeDelta::FromMilliseconds(1);
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->first_layout = first_layout;
timing.parse_timing->parse_start = parse_start;
timing.parse_timing->parse_stop = parse_stop;
timing.parse_timing->parse_blocked_on_script_load_duration =
parse_script_load_duration;
timing.parse_timing->parse_blocked_on_script_execution_duration =
parse_script_exec_duration;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramParseDuration,
(parse_stop - parse_start).InMilliseconds(), 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramParseBlockedOnScriptLoad,
parse_script_load_duration.InMilliseconds(), 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramParseBlockedOnScriptExecution,
parse_script_exec_duration.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, MultipleMetricsAfterCommits) {
base::TimeDelta response = base::TimeDelta::FromMilliseconds(1);
base::TimeDelta first_layout_1 = base::TimeDelta::FromMilliseconds(10);
base::TimeDelta first_layout_2 = base::TimeDelta::FromMilliseconds(20);
base::TimeDelta first_image_paint = base::TimeDelta::FromMilliseconds(30);
base::TimeDelta first_contentful_paint = first_image_paint;
base::TimeDelta dom_content = base::TimeDelta::FromMilliseconds(40);
base::TimeDelta load = base::TimeDelta::FromMilliseconds(100);
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.response_start = response;
timing.document_timing->first_layout = first_layout_1;
timing.paint_timing->first_image_paint = first_image_paint;
timing.paint_timing->first_contentful_paint = first_contentful_paint;
timing.document_timing->dom_content_loaded_event_start = dom_content;
timing.document_timing->load_event_start = load;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstContentfulPaint, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstContentfulPaint,
first_contentful_paint.InMilliseconds(), 1);
NavigateAndCommit(GURL(kDefaultTestUrl2));
page_load_metrics::mojom::PageLoadTiming timing2;
page_load_metrics::InitPageLoadTimingForTest(&timing2);
timing2.navigation_start = base::Time::FromDoubleT(200);
timing2.document_timing->first_layout = first_layout_2;
PopulateRequiredTimingFields(&timing2);
tester()->SimulateTimingUpdate(timing2);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
2);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstLayout, first_layout_1.InMilliseconds(), 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstLayout, first_layout_2.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstContentfulPaint, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstContentfulPaint,
first_contentful_paint.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstImagePaint, first_image_paint.InMilliseconds(),
1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramDomContentLoaded, dom_content.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 1);
tester()->histogram_tester().ExpectBucketCount(internal::kHistogramLoad,
load.InMilliseconds(), 1);
}
TEST_F(CorePageLoadMetricsObserverTest, BackgroundDifferentHistogram) {
base::TimeDelta first_layout = base::TimeDelta::FromSeconds(2);
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->first_layout = first_layout;
PopulateRequiredTimingFields(&timing);
// Simulate "Open link in new tab."
web_contents()->WasHidden();
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Simulate switching to the tab and making another navigation.
web_contents()->WasShown();
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramFirstLayout, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kBackgroundHistogramFirstLayout, first_layout.InMilliseconds(),
1);
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramFirstImagePaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, OnlyBackgroundLaterEvents) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->dom_content_loaded_event_start =
base::TimeDelta::FromMicroseconds(1);
PopulateRequiredTimingFields(&timing);
// Make sure first_image_paint hasn't been set (wasn't set by
// PopulateRequiredTimingFields), since we want to defer setting it until
// after backgrounding.
ASSERT_FALSE(timing.paint_timing->first_image_paint);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Background the tab, then foreground it.
web_contents()->WasHidden();
web_contents()->WasShown();
timing.paint_timing->first_image_paint = base::TimeDelta::FromSeconds(4);
PopulateRequiredTimingFields(&timing);
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// If the system clock is low resolution, PageLoadTracker's
// first_background_time_ may be same as other times such as
// dom_content_loaded_event_start.
if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
timing.document_timing->dom_content_loaded_event_start,
tester()->GetDelegateForCommittedLoad())) {
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramDomContentLoaded,
timing.document_timing->dom_content_loaded_event_start.value()
.InMilliseconds(),
1);
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramDomContentLoaded, 0);
} else {
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramDomContentLoaded, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
}
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kBackgroundHistogramFirstImagePaint, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kBackgroundHistogramFirstImagePaint,
timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, DontBackgroundQuickerLoad) {
// Set this event at 1 microsecond so it occurs before we foreground later in
// the test.
base::TimeDelta first_layout = base::TimeDelta::FromMicroseconds(1);
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.document_timing->first_layout = first_layout;
PopulateRequiredTimingFields(&timing);
web_contents()->WasHidden();
// Open in new tab
tester()->StartNavigation(GURL(kDefaultTestUrl));
// Switch to the tab
web_contents()->WasShown();
// Start another provisional load
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->SimulateTimingUpdate(timing);
// Navigate again to see if the timing updated for the foregrounded load.
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, FailedProvisionalLoad) {
GURL url(kDefaultTestUrl);
// The following tests a navigation that fails and should commit an error
// page, but finishes before the error page commit.
std::unique_ptr<content::NavigationSimulator> navigation =
content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
navigation->Fail(net::ERR_TIMED_OUT);
navigation->AbortCommit();
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramDomContentLoaded, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstImagePaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFailedProvisionalLoad, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageTimingForegroundDuration, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageTimingForegroundDurationNoCommit, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, FailedBackgroundProvisionalLoad) {
// Test that failed provisional event does not get logged in the
// histogram if it happened in the background
GURL url(kDefaultTestUrl);
web_contents()->WasHidden();
content::NavigationSimulator::NavigateAndFailFromDocument(
url, net::ERR_TIMED_OUT, main_rfh());
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFailedProvisionalLoad, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, Reload) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(5);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(10);
PopulateRequiredTimingFields(&timing);
GURL url(kDefaultTestUrl);
tester()->NavigateWithPageTransitionAndCommit(url,
ui::PAGE_TRANSITION_RELOAD);
tester()->SimulateTimingUpdate(timing);
auto resources =
GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
tester()->SimulateResourceDataUseUpdate(resources);
int64_t network_bytes = 0;
int64_t cache_bytes = 0;
for (const auto& resource : resources) {
if (resource->is_complete) {
if (resource->cache_type ==
page_load_metrics::mojom::CacheType::kNotCached)
network_bytes += resource->encoded_body_length;
else
cache_bytes += resource->encoded_body_length;
}
}
tester()->NavigateToUntrackedUrl();
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintReload, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeFirstContentfulPaintReload,
timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartReload, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeParseStartReload,
timing.parse_timing->parse_start.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartNewNavigation, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeNetworkBytesReload,
static_cast<int>((network_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesNewNavigation, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeCacheBytesReload,
static_cast<int>((cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesNewNavigation, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeTotalBytesReload,
static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesNewNavigation, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, ForwardBack) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(5);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(10);
PopulateRequiredTimingFields(&timing);
GURL url(kDefaultTestUrl);
// Back navigations to a page that was reloaded report a main transition type
// of PAGE_TRANSITION_RELOAD with a PAGE_TRANSITION_FORWARD_BACK
// modifier. This test verifies that when we encounter such a page, we log it
// as a forward/back navigation.
tester()->NavigateWithPageTransitionAndCommit(
url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_RELOAD |
ui::PAGE_TRANSITION_FORWARD_BACK));
tester()->SimulateTimingUpdate(timing);
auto resources =
GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
tester()->SimulateResourceDataUseUpdate(resources);
int64_t network_bytes = 0;
int64_t cache_bytes = 0;
for (const auto& resource : resources) {
if (resource->is_complete) {
if (resource->cache_type ==
page_load_metrics::mojom::CacheType::kNotCached)
network_bytes += resource->encoded_body_length;
else
cache_bytes += resource->encoded_body_length;
}
}
tester()->NavigateToUntrackedUrl();
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintReload, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeFirstContentfulPaintForwardBack,
timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartReload, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartForwardBack, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeParseStartForwardBack,
timing.parse_timing->parse_start.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartNewNavigation, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeNetworkBytesForwardBack,
static_cast<int>((network_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesNewNavigation, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesReload, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeCacheBytesForwardBack,
static_cast<int>((cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesNewNavigation, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesReload, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeTotalBytesForwardBack,
static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesNewNavigation, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesReload, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, NewNavigation) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(5);
timing.paint_timing->first_contentful_paint =
base::TimeDelta::FromMilliseconds(10);
PopulateRequiredTimingFields(&timing);
GURL url(kDefaultTestUrl);
tester()->NavigateWithPageTransitionAndCommit(url, ui::PAGE_TRANSITION_LINK);
tester()->SimulateTimingUpdate(timing);
auto resources =
GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
tester()->SimulateResourceDataUseUpdate(resources);
int64_t network_bytes = 0;
int64_t cache_bytes = 0;
for (const auto& resource : resources) {
if (resource->is_complete) {
if (resource->cache_type ==
page_load_metrics::mojom::CacheType::kNotCached)
network_bytes += resource->encoded_body_length;
else
cache_bytes += resource->encoded_body_length;
}
}
tester()->NavigateToUntrackedUrl();
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintReload, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation,
timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartReload, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeParseStartNewNavigation, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramLoadTypeParseStartNewNavigation,
timing.parse_timing->parse_start.value().InMilliseconds(), 1);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeNetworkBytesNewNavigation,
static_cast<int>((network_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeNetworkBytesReload, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeCacheBytesNewNavigation,
static_cast<int>((cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeCacheBytesReload, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramLoadTypeTotalBytesNewNavigation,
static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesForwardBack, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLoadTypeTotalBytesReload, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, BytesAndResourcesCounted) {
NavigateAndCommit(GURL(kDefaultTestUrl));
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageLoadTotalBytes, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageLoadNetworkBytes, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageLoadCacheBytes, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramPageLoadNetworkBytesIncludingHeaders, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTotalCompletedResources, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramNetworkCompletedResources, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramCacheCompletedResources, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, FirstMeaningfulPaint) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(5);
timing.paint_timing->first_meaningful_paint =
base::TimeDelta::FromMilliseconds(10);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstMeaningfulPaint, 1);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramParseStartToFirstMeaningfulPaint, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramFirstMeaningfulPaintStatus,
internal::FIRST_MEANINGFUL_PAINT_RECORDED, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestImagePaint) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 10u;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestImagePaint),
testing::ElementsAre(base::Bucket(4780, 1)));
}
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_OnlySubframeProvided) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(100);
// Intentionally not set candidates for the main frame.
PopulateRequiredTimingFields(&timing);
// Create a subframe timing with a largest_image_paint.
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(200);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
subframe_timing.paint_timing->largest_image_paint_size = 100u;
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
EXPECT_TRUE(
tester()
->histogram_tester()
.GetAllSamples(internal::kHistogramLargestContentfulPaintMainFrame)
.empty());
EXPECT_TRUE(
tester()
->histogram_tester()
.GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrameContentType)
.empty());
}
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_OnlyMainFrameProvided) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 50u;
PopulateRequiredTimingFields(&timing);
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
// Intentionally not set candidates for the subframes.
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrame),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrameContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
// This is to test whether LargestContentfulPaintAllFrames could merge
// candidates from different frames correctly. The merging will substitutes the
// existing candidate if a larger candidate from subframe is provided.
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_MergeFromFramesBySize_SubframeLarger) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
// Create a main frame timing with a largest_image_paint that happens late.
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(9382);
timing.paint_timing->largest_image_paint_size = 50u;
PopulateRequiredTimingFields(&timing);
// Create a candidate in subframe with a larger size.
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
subframe_timing.paint_timing->largest_image_paint_size = 100u;
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrame),
testing::ElementsAre(base::Bucket(9382, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrameContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
// This is to test whether LargestContentfulPaintAllFrames could merge
// candidates from different frames correctly. The merging will substitutes the
// existing candidate if a larger candidate from main frame is provided.
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_MergeFromFramesBySize_MainFrameLarger) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_text_paint_size = 100u;
PopulateRequiredTimingFields(&timing);
// Create a candidate in subframe with a smaller size.
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
subframe_timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(300);
subframe_timing.paint_timing->largest_text_paint_size = 50u;
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
1)));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrame),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintMainFrameContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
1)));
}
// This tests a trade-off we have made - aggregating all subframe candidates,
// which makes LCP unable to substitute the subframe candidate with a smaller
// candidate. This test provides two subframe candidates, the later larger than
// the first one.
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_SubframesCandidateOnlyGetLarger_Larger) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
PopulateRequiredTimingFields(&timing);
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
subframe_timing.paint_timing->largest_image_paint_size = 50u;
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(300);
subframe_timing.paint_timing->largest_image_paint_size = 10u;
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// Ensure that the largest_image_paint timing for the main frame is recorded.
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
// This tests a trade-off we have made - aggregating all subframe candidates,
// which makes LCP unable to substitute the subframe candidate with a smaller
// candidate. This test provides two subframe candidates, the later smaller than
// the first one.
TEST_F(
CorePageLoadMetricsObserverTest,
LargestContentfulPaintAllFrames_SubframesCandidateOnlyGetLarger_Smaller) {
const char kSubframeTestUrl[] = "https://google.com/subframe.html";
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
PopulateRequiredTimingFields(&timing);
page_load_metrics::mojom::PageLoadTiming subframe_timing;
page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
subframe_timing.navigation_start = base::Time::FromDoubleT(2);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
subframe_timing.paint_timing->largest_image_paint_size = 10u;
PopulateRequiredTimingFields(&subframe_timing);
// Commit the main frame and a subframe.
NavigateAndCommit(GURL(kDefaultTestUrl));
RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL(kSubframeTestUrl),
RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Simulate timing updates in the main frame and the subframe.
tester()->SimulateTimingUpdate(timing);
tester()->SimulateTimingUpdate(subframe_timing, subframe);
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(990);
subframe_timing.paint_timing->largest_image_paint_size = 50u;
tester()->SimulateTimingUpdate(subframe_timing, subframe);
// Navigate again to force histogram recording in the main frame.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// Ensure that the largest_image_paint timing for the main frame is recorded.
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(990, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
TEST_F(CorePageLoadMetricsObserverTest,
LargestImagePaint_DiscardBackgroundResult) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
web_contents()->WasHidden();
// This event happens after first background, so it will be discarded.
timing.paint_timing->largest_image_paint = base::Time::Now() - base::Time();
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestImagePaint_ReportLastCandidate) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(1000);
timing.paint_timing->largest_image_paint_size = 10u;
PopulateRequiredTimingFields(&timing);
tester()->SimulateTimingUpdate(timing);
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 5u;
PopulateRequiredTimingFields(&timing);
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestImagePaint),
testing::ElementsAre(base::Bucket(4780, 1)));
}
TEST_F(CorePageLoadMetricsObserverTest, ReportLastNullCandidate) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(1000);
timing.paint_timing->largest_image_paint_size = 10u;
PopulateRequiredTimingFields(&timing);
tester()->SimulateTimingUpdate(timing);
timing.paint_timing->largest_image_paint = base::Optional<base::TimeDelta>();
timing.paint_timing->largest_image_paint_size = 0;
PopulateRequiredTimingFields(&timing);
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestImagePaint, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestTextPaint) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_text_paint_size = 10u;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestTextPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// When the size is 0, the timing is regarded as not set and should be
// excluded from recording to UMA.
timing.paint_timing->largest_text_paint_size = 0u;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestContentfulPaint, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestContentfulPaintContentType, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestContentfulPaintMainFrame, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyText) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_text_paint_size = 100;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
1)));
}
TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_OnlyImage) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 100;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaint_ImageLargerThanText) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 100;
timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(1000);
timing.paint_timing->largest_text_paint_size = 10;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(4780, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
1)));
}
TEST_F(CorePageLoadMetricsObserverTest,
LargestContentfulPaint_TextLargerThanImage) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(4780);
timing.paint_timing->largest_image_paint_size = 10;
// Pick a value that lines up with a histogram bucket.
timing.paint_timing->largest_text_paint =
base::TimeDelta::FromMilliseconds(990);
timing.paint_timing->largest_text_paint_size = 100;
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaint),
testing::ElementsAre(base::Bucket(990, 1)));
EXPECT_THAT(
tester()->histogram_tester().GetAllSamples(
internal::kHistogramLargestContentfulPaintContentType),
testing::ElementsAre(base::Bucket(
static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
1)));
}
TEST_F(CorePageLoadMetricsObserverTest, ForegroundToFirstMeaningfulPaint) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->first_meaningful_paint = base::TimeDelta::FromSeconds(2);
PopulateRequiredTimingFields(&timing);
// Simulate "Open link in new tab."
web_contents()->WasHidden();
NavigateAndCommit(GURL(kDefaultTestUrl));
// First Meaningful Paint happens after tab is foregrounded.
web_contents()->WasShown();
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramForegroundToFirstMeaningfulPaint, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, TimeToInteractiveAlwaysForeground) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.interactive_timing->interactive =
base::TimeDelta::FromMilliseconds(100);
timing.interactive_timing->interactive_detection =
base::TimeDelta::FromMilliseconds(5200);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTimeToInteractive, 1);
tester()->histogram_tester().ExpectBucketCount(
internal::kHistogramTimeToInteractiveStatus,
internal::TIME_TO_INTERACTIVE_RECORDED, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, TimeToInteractiveStatusBackgrounded) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.interactive_timing->interactive =
base::TimeDelta::FromMilliseconds(100);
timing.interactive_timing->interactive_detection =
base::TimeDelta::FromMilliseconds(5200);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
// Background the tab, then foreground it.
web_contents()->WasHidden();
web_contents()->WasShown();
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTimeToInteractive, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramTimeToInteractiveStatus,
internal::TIME_TO_INTERACTIVE_BACKGROUNDED, 1);
}
TEST_F(CorePageLoadMetricsObserverTest,
TimeToInteractiveStatusUserInteractionBeforeInteractive) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->first_meaningful_paint =
base::TimeDelta::FromMilliseconds(200);
timing.interactive_timing->first_invalidating_input =
base::TimeDelta::FromMilliseconds(1000);
timing.interactive_timing->interactive =
base::TimeDelta::FromMilliseconds(2000);
timing.interactive_timing->interactive_detection =
base::TimeDelta::FromMilliseconds(7100);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTimeToInteractive, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramTimeToInteractiveStatus,
internal::TIME_TO_INTERACTIVE_USER_INTERACTION_BEFORE_INTERACTIVE, 1);
}
TEST_F(CorePageLoadMetricsObserverTest,
TimeToInteractiveStatusDidNotReachQuiescence) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->first_meaningful_paint =
base::TimeDelta::FromMilliseconds(200);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTimeToInteractive, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramTimeToInteractiveStatus,
internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_QUIESCENCE, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, TimeToInteractiveStatusDidNotReachFMP) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(200);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramTimeToInteractive, 0);
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramTimeToInteractiveStatus,
internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_FIRST_MEANINGFUL_PAINT, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, FirstInputDelayAndTimestamp) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.interactive_timing->first_input_delay =
base::TimeDelta::FromMilliseconds(5);
// Pick a value that lines up with a histogram bucket.
timing.interactive_timing->first_input_timestamp =
base::TimeDelta::FromMilliseconds(4780);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramFirstInputDelay4),
testing::ElementsAre(base::Bucket(5, 1)));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramFirstInputTimestamp4),
testing::ElementsAre(base::Bucket(4780, 1)));
}
TEST_F(CorePageLoadMetricsObserverTest, LongestInputDelayAndTimestamp) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.interactive_timing->longest_input_delay =
base::TimeDelta::FromMilliseconds(5);
// Pick a value that lines up with a histogram bucket.
timing.interactive_timing->longest_input_timestamp =
base::TimeDelta::FromMilliseconds(4780);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLongestInputDelay),
testing::ElementsAre(base::Bucket(5, 1)));
EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
internal::kHistogramLongestInputTimestamp),
testing::ElementsAre(base::Bucket(4780, 1)));
}
TEST_F(CorePageLoadMetricsObserverTest,
FirstInputDelayAndTimestampBackgrounded) {
page_load_metrics::mojom::PageLoadTiming timing;
page_load_metrics::InitPageLoadTimingForTest(&timing);
timing.navigation_start = base::Time::FromDoubleT(1);
timing.interactive_timing->first_input_delay =
base::TimeDelta::FromMilliseconds(5);
timing.interactive_timing->first_input_timestamp =
base::TimeDelta::FromMilliseconds(5000);
PopulateRequiredTimingFields(&timing);
NavigateAndCommit(GURL(kDefaultTestUrl));
// Background the tab, then foreground it.
web_contents()->WasHidden();
web_contents()->WasShown();
tester()->SimulateTimingUpdate(timing);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstInputDelay, 0);
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramFirstInputTimestamp, 0);
}
TEST_F(CorePageLoadMetricsObserverTest, NavigationToBackNavigationWithGesture) {
GURL url(kDefaultTestUrl);
// Navigate once to the page with a user gesture.
auto simulator =
content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
simulator->SetHasUserGesture(true);
simulator->Commit();
// Now the user presses the back button.
tester()->NavigateWithPageTransitionAndCommit(
url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramUserGestureNavigationToForwardBack, 1);
}
TEST_F(CorePageLoadMetricsObserverTest,
BrowserNavigationToBackNavigationWithGesture) {
GURL url(kDefaultTestUrl);
// Navigate once to the page with a user gesture.
auto simulator =
content::NavigationSimulator::CreateBrowserInitiated(url, web_contents());
simulator->SetHasUserGesture(true);
simulator->Commit();
// Now the user presses the back button.
tester()->NavigateWithPageTransitionAndCommit(
url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramUserGestureNavigationToForwardBack, 0);
}
TEST_F(CorePageLoadMetricsObserverTest,
NavigationToBackNavigationWithoutGesture) {
GURL url(kDefaultTestUrl);
// Navigate once to the page with a user gesture.
auto simulator =
content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
simulator->SetHasUserGesture(false);
simulator->Commit();
// Now the user presses the back button.
tester()->NavigateWithPageTransitionAndCommit(
url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramUserGestureNavigationToForwardBack, 0);
}
TEST_F(CorePageLoadMetricsObserverTest,
AbortedNavigationToBackNavigationWithGesture) {
GURL url(kDefaultTestUrl);
// Navigate once to the page with a user gesture.
auto simulator =
content::NavigationSimulator::CreateRendererInitiated(url, main_rfh());
simulator->SetHasUserGesture(true);
simulator->Start();
// Now the user presses the back button before the first navigation committed.
tester()->NavigateWithPageTransitionAndCommit(
url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
tester()->histogram_tester().ExpectTotalCount(
internal::kHistogramUserGestureNavigationToForwardBack, 1);
}
TEST_F(CorePageLoadMetricsObserverTest, UnfinishedBytesRecorded) {
NavigateAndCommit(GURL(kDefaultTestUrl));
std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
// Incomplete resource.
resources.push_back(
CreateResource(false /* was_cached */, 10 * 1024 /* delta_bytes */,
0 /* encoded_body_length */, false /* is_complete */));
tester()->SimulateResourceDataUseUpdate(resources);
// Navigate again to force histogram recording.
NavigateAndCommit(GURL(kDefaultTestUrl2));
// Verify that the unfinished resource bytes are recorded.
tester()->histogram_tester().ExpectUniqueSample(
internal::kHistogramPageLoadUnfinishedBytes, 10, 1);
}