Commit 7cb13798 authored by Chris Hamilton's avatar Chris Hamilton Committed by Commit Bot

Make PageAlmostIdle transitions determinate.

This adds a final timeout to PageAlmostIdle transitions so that they always fire at some point.
This notification is intended to mean "initial load has finished", and it makes sense for this
to fire at some point. This simplifies logic in an upcoming CL which factors
observation logic out of the TabManager into a helper component.

BUG=749789

Change-Id: I1db03f991220eebd33251dc5258a2760042eedef
Reviewed-on: https://chromium-review.googlesource.com/929985
Commit-Queue: Chris Hamilton <chrisha@chromium.org>
Reviewed-by: default avatarlpy <lpy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#542614}
parent 1d625d9e
...@@ -33,6 +33,13 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver, ...@@ -33,6 +33,13 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
// transitions. // transitions.
static const base::TimeDelta kLoadedAndIdlingTimeout; static const base::TimeDelta kLoadedAndIdlingTimeout;
// The maximum amount of time post-DidStopLoading a page can be waiting for
// an idle state to occur before the page is simply considered loaded anyways.
// Since PageAlmostIdle is intended as an "initial loading complete" signal,
// it needs to eventually terminate. This is strictly greater than the
// kLoadedAndIdlingTimeout.
static const base::TimeDelta kWaitingForIdleTimeout;
PageSignalGeneratorImpl(); PageSignalGeneratorImpl();
~PageSignalGeneratorImpl() override; ~PageSignalGeneratorImpl() override;
...@@ -61,12 +68,15 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver, ...@@ -61,12 +68,15 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
const service_manager::BindSourceInfo& source_info); const service_manager::BindSourceInfo& source_info);
private: private:
friend class PageSignalGeneratorImplTest;
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsLoading); FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsLoading);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsIdling); FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, IsIdling);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageDataCorrectlyManaged); PageDataCorrectlyManaged);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest, FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitions); PageAlmostIdleTransitionsNoTimeout);
FRIEND_TEST_ALL_PREFIXES(PageSignalGeneratorImplTest,
PageAlmostIdleTransitionsWithTimeout);
// The state transitions for the PageAlmostIdle signal. In general a page // The state transitions for the PageAlmostIdle signal. In general a page
// transitions through these states from top to bottom. // transitions through these states from top to bottom.
...@@ -96,8 +106,12 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver, ...@@ -96,8 +106,12 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
// to UpdateLoadIdleState. Is reset to kLoadingNotStarted when a non-same // to UpdateLoadIdleState. Is reset to kLoadingNotStarted when a non-same
// document navigation is committed. // document navigation is committed.
LoadIdleState load_idle_state; LoadIdleState load_idle_state;
// Marks the point in time when the transition to kLoadedAndIdling occurred. // Marks the point in time when the DidStopLoading signal was received,
// Used for gating the transition to kLoadedAndIdle. // transitioning to kLoadedAndNotIdling or kLoadedAndIdling. This is used as
// the basis for the kWaitingForIdleTimeout.
base::TimeTicks loading_stopped;
// Marks the point in time when the last transition to kLoadedAndIdling
// occurred. Used for gating the transition to kLoadedAndIdle.
base::TimeTicks idling_started; base::TimeTicks idling_started;
// A one-shot timer used for transitioning between kLoadedAndIdling and // A one-shot timer used for transitioning between kLoadedAndIdling and
// kLoadedAndIdle. // kLoadedAndIdle.
...@@ -112,6 +126,9 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver, ...@@ -112,6 +126,9 @@ class PageSignalGeneratorImpl : public CoordinationUnitGraphObserver,
void UpdateLoadIdleStateProcess( void UpdateLoadIdleStateProcess(
const ProcessCoordinationUnitImpl* process_cu); const ProcessCoordinationUnitImpl* process_cu);
// Helper function for transitioning to the final state.
void TransitionToLoadedAndIdle(const PageCoordinationUnitImpl* page_cu);
// Convenience accessors for state associated with a |page_cu|. // Convenience accessors for state associated with a |page_cu|.
PageData* GetPageData(const PageCoordinationUnitImpl* page_cu); PageData* GetPageData(const PageCoordinationUnitImpl* page_cu);
bool IsLoading(const PageCoordinationUnitImpl* page_cu); bool IsLoading(const PageCoordinationUnitImpl* page_cu);
......
...@@ -4,18 +4,38 @@ ...@@ -4,18 +4,38 @@
#include "services/resource_coordinator/observers/page_signal_generator_impl.h" #include "services/resource_coordinator/observers/page_signal_generator_impl.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/simple_test_tick_clock.h" #include "base/test/simple_test_tick_clock.h"
#include "services/resource_coordinator/coordination_unit/coordination_unit_test_harness.h" #include "services/resource_coordinator/coordination_unit/coordination_unit_test_harness.h"
#include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/frame_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/mock_coordination_unit_graphs.h" #include "services/resource_coordinator/coordination_unit/mock_coordination_unit_graphs.h"
#include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/page_coordination_unit_impl.h"
#include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h" #include "services/resource_coordinator/coordination_unit/process_coordination_unit_impl.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
#include "services/resource_coordinator/resource_coordinator_clock.h" #include "services/resource_coordinator/resource_coordinator_clock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
namespace resource_coordinator { namespace resource_coordinator {
// A wrapper for a tick clock. ResourceCoordinatorClock wants TickClock
// ownership, but the test harness only provides a raw pointer.
class TickClockWrapper : public base::TickClock {
public:
explicit TickClockWrapper(base::TickClock* tick_clock)
: tick_clock_(tick_clock) {}
~TickClockWrapper() override {}
// base::TickClock implementation:
base::TimeTicks NowTicks() override { return tick_clock_->NowTicks(); }
private:
base::TickClock* tick_clock_;
DISALLOW_COPY_AND_ASSIGN(TickClockWrapper);
};
class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl { class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl {
public: public:
// Overridden from PageSignalGeneratorImpl. // Overridden from PageSignalGeneratorImpl.
...@@ -34,12 +54,23 @@ class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl { ...@@ -34,12 +54,23 @@ class MockPageSignalGeneratorImpl : public PageSignalGeneratorImpl {
class PageSignalGeneratorImplTest : public CoordinationUnitTestHarness { class PageSignalGeneratorImplTest : public CoordinationUnitTestHarness {
protected: protected:
void TearDown() override { ResourceCoordinatorClock::ResetClockForTesting(); }
MockPageSignalGeneratorImpl* page_signal_generator() { MockPageSignalGeneratorImpl* page_signal_generator() {
return &page_signal_generator_; return &page_signal_generator_;
} }
void EnablePAI() {
feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
feature_list_->InitAndEnableFeature(features::kPageAlmostIdle);
ASSERT_TRUE(resource_coordinator::IsPageAlmostIdleSignalEnabled());
}
void TestPageAlmostIdleTransitions(bool timeout);
private: private:
MockPageSignalGeneratorImpl page_signal_generator_; MockPageSignalGeneratorImpl page_signal_generator_;
std::unique_ptr<base::test::ScopedFeatureList> feature_list_;
}; };
TEST_F(PageSignalGeneratorImplTest, TEST_F(PageSignalGeneratorImplTest,
...@@ -61,6 +92,7 @@ TEST_F(PageSignalGeneratorImplTest, ...@@ -61,6 +92,7 @@ TEST_F(PageSignalGeneratorImplTest,
} }
TEST_F(PageSignalGeneratorImplTest, IsLoading) { TEST_F(PageSignalGeneratorImplTest, IsLoading) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph; MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* page_cu = cu_graph.page.get(); auto* page_cu = cu_graph.page.get();
auto* psg = page_signal_generator(); auto* psg = page_signal_generator();
...@@ -80,6 +112,7 @@ TEST_F(PageSignalGeneratorImplTest, IsLoading) { ...@@ -80,6 +112,7 @@ TEST_F(PageSignalGeneratorImplTest, IsLoading) {
} }
TEST_F(PageSignalGeneratorImplTest, IsIdling) { TEST_F(PageSignalGeneratorImplTest, IsIdling) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph; MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* frame_cu = cu_graph.frame.get(); auto* frame_cu = cu_graph.frame.get();
auto* page_cu = cu_graph.page.get(); auto* page_cu = cu_graph.page.get();
...@@ -114,6 +147,7 @@ TEST_F(PageSignalGeneratorImplTest, IsIdling) { ...@@ -114,6 +147,7 @@ TEST_F(PageSignalGeneratorImplTest, IsIdling) {
} }
TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) { TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) {
EnablePAI();
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph; MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* page_cu = cu_graph.page.get(); auto* page_cu = cu_graph.page.get();
auto* psg = page_signal_generator(); auto* psg = page_signal_generator();
...@@ -127,12 +161,11 @@ TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) { ...@@ -127,12 +161,11 @@ TEST_F(PageSignalGeneratorImplTest, PageDataCorrectlyManaged) {
EXPECT_EQ(0u, psg->page_data_.count(page_cu)); EXPECT_EQ(0u, psg->page_data_.count(page_cu));
} }
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) { void PageSignalGeneratorImplTest::TestPageAlmostIdleTransitions(bool timeout) {
EnablePAI();
ResourceCoordinatorClock::SetClockForTesting( ResourceCoordinatorClock::SetClockForTesting(
std::make_unique<base::SimpleTestTickClock>()); std::make_unique<TickClockWrapper>(task_env().GetMockTickClock()));
auto* test_clock = static_cast<base::SimpleTestTickClock*>( task_env().FastForwardBy(base::TimeDelta::FromSeconds(1));
ResourceCoordinatorClock::GetClockForTesting());
test_clock->Advance(base::TimeDelta::FromSeconds(1));
MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph; MockSinglePageInSingleProcessCoordinationUnitGraph cu_graph;
auto* frame_cu = cu_graph.frame.get(); auto* frame_cu = cu_graph.frame.get();
...@@ -185,24 +218,37 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) { ...@@ -185,24 +218,37 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) {
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state); EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning()); EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Go back to not idling. We should transition back to kLoadedNotIdling. // Go back to not idling. We should transition back to kLoadedNotIdling, and
// a timer should still be running.
frame_cu->SetNetworkAlmostIdle(false); frame_cu->SetNetworkAlmostIdle(false);
EXPECT_EQ(LIS::kLoadedNotIdling, page_data->load_idle_state); EXPECT_EQ(LIS::kLoadedNotIdling, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
// Go back to idling.
frame_cu->SetNetworkAlmostIdle(true);
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning()); EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Let the timer evaluate. The final state transition should occur. base::TimeTicks start = ResourceCoordinatorClock::NowTicks();
// TODO(chrisha): Expose the tick clock from the ScopedTaskEnvironment, and if (timeout) {
// use that clock in the ResourceCoordinatorClock. Beats having two separate // Let the timeout run down. The final state transition should occur.
// fake clocks that need to be manually kept in sync. task_env().FastForwardUntilNoTasksRemain();
test_clock->Advance(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout); base::TimeTicks end = ResourceCoordinatorClock::NowTicks();
task_env().FastForwardUntilNoTasksRemain(); base::TimeDelta elapsed = end - start;
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state); EXPECT_LE(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout, elapsed);
EXPECT_FALSE(page_data->idling_timer.IsRunning()); EXPECT_LE(PageSignalGeneratorImpl::kWaitingForIdleTimeout, elapsed);
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
} else {
// Go back to idling.
frame_cu->SetNetworkAlmostIdle(true);
EXPECT_EQ(LIS::kLoadedAndIdling, page_data->load_idle_state);
EXPECT_TRUE(page_data->idling_timer.IsRunning());
// Let the idle timer evaluate. The final state transition should occur.
task_env().FastForwardUntilNoTasksRemain();
base::TimeTicks end = ResourceCoordinatorClock::NowTicks();
base::TimeDelta elapsed = end - start;
EXPECT_LE(PageSignalGeneratorImpl::kLoadedAndIdlingTimeout, elapsed);
EXPECT_GT(PageSignalGeneratorImpl::kWaitingForIdleTimeout, elapsed);
EXPECT_EQ(LIS::kLoadedAndIdle, page_data->load_idle_state);
EXPECT_FALSE(page_data->idling_timer.IsRunning());
}
// Firing other signals should not change the state at all. // Firing other signals should not change the state at all.
proc_cu->SetMainThreadTaskLoadIsLow(false); proc_cu->SetMainThreadTaskLoadIsLow(false);
...@@ -218,4 +264,12 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) { ...@@ -218,4 +264,12 @@ TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitions) {
EXPECT_FALSE(page_data->idling_timer.IsRunning()); EXPECT_FALSE(page_data->idling_timer.IsRunning());
} }
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitionsNoTimeout) {
TestPageAlmostIdleTransitions(false);
}
TEST_F(PageSignalGeneratorImplTest, PageAlmostIdleTransitionsWithTimeout) {
TestPageAlmostIdleTransitions(true);
}
} // namespace resource_coordinator } // namespace resource_coordinator
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment