blob: b17785bd546b79ca3445be7e618ca47ff641d0d8 [file] [log] [blame]
// Copyright 2016 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/amp_page_load_metrics_observer.h"
#include <string>
#include <utility>
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
#include "chrome/browser/page_load_metrics/page_load_tracker.h"
#include "chrome/common/page_load_metrics/page_load_timing.h"
#include "chrome/common/page_load_metrics/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 "services/metrics/public/cpp/metrics_utils.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "url/gurl.h"
using content::NavigationSimulator;
class AMPPageLoadMetricsObserverTest
: public page_load_metrics::PageLoadMetricsObserverTestHarness {
public:
AMPPageLoadMetricsObserverTest() {}
void SetUp() override {
PageLoadMetricsObserverTestHarness::SetUp();
ResetTest();
}
void ResetTest() {
page_load_metrics::InitPageLoadTimingForTest(&timing_);
// Reset to the default testing state. Does not reset histogram state.
timing_.navigation_start = base::Time::FromDoubleT(1);
timing_.response_start = base::TimeDelta::FromSeconds(2);
timing_.parse_timing->parse_start = base::TimeDelta::FromSeconds(3);
timing_.paint_timing->first_contentful_paint =
base::TimeDelta::FromSeconds(4);
timing_.paint_timing->first_image_paint = base::TimeDelta::FromSeconds(5);
timing_.document_timing->load_event_start = base::TimeDelta::FromSeconds(7);
PopulateRequiredTimingFields(&timing_);
}
void RunTest(const GURL& url) {
NavigateAndCommit(url);
SimulateTimingUpdate(timing_);
// Navigate again to force OnComplete, which happens when a new navigation
// occurs.
NavigateAndCommit(GURL("http://otherurl.com"));
}
void ValidateHistogramsFor(const std::string& histogram,
const char* view_type,
const base::Optional<base::TimeDelta>& event,
bool expect_histograms) {
const size_t kTypeOffset = strlen("PageLoad.Clients.AMP.");
std::string view_type_histogram = histogram;
view_type_histogram.insert(kTypeOffset, view_type);
histogram_tester().ExpectTotalCount(histogram, expect_histograms ? 1 : 0);
histogram_tester().ExpectTotalCount(view_type_histogram,
expect_histograms ? 1 : 0);
if (!expect_histograms)
return;
histogram_tester().ExpectUniqueSample(
histogram,
static_cast<base::HistogramBase::Sample>(
event.value().InMilliseconds()),
1);
histogram_tester().ExpectUniqueSample(
view_type_histogram,
static_cast<base::HistogramBase::Sample>(
event.value().InMilliseconds()),
1);
}
ukm::mojom::UkmEntryPtr GetAmpPageLoadUkmEntry() {
std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> entries =
test_ukm_recorder().GetMergedEntriesByName(
ukm::builders::AmpPageLoad::kEntryName);
if (entries.size() != 1ul) {
return nullptr;
}
return std::move(entries.begin()->second);
}
protected:
void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) override {
tracker->AddObserver(base::WrapUnique(new AMPPageLoadMetricsObserver()));
}
page_load_metrics::mojom::PageLoadTiming timing_;
private:
DISALLOW_COPY_AND_ASSIGN(AMPPageLoadMetricsObserverTest);
};
TEST_F(AMPPageLoadMetricsObserverTest, AMPCachePage) {
RunTest(GURL("https://cdn.ampproject.org/page"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePage) {
RunTest(GURL("https://www.google.com/amp/page"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePageBaseURL) {
RunTest(GURL("https://www.google.com/amp/"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePage) {
RunTest(GURL("https://news.google.com/news/amp?page"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePageBaseURL) {
RunTest(GURL("https://news.google.com/news/amp"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, NonAMPPage) {
RunTest(GURL("https://www.google.com/not-amp/page"));
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, GoogleSearchAMPViewerSameDocument) {
NavigationSimulator::CreateRendererInitiated(
GURL("https://www.google.com/search"), main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(
GURL("https://www.google.com/amp/page"), main_rfh())
->CommitSameDocument();
// Verify that subframe metrics aren't recorded without an AMP subframe.
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameInputBeforeNavigation) {
GURL amp_url("https://ampviewer.com/page");
// This emulates the AMP subframe non-prerender flow: first we perform a
// same-document navigation in the main frame to the AMP viewer URL, then we
// create and navigate the subframe to an AMP cache URL.
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
// We expect a source with a negative NavigationDelta metric, since the main
// frame navigation occurred before the AMP subframe navigation.
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, nav_delta_metric);
EXPECT_GE(*nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameNavigationBeforeInput) {
GURL amp_url("https://ampviewer.com/page");
// This emulates the AMP subframe prerender flow: first we create and navigate
// the subframe to an AMP cache URL, then we perform a same-document
// navigation in the main frame to the AMP viewer URL.
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
// We expect a source with a positive NavigationDelta metric, since the main
// frame navigation occurred after the AMP subframe navigation.
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, nav_delta_metric);
EXPECT_LE(*nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMetrics) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
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->first_contentful_paint =
base::TimeDelta::FromMilliseconds(5);
subframe_timing.paint_timing->largest_image_paint_size = 1;
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(10);
subframe_timing.interactive_timing->first_input_timestamp =
base::TimeDelta::FromMilliseconds(20);
subframe_timing.interactive_timing->first_input_delay =
base::TimeDelta::FromMilliseconds(3);
PopulateRequiredTimingFields(&subframe_timing);
SimulateTimingUpdate(subframe_timing, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe", 1);
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay3", 3);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentPaint", 10);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMetrics_LayoutStability) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
page_load_metrics::mojom::FrameRenderDataUpdate render_data(1.0);
SimulateRenderDataUpdate(render_data, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectUniqueSample(
"PageLoad.Clients.AMP.Experimental.LayoutStability.JankScore.Subframe",
10, 1);
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.LayoutStability.JankScore", 100);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMetricsFullNavigation) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())->Commit();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
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->first_contentful_paint =
base::TimeDelta::FromMilliseconds(5);
subframe_timing.paint_timing->largest_image_paint_size = 1;
subframe_timing.paint_timing->largest_image_paint =
base::TimeDelta::FromMilliseconds(10);
subframe_timing.interactive_timing->first_input_timestamp =
base::TimeDelta::FromMilliseconds(20);
subframe_timing.interactive_timing->first_input_delay =
base::TimeDelta::FromMilliseconds(3);
PopulateRequiredTimingFields(&subframe_timing);
SimulateTimingUpdate(subframe_timing, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe."
"FullNavigation",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.PaintTiming.InputToLargestContentPaint.Subframe."
"FullNavigation",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay3.Subframe."
"FullNavigation",
1);
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay3", 3);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
test_ukm_recorder().ExpectEntryMetric(
entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentPaint", 10);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFullNavigation) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(GURL("https://www.example.com/"),
main_rfh())
->Commit();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
1);
// We expect a source with a negative NavigationDelta metric, since the main
// frame navigation occurred before the AMP subframe navigation.
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, nav_delta_metric);
EXPECT_GE(*nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameRecordOnFrameDeleted) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
// Delete the subframe, which should trigger metrics recording.
content::RenderFrameHostTester::For(subframe)->Detach();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
1);
// We expect a source with a negative NavigationDelta metric, since the main
// frame navigation occurred before the AMP subframe navigation.
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, nav_delta_metric);
EXPECT_GE(*nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest, SubFrameMultipleFrames) {
GURL amp_url1("https://ampviewer.com/page");
GURL amp_url2("https://ampviewer.com/page2");
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
// Simulate a prerender.
content::RenderFrameHost* subframe2 =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page2"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage2"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe2"));
// Perform a main-frame navigation to a different AMP document (not the
// prerender).
NavigationSimulator::CreateRendererInitiated(amp_url1, main_rfh())
->CommitSameDocument();
// Load the associated AMP document in an iframe.
content::RenderFrameHost* subframe1 =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe1"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe1);
SimulateMetadataUpdate(metadata, subframe2);
// Navigate the main frame to trigger metrics recording - we expect metrics to
// have been recorded for 1 AMP page (the non-prerendered page).
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
// Now navigate to the main-frame URL for the prerendered AMP document. This
// should trigger metrics recording for that document.
NavigationSimulator::CreateRendererInitiated(amp_url2, main_rfh())
->CommitSameDocument();
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
// We now expect one NavigationToInput (for the prerender) and one
// InputToNavigation (for the non-prerender).
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
1);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> entries =
test_ukm_recorder().GetMergedEntriesByName(
ukm::builders::AmpPageLoad::kEntryName);
EXPECT_EQ(2ull, entries.size());
const ukm::UkmSource* source1 = nullptr;
const ukm::UkmSource* source2 = nullptr;
for (const auto& kv : entries) {
const ukm::UkmSource* candidate =
test_ukm_recorder().GetSourceForSourceId(kv.first);
ASSERT_NE(nullptr, candidate);
if (candidate->url() == amp_url1) {
source1 = candidate;
} else if (candidate->url() == amp_url2) {
source2 = candidate;
} else {
FAIL() << "Encountered unexpected source for: " << candidate->url();
}
}
EXPECT_NE(source1, source2);
const ukm::mojom::UkmEntry* entry1 = entries.at(source1->id()).get();
EXPECT_NE(nullptr, entry1);
const ukm::mojom::UkmEntry* entry2 = entries.at(source2->id()).get();
EXPECT_NE(nullptr, entry2);
// The entry for amp_url1 should have a negative NavigationDelta metric, since
// the main frame navigation occurred before the AMP subframe navigation.
const int64_t* entry1_nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry1, "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, entry1_nav_delta_metric);
EXPECT_GE(*entry1_nav_delta_metric, 0ll);
// The entry for amp_url2 should have a positive NavigationDelta metric, since
// the main frame navigation occurred after the AMP subframe navigation.
const int64_t* entry2_nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry2, "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, entry2_nav_delta_metric);
EXPECT_LE(*entry2_nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest,
SubFrameWithNonSameDocumentMainFrameNavigation) {
GURL amp_url("https://ampviewer.com/page");
NavigationSimulator::CreateRendererInitiated(amp_url, main_rfh())->Commit();
// Load the associated AMP document in an iframe.
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"
"?amp_js_v=0.1#viewerUrl=https%3A%2F%2Fampviewer.com%2Fpage"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
1);
// We expect a source with a negative NavigationDelta metric, since the main
// frame navigation occurred before the AMP subframe navigation.
ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry();
test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
EXPECT_NE(nullptr, nav_delta_metric);
EXPECT_GE(*nav_delta_metric, 0ll);
}
TEST_F(AMPPageLoadMetricsObserverTest, NoSubFrameMetricsForNonAmpSubFrame) {
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/page"), main_rfh())
->CommitSameDocument();
// Create a non-AMP subframe document.
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://example.com/"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}
TEST_F(AMPPageLoadMetricsObserverTest,
NoSubFrameMetricsForSubFrameWithoutViewerUrl) {
NavigationSimulator::CreateRendererInitiated(GURL("https://ampviewer.com/"),
main_rfh())
->Commit();
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/page"), main_rfh())
->CommitSameDocument();
content::RenderFrameHost* subframe =
NavigationSimulator::NavigateAndCommitFromDocument(
GURL("https://ampsubframe.com/page"),
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->AppendChild("subframe"));
page_load_metrics::mojom::PageLoadMetadata metadata;
metadata.behavior_flags =
blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
SimulateMetadataUpdate(metadata, subframe);
// Navigate the main frame to trigger metrics recording.
NavigationSimulator::CreateRendererInitiated(
GURL("https://ampviewer.com/other"), main_rfh())
->CommitSameDocument();
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
0);
histogram_tester().ExpectTotalCount(
"PageLoad.Clients.AMP.Experimental.PageTiming."
"MainFrameToSubFrameNavigationDelta.Subframe",
0);
EXPECT_TRUE(test_ukm_recorder()
.GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
.empty());
}