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