[go: nahoru, domu]

Add begin frame paused signal

This avoids a deadlock situation in android webview where
begin frames have stopped, but the blink main thread is blocked
indefinitely waiting for activation. See bug for details.

Plumb a signal that the BeginFrameSource is paused directly
through BeginFrameSourceObserver, and force activate any
pending activations when paused. This is similar to when
compositor becomes invisible, but BFS allows webview send
the signal without a hop to blink main thread.

BUG=539373
CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1536353003

Cr-Commit-Position: refs/heads/master@{#369238}
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc
index 17b4a26..d96ef4e 100644
--- a/cc/scheduler/begin_frame_source.cc
+++ b/cc/scheduler/begin_frame_source.cc
@@ -53,6 +53,7 @@
 BeginFrameSourceBase::BeginFrameSourceBase()
     : observer_(NULL),
       needs_begin_frames_(false),
+      paused_(false),
       inside_as_value_into_(false) {
   DCHECK(!observer_);
   DCHECK_EQ(inside_as_value_into_, false);
@@ -82,6 +83,8 @@
                obs);
   DCHECK(!observer_);
   observer_ = obs;
+  if (observer_)
+    return observer_->OnBeginFrameSourcePausedChanged(paused_);
 }
 
 void BeginFrameSourceBase::RemoveObserver(BeginFrameObserver* obs) {
@@ -105,6 +108,14 @@
   }
 }
 
+void BeginFrameSourceBase::SetBeginFrameSourcePaused(bool paused) {
+  if (paused_ == paused)
+    return;
+  paused_ = paused;
+  if (observer_)
+    return observer_->OnBeginFrameSourcePausedChanged(paused_);
+}
+
 // Tracing support
 void BeginFrameSourceBase::AsValueInto(
     base::trace_event::TracedValue* dict) const {
@@ -381,6 +392,10 @@
     return BeginFrameArgs();
 }
 
+void BeginFrameSourceMultiplexer::OnBeginFrameSourcePausedChanged(bool paused) {
+  BeginFrameSourceBase::SetBeginFrameSourcePaused(paused);
+}
+
 // BeginFrameSource support
 void BeginFrameSourceMultiplexer::OnNeedsBeginFramesChange(
     bool needs_begin_frames) {
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h
index ec8aaa5..a77d546 100644
--- a/cc/scheduler/begin_frame_source.h
+++ b/cc/scheduler/begin_frame_source.h
@@ -60,6 +60,8 @@
   // preventing "double dropping" and other bad side effects.
   virtual const BeginFrameArgs LastUsedBeginFrameArgs() const = 0;
 
+  virtual void OnBeginFrameSourcePausedChanged(bool paused) = 0;
+
   // Tracing support
   virtual void AsValueInto(base::trace_event::TracedValue* dict) const = 0;
 };
@@ -168,6 +170,7 @@
   // These methods should be used by subclasses to make the call to the
   // observers.
   void CallOnBeginFrame(const BeginFrameArgs& args);
+  void SetBeginFrameSourcePaused(bool paused);
 
   // This method should be overridden if you want to change some behaviour on
   // needs_begin_frames change.
@@ -175,6 +178,7 @@
 
   BeginFrameObserver* observer_;
   bool needs_begin_frames_;
+  bool paused_;
 
  private:
   bool inside_as_value_into_;
@@ -273,6 +277,7 @@
   // sources.
   void OnBeginFrame(const BeginFrameArgs& args) override;
   const BeginFrameArgs LastUsedBeginFrameArgs() const override;
+  void OnBeginFrameSourcePausedChanged(bool paused) override;
 
   // BeginFrameSource
   void DidFinishFrame(size_t remaining_frames) override;
diff --git a/cc/scheduler/begin_frame_source_unittest.cc b/cc/scheduler/begin_frame_source_unittest.cc
index 43ba4fa7..4591559c 100644
--- a/cc/scheduler/begin_frame_source_unittest.cc
+++ b/cc/scheduler/begin_frame_source_unittest.cc
@@ -13,6 +13,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::Mock;
+
 namespace cc {
 namespace {
 
@@ -20,6 +22,7 @@
 class MockMinimalBeginFrameObserverBase : public BeginFrameObserverBase {
  public:
   MOCK_METHOD1(OnBeginFrameDerivedImpl, bool(const BeginFrameArgs&));
+  MOCK_METHOD1(OnBeginFrameSourcePausedChanged, void(bool));
   int64_t dropped_begin_frame_args() const { return dropped_begin_frame_args_; }
 };
 
@@ -73,6 +76,7 @@
   MockBeginFrameObserver otherObs;
   FakeBeginFrameSource source;
 
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   source.AddObserver(&obs);
   EXPECT_EQ(&obs, source.GetObserver());
 
@@ -92,6 +96,7 @@
 #endif
   source.RemoveObserver(&obs);
 
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(otherObs, false);
   source.AddObserver(&otherObs);
   EXPECT_EQ(&otherObs, source.GetObserver());
   source.RemoveObserver(&otherObs);
@@ -100,6 +105,7 @@
 TEST(BeginFrameSourceBaseTest, Observer) {
   FakeBeginFrameSource source;
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   source.AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
   EXPECT_BEGIN_FRAME_DROP(obs, 400, 600, 300);
@@ -126,6 +132,18 @@
   EXPECT_FALSE(source.NeedsBeginFrames());
 }
 
+TEST(BeginFrameSourceBaseTest, SetBeginFrameSourcePaused) {
+  FakeBeginFrameSource source;
+  MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  source.AddObserver(&obs);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  source.SetBeginFrameSourcePaused(true);
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  source.SetBeginFrameSourcePaused(false);
+}
+
 class LoopingBeginFrameObserver : public BeginFrameObserverBase {
  public:
   BeginFrameSource* source_;
@@ -142,6 +160,8 @@
   bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override {
     return true;
   }
+
+  void OnBeginFrameSourcePausedChanged(bool paused) override {}
 };
 
 TEST(BeginFrameSourceBaseTest, DetectAsValueIntoLoop) {
@@ -196,6 +216,7 @@
     source_ = TestBackToBackBeginFrameSource::Create(now_src_.get(),
                                                      task_runner_.get());
     obs_ = make_scoped_ptr(new ::testing::StrictMock<MockBeginFrameObserver>());
+    EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
     source_->AddObserver(obs_.get());
   }
 
@@ -363,6 +384,7 @@
         now_src_.get(), task_runner_.get(),
         base::TimeDelta::FromMicroseconds(10000));
     obs_ = make_scoped_ptr(new MockBeginFrameObserver());
+    EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
     source_->AddObserver(obs_.get());
   }
 
@@ -524,6 +546,7 @@
   mux_->SetActiveSource(source1_);
 
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   mux_->AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
   EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
@@ -543,6 +566,7 @@
   mux_->AddSource(source2_);
 
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   mux_->AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
   EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -574,6 +598,7 @@
   mux_->AddSource(source1_);
 
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   mux_->AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
   EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
@@ -589,6 +614,7 @@
   mux_->AddSource(source1_);
 
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   mux_->AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 100, 200, 300);
   EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -604,6 +630,7 @@
   mux_->AddSource(source2_);
 
   MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
   mux_->AddObserver(&obs);
   EXPECT_BEGIN_FRAME_USED(obs, 400, 600, 300);
   EXPECT_BEGIN_FRAME_USED(obs, 700, 900, 300);
@@ -621,5 +648,71 @@
   SEND_BEGIN_FRAME_DROP(*source2_, 1100, 1400, 300);
 }
 
+TEST_F(BeginFrameSourceMultiplexerTest, BeginFrameSourcePaused) {
+  mux_->AddSource(source1_);
+  mux_->AddSource(source2_);
+  mux_->SetActiveSource(source1_);
+
+  MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  mux_->AddObserver(&obs);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  source1_->SetBeginFrameSourcePaused(true);
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  source1_->SetBeginFrameSourcePaused(false);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  mux_->SetActiveSource(source2_);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  source2_->SetBeginFrameSourcePaused(true);
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  source2_->SetBeginFrameSourcePaused(false);
+}
+
+TEST_F(BeginFrameSourceMultiplexerTest,
+       BeginFrameSourcePausedUpdateOnSourceTransition) {
+  mux_->AddSource(source1_);
+  mux_->AddSource(source2_);
+  source1_->SetBeginFrameSourcePaused(true);
+  source2_->SetBeginFrameSourcePaused(false);
+  mux_->SetActiveSource(source1_);
+
+  MockBeginFrameObserver obs;
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  mux_->AddObserver(&obs);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Paused to not paused.
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  mux_->SetActiveSource(source2_);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Not paused to paused.
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  mux_->SetActiveSource(source1_);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, false);
+  source1_->SetBeginFrameSourcePaused(false);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  // Not paused to not paused.
+  mux_->SetActiveSource(source2_);
+  Mock::VerifyAndClearExpectations(&obs);
+
+  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, true);
+  source2_->SetBeginFrameSourcePaused(true);
+  Mock::VerifyAndClearExpectations(&obs);
+  source1_->SetBeginFrameSourcePaused(true);
+
+  // Paused to paused.
+  mux_->SetActiveSource(source1_);
+  Mock::VerifyAndClearExpectations(&obs);
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 0bf8f25..f6dfccba 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -297,6 +297,15 @@
   PostBeginRetroFrameIfNeeded();
 }
 
+void Scheduler::OnBeginFrameSourcePausedChanged(bool paused) {
+  if (state_machine_.begin_frame_source_paused() == paused)
+    return;
+  TRACE_EVENT_INSTANT1("cc", "Scheduler::SetBeginFrameSourcePaused",
+                       TRACE_EVENT_SCOPE_THREAD, "paused", paused);
+  state_machine_.SetBeginFrameSourcePaused(paused);
+  ProcessScheduledActions();
+}
+
 // BeginFrame is the mechanism that tells us that now is a good time to start
 // making a frame. Usually this means that user input for the frame is complete.
 // If the scheduler is busy, we queue the BeginFrame to be handled later as
diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h
index e1a067c..61a0ea8f 100644
--- a/cc/scheduler/scheduler.h
+++ b/cc/scheduler/scheduler.h
@@ -66,6 +66,7 @@
   ~Scheduler() override;
 
   // BeginFrameObserverBase
+  void OnBeginFrameSourcePausedChanged(bool paused) override;
   bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override;
 
   void OnDrawForOutputSurface(bool resourceless_software_draw);
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 333b8edf..9bd4e16 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -42,6 +42,7 @@
       needs_begin_main_frame_(false),
       needs_one_begin_impl_frame_(false),
       visible_(false),
+      begin_frame_source_paused_(false),
       resourceless_draw_(false),
       can_draw_(false),
       has_pending_tree_(false),
@@ -235,6 +236,7 @@
   state->SetBoolean("needs_begin_main_frame", needs_begin_main_frame_);
   state->SetBoolean("needs_one_begin_impl_frame", needs_one_begin_impl_frame_);
   state->SetBoolean("visible", visible_);
+  state->SetBoolean("begin_frame_source_paused", begin_frame_source_paused_);
   state->SetBoolean("can_draw", can_draw_);
   state->SetBoolean("resourceless_draw", resourceless_draw_);
   state->SetBoolean("has_pending_tree", has_pending_tree_);
@@ -264,10 +266,10 @@
 }
 
 bool SchedulerStateMachine::PendingDrawsShouldBeAborted() const {
-  // Normally when |visible_| is false, pending activations will be forced and
-  // draws will be aborted. However, when the embedder is Android WebView,
-  // software draws could be scheduled by the Android OS at any time and draws
-  // should not be aborted in this case.
+  // Normally when |visible_| is false or |begin_frame_source_paused_| is true,
+  // pending activations will be forced and draws will be aborted. However,
+  // when the embedder is Android WebView, software draws could be scheduled by
+  // the Android OS at any time and draws should not be aborted in this case.
   bool is_output_surface_lost = (output_surface_state_ == OUTPUT_SURFACE_NONE);
   if (resourceless_draw_)
     return is_output_surface_lost || !can_draw_;
@@ -278,7 +280,8 @@
   // This should be a superset of PendingActivationsShouldBeForced() since
   // activation of the pending tree is blocked by drawing of the active tree and
   // the main thread might be blocked on activation of the most recent commit.
-  return is_output_surface_lost || !can_draw_ || !visible_;
+  return is_output_surface_lost || !can_draw_ || !visible_ ||
+         begin_frame_source_paused_;
 }
 
 bool SchedulerStateMachine::PendingActivationsShouldBeForced() const {
@@ -298,6 +301,11 @@
   if (!visible_)
     return true;
 
+  // Force pending activations when BeginFrameSource is paused to avoid
+  // deadlocking the main thread.
+  if (begin_frame_source_paused_)
+    return true;
+
   return false;
 }
 
@@ -389,6 +397,11 @@
   if (!visible_)
     return false;
 
+  // There are no BeginImplFrames while BeginFrameSource is paused,
+  // so should also stop BeginMainFrames.
+  if (begin_frame_source_paused_)
+    return false;
+
   // Do not make a new commits when it is deferred.
   if (defer_commits_)
     return false;
@@ -554,6 +567,7 @@
 void SchedulerStateMachine::WillSendBeginMainFrame() {
   DCHECK(!has_pending_tree_ || settings_.main_frame_before_activation_enabled);
   DCHECK(visible_);
+  DCHECK(!begin_frame_source_paused_);
   DCHECK(!send_begin_main_frame_funnel_);
   begin_main_frame_state_ = BEGIN_MAIN_FRAME_STATE_SENT;
   needs_begin_main_frame_ = false;
@@ -961,6 +975,10 @@
   wait_for_ready_to_draw_ = false;
 }
 
+void SchedulerStateMachine::SetBeginFrameSourcePaused(bool paused) {
+  begin_frame_source_paused_ = paused;
+}
+
 void SchedulerStateMachine::SetResourcelessSoftareDraw(bool resourceless_draw) {
   resourceless_draw_ = resourceless_draw;
 }
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 22c58d0e..7444432 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -174,6 +174,9 @@
   void SetVisible(bool visible);
   bool visible() const { return visible_; }
 
+  void SetBeginFrameSourcePaused(bool paused);
+  bool begin_frame_source_paused() const { return begin_frame_source_paused_; }
+
   // Indicates that a redraw is required, either due to the impl tree changing
   // or the screen being damaged and simply needing redisplay.
   void SetNeedsRedraw();
@@ -336,6 +339,7 @@
   bool needs_begin_main_frame_;
   bool needs_one_begin_impl_frame_;
   bool visible_;
+  bool begin_frame_source_paused_;
   bool resourceless_draw_;
   bool can_draw_;
   bool has_pending_tree_;
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 668f515..15e172e 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -1208,6 +1208,21 @@
   state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
   state.SetVisible(false);
   state.SetNeedsBeginMainFrame();
+  EXPECT_FALSE(state.CouldSendBeginMainFrame());
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
+TEST(SchedulerStateMachineTest, TestNoRequestCommitWhenBeginFrameSourcePaused) {
+  SchedulerSettings default_scheduler_settings;
+  StateMachine state(default_scheduler_settings);
+  state.SetVisible(true);
+  EXPECT_ACTION_UPDATE_STATE(
+      SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+  state.SetBeginFrameSourcePaused(true);
+  state.SetNeedsBeginMainFrame();
+  EXPECT_FALSE(state.CouldSendBeginMainFrame());
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 }
 
@@ -1754,6 +1769,32 @@
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
 }
 
+TEST(SchedulerStateMachineTest,
+     TestFinishCommitWhenCommitInProgressAndBeginFrameSourcePaused) {
+  SchedulerSettings default_scheduler_settings;
+  StateMachine state(default_scheduler_settings);
+  state.SetVisible(true);
+  EXPECT_ACTION_UPDATE_STATE(
+      SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+  state.CreateAndInitializeOutputSurfaceWithActivatedCommit();
+  state.SetBeginFrameSourcePaused(true);
+  state.SetBeginMainFrameState(
+      SchedulerStateMachine::BEGIN_MAIN_FRAME_STATE_SENT);
+  state.SetNeedsBeginMainFrame();
+
+  // After the commit completes, activation and draw happen immediately
+  // because we are not visible.
+  state.NotifyBeginMainFrameStarted();
+  state.NotifyReadyToCommit();
+  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_COMMIT);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_ACTIVATE_SYNC_TREE);
+  EXPECT_TRUE(state.active_tree_needs_first_draw());
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_DRAW_AND_SWAP_ABORT);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+}
+
 TEST(SchedulerStateMachineTest, TestInitialActionsWhenContextLost) {
   SchedulerSettings default_scheduler_settings;
   StateMachine state(default_scheduler_settings);
@@ -1797,6 +1838,10 @@
 
   state.SetCanDraw(true);
   state.SetVisible(true);
+  state.SetBeginFrameSourcePaused(true);
+  EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+
+  state.SetBeginFrameSourcePaused(false);
   EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
 }
 
@@ -1815,6 +1860,17 @@
 
   state.SetResourcelessSoftareDraw(true);
   EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+  state.SetVisible(true);
+
+  state.SetBeginFrameSourcePaused(true);
+  EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+
+  state.SetResourcelessSoftareDraw(false);
+  EXPECT_TRUE(state.PendingDrawsShouldBeAborted());
+
+  state.SetResourcelessSoftareDraw(true);
+  EXPECT_FALSE(state.PendingDrawsShouldBeAborted());
+  state.SetBeginFrameSourcePaused(false);
 
   state.SetVisible(false);
   state.DidLoseOutputSurface();
@@ -1980,6 +2036,27 @@
 
   state.SetVisible(false);
   EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
+  EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
+}
+
+TEST(SchedulerStateMachineTest,
+     TestTriggerDeadlineImmediatelyWhenBeginFrameSourcePaused) {
+  SchedulerSettings default_scheduler_settings;
+  StateMachine state(default_scheduler_settings);
+  SET_UP_STATE(state)
+
+  state.SetNeedsBeginMainFrame();
+
+  state.OnBeginImplFrame();
+  EXPECT_ACTION_UPDATE_STATE(
+      SchedulerStateMachine::ACTION_SEND_BEGIN_MAIN_FRAME);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+  EXPECT_FALSE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
+
+  state.SetBeginFrameSourcePaused(true);
+  EXPECT_ACTION_UPDATE_STATE(SchedulerStateMachine::ACTION_NONE);
+  EXPECT_TRUE(state.PendingActivationsShouldBeForced());
   EXPECT_TRUE(state.ShouldTriggerBeginImplFrameDeadlineImmediately());
 }
 
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 7f577a64..9a30c34 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -232,6 +232,11 @@
     return CallOnBeginFrame(args);
   }
 
+  void SetPaused(bool paused) {
+    DCHECK(observer_);
+    observer_->OnBeginFrameSourcePausedChanged(paused);
+  }
+
  private:
   FakeSchedulerClient* client_;
 };
@@ -2924,6 +2929,34 @@
   EXPECT_ACTION("SendBeginMainFrameNotExpectedSoon", client_, 2, 3);
 }
 
+TEST_F(SchedulerTest, ScheduledActionActivateAfterBeginFrameSourcePaused) {
+  scheduler_settings_.use_external_begin_frame_source = true;
+  SetUpScheduler(true);
+
+  // SetNeedsBeginMainFrame should begin the frame.
+  scheduler_->SetNeedsBeginMainFrame();
+  EXPECT_SINGLE_ACTION("SetNeedsBeginFrames(true)", client_);
+
+  client_->Reset();
+  EXPECT_SCOPED(AdvanceFrame());
+  EXPECT_ACTION("WillBeginImplFrame", client_, 0, 2);
+  EXPECT_ACTION("ScheduledActionSendBeginMainFrame", client_, 1, 2);
+  EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+
+  client_->Reset();
+  scheduler_->NotifyBeginMainFrameStarted(base::TimeTicks());
+  scheduler_->NotifyReadyToCommit();
+  EXPECT_SINGLE_ACTION("ScheduledActionCommit", client_);
+  EXPECT_TRUE(scheduler_->BeginImplFrameDeadlinePending());
+
+  client_->Reset();
+  fake_external_begin_frame_source_->SetPaused(true);
+  task_runner().RunPendingTasks();  // Run posted deadline.
+
+  // Sync tree should be forced to activate.
+  EXPECT_SINGLE_ACTION("ScheduledActionActivateSyncTree", client_);
+}
+
 // Tests to ensure frame sources can be successfully changed while drawing.
 TEST_F(SchedulerTest, SwitchFrameSourceToUnthrottled) {
   scheduler_settings_.use_external_begin_frame_source = true;
diff --git a/cc/surfaces/display_scheduler.cc b/cc/surfaces/display_scheduler.cc
index feb84ea..f87d4fb09 100644
--- a/cc/surfaces/display_scheduler.cc
+++ b/cc/surfaces/display_scheduler.cc
@@ -152,6 +152,12 @@
   return true;
 }
 
+void DisplayScheduler::OnBeginFrameSourcePausedChanged(bool paused) {
+  // BeginFrameSources used with DisplayScheduler do not make use of this
+  // feature.
+  NOTIMPLEMENTED();
+}
+
 base::TimeTicks DisplayScheduler::DesiredBeginFrameDeadlineTime() {
   if (output_surface_lost_) {
     TRACE_EVENT_INSTANT0("cc", "Lost output surface", TRACE_EVENT_SCOPE_THREAD);
diff --git a/cc/surfaces/display_scheduler.h b/cc/surfaces/display_scheduler.h
index 838af40..cb8a365 100644
--- a/cc/surfaces/display_scheduler.h
+++ b/cc/surfaces/display_scheduler.h
@@ -48,6 +48,7 @@
 
   // BeginFrameObserverBase implementation
   bool OnBeginFrameDerivedImpl(const BeginFrameArgs& args) override;
+  void OnBeginFrameSourcePausedChanged(bool paused) override;
 
   BeginFrameSource* begin_frame_source_for_children() {
     return begin_frame_source_for_children_.get();
diff --git a/cc/test/begin_frame_source_test.h b/cc/test/begin_frame_source_test.h
index 1de8b99..351e090c 100644
--- a/cc/test/begin_frame_source_test.h
+++ b/cc/test/begin_frame_source_test.h
@@ -26,6 +26,11 @@
       .InSequence((obs).sequence)                                         \
       .WillOnce(::testing::SaveArg<0>(&((obs).last_begin_frame_args)))
 
+#define EXPECT_BEGIN_FRAME_SOURCE_PAUSED(obs, paused)         \
+  EXPECT_CALL((obs), OnBeginFrameSourcePausedChanged(paused)) \
+      .Times(1)                                               \
+      .InSequence((obs).sequence)
+
 // Macros to send BeginFrameArgs on a FakeBeginFrameSink (and verify resulting
 // observer behaviour).
 #define SEND_BEGIN_FRAME(args_equal_to, source, frame_time, deadline, \
@@ -53,6 +58,7 @@
  public:
   MOCK_METHOD1(OnBeginFrame, void(const BeginFrameArgs&));
   MOCK_CONST_METHOD0(LastUsedBeginFrameArgs, const BeginFrameArgs());
+  MOCK_METHOD1(OnBeginFrameSourcePausedChanged, void(bool));
 
   virtual void AsValueInto(base::trace_event::TracedValue* dict) const;
 
diff --git a/cc/test/scheduler_test_common.h b/cc/test/scheduler_test_common.h
index e561d82..cf73a25e 100644
--- a/cc/test/scheduler_test_common.h
+++ b/cc/test/scheduler_test_common.h
@@ -111,6 +111,8 @@
   void DidFinishFrame(size_t remaining_frames) override;
   void AsValueInto(base::trace_event::TracedValue* dict) const override;
 
+  using BeginFrameSourceBase::SetBeginFrameSourcePaused;
+
  private:
   bool remaining_frames_;
 
diff --git a/content/browser/android/in_process/synchronous_compositor_impl.cc b/content/browser/android/in_process/synchronous_compositor_impl.cc
index e5c789b..e179137 100644
--- a/content/browser/android/in_process/synchronous_compositor_impl.cc
+++ b/content/browser/android/in_process/synchronous_compositor_impl.cc
@@ -130,6 +130,7 @@
 
   output_surface_->SetSyncClient(this);
   begin_frame_source_->SetClient(this);
+  begin_frame_source_->SetBeginFrameSourcePaused(!is_active_);
 }
 
 void SynchronousCompositorImpl::DidDestroyRendererObjects() {
@@ -244,8 +245,13 @@
 void SynchronousCompositorImpl::SetIsActive(bool is_active) {
   TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active",
                is_active);
+  if (is_active_ == is_active)
+    return;
+
   is_active_ = is_active;
   UpdateNeedsBeginFrames();
+  if (begin_frame_source_)
+    begin_frame_source_->SetBeginFrameSourcePaused(!is_active_);
 }
 
 void SynchronousCompositorImpl::OnComputeScroll(
diff --git a/content/browser/android/synchronous_compositor_host.cc b/content/browser/android/synchronous_compositor_host.cc
index 8b6e798..83dd85fea 100644
--- a/content/browser/android/synchronous_compositor_host.cc
+++ b/content/browser/android/synchronous_compositor_host.cc
@@ -48,6 +48,8 @@
 
 SynchronousCompositorHost::~SynchronousCompositorHost() {
   client_->DidDestroyCompositor(this);
+  if (weak_ptr_factory_.HasWeakPtrs())
+    UpdateStateTask();
 }
 
 bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) {
@@ -260,8 +262,11 @@
 }
 
 void SynchronousCompositorHost::SetIsActive(bool is_active) {
+  if (is_active_ == is_active)
+    return;
   is_active_ = is_active;
   UpdateNeedsBeginFrames();
+  SendAsyncCompositorStateIfNeeded();
 }
 
 void SynchronousCompositorHost::OnComputeScroll(
@@ -331,6 +336,7 @@
     params->update_root_scroll_offset = root_scroll_offset_updated_by_browser_;
     root_scroll_offset_updated_by_browser_ = false;
   }
+  params->begin_frame_source_paused = !is_active_;
 
   weak_ptr_factory_.InvalidateWeakPtrs();
 }
diff --git a/content/common/android/sync_compositor_messages.cc b/content/common/android/sync_compositor_messages.cc
index c736f7f6..f44415f3 100644
--- a/content/common/android/sync_compositor_messages.cc
+++ b/content/common/android/sync_compositor_messages.cc
@@ -7,7 +7,9 @@
 namespace content {
 
 SyncCompositorCommonBrowserParams::SyncCompositorCommonBrowserParams()
-    : bytes_limit(0u), update_root_scroll_offset(false) {}
+    : bytes_limit(0u),
+      update_root_scroll_offset(false),
+      begin_frame_source_paused(false) {}
 
 SyncCompositorCommonBrowserParams::~SyncCompositorCommonBrowserParams() {}
 
diff --git a/content/common/android/sync_compositor_messages.h b/content/common/android/sync_compositor_messages.h
index b2dccda..a9144076 100644
--- a/content/common/android/sync_compositor_messages.h
+++ b/content/common/android/sync_compositor_messages.h
@@ -29,6 +29,7 @@
   cc::CompositorFrameAck ack;
   gfx::ScrollOffset root_scroll_offset;
   bool update_root_scroll_offset;
+  bool begin_frame_source_paused;
 };
 
 struct SyncCompositorDemandDrawHwParams {
@@ -98,6 +99,7 @@
   IPC_STRUCT_TRAITS_MEMBER(ack)
   IPC_STRUCT_TRAITS_MEMBER(root_scroll_offset)
   IPC_STRUCT_TRAITS_MEMBER(update_root_scroll_offset)
+  IPC_STRUCT_TRAITS_MEMBER(begin_frame_source_paused)
 IPC_STRUCT_TRAITS_END()
 
 IPC_STRUCT_TRAITS_BEGIN(content::SyncCompositorDemandDrawHwParams)
diff --git a/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc b/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
index 08ea729..c5337d0 100644
--- a/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
+++ b/content/renderer/android/synchronous_compositor_external_begin_frame_source.cc
@@ -49,6 +49,9 @@
 
   if (client_)
     client_->OnNeedsBeginFramesChange(needs_begin_frames_);
+
+  // State without client is paused, and default client state is not paused.
+  SetBeginFrameSourcePaused(!client_);
 }
 
 void SynchronousCompositorExternalBeginFrameSource::OnNeedsBeginFramesChange(
diff --git a/content/renderer/android/synchronous_compositor_external_begin_frame_source.h b/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
index cc3e9e53..83fed03b 100644
--- a/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
+++ b/content/renderer/android/synchronous_compositor_external_begin_frame_source.h
@@ -33,8 +33,8 @@
   ~SynchronousCompositorExternalBeginFrameSource() override;
 
   void BeginFrame(const cc::BeginFrameArgs& args);
-  void SetClient(
-      SynchronousCompositorExternalBeginFrameSourceClient* client);
+  void SetClient(SynchronousCompositorExternalBeginFrameSourceClient* client);
+  using cc::BeginFrameSourceBase::SetBeginFrameSourcePaused;
 
   // cc::BeginFrameSourceBase implementation.
   void OnNeedsBeginFramesChange(bool needs_begin_frames) override;
diff --git a/content/renderer/android/synchronous_compositor_proxy.cc b/content/renderer/android/synchronous_compositor_proxy.cc
index 6e4a899a..e8fc5f3 100644
--- a/content/renderer/android/synchronous_compositor_proxy.cc
+++ b/content/renderer/android/synchronous_compositor_proxy.cc
@@ -390,6 +390,8 @@
     input_handler_proxy_->SynchronouslySetRootScrollOffset(
         total_scroll_offset_);
   }
+  begin_frame_source_->SetBeginFrameSourcePaused(
+      common_params.begin_frame_source_paused);
   if (!common_params.ack.resources.empty()) {
     output_surface_->ReturnResources(common_params.ack);
   }