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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index dd8592193b71f0dbc49d9029d338c3dc7a7070b0..bf448aab3cc2ded68e8d814956b87097cd401861 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -9,12 +9,15 @@
#include "base/macros.h"
#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
#include "base/process/kill.h"
#include "base/test/histogram_tester.h"
#include "base/time/time.h"
+#include "base/timer/mock_timer.h"
#include "chrome/browser/page_load_metrics/metrics_navigation_throttle.h"
#include "chrome/browser/page_load_metrics/page_load_metrics_embedder_interface.h"
#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.h"
#include "chrome/browser/page_load_metrics/page_load_tracker.h"
#include "chrome/common/page_load_metrics/page_load_metrics_messages.h"
#include "chrome/common/url_constants.h"
@@ -36,6 +39,13 @@ const char kDefaultTestUrl2[] = "https://whatever.com/";
const char kFilteredStartUrl[] = "https://whatever.com/ignore-on-start";
const char kFilteredCommitUrl[] = "https://whatever.com/ignore-on-commit";
+class WeakMockTimer : public base::MockTimer,
+ public base::SupportsWeakPtr<WeakMockTimer> {
+ public:
+ WeakMockTimer()
+ : MockTimer(false /* retain_user_task */, false /* is_repeating */) {}
+};
+
// Simple PageLoadMetricsObserver that copies observed PageLoadTimings into the
// provided std::vector, so they can be analyzed by unit tests.
class TestPageLoadMetricsObserver : public PageLoadMetricsObserver {
@@ -129,6 +139,11 @@ class TestPageLoadMetricsEmbedderInterface
tracker->AddObserver(base::MakeUnique<FilteringPageLoadMetricsObserver>(
&completed_filtered_urls_));
}
+ std::unique_ptr<base::Timer> CreateTimer() override {
+ std::unique_ptr<WeakMockTimer> timer = base::MakeUnique<WeakMockTimer>();
+ most_recent_timer_ = timer->AsWeakPtr();
+ return std::move(timer);
+ }
const std::vector<mojom::PageLoadTimingPtr>& updated_timings() const {
return updated_timings_;
}
@@ -149,6 +164,9 @@ class TestPageLoadMetricsEmbedderInterface
const std::vector<GURL>& completed_filtered_urls() const {
return completed_filtered_urls_;
}
+ base::MockTimer* GetMostRecentTimer() const {
+ return most_recent_timer_.get();
+ }
private:
std::vector<mojom::PageLoadTimingPtr> updated_timings_;
@@ -156,9 +174,18 @@ class TestPageLoadMetricsEmbedderInterface
std::vector<mojom::PageLoadTimingPtr> complete_timings_;
std::vector<GURL> observed_committed_urls_;
std::vector<GURL> completed_filtered_urls_;
+ base::WeakPtr<WeakMockTimer> most_recent_timer_;
bool is_ntp_;
};
+void PopulatePageLoadTiming(mojom::PageLoadTiming* timing) {
+ page_load_metrics::InitPageLoadTimingForTest(timing);
+ timing->navigation_start = base::Time::FromDoubleT(1);
+ timing->response_start = base::TimeDelta::FromMilliseconds(10);
+ timing->parse_timing->parse_start = base::TimeDelta::FromMilliseconds(20);
+ timing->document_timing->first_layout = base::TimeDelta::FromMilliseconds(30);
+}
+
} // namespace
class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness {
@@ -175,12 +202,30 @@ class MetricsWebContentsObserverTest : public ChromeRenderViewHostTestHarness {
->NavigateAndCommit(GURL(url::kAboutBlankURL));
}
+ // Returns the mock timer used for buffering updates in the
+ // PageLoadMetricsUpdateDispatcher.
+ base::MockTimer* GetMostRecentTimer() {
+ return embedder_interface_->GetMostRecentTimer();
+ }
+
void SimulateTimingUpdate(const mojom::PageLoadTiming& timing) {
SimulateTimingUpdate(timing, web_contents()->GetMainFrame());
}
void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
content::RenderFrameHost* render_frame_host) {
+ SimulateTimingUpdateWithoutFiringDispatchTimer(timing, render_frame_host);
+ // If sending the timing update caused the PageLoadMetricsUpdateDispatcher
+ // to schedule a buffering timer, then fire it now so metrics are dispatched
+ // to observers.
+ base::MockTimer* mock_timer = GetMostRecentTimer();
+ if (mock_timer && mock_timer->IsRunning())
+ mock_timer->Fire();
+ }
+
+ void SimulateTimingUpdateWithoutFiringDispatchTimer(
+ const mojom::PageLoadTiming& timing,
+ content::RenderFrameHost* render_frame_host) {
observer()->OnTimingUpdated(render_frame_host, timing,
mojom::PageLoadMetadata());
}
@@ -735,13 +780,7 @@ TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming) {
// Dispatch a timing update for the child frame that includes a first paint.
mojom::PageLoadTiming subframe_timing;
- page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
- subframe_timing.navigation_start = base::Time::FromDoubleT(2);
- subframe_timing.response_start = base::TimeDelta::FromMilliseconds(10);
- subframe_timing.parse_timing->parse_start =
- base::TimeDelta::FromMilliseconds(20);
- subframe_timing.document_timing->first_layout =
- base::TimeDelta::FromMilliseconds(30);
+ PopulatePageLoadTiming(&subframe_timing);
subframe_timing.paint_timing->first_paint =
base::TimeDelta::FromMilliseconds(40);
content::RenderFrameHostTester* subframe_tester =
@@ -797,4 +836,100 @@ TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming) {
CheckNoErrorEvents();
}
+// We buffer cross-frame paint updates to account for paint timings from
+// different frames arriving out of order.
+TEST_F(MetricsWebContentsObserverTest, OutOfOrderCrossFrameTiming2) {
+ // Dispatch a timing update for the main frame that includes a first
+ // paint. This should be buffered, with the dispatch timer running.
+ mojom::PageLoadTiming timing;
+ PopulatePageLoadTiming(&timing);
+ timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(1000);
+ content::WebContentsTester* web_contents_tester =
+ content::WebContentsTester::For(web_contents());
+ web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
+ SimulateTimingUpdateWithoutFiringDispatchTimer(timing, main_rfh());
+
+ EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
+ ASSERT_EQ(0, CountUpdatedTimingReported());
+
+ content::RenderFrameHostTester* rfh_tester =
+ content::RenderFrameHostTester::For(main_rfh());
+
+ // Dispatch a timing update for a child frame that includes a first paint.
+ mojom::PageLoadTiming subframe_timing;
+ PopulatePageLoadTiming(&subframe_timing);
+ subframe_timing.paint_timing->first_paint =
+ base::TimeDelta::FromMilliseconds(500);
+ content::RenderFrameHost* subframe = rfh_tester->AppendChild("subframe");
+ content::RenderFrameHostTester* subframe_tester =
+ content::RenderFrameHostTester::For(subframe);
+ subframe_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
+ subframe_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
+ SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe);
+ subframe_tester->SimulateNavigationStop();
+
+ histogram_tester_.ExpectTotalCount(
+ page_load_metrics::internal::kHistogramOutOfOrderTiming, 1);
+
+ EXPECT_TRUE(GetMostRecentTimer()->IsRunning());
+ ASSERT_EQ(0, CountUpdatedTimingReported());
+
+ // At this point, the timing update is buffered, waiting for the timer to
+ // fire.
+ GetMostRecentTimer()->Fire();
+
+ // Firing the timer should produce a timing update. The update should be a
+ // merged view of the main frame timing, with a first paint timestamp from the
+ // subframe.
+ ASSERT_EQ(1, CountUpdatedTimingReported());
+ EXPECT_FALSE(timing.Equals(*updated_timings().back()));
+ EXPECT_TRUE(
+ updated_timings().back()->parse_timing->Equals(*timing.parse_timing));
+ EXPECT_TRUE(updated_timings().back()->document_timing->Equals(
+ *timing.document_timing));
+ EXPECT_FALSE(
+ updated_timings().back()->paint_timing->Equals(*timing.paint_timing));
+ EXPECT_TRUE(updated_timings().back()->paint_timing->first_paint);
+
+ // 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
+ // in this case is the first paint from the subframe. Since it is offset by
+ // the subframe's navigation start, the received value should be >= the first
+ // paint value specified in the subframe.
+ EXPECT_GE(updated_timings().back()->paint_timing->first_paint,
+ subframe_timing.paint_timing->first_paint);
+ EXPECT_LT(updated_timings().back()->paint_timing->first_paint,
+ timing.paint_timing->first_paint);
+
+ base::TimeDelta initial_first_paint =
+ updated_timings().back()->paint_timing->first_paint.value();
+
+ // Dispatch a timing update for an additional child frame, with an earlier
+ // first paint time. This should cause an immediate update, without a timer
+ // delay.
+ subframe_timing.paint_timing->first_paint =
+ base::TimeDelta::FromMilliseconds(50);
+ content::RenderFrameHost* subframe2 = rfh_tester->AppendChild("subframe");
+ content::RenderFrameHostTester* subframe2_tester =
+ content::RenderFrameHostTester::For(subframe2);
+ subframe2_tester->SimulateNavigationStart(GURL(kDefaultTestUrl2));
+ subframe2_tester->SimulateNavigationCommit(GURL(kDefaultTestUrl2));
+ SimulateTimingUpdateWithoutFiringDispatchTimer(subframe_timing, subframe2);
+ subframe2_tester->SimulateNavigationStop();
+
+ base::TimeDelta updated_first_paint =
+ updated_timings().back()->paint_timing->first_paint.value();
+
+ EXPECT_FALSE(GetMostRecentTimer()->IsRunning());
+ ASSERT_EQ(2, CountUpdatedTimingReported());
+ EXPECT_LT(updated_first_paint, initial_first_paint);
+
+ histogram_tester_.ExpectTotalCount(
+ page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered, 1);
+ histogram_tester_.ExpectBucketCount(
+ page_load_metrics::internal::kHistogramOutOfOrderTimingBuffered,
+ (initial_first_paint - updated_first_paint).InMilliseconds(), 1);
+
+ CheckNoErrorEvents();
+}
+
} // namespace page_load_metrics

Powered by Google App Engine
This is Rietveld 408576698