1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
6
7 #include <memory>
8
9 #include "base/macros.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/process/kill.h"
12 #include "base/test/metrics/histogram_tester.h"
13 #include "base/test/scoped_feature_list.h"
14 #include "base/time/time.h"
15 #include "base/timer/mock_timer.h"
16 #include "components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h"
17 #include "components/page_load_metrics/browser/page_load_tracker.h"
18 #include "components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h"
19 #include "content/public/browser/navigation_handle.h"
20 #include "content/public/common/content_client.h"
21 #include "content/public/common/content_features.h"
22 #include "content/public/test/navigation_simulator.h"
23 #include "content/public/test/render_frame_host_test_support.h"
24 #include "content/public/test/test_renderer_host.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/cert_status_flags.h"
27 #include "services/network/public/mojom/fetch_api.mojom.h"
28 #include "testing/gmock/include/gmock/gmock.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
31 #include "url/url_constants.h"
32
33 using content::NavigationSimulator;
34
35 namespace page_load_metrics {
36
37 namespace {
38
39 const char kDefaultTestUrl[] = "https://google.com/";
40 const char kDefaultTestUrlAnchor[] = "https://google.com/#samedocument";
41 const char kDefaultTestUrl2[] = "https://whatever.com/";
42 const char kFilteredStartUrl[] = "https://whatever.com/ignore-on-start";
43 const char kFilteredCommitUrl[] = "https://whatever.com/ignore-on-commit";
44
PopulatePageLoadTiming(mojom::PageLoadTiming * timing)45 void PopulatePageLoadTiming(mojom::PageLoadTiming* timing) {
46 page_load_metrics::InitPageLoadTimingForTest(timing);
47 timing->navigation_start = base::Time::FromDoubleT(1);
48 timing->response_start = base::TimeDelta::FromMilliseconds(10);
49 timing->parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
50 }
51
CreateResourceLoadInfo(const GURL & url,network::mojom::RequestDestination request_destination)52 blink::mojom::ResourceLoadInfoPtr CreateResourceLoadInfo(
53 const GURL& url,
54 network::mojom::RequestDestination request_destination) {
55 blink::mojom::ResourceLoadInfoPtr resource_load_info =
56 blink::mojom::ResourceLoadInfo::New();
57 resource_load_info->final_url = url;
58 resource_load_info->original_url = url;
59 resource_load_info->request_destination = request_destination;
60 resource_load_info->was_cached = false;
61 resource_load_info->raw_body_bytes = 0;
62 resource_load_info->net_error = net::OK;
63 resource_load_info->network_info = blink::mojom::CommonNetworkInfo::New();
64 resource_load_info->network_info->remote_endpoint = net::IPEndPoint();
65 resource_load_info->load_timing_info.request_start = base::TimeTicks::Now();
66 return resource_load_info;
67 }
68
69 } // namespace
70
71 class MetricsWebContentsObserverTest
72 : public content::RenderViewHostTestHarness {
73 public:
MetricsWebContentsObserverTest()74 MetricsWebContentsObserverTest() {
75 mojom::PageLoadTiming timing;
76 PopulatePageLoadTiming(&timing);
77 previous_timing_ = timing.Clone();
78 }
79
SetUp()80 void SetUp() override {
81 content::RenderViewHostTestHarness::SetUp();
82 original_browser_client_ =
83 content::SetBrowserClientForTesting(&browser_client_);
84 AttachObserver();
85 }
86
TearDown()87 void TearDown() override {
88 content::SetBrowserClientForTesting(original_browser_client_);
89 RenderViewHostTestHarness::TearDown();
90 }
91
NavigateToUntrackedUrl()92 void NavigateToUntrackedUrl() {
93 content::NavigationSimulator::NavigateAndCommitFromBrowser(
94 web_contents(), GURL(url::kAboutBlankURL));
95 }
96
97 // Returns the mock timer used for buffering updates in the
98 // PageLoadMetricsUpdateDispatcher.
GetMostRecentTimer()99 base::MockOneShotTimer* GetMostRecentTimer() {
100 return embedder_interface_->GetMockTimer();
101 }
102
SimulateTimingUpdate(const mojom::PageLoadTiming & timing)103 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) {
104 SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
105 }
106
SimulateCpuTimingUpdate(const mojom::CpuTiming & timing,content::RenderFrameHost * render_frame_host)107 void SimulateCpuTimingUpdate(const mojom::CpuTiming& timing,
108 content::RenderFrameHost* render_frame_host) {
109 observer()->OnTimingUpdated(
110 render_frame_host, previous_timing_->Clone(),
111 mojom::FrameMetadataPtr(base::in_place),
112 mojom::PageLoadFeaturesPtr(base::in_place),
113 std::vector<mojom::ResourceDataUpdatePtr>(),
114 mojom::FrameRenderDataUpdatePtr(base::in_place), timing.Clone(),
115 mojom::DeferredResourceCountsPtr(base::in_place),
116 mojom::InputTimingPtr(base::in_place));
117 }
118
SimulateTimingUpdate(const mojom::PageLoadTiming & timing,content::RenderFrameHost * render_frame_host)119 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
120 content::RenderFrameHost* render_frame_host) {
121 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, render_frame_host);
122 // If sending the timing update caused the PageLoadMetricsUpdateDispatcher
123 // to schedule a buffering timer, then fire it now so metrics are dispatched
124 // to observers.
125 base::MockOneShotTimer* mock_timer = GetMostRecentTimer();
126 if (mock_timer && mock_timer->IsRunning())
127 mock_timer->Fire();
128 }
129
SimulateTimingUpdateWithoutFiringDispatchTimer(const mojom::PageLoadTiming & timing,content::RenderFrameHost * render_frame_host)130 void SimulateTimingUpdateWithoutFiringDispatchTimer(
131 const mojom::PageLoadTiming& timing,
132 content::RenderFrameHost* render_frame_host) {
133 previous_timing_ = timing.Clone();
134 observer()->OnTimingUpdated(
135 render_frame_host, timing.Clone(),
136 mojom::FrameMetadataPtr(base::in_place),
137 mojom::PageLoadFeaturesPtr(base::in_place),
138 std::vector<mojom::ResourceDataUpdatePtr>(),
139 mojom::FrameRenderDataUpdatePtr(base::in_place),
140 mojom::CpuTimingPtr(base::in_place),
141 mojom::DeferredResourceCountsPtr(base::in_place),
142 mojom::InputTimingPtr(base::in_place));
143 }
144
AttachObserver()145 void AttachObserver() {
146 auto embedder_interface =
147 std::make_unique<TestMetricsWebContentsObserverEmbedder>();
148 embedder_interface_ = embedder_interface.get();
149 MetricsWebContentsObserver* observer =
150 MetricsWebContentsObserver::CreateForWebContents(
151 web_contents(), std::move(embedder_interface));
152 observer->OnVisibilityChanged(content::Visibility::VISIBLE);
153 }
154
CheckErrorEvent(InternalErrorLoadEvent error,int count)155 void CheckErrorEvent(InternalErrorLoadEvent error, int count) {
156 histogram_tester_.ExpectBucketCount(internal::kErrorEvents, error, count);
157 num_errors_ += count;
158 }
159
CheckTotalErrorEvents()160 void CheckTotalErrorEvents() {
161 histogram_tester_.ExpectTotalCount(internal::kErrorEvents, num_errors_);
162 }
163
CheckNoErrorEvents()164 void CheckNoErrorEvents() {
165 histogram_tester_.ExpectTotalCount(internal::kErrorEvents, 0);
166 }
167
CountEmptyCompleteTimingReported()168 int CountEmptyCompleteTimingReported() {
169 int empty = 0;
170 for (const auto& timing : embedder_interface_->complete_timings()) {
171 if (page_load_metrics::IsEmpty(*timing))
172 ++empty;
173 }
174 return empty;
175 }
176
updated_timings() const177 const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const {
178 return embedder_interface_->updated_timings();
179 }
updated_cpu_timings() const180 const std::vector<mojom::CpuTimingPtr>& updated_cpu_timings() const {
181 return embedder_interface_->updated_cpu_timings();
182 }
complete_timings() const183 const std::vector<mojom::PageLoadTimingPtr>& complete_timings() const {
184 return embedder_interface_->complete_timings();
185 }
updated_subframe_timings() const186 const std::vector<mojom::PageLoadTimingPtr>& updated_subframe_timings()
187 const {
188 return embedder_interface_->updated_subframe_timings();
189 }
CountCompleteTimingReported()190 int CountCompleteTimingReported() { return complete_timings().size(); }
CountUpdatedTimingReported()191 int CountUpdatedTimingReported() { return updated_timings().size(); }
CountUpdatedCpuTimingReported()192 int CountUpdatedCpuTimingReported() { return updated_cpu_timings().size(); }
CountUpdatedSubFrameTimingReported()193 int CountUpdatedSubFrameTimingReported() {
194 return updated_subframe_timings().size();
195 }
196
observed_committed_urls_from_on_start() const197 const std::vector<GURL>& observed_committed_urls_from_on_start() const {
198 return embedder_interface_->observed_committed_urls_from_on_start();
199 }
200
observed_aborted_urls() const201 const std::vector<GURL>& observed_aborted_urls() const {
202 return embedder_interface_->observed_aborted_urls();
203 }
204
observed_features() const205 const std::vector<mojom::PageLoadFeatures>& observed_features() const {
206 return embedder_interface_->observed_features();
207 }
208
is_first_navigation_in_web_contents() const209 const base::Optional<bool>& is_first_navigation_in_web_contents() const {
210 return embedder_interface_->is_first_navigation_in_web_contents();
211 }
212
completed_filtered_urls() const213 const std::vector<GURL>& completed_filtered_urls() const {
214 return embedder_interface_->completed_filtered_urls();
215 }
216
loaded_resources() const217 const std::vector<ExtraRequestCompleteInfo>& loaded_resources() const {
218 return embedder_interface_->loaded_resources();
219 }
220
221 protected:
observer()222 MetricsWebContentsObserver* observer() {
223 return MetricsWebContentsObserver::FromWebContents(web_contents());
224 }
225
226 base::HistogramTester histogram_tester_;
227 TestMetricsWebContentsObserverEmbedder* embedder_interface_;
228
229 private:
230 int num_errors_ = 0;
231 // Since we have two types of updates, both CpuTiming and PageLoadTiming, and
232 // these feed into a singular OnTimingUpdated, we need to pass in an unchanged
233 // PageLoadTiming structure to this function, so we need to keep track of the
234 // previous structure that was passed when updating the PageLoadTiming.
235 mojom::PageLoadTimingPtr previous_timing_;
236 PageLoadMetricsTestContentBrowserClient browser_client_;
237 content::ContentBrowserClient* original_browser_client_ = nullptr;
238
239 DISALLOW_COPY_AND_ASSIGN(MetricsWebContentsObserverTest);
240 };
241
TEST_F(MetricsWebContentsObserverTest,SuccessfulMainFrameNavigation)242 TEST_F(MetricsWebContentsObserverTest, SuccessfulMainFrameNavigation) {
243 mojom::PageLoadTiming timing;
244 page_load_metrics::InitPageLoadTimingForTest(&timing);
245 timing.navigation_start = base::Time::FromDoubleT(1);
246
247 ASSERT_TRUE(observed_committed_urls_from_on_start().empty());
248 ASSERT_FALSE(is_first_navigation_in_web_contents().has_value());
249 content::NavigationSimulator::NavigateAndCommitFromBrowser(
250 web_contents(), GURL(kDefaultTestUrl));
251 ASSERT_EQ(1u, observed_committed_urls_from_on_start().size());
252 ASSERT_TRUE(observed_committed_urls_from_on_start().at(0).is_empty());
253 ASSERT_TRUE(is_first_navigation_in_web_contents().has_value());
254 ASSERT_TRUE(is_first_navigation_in_web_contents().value());
255
256 ASSERT_EQ(0, CountUpdatedTimingReported());
257 SimulateTimingUpdate(timing);
258 ASSERT_EQ(1, CountUpdatedTimingReported());
259 ASSERT_EQ(0, CountCompleteTimingReported());
260
261 content::NavigationSimulator::NavigateAndCommitFromBrowser(
262 web_contents(), GURL(kDefaultTestUrl2));
263 ASSERT_FALSE(is_first_navigation_in_web_contents().value());
264 ASSERT_EQ(1, CountCompleteTimingReported());
265 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
266 ASSERT_EQ(2u, observed_committed_urls_from_on_start().size());
267 ASSERT_EQ(kDefaultTestUrl,
268 observed_committed_urls_from_on_start().at(1).spec());
269 ASSERT_EQ(1, CountUpdatedTimingReported());
270 ASSERT_EQ(0, CountUpdatedSubFrameTimingReported());
271
272 CheckNoErrorEvents();
273 }
274
TEST_F(MetricsWebContentsObserverTest,DISABLED_MainFrameNavigationInternalAbort)275 TEST_F(MetricsWebContentsObserverTest,
276 DISABLED_MainFrameNavigationInternalAbort) {
277 auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
278 GURL(kDefaultTestUrl), web_contents());
279 navigation->FailWithResponseHeaders(
280 net::ERR_ABORTED,
281 base::MakeRefCounted<net::HttpResponseHeaders>("some_headers"));
282 ASSERT_EQ(1u, observed_aborted_urls().size());
283 ASSERT_EQ(kDefaultTestUrl, observed_aborted_urls().front().spec());
284 }
285
TEST_F(MetricsWebContentsObserverTest,SubFrame)286 TEST_F(MetricsWebContentsObserverTest, SubFrame) {
287 mojom::PageLoadTiming timing;
288 page_load_metrics::InitPageLoadTimingForTest(&timing);
289 timing.navigation_start = base::Time::FromDoubleT(1);
290 timing.response_start = base::TimeDelta::FromMilliseconds(10);
291 timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
292
293 content::NavigationSimulator::NavigateAndCommitFromBrowser(
294 web_contents(), GURL(kDefaultTestUrl));
295 SimulateTimingUpdate(timing);
296
297 ASSERT_EQ(1, CountUpdatedTimingReported());
298 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
299
300 content::RenderFrameHostTester* rfh_tester =
301 content::RenderFrameHostTester::For(main_rfh());
302 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
303
304 // Dispatch a timing update for the child frame that includes a first paint.
305 mojom::PageLoadTiming subframe_timing;
306 page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
307 subframe_timing.navigation_start = base::Time::FromDoubleT(2);
308 subframe_timing.response_start = base::TimeDelta::FromMilliseconds(10);
309 subframe_timing.parse_timing->parse_start =
310 base::TimeDelta::FromMilliseconds(20);
311 subframe_timing.paint_timing->first_paint =
312 base::TimeDelta::FromMilliseconds(40);
313 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
314 GURL(kDefaultTestUrl2), subframe);
315 SimulateTimingUpdate(subframe_timing, subframe);
316
317 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
318 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
319
320 // The subframe update which included a paint should have also triggered
321 // a main frame update, which includes a first paint.
322 ASSERT_EQ(2, CountUpdatedTimingReported());
323 EXPECT_FALSE(timing.Equals(*updated_timings().back()));
324 EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
325
326 // Navigate again to see if the timing updated for a subframe message.
327 content::NavigationSimulator::NavigateAndCommitFromBrowser(
328 web_contents(), GURL(kDefaultTestUrl2));
329
330 ASSERT_EQ(1, CountCompleteTimingReported());
331 ASSERT_EQ(2, CountUpdatedTimingReported());
332 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
333
334 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
335 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
336
337 CheckNoErrorEvents();
338 }
339
TEST_F(MetricsWebContentsObserverTest,SameDocumentNoTrigger)340 TEST_F(MetricsWebContentsObserverTest, SameDocumentNoTrigger) {
341 mojom::PageLoadTiming timing;
342 page_load_metrics::InitPageLoadTimingForTest(&timing);
343 timing.navigation_start = base::Time::FromDoubleT(1);
344
345 content::NavigationSimulator::NavigateAndCommitFromBrowser(
346 web_contents(), GURL(kDefaultTestUrl));
347 ASSERT_EQ(0, CountUpdatedTimingReported());
348 SimulateTimingUpdate(timing);
349 ASSERT_EQ(1, CountUpdatedTimingReported());
350 content::NavigationSimulator::NavigateAndCommitFromBrowser(
351 web_contents(), GURL(kDefaultTestUrlAnchor));
352 // Send the same timing update. The original tracker for kDefaultTestUrl
353 // should dedup the update, and the tracker for kDefaultTestUrlAnchor should
354 // have been destroyed as a result of its being a same page navigation, so
355 // CountUpdatedTimingReported() should continue to return 1.
356 SimulateTimingUpdate(timing);
357
358 ASSERT_EQ(1, CountUpdatedTimingReported());
359 ASSERT_EQ(0, CountCompleteTimingReported());
360
361 // Navigate again to force histogram logging.
362 content::NavigationSimulator::NavigateAndCommitFromBrowser(
363 web_contents(), GURL(kDefaultTestUrl2));
364
365 // A same page navigation shouldn't trigger logging UMA for the original.
366 ASSERT_EQ(1, CountUpdatedTimingReported());
367 ASSERT_EQ(1, CountCompleteTimingReported());
368 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
369 CheckNoErrorEvents();
370 }
371
TEST_F(MetricsWebContentsObserverTest,DontLogNewTabPage)372 TEST_F(MetricsWebContentsObserverTest, DontLogNewTabPage) {
373 mojom::PageLoadTiming timing;
374 page_load_metrics::InitPageLoadTimingForTest(&timing);
375 timing.navigation_start = base::Time::FromDoubleT(1);
376
377 embedder_interface_->set_is_ntp(true);
378
379 content::NavigationSimulator::NavigateAndCommitFromBrowser(
380 web_contents(), GURL(kDefaultTestUrl));
381 SimulateTimingUpdate(timing);
382 content::NavigationSimulator::NavigateAndCommitFromBrowser(
383 web_contents(), GURL(kDefaultTestUrl2));
384 ASSERT_EQ(0, CountUpdatedTimingReported());
385 ASSERT_EQ(0, CountCompleteTimingReported());
386
387 // Ensure that NTP and other untracked loads are still accounted for as part
388 // of keeping track of the first navigation in the WebContents.
389 embedder_interface_->set_is_ntp(false);
390 content::NavigationSimulator::NavigateAndCommitFromBrowser(
391 web_contents(), GURL(kDefaultTestUrl));
392 ASSERT_TRUE(is_first_navigation_in_web_contents().has_value());
393 ASSERT_FALSE(is_first_navigation_in_web_contents().value());
394
395 CheckErrorEvent(ERR_IPC_WITH_NO_RELEVANT_LOAD, 1);
396 CheckTotalErrorEvents();
397 }
398
TEST_F(MetricsWebContentsObserverTest,DontLogIrrelevantNavigation)399 TEST_F(MetricsWebContentsObserverTest, DontLogIrrelevantNavigation) {
400 mojom::PageLoadTiming timing;
401 page_load_metrics::InitPageLoadTimingForTest(&timing);
402 timing.navigation_start = base::Time::FromDoubleT(10);
403
404 GURL about_blank_url = GURL("about:blank");
405 content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
406 about_blank_url);
407 SimulateTimingUpdate(timing);
408 ASSERT_EQ(0, CountUpdatedTimingReported());
409 content::NavigationSimulator::NavigateAndCommitFromBrowser(
410 web_contents(), GURL(kDefaultTestUrl));
411 ASSERT_EQ(0, CountUpdatedTimingReported());
412 ASSERT_EQ(0, CountCompleteTimingReported());
413
414 // Ensure that NTP and other untracked loads are still accounted for as part
415 // of keeping track of the first navigation in the WebContents.
416 ASSERT_TRUE(is_first_navigation_in_web_contents().has_value());
417 ASSERT_FALSE(is_first_navigation_in_web_contents().value());
418
419 CheckErrorEvent(ERR_IPC_FROM_BAD_URL_SCHEME, 1);
420 CheckErrorEvent(ERR_IPC_WITH_NO_RELEVANT_LOAD, 1);
421 CheckTotalErrorEvents();
422 }
423
TEST_F(MetricsWebContentsObserverTest,EmptyTimingError)424 TEST_F(MetricsWebContentsObserverTest, EmptyTimingError) {
425 mojom::PageLoadTiming timing;
426 page_load_metrics::InitPageLoadTimingForTest(&timing);
427
428 content::NavigationSimulator::NavigateAndCommitFromBrowser(
429 web_contents(), GURL(kDefaultTestUrl));
430 SimulateTimingUpdate(timing);
431 ASSERT_EQ(0, CountUpdatedTimingReported());
432 NavigateToUntrackedUrl();
433 ASSERT_EQ(0, CountUpdatedTimingReported());
434 ASSERT_EQ(1, CountCompleteTimingReported());
435
436 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
437 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
438 CheckTotalErrorEvents();
439
440 histogram_tester_.ExpectTotalCount(
441 page_load_metrics::internal::kPageLoadTimingStatus, 1);
442 histogram_tester_.ExpectBucketCount(
443 page_load_metrics::internal::kPageLoadTimingStatus,
444 page_load_metrics::internal::INVALID_EMPTY_TIMING, 1);
445 }
446
TEST_F(MetricsWebContentsObserverTest,NullNavigationStartError)447 TEST_F(MetricsWebContentsObserverTest, NullNavigationStartError) {
448 mojom::PageLoadTiming timing;
449 page_load_metrics::InitPageLoadTimingForTest(&timing);
450 timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(1);
451
452 content::NavigationSimulator::NavigateAndCommitFromBrowser(
453 web_contents(), GURL(kDefaultTestUrl));
454 SimulateTimingUpdate(timing);
455 ASSERT_EQ(0, CountUpdatedTimingReported());
456 NavigateToUntrackedUrl();
457 ASSERT_EQ(0, CountUpdatedTimingReported());
458 ASSERT_EQ(1, CountCompleteTimingReported());
459
460 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
461 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
462 CheckTotalErrorEvents();
463
464 histogram_tester_.ExpectTotalCount(
465 page_load_metrics::internal::kPageLoadTimingStatus, 1);
466 histogram_tester_.ExpectBucketCount(
467 page_load_metrics::internal::kPageLoadTimingStatus,
468 page_load_metrics::internal::INVALID_NULL_NAVIGATION_START, 1);
469 }
470
TEST_F(MetricsWebContentsObserverTest,TimingOrderError)471 TEST_F(MetricsWebContentsObserverTest, TimingOrderError) {
472 mojom::PageLoadTiming timing;
473 page_load_metrics::InitPageLoadTimingForTest(&timing);
474 timing.navigation_start = base::Time::FromDoubleT(1);
475 timing.parse_timing->parse_stop = base::TimeDelta::FromMilliseconds(1);
476
477 content::NavigationSimulator::NavigateAndCommitFromBrowser(
478 web_contents(), GURL(kDefaultTestUrl));
479 SimulateTimingUpdate(timing);
480 ASSERT_EQ(0, CountUpdatedTimingReported());
481 NavigateToUntrackedUrl();
482 ASSERT_EQ(0, CountUpdatedTimingReported());
483 ASSERT_EQ(1, CountCompleteTimingReported());
484
485 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
486 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
487 CheckTotalErrorEvents();
488
489 histogram_tester_.ExpectTotalCount(
490 page_load_metrics::internal::kPageLoadTimingStatus, 1);
491 histogram_tester_.ExpectBucketCount(
492 page_load_metrics::internal::kPageLoadTimingStatus,
493 page_load_metrics::internal::INVALID_ORDER_PARSE_START_PARSE_STOP, 1);
494 }
495
TEST_F(MetricsWebContentsObserverTest,BadIPC)496 TEST_F(MetricsWebContentsObserverTest, BadIPC) {
497 mojom::PageLoadTiming timing;
498 page_load_metrics::InitPageLoadTimingForTest(&timing);
499 timing.navigation_start = base::Time::FromDoubleT(10);
500 mojom::PageLoadTiming timing2;
501 page_load_metrics::InitPageLoadTimingForTest(&timing2);
502 timing2.navigation_start = base::Time::FromDoubleT(100);
503
504 content::NavigationSimulator::NavigateAndCommitFromBrowser(
505 web_contents(), GURL(kDefaultTestUrl));
506
507 SimulateTimingUpdate(timing);
508 ASSERT_EQ(1, CountUpdatedTimingReported());
509 SimulateTimingUpdate(timing2);
510 ASSERT_EQ(1, CountUpdatedTimingReported());
511
512 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING_DESCENDENT, 1);
513 CheckTotalErrorEvents();
514 }
515
TEST_F(MetricsWebContentsObserverTest,ObservePartialNavigation)516 TEST_F(MetricsWebContentsObserverTest, ObservePartialNavigation) {
517 // Reset the state of the tests, and attach the MetricsWebContentsObserver in
518 // the middle of a navigation. This tests that the class is robust to only
519 // observing some of a navigation.
520 DeleteContents();
521 SetContents(CreateTestWebContents());
522
523 mojom::PageLoadTiming timing;
524 page_load_metrics::InitPageLoadTimingForTest(&timing);
525 timing.navigation_start = base::Time::FromDoubleT(10);
526
527 // Start the navigation, then start observing the web contents. This used to
528 // crash us. Make sure we bail out and don't log histograms.
529 std::unique_ptr<NavigationSimulator> navigation =
530 NavigationSimulator::CreateBrowserInitiated(GURL(kDefaultTestUrl),
531 web_contents());
532 navigation->Start();
533 AttachObserver();
534 navigation->Commit();
535
536 SimulateTimingUpdate(timing);
537
538 // Navigate again to force histogram logging.
539 content::NavigationSimulator::NavigateAndCommitFromBrowser(
540 web_contents(), GURL(kDefaultTestUrl2));
541 ASSERT_EQ(0, CountCompleteTimingReported());
542 ASSERT_EQ(0, CountUpdatedTimingReported());
543 CheckErrorEvent(ERR_IPC_WITH_NO_RELEVANT_LOAD, 1);
544 CheckTotalErrorEvents();
545 }
546
TEST_F(MetricsWebContentsObserverTest,DontLogAbortChains)547 TEST_F(MetricsWebContentsObserverTest, DontLogAbortChains) {
548 NavigateAndCommit(GURL(kDefaultTestUrl));
549 NavigateAndCommit(GURL(kDefaultTestUrl2));
550 NavigateAndCommit(GURL(kDefaultTestUrl));
551 histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 0);
552 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 2);
553 CheckTotalErrorEvents();
554 }
555
TEST_F(MetricsWebContentsObserverTest,LogAbortChains)556 TEST_F(MetricsWebContentsObserverTest, LogAbortChains) {
557 // Start and abort three loads before one finally commits.
558 NavigationSimulator::NavigateAndFailFromBrowser(
559 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
560
561 NavigationSimulator::NavigateAndFailFromBrowser(
562 web_contents(), GURL(kDefaultTestUrl2), net::ERR_ABORTED);
563
564 NavigationSimulator::NavigateAndFailFromBrowser(
565 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
566
567 NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
568 GURL(kDefaultTestUrl2));
569
570 histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 1);
571 histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNewNavigation, 3,
572 1);
573 CheckNoErrorEvents();
574 }
575
TEST_F(MetricsWebContentsObserverTest,LogAbortChainsSameURL)576 TEST_F(MetricsWebContentsObserverTest, LogAbortChainsSameURL) {
577 // Start and abort three loads before one finally commits.
578 NavigationSimulator::NavigateAndFailFromBrowser(
579 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
580
581 NavigationSimulator::NavigateAndFailFromBrowser(
582 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
583
584 NavigationSimulator::NavigateAndFailFromBrowser(
585 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
586
587 NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
588 GURL(kDefaultTestUrl));
589 histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNewNavigation, 1);
590 histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNewNavigation, 3,
591 1);
592 histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeSameURL, 1);
593 histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeSameURL, 3, 1);
594 }
595
TEST_F(MetricsWebContentsObserverTest,LogAbortChainsNoCommit)596 TEST_F(MetricsWebContentsObserverTest, LogAbortChainsNoCommit) {
597 // Start and abort three loads before one finally commits.
598 NavigationSimulator::NavigateAndFailFromBrowser(
599 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
600
601 NavigationSimulator::NavigateAndFailFromBrowser(
602 web_contents(), GURL(kDefaultTestUrl2), net::ERR_ABORTED);
603
604 NavigationSimulator::NavigateAndFailFromBrowser(
605 web_contents(), GURL(kDefaultTestUrl), net::ERR_ABORTED);
606
607 web_contents()->Stop();
608
609 histogram_tester_.ExpectTotalCount(internal::kAbortChainSizeNoCommit, 1);
610 histogram_tester_.ExpectBucketCount(internal::kAbortChainSizeNoCommit, 3, 1);
611 }
612
TEST_F(MetricsWebContentsObserverTest,FlushMetricsOnAppEnterBackground)613 TEST_F(MetricsWebContentsObserverTest, FlushMetricsOnAppEnterBackground) {
614 content::NavigationSimulator::NavigateAndCommitFromBrowser(
615 web_contents(), GURL(kDefaultTestUrl));
616
617 histogram_tester_.ExpectTotalCount(
618 internal::kPageLoadCompletedAfterAppBackground, 0);
619
620 observer()->FlushMetricsOnAppEnterBackground();
621
622 histogram_tester_.ExpectTotalCount(
623 internal::kPageLoadCompletedAfterAppBackground, 1);
624 histogram_tester_.ExpectBucketCount(
625 internal::kPageLoadCompletedAfterAppBackground, false, 1);
626 histogram_tester_.ExpectBucketCount(
627 internal::kPageLoadCompletedAfterAppBackground, true, 0);
628
629 // Navigate again, which forces completion callbacks on the previous
630 // navigation to be invoked.
631 content::NavigationSimulator::NavigateAndCommitFromBrowser(
632 web_contents(), GURL(kDefaultTestUrl2));
633
634 // Verify that, even though the page load completed, no complete timings were
635 // reported, because the TestPageLoadMetricsObserver's
636 // FlushMetricsOnAppEnterBackground implementation returned STOP_OBSERVING,
637 // thus preventing OnComplete from being invoked.
638 ASSERT_EQ(0, CountCompleteTimingReported());
639
640 DeleteContents();
641
642 histogram_tester_.ExpectTotalCount(
643 internal::kPageLoadCompletedAfterAppBackground, 2);
644 histogram_tester_.ExpectBucketCount(
645 internal::kPageLoadCompletedAfterAppBackground, false, 1);
646 histogram_tester_.ExpectBucketCount(
647 internal::kPageLoadCompletedAfterAppBackground, true, 1);
648 }
649
TEST_F(MetricsWebContentsObserverTest,StopObservingOnCommit)650 TEST_F(MetricsWebContentsObserverTest, StopObservingOnCommit) {
651 ASSERT_TRUE(completed_filtered_urls().empty());
652
653 content::NavigationSimulator::NavigateAndCommitFromBrowser(
654 web_contents(), GURL(kDefaultTestUrl));
655 ASSERT_TRUE(completed_filtered_urls().empty());
656
657 // kFilteredCommitUrl should stop observing in OnCommit, and thus should not
658 // reach OnComplete().
659 content::NavigationSimulator::NavigateAndCommitFromBrowser(
660 web_contents(), GURL(kFilteredCommitUrl));
661 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl)}),
662 completed_filtered_urls());
663
664 content::NavigationSimulator::NavigateAndCommitFromBrowser(
665 web_contents(), GURL(kDefaultTestUrl2));
666 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl)}),
667 completed_filtered_urls());
668
669 content::NavigationSimulator::NavigateAndCommitFromBrowser(
670 web_contents(), GURL(kDefaultTestUrl));
671 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl), GURL(kDefaultTestUrl2)}),
672 completed_filtered_urls());
673 }
674
TEST_F(MetricsWebContentsObserverTest,StopObservingOnStart)675 TEST_F(MetricsWebContentsObserverTest, StopObservingOnStart) {
676 ASSERT_TRUE(completed_filtered_urls().empty());
677
678 content::NavigationSimulator::NavigateAndCommitFromBrowser(
679 web_contents(), GURL(kDefaultTestUrl));
680 ASSERT_TRUE(completed_filtered_urls().empty());
681
682 // kFilteredCommitUrl should stop observing in OnStart, and thus should not
683 // reach OnComplete().
684 content::NavigationSimulator::NavigateAndCommitFromBrowser(
685 web_contents(), GURL(kFilteredStartUrl));
686 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl)}),
687 completed_filtered_urls());
688
689 content::NavigationSimulator::NavigateAndCommitFromBrowser(
690 web_contents(), GURL(kDefaultTestUrl2));
691 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl)}),
692 completed_filtered_urls());
693
694 content::NavigationSimulator::NavigateAndCommitFromBrowser(
695 web_contents(), GURL(kDefaultTestUrl));
696 ASSERT_EQ(std::vector<GURL>({GURL(kDefaultTestUrl), GURL(kDefaultTestUrl2)}),
697 completed_filtered_urls());
698 }
699
700 // We buffer cross frame timings in order to provide a consistent view of
701 // timing data to observers. See crbug.com/722860 for more.
TEST_F(MetricsWebContentsObserverTest,OutOfOrderCrossFrameTiming)702 TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming) {
703 mojom::PageLoadTiming timing;
704 page_load_metrics::InitPageLoadTimingForTest(&timing);
705 timing.navigation_start = base::Time::FromDoubleT(1);
706 timing.response_start = base::TimeDelta::FromMilliseconds(10);
707
708 content::NavigationSimulator::NavigateAndCommitFromBrowser(
709 web_contents(), GURL(kDefaultTestUrl));
710 SimulateTimingUpdate(timing);
711
712 ASSERT_EQ(1, CountUpdatedTimingReported());
713 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
714
715 content::RenderFrameHostTester* rfh_tester =
716 content::RenderFrameHostTester::For(main_rfh());
717 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
718
719 // Dispatch a timing update for the child frame that includes a first paint.
720 mojom::PageLoadTiming subframe_timing;
721 PopulatePageLoadTiming(&subframe_timing);
722 subframe_timing.paint_timing->first_paint =
723 base::TimeDelta::FromMilliseconds(40);
724 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
725 GURL(kDefaultTestUrl2), subframe);
726 SimulateTimingUpdate(subframe_timing, subframe);
727
728 // Though a first paint was dispatched in the child, it should not yet be
729 // reflected as an updated timing in the main frame, since the main frame
730 // hasn't received updates for required earlier events such as parse_start.
731 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
732 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
733 ASSERT_EQ(1, CountUpdatedTimingReported());
734 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
735
736 // Dispatch the parse_start event in the parent. We should now unbuffer the
737 // first paint main frame update and receive a main frame update with a first
738 // paint value.
739 timing.parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
740 SimulateTimingUpdate(timing);
741 ASSERT_EQ(2, CountUpdatedTimingReported());
742 EXPECT_FALSE(timing.Equals(*updated_timings().back()));
743 EXPECT_TRUE(
744 updated_timings().back()->parse_timing->Equals(*timing.parse_timing));
745 EXPECT_TRUE(updated_timings().back()->document_timing->Equals(
746 *timing.document_timing));
747 EXPECT_FALSE(
748 updated_timings().back()->paint_timing->Equals(*timing.paint_timing));
749 EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
750
751 // Navigate again to see if the timing updated for a subframe message.
752 content::NavigationSimulator::NavigateAndCommitFromBrowser(
753 web_contents(), GURL(kDefaultTestUrl2));
754
755 ASSERT_EQ(1, CountCompleteTimingReported());
756 ASSERT_EQ(2, CountUpdatedTimingReported());
757 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
758
759 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
760 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
761
762 CheckNoErrorEvents();
763 }
764
765 // We buffer cross-frame paint updates to account for paint timings from
766 // different frames arriving out of order.
TEST_F(MetricsWebContentsObserverTest,OutOfOrderCrossFrameTiming2)767 TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming2) {
768 // Dispatch a timing update for the main frame that includes a first
769 // paint. This should be buffered, with the dispatch timer running.
770 mojom::PageLoadTiming timing;
771 PopulatePageLoadTiming(&timing);
772 // Ensure this is much bigger than the subframe first paint below. We
773 // currently can't inject the navigation start offset, so we must ensure that
774 // subframe first paint + navigation start offset < main frame first paint.
775 timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(100000);
776 content::NavigationSimulator::NavigateAndCommitFromBrowser(
777 web_contents(), GURL(kDefaultTestUrl));
778 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
779
780 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
781 ASSERT_EQ(0, CountUpdatedTimingReported());
782
783 content::RenderFrameHostTester* rfh_tester =
784 content::RenderFrameHostTester::For(main_rfh());
785
786 // Dispatch a timing update for a child frame that includes a first paint.
787 mojom::PageLoadTiming subframe_timing;
788 PopulatePageLoadTiming(&subframe_timing);
789 subframe_timing.paint_timing->first_paint =
790 base::TimeDelta::FromMilliseconds(500);
791 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
792 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
793 GURL(kDefaultTestUrl2), subframe);
794 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe);
795
796 histogram_tester_.ExpectTotalCount(
797 page_load_metrics::internal::kHistogramOutOfOrderTiming, 1);
798
799 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
800 ASSERT_EQ(0, CountUpdatedTimingReported());
801
802 // At this point, the timing update is buffered, waiting for the timer to
803 // fire.
804 GetMostRecentTimer()->Fire();
805
806 // Firing the timer should produce a timing update. The update should be a
807 // merged view of the main frame timing, with a first paint timestamp from the
808 // subframe.
809 ASSERT_EQ(1, CountUpdatedTimingReported());
810 EXPECT_FALSE(timing.Equals(*updated_timings().back()));
811 EXPECT_TRUE(
812 updated_timings().back()->parse_timing->Equals(*timing.parse_timing));
813 EXPECT_TRUE(updated_timings().back()->document_timing->Equals(
814 *timing.document_timing));
815 EXPECT_FALSE(
816 updated_timings().back()->paint_timing->Equals(*timing.paint_timing));
817 EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
818
819 // The first paint value should be the min of all received first paints, which
820 // in this case is the first paint from the subframe. Since it is offset by
821 // the subframe's navigation start, the received value should be >= the first
822 // paint value specified in the subframe.
823 EXPECT_GE(updated_timings().back()->paint_timing->first_paint,
824 subframe_timing.paint_timing->first_paint);
825 EXPECT_LT(updated_timings().back()->paint_timing->first_paint,
826 timing.paint_timing->first_paint);
827
828 base::TimeDelta initial_first_paint =
829 updated_timings().back()->paint_timing->first_paint.value();
830
831 // Dispatch a timing update for an additional child frame, with an earlier
832 // first paint time. This should cause an immediate update, without a timer
833 // delay.
834 subframe_timing.paint_timing->first_paint =
835 base::TimeDelta::FromMilliseconds(50);
836 content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe");
837 subframe2 = content::NavigationSimulator::NavigateAndCommitFromDocument(
838 GURL(kDefaultTestUrl2), subframe2);
839 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe2);
840
841 base::TimeDelta updated_first_paint =
842 updated_timings().back()->paint_timing->first_paint.value();
843
844 EXPECT_FALSE(GetMostRecentTimer()->IsRunning());
845 ASSERT_EQ(2, CountUpdatedTimingReported());
846 EXPECT_LT(updated_first_paint, initial_first_paint);
847
848 histogram_tester_.ExpectTotalCount(
849 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered, 1);
850 histogram_tester_.ExpectBucketCount(
851 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered,
852 (initial_first_paint - updated_first_paint).InMilliseconds(), 1);
853
854 CheckNoErrorEvents();
855 }
856
TEST_F(MetricsWebContentsObserverTest,FirstInputDelayMissingFirstInputTimestamp)857 TEST_F(MetricsWebContentsObserverTest,
858 FirstInputDelayMissingFirstInputTimestamp) {
859 mojom::PageLoadTiming timing;
860 PopulatePageLoadTiming(&timing);
861 timing.interactive_timing->first_input_delay =
862 base::TimeDelta::FromMilliseconds(10);
863
864 content::NavigationSimulator::NavigateAndCommitFromBrowser(
865 web_contents(), GURL(kDefaultTestUrl));
866 SimulateTimingUpdate(timing);
867 content::NavigationSimulator::NavigateAndCommitFromBrowser(
868 web_contents(), GURL(kDefaultTestUrl2));
869
870 const mojom::InteractiveTiming& interactive_timing =
871 *complete_timings().back()->interactive_timing;
872
873 // Won't have been set, as we're missing the first_input_timestamp.
874 EXPECT_FALSE(interactive_timing.first_input_delay.has_value());
875
876 histogram_tester_.ExpectTotalCount(
877 page_load_metrics::internal::kPageLoadTimingStatus, 1);
878 histogram_tester_.ExpectBucketCount(
879 page_load_metrics::internal::kPageLoadTimingStatus,
880 page_load_metrics::internal::INVALID_NULL_FIRST_INPUT_TIMESTAMP, 1);
881
882 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
883 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
884 CheckTotalErrorEvents();
885 }
886
TEST_F(MetricsWebContentsObserverTest,FirstInputTimestampMissingFirstInputDelay)887 TEST_F(MetricsWebContentsObserverTest,
888 FirstInputTimestampMissingFirstInputDelay) {
889 mojom::PageLoadTiming timing;
890 PopulatePageLoadTiming(&timing);
891 timing.interactive_timing->first_input_timestamp =
892 base::TimeDelta::FromMilliseconds(10);
893
894 content::NavigationSimulator::NavigateAndCommitFromBrowser(
895 web_contents(), GURL(kDefaultTestUrl));
896 SimulateTimingUpdate(timing);
897 content::NavigationSimulator::NavigateAndCommitFromBrowser(
898 web_contents(), GURL(kDefaultTestUrl2));
899
900 const mojom::InteractiveTiming& interactive_timing =
901 *complete_timings().back()->interactive_timing;
902
903 // Won't have been set, as we're missing the first_input_delay.
904 EXPECT_FALSE(interactive_timing.first_input_timestamp.has_value());
905
906 histogram_tester_.ExpectTotalCount(
907 page_load_metrics::internal::kPageLoadTimingStatus, 1);
908 histogram_tester_.ExpectBucketCount(
909 page_load_metrics::internal::kPageLoadTimingStatus,
910 page_load_metrics::internal::INVALID_NULL_FIRST_INPUT_DELAY, 1);
911
912 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
913 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
914 CheckTotalErrorEvents();
915 }
916
TEST_F(MetricsWebContentsObserverTest,LongestInputDelayMissingLongestInputTimestamp)917 TEST_F(MetricsWebContentsObserverTest,
918 LongestInputDelayMissingLongestInputTimestamp) {
919 mojom::PageLoadTiming timing;
920 PopulatePageLoadTiming(&timing);
921 timing.interactive_timing->longest_input_delay =
922 base::TimeDelta::FromMilliseconds(10);
923
924 content::NavigationSimulator::NavigateAndCommitFromBrowser(
925 web_contents(), GURL(kDefaultTestUrl));
926 SimulateTimingUpdate(timing);
927 content::NavigationSimulator::NavigateAndCommitFromBrowser(
928 web_contents(), GURL(kDefaultTestUrl2));
929
930 const mojom::InteractiveTiming& interactive_timing =
931 *complete_timings().back()->interactive_timing;
932
933 // Won't have been set, as we're missing the longest_input_timestamp.
934 EXPECT_FALSE(interactive_timing.longest_input_delay.has_value());
935
936 histogram_tester_.ExpectTotalCount(
937 page_load_metrics::internal::kPageLoadTimingStatus, 1);
938 histogram_tester_.ExpectBucketCount(
939 page_load_metrics::internal::kPageLoadTimingStatus,
940 page_load_metrics::internal::INVALID_NULL_LONGEST_INPUT_TIMESTAMP, 1);
941
942 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
943 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
944 CheckTotalErrorEvents();
945 }
946
TEST_F(MetricsWebContentsObserverTest,LongestInputTimestampMissingLongestInputDelay)947 TEST_F(MetricsWebContentsObserverTest,
948 LongestInputTimestampMissingLongestInputDelay) {
949 mojom::PageLoadTiming timing;
950 PopulatePageLoadTiming(&timing);
951 timing.interactive_timing->longest_input_timestamp =
952 base::TimeDelta::FromMilliseconds(10);
953
954 content::NavigationSimulator::NavigateAndCommitFromBrowser(
955 web_contents(), GURL(kDefaultTestUrl));
956 SimulateTimingUpdate(timing);
957 content::NavigationSimulator::NavigateAndCommitFromBrowser(
958 web_contents(), GURL(kDefaultTestUrl2));
959
960 const mojom::InteractiveTiming& interactive_timing =
961 *complete_timings().back()->interactive_timing;
962
963 // Won't have been set, as we're missing the longest_input_delay.
964 EXPECT_FALSE(interactive_timing.longest_input_timestamp.has_value());
965
966 histogram_tester_.ExpectTotalCount(
967 page_load_metrics::internal::kPageLoadTimingStatus, 1);
968 histogram_tester_.ExpectBucketCount(
969 page_load_metrics::internal::kPageLoadTimingStatus,
970 page_load_metrics::internal::INVALID_NULL_LONGEST_INPUT_DELAY, 1);
971
972 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
973 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
974 CheckTotalErrorEvents();
975 }
976
TEST_F(MetricsWebContentsObserverTest,LongestInputDelaySmallerThanFirstInputDelay)977 TEST_F(MetricsWebContentsObserverTest,
978 LongestInputDelaySmallerThanFirstInputDelay) {
979 mojom::PageLoadTiming timing;
980 PopulatePageLoadTiming(&timing);
981 timing.interactive_timing->first_input_delay =
982 base::TimeDelta::FromMilliseconds(50);
983 timing.interactive_timing->first_input_timestamp =
984 base::TimeDelta::FromMilliseconds(1000);
985
986 timing.interactive_timing->longest_input_delay =
987 base::TimeDelta::FromMilliseconds(10);
988 timing.interactive_timing->longest_input_timestamp =
989 base::TimeDelta::FromMilliseconds(2000);
990
991 content::NavigationSimulator::NavigateAndCommitFromBrowser(
992 web_contents(), GURL(kDefaultTestUrl));
993 SimulateTimingUpdate(timing);
994 content::NavigationSimulator::NavigateAndCommitFromBrowser(
995 web_contents(), GURL(kDefaultTestUrl2));
996
997 const mojom::InteractiveTiming& interactive_timing =
998 *complete_timings().back()->interactive_timing;
999
1000 // Won't have been set, as it's invalid.
1001 EXPECT_FALSE(interactive_timing.longest_input_delay.has_value());
1002
1003 histogram_tester_.ExpectTotalCount(
1004 page_load_metrics::internal::kPageLoadTimingStatus, 1);
1005 histogram_tester_.ExpectBucketCount(
1006 page_load_metrics::internal::kPageLoadTimingStatus,
1007 page_load_metrics::internal::
1008 INVALID_LONGEST_INPUT_DELAY_LESS_THAN_FIRST_INPUT_DELAY,
1009 1);
1010
1011 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
1012 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
1013 CheckTotalErrorEvents();
1014 }
1015
TEST_F(MetricsWebContentsObserverTest,LongestInputTimestampEarlierThanFirstInputTimestamp)1016 TEST_F(MetricsWebContentsObserverTest,
1017 LongestInputTimestampEarlierThanFirstInputTimestamp) {
1018 mojom::PageLoadTiming timing;
1019 PopulatePageLoadTiming(&timing);
1020 timing.interactive_timing->first_input_delay =
1021 base::TimeDelta::FromMilliseconds(50);
1022 timing.interactive_timing->first_input_timestamp =
1023 base::TimeDelta::FromMilliseconds(1000);
1024
1025 timing.interactive_timing->longest_input_delay =
1026 base::TimeDelta::FromMilliseconds(60);
1027 timing.interactive_timing->longest_input_timestamp =
1028 base::TimeDelta::FromMilliseconds(500);
1029
1030 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1031 web_contents(), GURL(kDefaultTestUrl));
1032 SimulateTimingUpdate(timing);
1033 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1034 web_contents(), GURL(kDefaultTestUrl2));
1035
1036 const mojom::InteractiveTiming& interactive_timing =
1037 *complete_timings().back()->interactive_timing;
1038
1039 // Won't have been set, as it's invalid.
1040 EXPECT_FALSE(interactive_timing.longest_input_delay.has_value());
1041
1042 histogram_tester_.ExpectTotalCount(
1043 page_load_metrics::internal::kPageLoadTimingStatus, 1);
1044 histogram_tester_.ExpectBucketCount(
1045 page_load_metrics::internal::kPageLoadTimingStatus,
1046 page_load_metrics::internal::
1047 INVALID_LONGEST_INPUT_TIMESTAMP_LESS_THAN_FIRST_INPUT_TIMESTAMP,
1048 1);
1049
1050 CheckErrorEvent(ERR_BAD_TIMING_IPC_INVALID_TIMING, 1);
1051 CheckErrorEvent(ERR_NO_IPCS_RECEIVED, 1);
1052 CheckTotalErrorEvents();
1053 }
1054
1055 // Main frame delivers an input notification. Subsequently, a subframe delivers
1056 // an input notification, where the input occurred first. Verify that
1057 // FirstInputDelay and FirstInputTimestamp come from the subframe.
TEST_F(MetricsWebContentsObserverTest,FirstInputDelayAndTimingSubframeFirstDeliveredSecond)1058 TEST_F(MetricsWebContentsObserverTest,
1059 FirstInputDelayAndTimingSubframeFirstDeliveredSecond) {
1060 mojom::PageLoadTiming timing;
1061 PopulatePageLoadTiming(&timing);
1062 timing.interactive_timing->first_input_delay =
1063 base::TimeDelta::FromMilliseconds(10);
1064 // Set this far in the future. We currently can't control the navigation start
1065 // offset, so we ensure that the subframe timestamp + the unknown offset is
1066 // less than the main frame timestamp.
1067 timing.interactive_timing->first_input_timestamp =
1068 base::TimeDelta::FromMinutes(100);
1069
1070 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1071 web_contents(), GURL(kDefaultTestUrl));
1072 SimulateTimingUpdate(timing);
1073
1074 content::RenderFrameHostTester* rfh_tester =
1075 content::RenderFrameHostTester::For(main_rfh());
1076 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
1077
1078 // Dispatch a timing update for the child frame that includes a first input
1079 // earlier than the one for the main frame.
1080 mojom::PageLoadTiming subframe_timing;
1081 PopulatePageLoadTiming(&subframe_timing);
1082 subframe_timing.interactive_timing->first_input_delay =
1083 base::TimeDelta::FromMilliseconds(15);
1084 subframe_timing.interactive_timing->first_input_timestamp =
1085 base::TimeDelta::FromMilliseconds(90);
1086
1087 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
1088 GURL(kDefaultTestUrl2), subframe);
1089 SimulateTimingUpdate(subframe_timing, subframe);
1090
1091 // Navigate again to confirm the timing updated for a subframe message.
1092 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1093 web_contents(), GURL(kDefaultTestUrl2));
1094
1095 const mojom::InteractiveTiming& interactive_timing =
1096 *complete_timings().back()->interactive_timing;
1097
1098 EXPECT_EQ(base::TimeDelta::FromMilliseconds(15),
1099 interactive_timing.first_input_delay);
1100 // Ensure the timestamp is from the subframe. The main frame timestamp was 100
1101 // minutes.
1102 EXPECT_LT(interactive_timing.first_input_timestamp,
1103 base::TimeDelta::FromMinutes(10));
1104
1105 CheckNoErrorEvents();
1106 }
1107
1108 // A subframe delivers an input notification. Subsequently, the mainframe
1109 // delivers an input notification, where the input occurred first. Verify that
1110 // FirstInputDelay and FirstInputTimestamp come from the main frame.
TEST_F(MetricsWebContentsObserverTest,FirstInputDelayAndTimingMainframeFirstDeliveredSecond)1111 TEST_F(MetricsWebContentsObserverTest,
1112 FirstInputDelayAndTimingMainframeFirstDeliveredSecond) {
1113 // We need to navigate before we can navigate the subframe.
1114 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1115 web_contents(), GURL(kDefaultTestUrl));
1116
1117 content::RenderFrameHostTester* rfh_tester =
1118 content::RenderFrameHostTester::For(main_rfh());
1119 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
1120
1121 mojom::PageLoadTiming subframe_timing;
1122 PopulatePageLoadTiming(&subframe_timing);
1123 subframe_timing.interactive_timing->first_input_delay =
1124 base::TimeDelta::FromMilliseconds(10);
1125 subframe_timing.interactive_timing->first_input_timestamp =
1126 base::TimeDelta::FromMinutes(100);
1127
1128 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
1129 GURL(kDefaultTestUrl2), subframe);
1130 SimulateTimingUpdate(subframe_timing, subframe);
1131
1132 mojom::PageLoadTiming timing;
1133 PopulatePageLoadTiming(&timing);
1134 // Dispatch a timing update for the main frame that includes a first input
1135 // earlier than the one for the subframe.
1136
1137 timing.interactive_timing->first_input_delay =
1138 base::TimeDelta::FromMilliseconds(15);
1139 // Set this far in the future. We currently can't control the navigation start
1140 // offset, so we ensure that the main frame timestamp + the unknown offset is
1141 // less than the subframe timestamp.
1142 timing.interactive_timing->first_input_timestamp =
1143 base::TimeDelta::FromMilliseconds(90);
1144
1145 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1146 web_contents(), GURL(kDefaultTestUrl));
1147 SimulateTimingUpdate(timing);
1148
1149 // Navigate again to confirm the timing updated for the mainframe message.
1150 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1151 web_contents(), GURL(kDefaultTestUrl2));
1152
1153 const mojom::InteractiveTiming& interactive_timing =
1154 *complete_timings().back()->interactive_timing;
1155
1156 EXPECT_EQ(base::TimeDelta::FromMilliseconds(15),
1157 interactive_timing.first_input_delay);
1158 // Ensure the timestamp is from the main frame. The subframe timestamp was 100
1159 // minutes.
1160 EXPECT_LT(interactive_timing.first_input_timestamp,
1161 base::TimeDelta::FromMinutes(10));
1162
1163 CheckNoErrorEvents();
1164 }
1165
TEST_F(MetricsWebContentsObserverTest,DISABLED_LongestInputInMainFrame)1166 TEST_F(MetricsWebContentsObserverTest, DISABLED_LongestInputInMainFrame) {
1167 // We need to navigate before we can navigate the subframe.
1168 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1169 web_contents(), GURL(kDefaultTestUrl));
1170
1171 content::RenderFrameHostTester* rfh_tester =
1172 content::RenderFrameHostTester::For(main_rfh());
1173 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
1174
1175 mojom::PageLoadTiming subframe_timing;
1176 PopulatePageLoadTiming(&subframe_timing);
1177 subframe_timing.interactive_timing->longest_input_delay =
1178 base::TimeDelta::FromMilliseconds(70);
1179 subframe_timing.interactive_timing->longest_input_timestamp =
1180 base::TimeDelta::FromMilliseconds(1000);
1181
1182 subframe = content::NavigationSimulator::NavigateAndCommitFromDocument(
1183 GURL(kDefaultTestUrl2), subframe);
1184 SimulateTimingUpdate(subframe_timing, subframe);
1185
1186 mojom::PageLoadTiming main_frame_timing;
1187 PopulatePageLoadTiming(&main_frame_timing);
1188
1189 // Dispatch a timing update for the main frame that includes a longest input
1190 // delay longer than the one for the subframe.
1191 main_frame_timing.interactive_timing->longest_input_delay =
1192 base::TimeDelta::FromMilliseconds(100);
1193 main_frame_timing.interactive_timing->longest_input_timestamp =
1194 base::TimeDelta::FromMilliseconds(2000);
1195 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1196 web_contents(), GURL(kDefaultTestUrl));
1197 SimulateTimingUpdate(main_frame_timing);
1198
1199 // Second subframe.
1200 content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe2");
1201 mojom::PageLoadTiming subframe2_timing;
1202 PopulatePageLoadTiming(&subframe2_timing);
1203 subframe2_timing.interactive_timing->longest_input_delay =
1204 base::TimeDelta::FromMilliseconds(80);
1205 subframe2_timing.interactive_timing->longest_input_timestamp =
1206 base::TimeDelta::FromMilliseconds(3000);
1207 subframe2 = content::NavigationSimulator::NavigateAndCommitFromDocument(
1208 GURL(kDefaultTestUrl2), subframe2);
1209 SimulateTimingUpdate(subframe2_timing, subframe2);
1210
1211 // Navigate again to confirm all timings are updated.
1212 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1213 web_contents(), GURL(kDefaultTestUrl2));
1214
1215 const mojom::InteractiveTiming& interactive_timing =
1216 *complete_timings().back()->interactive_timing;
1217
1218 EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
1219 interactive_timing.longest_input_delay);
1220 EXPECT_EQ(base::TimeDelta::FromMilliseconds(2000),
1221 interactive_timing.longest_input_timestamp);
1222
1223 CheckNoErrorEvents();
1224 }
1225
1226 // -----------------------------------------------------------------------------
1227 // | | |
1228 // 1s 2s 3s
1229 // Subframe1 Main Frame Subframe2
1230 // LID (15ms) LID (100ms) LID (200ms)
1231 //
1232 // Delivery order: Main Frame -> Subframe1 -> Subframe2.
TEST_F(MetricsWebContentsObserverTest,LongestInputInSubframe)1233 TEST_F(MetricsWebContentsObserverTest, LongestInputInSubframe) {
1234 mojom::PageLoadTiming main_frame_timing;
1235 PopulatePageLoadTiming(&main_frame_timing);
1236 main_frame_timing.interactive_timing->longest_input_delay =
1237 base::TimeDelta::FromMilliseconds(100);
1238 main_frame_timing.interactive_timing->longest_input_timestamp =
1239 base::TimeDelta::FromMilliseconds(2000);
1240 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1241 web_contents(), GURL(kDefaultTestUrl));
1242 SimulateTimingUpdate(main_frame_timing);
1243
1244 content::RenderFrameHostTester* rfh_tester =
1245 content::RenderFrameHostTester::For(main_rfh());
1246
1247 // First subframe.
1248 content::RenderFrameHost* subframe1 = rfh_tester->AppendChild("subframe1");
1249 mojom::PageLoadTiming subframe_timing;
1250 PopulatePageLoadTiming(&subframe_timing);
1251 subframe_timing.interactive_timing->longest_input_delay =
1252 base::TimeDelta::FromMilliseconds(15);
1253 subframe_timing.interactive_timing->longest_input_timestamp =
1254 base::TimeDelta::FromMilliseconds(1000);
1255 subframe1 = content::NavigationSimulator::NavigateAndCommitFromDocument(
1256 GURL(kDefaultTestUrl2), subframe1);
1257 SimulateTimingUpdate(subframe_timing, subframe1);
1258
1259 // Second subframe.
1260 content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe2");
1261 mojom::PageLoadTiming subframe2_timing;
1262 PopulatePageLoadTiming(&subframe2_timing);
1263 subframe2_timing.interactive_timing->longest_input_delay =
1264 base::TimeDelta::FromMilliseconds(200);
1265 subframe2_timing.interactive_timing->longest_input_timestamp =
1266 base::TimeDelta::FromMilliseconds(3000);
1267 // TODO: Make this url3.
1268 subframe2 = content::NavigationSimulator::NavigateAndCommitFromDocument(
1269 GURL(kDefaultTestUrl2), subframe2);
1270 SimulateTimingUpdate(subframe2_timing, subframe2);
1271
1272 // Navigate again to confirm all timings are updated.
1273 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1274 web_contents(), GURL(kDefaultTestUrl2));
1275
1276 const mojom::InteractiveTiming& interactive_timing =
1277 *complete_timings().back()->interactive_timing;
1278
1279 EXPECT_EQ(base::TimeDelta::FromMilliseconds(200),
1280 interactive_timing.longest_input_delay);
1281
1282 // Actual LID timestamp includes the delta between navigation start in
1283 // subframe2 and navigation time in the main frame. That delta varies with
1284 // different runs, so we only check here that the timestamp is greater than
1285 // 3s.
1286 EXPECT_GT(interactive_timing.longest_input_timestamp.value(),
1287 base::TimeDelta::FromMilliseconds(3000));
1288
1289 CheckNoErrorEvents();
1290 }
1291
TEST_F(MetricsWebContentsObserverTest,DispatchDelayedMetricsOnPageClose)1292 TEST_F(MetricsWebContentsObserverTest, DispatchDelayedMetricsOnPageClose) {
1293 mojom::PageLoadTiming timing;
1294 PopulatePageLoadTiming(&timing);
1295 timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(1000);
1296 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1297 web_contents(), GURL(kDefaultTestUrl));
1298 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
1299
1300 // Throw in a cpu timing update, shouldn't affect the page timing results.
1301 mojom::CpuTiming cpu_timing;
1302 cpu_timing.task_time = base::TimeDelta::FromMilliseconds(1000);
1303 SimulateCpuTimingUpdate(cpu_timing, main_rfh());
1304
1305 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
1306 ASSERT_EQ(0, CountUpdatedTimingReported());
1307 ASSERT_EQ(0, CountCompleteTimingReported());
1308
1309 // Navigate to a new page. This should force dispatch of the buffered timing
1310 // update.
1311 NavigateToUntrackedUrl();
1312
1313 ASSERT_EQ(1, CountUpdatedTimingReported());
1314 ASSERT_EQ(1, CountUpdatedCpuTimingReported());
1315 ASSERT_EQ(1, CountCompleteTimingReported());
1316 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
1317 EXPECT_TRUE(timing.Equals(*complete_timings().back()));
1318 EXPECT_TRUE(cpu_timing.Equals(*updated_cpu_timings().back()));
1319
1320 CheckNoErrorEvents();
1321 }
1322
1323 // Make sure the dispatch of CPU occurs immediately.
TEST_F(MetricsWebContentsObserverTest,DispatchCpuMetricsImmediately)1324 TEST_F(MetricsWebContentsObserverTest, DispatchCpuMetricsImmediately) {
1325 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1326 web_contents(), GURL(kDefaultTestUrl));
1327
1328 mojom::CpuTiming timing;
1329 timing.task_time = base::TimeDelta::FromMilliseconds(1000);
1330 SimulateCpuTimingUpdate(timing, main_rfh());
1331 ASSERT_EQ(1, CountUpdatedCpuTimingReported());
1332 EXPECT_TRUE(timing.Equals(*updated_cpu_timings().back()));
1333
1334 // Navigate to a new page. This should force dispatch of the buffered timing
1335 // update.
1336 NavigateToUntrackedUrl();
1337
1338 ASSERT_EQ(1, CountUpdatedCpuTimingReported());
1339 EXPECT_TRUE(timing.Equals(*updated_cpu_timings().back()));
1340
1341 CheckNoErrorEvents();
1342 }
1343
TEST_F(MetricsWebContentsObserverTest,OnLoadedResource_MainFrame)1344 TEST_F(MetricsWebContentsObserverTest, OnLoadedResource_MainFrame) {
1345 GURL main_resource_url(kDefaultTestUrl);
1346 content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
1347 main_resource_url);
1348
1349 auto navigation_simulator =
1350 content::NavigationSimulator::CreateRendererInitiated(
1351 main_resource_url, web_contents()->GetMainFrame());
1352 navigation_simulator->Start();
1353 navigation_simulator->Commit();
1354
1355 const auto request_id = navigation_simulator->GetGlobalRequestID();
1356
1357 observer()->ResourceLoadComplete(
1358 web_contents()->GetMainFrame(), request_id,
1359 *CreateResourceLoadInfo(main_resource_url,
1360 network::mojom::RequestDestination::kFrame));
1361 EXPECT_EQ(1u, loaded_resources().size());
1362 EXPECT_EQ(url::Origin::Create(main_resource_url),
1363 loaded_resources().back().origin_of_final_url);
1364
1365 NavigateToUntrackedUrl();
1366
1367 // Deliver a second main frame resource. This one should be ignored, since the
1368 // specified |request_id| is no longer associated with any tracked page loads.
1369 observer()->ResourceLoadComplete(
1370 web_contents()->GetMainFrame(), request_id,
1371 *CreateResourceLoadInfo(main_resource_url,
1372 network::mojom::RequestDestination::kFrame));
1373 EXPECT_EQ(1u, loaded_resources().size());
1374 EXPECT_EQ(url::Origin::Create(main_resource_url),
1375 loaded_resources().back().origin_of_final_url);
1376 }
1377
TEST_F(MetricsWebContentsObserverTest,OnLoadedResource_Subresource)1378 TEST_F(MetricsWebContentsObserverTest, OnLoadedResource_Subresource) {
1379 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1380 web_contents(), GURL(kDefaultTestUrl));
1381 GURL loaded_resource_url("http://www.other.com/");
1382 observer()->ResourceLoadComplete(
1383 web_contents()->GetMainFrame(), content::GlobalRequestID(),
1384 *CreateResourceLoadInfo(loaded_resource_url,
1385 network::mojom::RequestDestination::kScript));
1386
1387 EXPECT_EQ(1u, loaded_resources().size());
1388 EXPECT_EQ(url::Origin::Create(loaded_resource_url),
1389 loaded_resources().back().origin_of_final_url);
1390 }
1391
TEST_F(MetricsWebContentsObserverTest,OnLoadedResource_ResourceFromOtherRFHIgnored)1392 TEST_F(MetricsWebContentsObserverTest,
1393 OnLoadedResource_ResourceFromOtherRFHIgnored) {
1394 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1395 web_contents(), GURL(kDefaultTestUrl));
1396
1397 content::RenderFrameHost* old_rfh = web_contents()->GetMainFrame();
1398 content::LeaveInPendingDeletionState(old_rfh);
1399
1400 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1401 web_contents(), GURL(kDefaultTestUrl2));
1402
1403 DCHECK(!old_rfh->IsCurrent());
1404 observer()->ResourceLoadComplete(
1405 old_rfh, content::GlobalRequestID(),
1406 *CreateResourceLoadInfo(GURL("http://www.other.com/"),
1407 network::mojom::RequestDestination::kScript));
1408
1409 EXPECT_TRUE(loaded_resources().empty());
1410 }
1411
TEST_F(MetricsWebContentsObserverTest,OnLoadedResource_IgnoreNonHttpOrHttpsScheme)1412 TEST_F(MetricsWebContentsObserverTest,
1413 OnLoadedResource_IgnoreNonHttpOrHttpsScheme) {
1414 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1415 web_contents(), GURL(kDefaultTestUrl));
1416 GURL loaded_resource_url("data:text/html,Hello world");
1417 observer()->ResourceLoadComplete(
1418 web_contents()->GetMainFrame(), content::GlobalRequestID(),
1419 *CreateResourceLoadInfo(loaded_resource_url,
1420 network::mojom::RequestDestination::kScript));
1421
1422 EXPECT_TRUE(loaded_resources().empty());
1423 }
1424
TEST_F(MetricsWebContentsObserverTest,RecordFeatureUsage)1425 TEST_F(MetricsWebContentsObserverTest, RecordFeatureUsage) {
1426 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1427 web_contents(), GURL(kDefaultTestUrl));
1428 ASSERT_EQ(main_rfh()->GetLastCommittedURL().spec(), GURL(kDefaultTestUrl));
1429
1430 std::vector<blink::mojom::WebFeature> web_features;
1431 web_features.push_back(blink::mojom::WebFeature::kHTMLMarqueeElement);
1432 web_features.push_back(blink::mojom::WebFeature::kFormAttribute);
1433 mojom::PageLoadFeatures features(web_features, {}, {});
1434 MetricsWebContentsObserver::RecordFeatureUsage(main_rfh(), features);
1435
1436 ASSERT_EQ(observed_features().size(), 1ul);
1437 ASSERT_EQ(observed_features()[0].features.size(), 2ul);
1438 EXPECT_EQ(observed_features()[0].features[0],
1439 blink::mojom::WebFeature::kHTMLMarqueeElement);
1440 EXPECT_EQ(observed_features()[0].features[1],
1441 blink::mojom::WebFeature::kFormAttribute);
1442 }
1443
TEST_F(MetricsWebContentsObserverTest,RecordFeatureUsageNoObserver)1444 TEST_F(MetricsWebContentsObserverTest, RecordFeatureUsageNoObserver) {
1445 // Reset the state of the tests, and don't add an observer.
1446 DeleteContents();
1447 SetContents(CreateTestWebContents());
1448
1449 // This call should just do nothing, and should not crash - if that happens,
1450 // we are good.
1451 std::vector<blink::mojom::WebFeature> web_features;
1452 web_features.push_back(blink::mojom::WebFeature::kHTMLMarqueeElement);
1453 web_features.push_back(blink::mojom::WebFeature::kFormAttribute);
1454 mojom::PageLoadFeatures features(web_features, {}, {});
1455 MetricsWebContentsObserver::RecordFeatureUsage(main_rfh(), features);
1456 }
1457
1458 class MetricsWebContentsObserverBackForwardCacheTest
1459 : public MetricsWebContentsObserverTest {
1460 public:
MetricsWebContentsObserverBackForwardCacheTest()1461 MetricsWebContentsObserverBackForwardCacheTest() {
1462 feature_list_.InitWithFeaturesAndParameters(
1463 {{features::kBackForwardCache,
1464 {{"TimeToLiveInBackForwardCacheInSeconds", "3600"}}}},
1465 {});
1466 }
1467
~MetricsWebContentsObserverBackForwardCacheTest()1468 ~MetricsWebContentsObserverBackForwardCacheTest() override {}
1469
1470 private:
1471 base::test::ScopedFeatureList feature_list_;
1472 };
1473
TEST_F(MetricsWebContentsObserverBackForwardCacheTest,RecordFeatureUsageWithBackForwardCache)1474 TEST_F(MetricsWebContentsObserverBackForwardCacheTest,
1475 RecordFeatureUsageWithBackForwardCache) {
1476 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1477 web_contents(), GURL(kDefaultTestUrl));
1478 ASSERT_EQ(main_rfh()->GetLastCommittedURL().spec(), GURL(kDefaultTestUrl));
1479
1480 std::vector<blink::mojom::WebFeature> web_features1{
1481 blink::mojom::WebFeature::kHTMLMarqueeElement};
1482 mojom::PageLoadFeatures features1(web_features1, {}, {});
1483 MetricsWebContentsObserver::RecordFeatureUsage(main_rfh(), features1);
1484
1485 content::NavigationSimulator::NavigateAndCommitFromBrowser(
1486 web_contents(), GURL(kDefaultTestUrl2));
1487 content::NavigationSimulator::GoBack(web_contents());
1488
1489 std::vector<blink::mojom::WebFeature> web_features2{
1490 blink::mojom::WebFeature::kFormAttribute};
1491 mojom::PageLoadFeatures features2(web_features2, {}, {});
1492 MetricsWebContentsObserver::RecordFeatureUsage(main_rfh(), features2);
1493
1494 std::vector<std::vector<blink::mojom::WebFeature>> features;
1495 for (const auto& observation : observed_features()) {
1496 features.push_back(observation.features);
1497 }
1498
1499 // For now back-forward cached navigations are not tracked and the events
1500 // after the history navigation are not tracked.
1501 EXPECT_THAT(features, testing::ElementsAre(web_features1));
1502 }
1503
1504 } // namespace page_load_metrics
1505