| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/paint/first_meaningful_paint_detector.h" |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/platform/web_layer_tree_view.h" |
| #include "third_party/blink/renderer/core/paint/paint_event.h" |
| #include "third_party/blink/renderer/core/paint/paint_timing.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| #include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h" |
| #include "third_party/blink/renderer/platform/wtf/text/string_builder.h" |
| #include "third_party/blink/renderer/platform/wtf/time.h" |
| |
| namespace blink { |
| |
| class FirstMeaningfulPaintDetectorTest : public PageTestBase { |
| protected: |
| void SetUp() override { |
| platform_->AdvanceClockSeconds(1); |
| PageTestBase::SetUp(); |
| ResetNetworkQuietTimer(); |
| } |
| |
| // The initial document doesn't need to load any resources other than itself. |
| // It means initially, the network quiet timers are already active. This |
| // function is used to reset them. |
| void ResetNetworkQuietTimer() { |
| Detector().network2_quiet_timer_.Stop(); |
| Detector().network0_quiet_timer_.Stop(); |
| } |
| |
| TimeTicks AdvanceClockAndGetTime() { |
| platform_->AdvanceClockSeconds(1); |
| return CurrentTimeTicks(); |
| } |
| |
| PaintTiming& GetPaintTiming() { return PaintTiming::From(GetDocument()); } |
| FirstMeaningfulPaintDetector& Detector() { |
| return GetPaintTiming().GetFirstMeaningfulPaintDetector(); |
| } |
| |
| void SimulateLayoutAndPaint(int new_elements) { |
| platform_->AdvanceClockSeconds(0.001); |
| StringBuilder builder; |
| for (int i = 0; i < new_elements; i++) |
| builder.Append("<span>a</span>"); |
| GetDocument().write(builder.ToString()); |
| GetDocument().UpdateStyleAndLayout(); |
| Detector().NotifyPaint(); |
| } |
| |
| void SimulateNetworkStable() { |
| GetDocument().SetParsingState(Document::kFinishedParsing); |
| Detector().Network0QuietTimerFired(nullptr); |
| Detector().Network2QuietTimerFired(nullptr); |
| } |
| |
| void SimulateNetwork0Quiet() { |
| GetDocument().SetParsingState(Document::kFinishedParsing); |
| Detector().Network0QuietTimerFired(nullptr); |
| } |
| |
| void SimulateNetwork2Quiet() { |
| GetDocument().SetParsingState(Document::kFinishedParsing); |
| Detector().Network2QuietTimerFired(nullptr); |
| } |
| |
| void SimulateUserInput() { Detector().NotifyInputEvent(); } |
| |
| void SetActiveConnections(int connections) { |
| Detector().SetNetworkQuietTimers(connections); |
| } |
| |
| bool IsNetwork0QuietTimerActive() { |
| return Detector().network0_quiet_timer_.IsActive(); |
| } |
| |
| bool IsNetwork2QuietTimerActive() { |
| return Detector().network2_quiet_timer_.IsActive(); |
| } |
| |
| bool HadNetwork0Quiet() { return Detector().network0_quiet_reached_; } |
| bool HadNetwork2Quiet() { return Detector().network2_quiet_reached_; } |
| |
| void ClearFirstPaintSwapPromise() { |
| platform_->AdvanceClockSeconds(0.001); |
| GetPaintTiming().ReportSwapTime(PaintEvent::kFirstPaint, |
| WebLayerTreeView::SwapResult::kDidSwap, |
| CurrentTimeTicks()); |
| } |
| |
| void ClearFirstContentfulPaintSwapPromise() { |
| platform_->AdvanceClockSeconds(0.001); |
| GetPaintTiming().ReportSwapTime(PaintEvent::kFirstContentfulPaint, |
| WebLayerTreeView::SwapResult::kDidSwap, |
| CurrentTimeTicks()); |
| } |
| |
| void ClearProvisionalFirstMeaningfulPaintSwapPromise() { |
| platform_->AdvanceClockSeconds(0.001); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(CurrentTimeTicks()); |
| } |
| |
| void ClearProvisionalFirstMeaningfulPaintSwapPromise( |
| base::TimeTicks timestamp) { |
| Detector().ReportSwapTime(PaintEvent::kProvisionalFirstMeaningfulPaint, |
| WebLayerTreeView::SwapResult::kDidSwap, |
| timestamp); |
| } |
| |
| unsigned OutstandingDetectorSwapPromiseCount() { |
| return Detector().outstanding_swap_promise_count_; |
| } |
| |
| void MarkFirstContentfulPaintAndClearSwapPromise() { |
| GetPaintTiming().MarkFirstContentfulPaint(); |
| ClearFirstContentfulPaintSwapPromise(); |
| } |
| |
| void MarkFirstPaintAndClearSwapPromise() { |
| GetPaintTiming().MarkFirstPaint(); |
| ClearFirstPaintSwapPromise(); |
| } |
| |
| protected: |
| ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler> |
| platform_; |
| |
| static constexpr double kNetwork0QuietWindowSeconds = |
| FirstMeaningfulPaintDetector::kNetwork0QuietWindowSeconds; |
| static constexpr double kNetwork2QuietWindowSeconds = |
| FirstMeaningfulPaintDetector::kNetwork2QuietWindowSeconds; |
| }; |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, NoFirstPaint) { |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, OneLayout) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_paint = AdvanceClockAndGetTime(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| SimulateNetworkStable(); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| GetPaintTiming().FirstPaintRendered()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_paint); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaint(), after_paint); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, TwoLayoutsSignificantSecond) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_layout1 = AdvanceClockAndGetTime(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_layout2 = AdvanceClockAndGetTime(); |
| SimulateNetworkStable(); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_layout1); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), after_layout1); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_layout2); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaint(), after_layout2); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, TwoLayoutsSignificantFirst) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_layout1 = AdvanceClockAndGetTime(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| SimulateNetworkStable(); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| GetPaintTiming().FirstPaintRendered()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstPaintRendered()); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_layout1); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaint(), after_layout1); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, FirstMeaningfulPaintCandidate) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), TimeTicks()); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_paint = AdvanceClockAndGetTime(); |
| // The first candidate gets ignored. |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), TimeTicks()); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| // The second candidate gets reported. |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintCandidate(), after_paint); |
| TimeTicks candidate = GetPaintTiming().FirstMeaningfulPaintCandidate(); |
| // The third candidate gets ignored since we already saw the first candidate. |
| SimulateLayoutAndPaint(20); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), candidate); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| OnlyOneFirstMeaningfulPaintCandidateBeforeNetworkStable) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), TimeTicks()); |
| TimeTicks before_paint = AdvanceClockAndGetTime(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| // The first candidate is initially ignored. |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), TimeTicks()); |
| SimulateNetworkStable(); |
| // The networkStable then promotes the first candidate. |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintCandidate(), before_paint); |
| TimeTicks candidate = GetPaintTiming().FirstMeaningfulPaintCandidate(); |
| // The second candidate is then ignored. |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintCandidate(), candidate); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| NetworkStableBeforeFirstContentfulPaint) { |
| MarkFirstPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateNetworkStable(); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| FirstMeaningfulPaintShouldNotBeBeforeFirstContentfulPaint) { |
| MarkFirstPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| platform_->AdvanceClockSeconds(0.001); |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateNetworkStable(); |
| EXPECT_GE(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| GetPaintTiming().FirstContentfulPaintRendered()); |
| EXPECT_GE(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstContentfulPaint()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, Network2QuietThen0Quiet) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| TimeTicks after_first_paint = AdvanceClockAndGetTime(); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_first_paint_swap = AdvanceClockAndGetTime(); |
| SimulateNetwork2Quiet(); |
| |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| SimulateNetwork0Quiet(); |
| |
| // The first paint is FirstMeaningfulPaint. |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_first_paint); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), after_first_paint); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaint(), after_first_paint_swap); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, Network0QuietThen2Quiet) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_first_paint = AdvanceClockAndGetTime(); |
| SimulateNetwork0Quiet(); |
| |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_second_paint = AdvanceClockAndGetTime(); |
| SimulateNetwork2Quiet(); |
| |
| // The second paint is FirstMeaningfulPaint. |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), after_first_paint); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), after_first_paint); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| after_second_paint); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaint(), after_second_paint); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, Network0QuietTimer) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| |
| SetActiveConnections(1); |
| EXPECT_FALSE(IsNetwork0QuietTimerActive()); |
| |
| SetActiveConnections(0); |
| platform_->RunForPeriodSeconds(kNetwork0QuietWindowSeconds - 0.1); |
| EXPECT_TRUE(IsNetwork0QuietTimerActive()); |
| EXPECT_FALSE(HadNetwork0Quiet()); |
| |
| SetActiveConnections(0); // This should reset the 0-quiet timer. |
| platform_->RunForPeriodSeconds(kNetwork0QuietWindowSeconds - 0.1); |
| EXPECT_TRUE(IsNetwork0QuietTimerActive()); |
| EXPECT_FALSE(HadNetwork0Quiet()); |
| |
| platform_->RunForPeriodSeconds(0.1001); |
| EXPECT_TRUE(HadNetwork0Quiet()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, Network2QuietTimer) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| |
| SetActiveConnections(3); |
| EXPECT_FALSE(IsNetwork2QuietTimerActive()); |
| |
| SetActiveConnections(2); |
| platform_->RunForPeriodSeconds(kNetwork2QuietWindowSeconds - 0.1); |
| EXPECT_TRUE(IsNetwork2QuietTimerActive()); |
| EXPECT_FALSE(HadNetwork2Quiet()); |
| |
| SetActiveConnections(2); // This should reset the 2-quiet timer. |
| platform_->RunForPeriodSeconds(kNetwork2QuietWindowSeconds - 0.1); |
| EXPECT_TRUE(IsNetwork2QuietTimerActive()); |
| EXPECT_FALSE(HadNetwork2Quiet()); |
| |
| SetActiveConnections(1); // This should not reset the 2-quiet timer. |
| platform_->RunForPeriodSeconds(0.1001); |
| EXPECT_TRUE(HadNetwork2Quiet()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| FirstMeaningfulPaintAfterUserInteraction) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateUserInput(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, UserInteractionBeforeFirstPaint) { |
| SimulateUserInput(); |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| SimulateNetworkStable(); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| WaitForSingleOutstandingSwapPromiseAfterNetworkStable) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_NE(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| WaitForMultipleOutstandingSwapPromisesAfterNetworkStable) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| platform_->AdvanceClockSeconds(0.001); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 2U); |
| // Having outstanding swap promises should defer setting FMP. |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| // Clearing the first swap promise should have no effect on FMP. |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| TimeTicks after_first_swap = AdvanceClockAndGetTime(); |
| // Clearing the last outstanding swap promise should set FMP. |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), after_first_swap); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| WaitForFirstContentfulPaintSwapAfterNetworkStable) { |
| MarkFirstPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(); |
| TimeTicks after_first_meaningful_paint_candidate = AdvanceClockAndGetTime(); |
| platform_->AdvanceClockSeconds(0.001); |
| GetPaintTiming().MarkFirstContentfulPaint(); |
| // FCP > FMP candidate, but still waiting for FCP swap. |
| SimulateNetworkStable(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| // Trigger notifying the detector about the FCP swap. |
| ClearFirstContentfulPaintSwapPromise(); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| GetPaintTiming().FirstContentfulPaintRendered()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstContentfulPaint()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| after_first_meaningful_paint_candidate); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), |
| GetPaintTiming().FirstMeaningfulPaintRendered()); |
| } |
| |
| TEST_F(FirstMeaningfulPaintDetectorTest, |
| ProvisionalTimestampChangesAfterNetworkQuietWithOutstandingSwapPromise) { |
| MarkFirstContentfulPaintAndClearSwapPromise(); |
| SimulateLayoutAndPaint(1); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| |
| // Simulate only network 2-quiet so provisional FMP will be set on next |
| // layout. |
| TimeTicks pre_stable_timestamp = AdvanceClockAndGetTime(); |
| platform_->AdvanceClockSeconds(0.001); |
| SimulateNetwork2Quiet(); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| |
| // Force another FMP candidate while there is a pending swap promise and the |
| // network 2-quiet FMP non-swap timestamp is set. |
| platform_->AdvanceClockSeconds(0.001); |
| SimulateLayoutAndPaint(10); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 1U); |
| |
| // Simulate a delay in receiving the SwapPromise timestamp. Clearing this |
| // SwapPromise will set FMP, and this will crash if the new provisional |
| // non-swap timestamp is used. |
| ClearProvisionalFirstMeaningfulPaintSwapPromise(pre_stable_timestamp); |
| EXPECT_EQ(OutstandingDetectorSwapPromiseCount(), 0U); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaintRendered(), TimeTicks()); |
| EXPECT_GT(GetPaintTiming().FirstMeaningfulPaint(), TimeTicks()); |
| EXPECT_EQ(GetPaintTiming().FirstMeaningfulPaint(), pre_stable_timestamp); |
| EXPECT_LT(GetPaintTiming().FirstMeaningfulPaintRendered(), |
| pre_stable_timestamp); |
| } |
| |
| } // namespace blink |