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

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: fix android build Created 3 years, 7 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"
20 #include "chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h "
18 #include "chrome/browser/page_load_metrics/page_load_tracker.h" 21 #include "chrome/browser/page_load_metrics/page_load_tracker.h"
19 #include "chrome/common/page_load_metrics/page_load_metrics_messages.h" 22 #include "chrome/common/page_load_metrics/page_load_metrics_messages.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 {
30 33
31 namespace { 34 namespace {
32 35
33 const char kDefaultTestUrl[] = "https://google.com/"; 36 const char kDefaultTestUrl[] = "https://google.com/";
34 const char kDefaultTestUrlAnchor[] = "https://google.com/#samedocument"; 37 const char kDefaultTestUrlAnchor[] = "https://google.com/#samedocument";
35 const char kDefaultTestUrl2[] = "https://whatever.com/"; 38 const char kDefaultTestUrl2[] = "https://whatever.com/";
36 const char kFilteredStartUrl[] = "https://whatever.com/ignore-on-start"; 39 const char kFilteredStartUrl[] = "https://whatever.com/ignore-on-start";
37 const char kFilteredCommitUrl[] = "https://whatever.com/ignore-on-commit"; 40 const char kFilteredCommitUrl[] = "https://whatever.com/ignore-on-commit";
38 41
42 class WeakMockTimer : public base::MockTimer,
43 public base::SupportsWeakPtr<WeakMockTimer> {
44 public:
45 WeakMockTimer()
46 : MockTimer(false /* retain_user_task */, false /* is_repeating */) {}
47 };
48
39 // Simple PageLoadMetricsObserver that copies observed PageLoadTimings into the 49 // Simple PageLoadMetricsObserver that copies observed PageLoadTimings into the
40 // provided std::vector, so they can be analyzed by unit tests. 50 // provided std::vector, so they can be analyzed by unit tests.
41 class TestPageLoadMetricsObserver : public PageLoadMetricsObserver { 51 class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
42 public: 52 public:
43 TestPageLoadMetricsObserver( 53 TestPageLoadMetricsObserver(
44 std::vector<mojom::PageLoadTimingPtr>* updated_timings, 54 std::vector<mojom::PageLoadTimingPtr>* updated_timings,
45 std::vector<mojom::PageLoadTimingPtr>* updated_subframe_timings, 55 std::vector<mojom::PageLoadTimingPtr>* updated_subframe_timings,
46 std::vector<mojom::PageLoadTimingPtr>* complete_timings, 56 std::vector<mojom::PageLoadTimingPtr>* complete_timings,
47 std::vector<GURL>* observed_committed_urls) 57 std::vector<GURL>* observed_committed_urls)
48 : updated_timings_(updated_timings), 58 : updated_timings_(updated_timings),
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 132
123 bool IsNewTabPageUrl(const GURL& url) override { return is_ntp_; } 133 bool IsNewTabPageUrl(const GURL& url) override { return is_ntp_; }
124 void set_is_ntp(bool is_ntp) { is_ntp_ = is_ntp; } 134 void set_is_ntp(bool is_ntp) { is_ntp_ = is_ntp; }
125 void RegisterObservers(PageLoadTracker* tracker) override { 135 void RegisterObservers(PageLoadTracker* tracker) override {
126 tracker->AddObserver(base::MakeUnique<TestPageLoadMetricsObserver>( 136 tracker->AddObserver(base::MakeUnique<TestPageLoadMetricsObserver>(
127 &updated_timings_, &updated_subframe_timings_, &complete_timings_, 137 &updated_timings_, &updated_subframe_timings_, &complete_timings_,
128 &observed_committed_urls_)); 138 &observed_committed_urls_));
129 tracker->AddObserver(base::MakeUnique<FilteringPageLoadMetricsObserver>( 139 tracker->AddObserver(base::MakeUnique<FilteringPageLoadMetricsObserver>(
130 &completed_filtered_urls_)); 140 &completed_filtered_urls_));
131 } 141 }
142 std::unique_ptr<base::Timer> CreateTimer() override {
143 std::unique_ptr<WeakMockTimer> timer = base::MakeUnique<WeakMockTimer>();
144 most_recent_timer_ = timer->AsWeakPtr();
145 return std::move(timer);
146 }
132 const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const { 147 const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const {
133 return updated_timings_; 148 return updated_timings_;
134 } 149 }
135 const std::vector<mojom::PageLoadTimingPtr>& complete_timings() const { 150 const std::vector<mojom::PageLoadTimingPtr>& complete_timings() const {
136 return complete_timings_; 151 return complete_timings_;
137 } 152 }
138 const std::vector<mojom::PageLoadTimingPtr>& updated_subframe_timings() 153 const std::vector<mojom::PageLoadTimingPtr>& updated_subframe_timings()
139 const { 154 const {
140 return updated_subframe_timings_; 155 return updated_subframe_timings_;
141 } 156 }
142 157
143 // currently_committed_urls passed to OnStart(). 158 // currently_committed_urls passed to OnStart().
144 const std::vector<GURL>& observed_committed_urls_from_on_start() const { 159 const std::vector<GURL>& observed_committed_urls_from_on_start() const {
145 return observed_committed_urls_; 160 return observed_committed_urls_;
146 } 161 }
147 162
148 // committed URLs passed to FilteringPageLoadMetricsObserver::OnComplete(). 163 // committed URLs passed to FilteringPageLoadMetricsObserver::OnComplete().
149 const std::vector<GURL>& completed_filtered_urls() const { 164 const std::vector<GURL>& completed_filtered_urls() const {
150 return completed_filtered_urls_; 165 return completed_filtered_urls_;
151 } 166 }
167 base::MockTimer* GetMostRecentTimer() const {
168 return most_recent_timer_.get();
169 }
152 170
153 private: 171 private:
154 std::vector<mojom::PageLoadTimingPtr> updated_timings_; 172 std::vector<mojom::PageLoadTimingPtr> updated_timings_;
155 std::vector<mojom::PageLoadTimingPtr> updated_subframe_timings_; 173 std::vector<mojom::PageLoadTimingPtr> updated_subframe_timings_;
156 std::vector<mojom::PageLoadTimingPtr> complete_timings_; 174 std::vector<mojom::PageLoadTimingPtr> complete_timings_;
157 std::vector<GURL> observed_committed_urls_; 175 std::vector<GURL> observed_committed_urls_;
158 std::vector<GURL> completed_filtered_urls_; 176 std::vector<GURL> completed_filtered_urls_;
177 base::WeakPtr<WeakMockTimer> most_recent_timer_;
159 bool is_ntp_; 178 bool is_ntp_;
160 }; 179 };
161 180
181 void PopulatePageLoadTiming(mojom::PageLoadTiming* timing) {
182 page_load_metrics::InitPageLoadTimingForTest(timing);
183 timing->navigation_start = base::Time::FromDoubleT(1);
184 timing->response_start = base::TimeDelta::FromMilliseconds(10);
185 timing->parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
186 timing->document_timing->first_layout = base::TimeDelta::FromMilliseconds(30);
187 }
188
162 } // namespace 189 } // namespace
163 190
164 class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness { 191 class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness {
165 public: 192 public:
166 MetricsWebContentsObserverTest() : num_errors_(0) {} 193 MetricsWebContentsObserverTest() : num_errors_(0) {}
167 194
168 void SetUp() override { 195 void SetUp() override {
169 ChromeRenderViewHostTestHarness::SetUp(); 196 ChromeRenderViewHostTestHarness::SetUp();
170 AttachObserver(); 197 AttachObserver();
171 } 198 }
172 199
173 void NavigateToUntrackedUrl() { 200 void NavigateToUntrackedUrl() {
174 content::WebContentsTester::For(web_contents()) 201 content::WebContentsTester::For(web_contents())
175 ->NavigateAndCommit(GURL(url::kAboutBlankURL)); 202 ->NavigateAndCommit(GURL(url::kAboutBlankURL));
176 } 203 }
177 204
205 // Returns the mock timer used for buffering updates in the
206 // PageLoadMetricsUpdateDispatcher.
207 base::MockTimer* GetMostRecentTimer() {
208 return embedder_interface_->GetMostRecentTimer();
209 }
210
178 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) { 211 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) {
179 SimulateTimingUpdate(timing, web_contents()->GetMainFrame()); 212 SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
180 } 213 }
181 214
182 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing, 215 void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
183 content::RenderFrameHost* render_frame_host) { 216 content::RenderFrameHost* render_frame_host) {
217 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, render_frame_host);
218 // If sending the timing update caused the PageLoadMetricsUpdateDispatcher
219 // to schedule a buffering timer, then fire it now so metrics are dispatched
220 // to observers.
221 base::MockTimer* mock_timer = GetMostRecentTimer();
222 if (mock_timer && mock_timer->IsRunning())
223 mock_timer->Fire();
224 }
225
226 void SimulateTimingUpdateWithoutFiringDispatchTimer(
227 const mojom::PageLoadTiming& timing,
228 content::RenderFrameHost* render_frame_host) {
184 observer()->OnTimingUpdated(render_frame_host, timing, 229 observer()->OnTimingUpdated(render_frame_host, timing,
185 mojom::PageLoadMetadata()); 230 mojom::PageLoadMetadata());
186 } 231 }
187 232
188 void AttachObserver() { 233 void AttachObserver() {
189 auto embedder_interface = 234 auto embedder_interface =
190 base::MakeUnique<TestPageLoadMetricsEmbedderInterface>(); 235 base::MakeUnique<TestPageLoadMetricsEmbedderInterface>();
191 embedder_interface_ = embedder_interface.get(); 236 embedder_interface_ = embedder_interface.get();
192 MetricsWebContentsObserver* observer = 237 MetricsWebContentsObserver* observer =
193 MetricsWebContentsObserver::CreateForWebContents( 238 MetricsWebContentsObserver::CreateForWebContents(
(...skipping 534 matching lines...) Expand 10 before | Expand all | Expand 10 after
728 773
729 ASSERT_EQ(1, CountUpdatedTimingReported()); 774 ASSERT_EQ(1, CountUpdatedTimingReported());
730 EXPECT_TRUE(timing.Equals(*updated_timings().back())); 775 EXPECT_TRUE(timing.Equals(*updated_timings().back()));
731 776
732 content::RenderFrameHostTester* rfh_tester = 777 content::RenderFrameHostTester* rfh_tester =
733 content::RenderFrameHostTester::For(main_rfh()); 778 content::RenderFrameHostTester::For(main_rfh());
734 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe"); 779 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
735 780
736 // Dispatch a timing update for the child frame that includes a first paint. 781 // Dispatch a timing update for the child frame that includes a first paint.
737 mojom::PageLoadTiming subframe_timing; 782 mojom::PageLoadTiming subframe_timing;
738 page_load_metrics::InitPageLoadTimingForTest(&subframe_timing); 783 PopulatePageLoadTiming(&subframe_timing);
739 subframe_timing.navigation_start = base::Time::FromDoubleT(2);
740 subframe_timing.response_start = base::TimeDelta::FromMilliseconds(10);
741 subframe_timing.parse_timing->parse_start =
742 base::TimeDelta::FromMilliseconds(20);
743 subframe_timing.document_timing->first_layout =
744 base::TimeDelta::FromMilliseconds(30);
745 subframe_timing.paint_timing->first_paint = 784 subframe_timing.paint_timing->first_paint =
746 base::TimeDelta::FromMilliseconds(40); 785 base::TimeDelta::FromMilliseconds(40);
747 content::RenderFrameHostTester* subframe_tester = 786 content::RenderFrameHostTester* subframe_tester =
748 content::RenderFrameHostTester::For(subframe); 787 content::RenderFrameHostTester::For(subframe);
749 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2)); 788 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
750 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2)); 789 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
751 SimulateTimingUpdate(subframe_timing, subframe); 790 SimulateTimingUpdate(subframe_timing, subframe);
752 subframe_tester->SimulateNavigationStop(); 791 subframe_tester->SimulateNavigationStop();
753 792
754 // Though a first paint was dispatched in the child, it should not yet be 793 // 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
790 ASSERT_EQ(1, CountCompleteTimingReported()); 829 ASSERT_EQ(1, CountCompleteTimingReported());
791 ASSERT_EQ(2, CountUpdatedTimingReported()); 830 ASSERT_EQ(2, CountUpdatedTimingReported());
792 ASSERT_EQ(0, CountEmptyCompleteTimingReported()); 831 ASSERT_EQ(0, CountEmptyCompleteTimingReported());
793 832
794 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported()); 833 ASSERT_EQ(1, CountUpdatedSubFrameTimingReported());
795 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back())); 834 EXPECT_TRUE(subframe_timing.Equals(*updated_subframe_timings().back()));
796 835
797 CheckNoErrorEvents(); 836 CheckNoErrorEvents();
798 } 837 }
799 838
839 // We buffer cross-frame paint updates to account for paint timings from
840 // different frames arriving out of order.
841 TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming2) {
842 // Dispatch a timing update for the main frame that includes a first
843 // paint. This should be buffered, with the dispatch timer running.
844 mojom::PageLoadTiming timing;
845 PopulatePageLoadTiming(&timing);
846 timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(1000);
847 content::WebContentsTester* web_contents_tester =
848 content::WebContentsTester::For(web_contents());
849 web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
850 SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
851
852 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
853 ASSERT_EQ(0, CountUpdatedTimingReported());
854
855 content::RenderFrameHostTester* rfh_tester =
856 content::RenderFrameHostTester::For(main_rfh());
857
858 // Dispatch a timing update for a child frame that includes a first paint.
859 mojom::PageLoadTiming subframe_timing;
860 PopulatePageLoadTiming(&subframe_timing);
861 subframe_timing.paint_timing->first_paint =
862 base::TimeDelta::FromMilliseconds(500);
863 content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
864 content::RenderFrameHostTester* subframe_tester =
865 content::RenderFrameHostTester::For(subframe);
866 subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
867 subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
868 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe);
869 subframe_tester->SimulateNavigationStop();
870
871 histogram_tester_.ExpectTotalCount(
872 page_load_metrics::internal::kHistogramOutOfOrderTiming, 1);
873
874 EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
875 ASSERT_EQ(0, CountUpdatedTimingReported());
876
877 // At this point, the timing update is buffered, waiting for the timer to
878 // fire.
879 GetMostRecentTimer()->Fire();
880
881 // Firing the timer should produce a timing update. The update should be a
882 // merged view of the main frame timing, with a first paint timestamp from the
883 // subframe.
884 ASSERT_EQ(1, CountUpdatedTimingReported());
885 EXPECT_FALSE(timing.Equals(*updated_timings().back()));
886 EXPECT_TRUE(
887 updated_timings().back()->parse_timing->Equals(*timing.parse_timing));
888 EXPECT_TRUE(updated_timings().back()->document_timing->Equals(
889 *timing.document_timing));
890 EXPECT_FALSE(
891 updated_timings().back()->paint_timing->Equals(*timing.paint_timing));
892 EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
893
894 // The first paint value should be the min of all received first paints, wich
Charlie Harrison 2017/05/26 19:31:34 which
Bryan McQuade 2017/05/26 20:07:32 Done
895 // in this case is the first paint from the subframe. Since it is offset by
896 // the subframe's navigation start, the received value should be >= the first
897 // paint value specified in the subframe.
898 EXPECT_GE(updated_timings().back()->paint_timing->first_paint,
899 subframe_timing.paint_timing->first_paint);
900 EXPECT_LT(updated_timings().back()->paint_timing->first_paint,
901 timing.paint_timing->first_paint);
902
903 base::TimeDelta initial_first_paint =
904 updated_timings().back()->paint_timing->first_paint.value();
905
906 // Dispatch a timing update for an additional child frame, with an earlier
907 // first paint time. This should cause an immediate update, without a timer
908 // delay.
909 subframe_timing.paint_timing->first_paint =
910 base::TimeDelta::FromMilliseconds(50);
911 content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe");
912 content::RenderFrameHostTester* subframe2_tester =
913 content::RenderFrameHostTester::For(subframe2);
914 subframe2_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
915 subframe2_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
916 SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe2);
917 subframe2_tester->SimulateNavigationStop();
918
919 base::TimeDelta updated_first_paint =
920 updated_timings().back()->paint_timing->first_paint.value();
921
922 EXPECT_FALSE(GetMostRecentTimer()->IsRunning());
923 ASSERT_EQ(2, CountUpdatedTimingReported());
924 EXPECT_LT(updated_first_paint, initial_first_paint);
925
926 histogram_tester_.ExpectTotalCount(
927 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered, 1);
928 histogram_tester_.ExpectBucketCount(
929 page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered,
930 (initial_first_paint - updated_first_paint).InMilliseconds(), 1);
931
932 CheckNoErrorEvents();
933 }
934
800 } // namespace page_load_metrics 935 } // namespace page_load_metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698