Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(374)

Side by Side Diff: chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc

Issue 2901383002: Buffer cross frame paint timing updates. (Closed)
Patch Set: address comment Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h" 5 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
6 6
7 #include <memory> 7 #include <memory>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/macros.h" 10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h" 11 #include "base/memory/ptr_util.h"
12 #include "base/memory/weak_ptr.h"
12 #include "base/process/kill.h" 13 #include "base/process/kill.h"
13 #include "base/test/histogram_tester.h" 14 #include "base/test/histogram_tester.h"
14 #include "base/time/time.h" 15 #include "base/time/time.h"
16 #include "base/timer/mock_timer.h"
15 #include "chrome/browser/page_load_metrics/metrics_navigation_throttle.h" 17 #include "chrome/browser/page_load_metrics/metrics_navigation_throttle.h"
16 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface. h" 18 #include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface. h"
17 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h" 19 #include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
18 #include "chrome/browser/page_load_metrics/page_load_tracker.h" 20 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
19 #include "chrome/common/page_load_metrics/page_load_metrics_messages.h" 21 #include "chrome/common/page_load_metrics/page_load_metrics_messages.h"
22 #include "chrome/common/page_load_metrics/test/weak_mock_timer.h"
20 #include "chrome/common/url_constants.h" 23 #include "chrome/common/url_constants.h"
21 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 24 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
22 #include "content/public/browser/navigation_handle.h" 25 #include "content/public/browser/navigation_handle.h"
23 #include "content/public/browser/render_frame_host.h" 26 #include "content/public/browser/render_frame_host.h"
24 #include "content/public/test/test_renderer_host.h" 27 #include "content/public/test/test_renderer_host.h"
25 #include "content/public/test/web_contents_tester.h" 28 #include "content/public/test/web_contents_tester.h"
26 #include "testing/gtest/include/gtest/gtest.h" 29 #include "testing/gtest/include/gtest/gtest.h"
27 #include "url/gurl.h" 30 #include "url/gurl.h"
28 31
29 namespace page_load_metrics { 32 namespace page_load_metrics {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 void OnComplete(const mojom::PageLoadTiming& timing, 113 void OnComplete(const mojom::PageLoadTiming& timing,
111 const PageLoadExtraInfo& extra_info) override { 114 const PageLoadExtraInfo& extra_info) override {
112 completed_filtered_urls_->push_back(extra_info.url); 115 completed_filtered_urls_->push_back(extra_info.url);
113 } 116 }
114 117
115 private: 118 private:
116 std::vector<GURL>* const completed_filtered_urls_; 119 std::vector<GURL>* const completed_filtered_urls_;
117 }; 120 };
118 121
119 class TestPageLoadMetricsEmbedderInterface 122 class TestPageLoadMetricsEmbedderInterface
120 : public PageLoadMetricsEmbedderInterface { 123 : public PageLoadMetricsEmbedderInterface,
124 public test::WeakMockTimerProvider {
121 public: 125 public:
122 TestPageLoadMetricsEmbedderInterface() : is_ntp_(false) {} 126 TestPageLoadMetricsEmbedderInterface() : is_ntp_(false) {}
123 127
124 bool IsNewTabPageUrl(const GURL& url) override { return is_ntp_; } 128 bool IsNewTabPageUrl(const GURL& url) override { return is_ntp_; }
125 void set_is_ntp(bool is_ntp) { is_ntp_ = is_ntp; } 129 void set_is_ntp(bool is_ntp) { is_ntp_ = is_ntp; }
126 void RegisterObservers(PageLoadTracker* tracker) override { 130 void RegisterObservers(PageLoadTracker* tracker) override {
127 tracker->AddObserver(base::MakeUnique<TestPageLoadMetricsObserver>( 131 tracker->AddObserver(base::MakeUnique<TestPageLoadMetricsObserver>(
128 &updated_timings_, &updated_subframe_timings_, &complete_timings_, 132 &updated_timings_, &updated_subframe_timings_, &complete_timings_,
129 &observed_committed_urls_)); 133 &observed_committed_urls_));
130 tracker->AddObserver(base::MakeUnique<FilteringPageLoadMetricsObserver>( 134 tracker->AddObserver(base::MakeUnique<FilteringPageLoadMetricsObserver>(
131 &completed_filtered_urls_)); 135 &completed_filtered_urls_));
132 } 136 }
137 std::unique_ptr<base::Timer> CreateTimer() override {
138 auto timer = base::MakeUnique<test::WeakMockTimer>();
139 SetMockTimer(timer->AsWeakPtr());
140 return std::move(timer);
141 }
133 const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const { 142 const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const {
134 return updated_timings_; 143 return updated_timings_;
135 } 144 }
136 const std::vector<mojom::PageLoadTimingPtr>& complete_timings() const { 145 const std::vector<mojom::PageLoadTimingPtr>& complete_timings() const {
137 return complete_timings_; 146 return complete_timings_;
138 } 147 }
139 const std::vector<mojom::PageLoadTimingPtr>& updated_subframe_timings() 148 const std::vector<mojom::PageLoadTimingPtr>& updated_subframe_timings()
140 const { 149 const {
141 return updated_subframe_timings_; 150 return updated_subframe_timings_;
142 } 151 }
(...skipping 10 matching lines...) Expand all
153 162
154 private: 163 private:
155 std::vector<mojom::PageLoadTimingPtr> updated_timings_; 164 std::vector<mojom::PageLoadTimingPtr> updated_timings_;
156 std::vector<mojom::PageLoadTimingPtr> updated_subframe_timings_; 165 std::vector<mojom::PageLoadTimingPtr> updated_subframe_timings_;
157 std::vector<mojom::PageLoadTimingPtr> complete_timings_; 166 std::vector<mojom::PageLoadTimingPtr> complete_timings_;
158 std::vector<GURL> observed_committed_urls_; 167 std::vector<GURL> observed_committed_urls_;
159 std::vector<GURL> completed_filtered_urls_; 168 std::vector<GURL> completed_filtered_urls_;
160 bool is_ntp_; 169 bool is_ntp_;
161 }; 170 };
162 171
172 void PopulatePageLoadTiming(mojom::PageLoadTiming* timing) {
173 page_load_metrics::InitPageLoadTimingForTest(timing);
174 timing->navigation_start = base::Time::FromDoubleT(1);
175 timing->response_start = base::TimeDelta::FromMilliseconds(10);
176 timing->parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
177 timing->document_timing->first_layout = base::TimeDelta::FromMilliseconds(30);
178 }
179
163 } // namespace 180 } // namespace
164 181
165 class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness { 182 class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness {
166 public: 183 public:
167 MetricsWebContentsObserverTest() : num_errors_(0) {} 184 MetricsWebContentsObserverTest() : num_errors_(0) {}
168 185
169 void SetUp() override { 186 void SetUp() override {
170 ChromeRenderViewHostTestHarness::SetUp(); 187 ChromeRenderViewHostTestHarness::SetUp();
171 AttachObserver(); 188 AttachObserver();
172 } 189 }
173 190
174 void NavigateToUntrackedUrl() { 191 void NavigateToUntrackedUrl() {
175 content::WebContentsTester::For(web_contents()) 192 content::WebContentsTester::For(web_contents())
176 ->NavigateAndCommit(GURL(url::kAboutBlankURL)); 193 ->NavigateAndCommit(GURL(url::kAboutBlankURL));
177 } 194 }
178 195
196 // Returns the mock timer used for buffering updates in the
197 // PageLoadMetricsUpdateDispatcher.
198 base::MockTimer* GetMostRecentTimer() {
199 return embedder_interface_->GetMockTimer();
200 }
201
179 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) { 202 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) {
180 SimulateTimingUpdate(timing, web_contents()->GetMainFrame()); 203 SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
181 } 204 }
182 205
183 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing, 206 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
184 content::RenderFrameHost* render_frame_host) { 207 content::RenderFrameHost* render_frame_host) {
208 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, render_frame_host);
209 // If sending the timing update caused the PageLoadMetricsUpdateDispatcher
210 // to schedule a buffering timer, then fire it now so metrics are dispatched
211 // to observers.
212 base::MockTimer* mock_timer = GetMostRecentTimer();
213 if (mock_timer && mock_timer->IsRunning())
214 mock_timer->Fire();
215 }
216
217 void SimulateTimingUpdateWithoutFiringDispatchTimer(
218 const mojom::PageLoadTiming& timing,
219 content::RenderFrameHost* render_frame_host) {
185 observer()->OnTimingUpdated(render_frame_host, timing, 220 observer()->OnTimingUpdated(render_frame_host, timing,
186 mojom::PageLoadMetadata()); 221 mojom::PageLoadMetadata());
187 } 222 }
188 223
189 void AttachObserver() { 224 void AttachObserver() {
190 auto embedder_interface = 225 auto embedder_interface =
191 base::MakeUnique<TestPageLoadMetricsEmbedderInterface>(); 226 base::MakeUnique<TestPageLoadMetricsEmbedderInterface>();
192 embedder_interface_ = embedder_interface.get(); 227 embedder_interface_ = embedder_interface.get();
193 MetricsWebContentsObserver* observer = 228 MetricsWebContentsObserver* observer =
194 MetricsWebContentsObserver::CreateForWebContents( 229 MetricsWebContentsObserver::CreateForWebContents(
(...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after
729 764
730 ASSERT_EQ(1, CountUpdatedTimingReported()); 765 ASSERT_EQ(1, CountUpdatedTimingReported());
731 EXPECT_TRUE(timing.Equals(*updated_timings().back())); 766 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
732 767
733 content::RenderFrameHostTester* rfh_tester = 768 content::RenderFrameHostTester* rfh_tester =
734 content::RenderFrameHostTester::For(main_rfh()); 769 content::RenderFrameHostTester::For(main_rfh());
735 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe"); 770 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
736 771
737 // Dispatch a timing update for the child frame that includes a first paint. 772 // Dispatch a timing update for the child frame that includes a first paint.
738 mojom::PageLoadTiming subframe_timing; 773 mojom::PageLoadTiming subframe_timing;
739 page_load_metrics::InitPageLoadTimingForTest(&subframe_timing); 774 PopulatePageLoadTiming(&subframe_timing);
740 subframe_timing.navigation_start = base::Time::FromDoubleT(2);
741 subframe_timing.response_start = base::TimeDelta::FromMilliseconds(10);
742 subframe_timing.parse_timing->parse_start =
743 base::TimeDelta::FromMilliseconds(20);
744 subframe_timing.document_timing->first_layout =
745 base::TimeDelta::FromMilliseconds(30);
746 subframe_timing.paint_timing->first_paint = 775 subframe_timing.paint_timing->first_paint =
747 base::TimeDelta::FromMilliseconds(40); 776 base::TimeDelta::FromMilliseconds(40);
748 content::RenderFrameHostTester* subframe_tester = 777 content::RenderFrameHostTester* subframe_tester =
749 content::RenderFrameHostTester::For(subframe); 778 content::RenderFrameHostTester::For(subframe);
750 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2)); 779 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
751 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2)); 780 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
752 SimulateTimingUpdate(subframe_timing, subframe); 781 SimulateTimingUpdate(subframe_timing, subframe);
753 subframe_tester->SimulateNavigationStop(); 782 subframe_tester->SimulateNavigationStop();
754 783
755 // Though a first paint was dispatched in the child, it should not yet be 784 // Though a first paint was dispatched in the child, it should not yet be
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
791 ASSERT_EQ(1, CountCompleteTimingReported()); 820 ASSERT_EQ(1, CountCompleteTimingReported());
792 ASSERT_EQ(2, CountUpdatedTimingReported()); 821 ASSERT_EQ(2, CountUpdatedTimingReported());
793 ASSERT_EQ(0, CountEmptyCompleteTimingReported()); 822 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
794 823
795 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported()); 824 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
796 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back())); 825 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
797 826
798 CheckNoErrorEvents(); 827 CheckNoErrorEvents();
799 } 828 }
800 829
830 // We buffer cross-frame paint updates to account for paint timings from
831 // different frames arriving out of order.
832 TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming2) {
833 // Dispatch a timing update for the main frame that includes a first
834 // paint. This should be buffered, with the dispatch timer running.
835 mojom::PageLoadTiming timing;
836 PopulatePageLoadTiming(&timing);
837 timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(1000);
838 content::WebContentsTester* web_contents_tester =
839 content::WebContentsTester::For(web_contents());
840 web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
841 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
842
843 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
844 ASSERT_EQ(0, CountUpdatedTimingReported());
845
846 content::RenderFrameHostTester* rfh_tester =
847 content::RenderFrameHostTester::For(main_rfh());
848
849 // Dispatch a timing update for a child frame that includes a first paint.
850 mojom::PageLoadTiming subframe_timing;
851 PopulatePageLoadTiming(&subframe_timing);
852 subframe_timing.paint_timing->first_paint =
853 base::TimeDelta::FromMilliseconds(500);
854 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
855 content::RenderFrameHostTester* subframe_tester =
856 content::RenderFrameHostTester::For(subframe);
857 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
858 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
859 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe);
860 subframe_tester->SimulateNavigationStop();
861
862 histogram_tester_.ExpectTotalCount(
863 page_load_metrics::internal::kHistogramOutOfOrderTiming, 1);
864
865 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
866 ASSERT_EQ(0, CountUpdatedTimingReported());
867
868 // At this point, the timing update is buffered, waiting for the timer to
869 // fire.
870 GetMostRecentTimer()->Fire();
871
872 // Firing the timer should produce a timing update. The update should be a
873 // merged view of the main frame timing, with a first paint timestamp from the
874 // subframe.
875 ASSERT_EQ(1, CountUpdatedTimingReported());
876 EXPECT_FALSE(timing.Equals(*updated_timings().back()));
877 EXPECT_TRUE(
878 updated_timings().back()->parse_timing->Equals(*timing.parse_timing));
879 EXPECT_TRUE(updated_timings().back()->document_timing->Equals(
880 *timing.document_timing));
881 EXPECT_FALSE(
882 updated_timings().back()->paint_timing->Equals(*timing.paint_timing));
883 EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
884
885 // The first paint value should be the min of all received first paints, which
886 // in this case is the first paint from the subframe. Since it is offset by
887 // the subframe's navigation start, the received value should be >= the first
888 // paint value specified in the subframe.
889 EXPECT_GE(updated_timings().back()->paint_timing->first_paint,
890 subframe_timing.paint_timing->first_paint);
891 EXPECT_LT(updated_timings().back()->paint_timing->first_paint,
892 timing.paint_timing->first_paint);
893
894 base::TimeDelta initial_first_paint =
895 updated_timings().back()->paint_timing->first_paint.value();
896
897 // Dispatch a timing update for an additional child frame, with an earlier
898 // first paint time. This should cause an immediate update, without a timer
899 // delay.
900 subframe_timing.paint_timing->first_paint =
901 base::TimeDelta::FromMilliseconds(50);
902 content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe");
903 content::RenderFrameHostTester* subframe2_tester =
904 content::RenderFrameHostTester::For(subframe2);
905 subframe2_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
906 subframe2_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
907 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe2);
908 subframe2_tester->SimulateNavigationStop();
909
910 base::TimeDelta updated_first_paint =
911 updated_timings().back()->paint_timing->first_paint.value();
912
913 EXPECT_FALSE(GetMostRecentTimer()->IsRunning());
914 ASSERT_EQ(2, CountUpdatedTimingReported());
915 EXPECT_LT(updated_first_paint, initial_first_paint);
916
917 histogram_tester_.ExpectTotalCount(
918 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered, 1);
919 histogram_tester_.ExpectBucketCount(
920 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered,
921 (initial_first_paint - updated_first_paint).InMilliseconds(), 1);
922
923 CheckNoErrorEvents();
924 }
925
926 TEST_F(MetricsWebContentsObserverTest, DispatchDelayedMetricsOnPageClose) {
927 mojom::PageLoadTiming timing;
928 PopulatePageLoadTiming(&timing);
929 timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(1000);
930 content::WebContentsTester* web_contents_tester =
931 content::WebContentsTester::For(web_contents());
932 web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
933 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
934
935 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
936 ASSERT_EQ(0, CountUpdatedTimingReported());
937 ASSERT_EQ(0, CountCompleteTimingReported());
938
939 // Navigate to a new page. This should force dispatch of the buffered timing
940 // update.
941 NavigateToUntrackedUrl();
942
943 ASSERT_EQ(1, CountUpdatedTimingReported());
944 ASSERT_EQ(1, CountCompleteTimingReported());
945 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
946 EXPECT_TRUE(timing.Equals(*complete_timings().back()));
947
948 CheckNoErrorEvents();
949 }
950
801 } // namespace page_load_metrics 951 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698