Revert of Move throttling of background timers into the renderer scheduler (patchset #16 id:300001 of https://codereview.chromium.org/1441073006/ )

Reason for revert:
Oops I'd meant to upload one final patchset before committing.

Original issue's description:
> Move throttling of background timers into the renderer scheduler
>
> Not only does this simplify the code, it's more efficent since
> previously setting the timer alignment resulted in mass cancellation
> and reposting of timers.
>
> BUG=510398, 546953, 560402
>
> Committed: https://crrev.com/ec5adec0a9879a31866e98c65ddc7b506b9f49c3
> Cr-Commit-Position: refs/heads/master@{#361971}

[email protected],[email protected]
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=510398, 546953, 560402

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

Cr-Commit-Position: refs/heads/master@{#361972}
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 2b9f6c9..14194f6 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -619,7 +619,6 @@
       'scheduler/renderer/renderer_scheduler_impl_unittest.cc',
       'scheduler/renderer/render_widget_signals_unittest.cpp',
       'scheduler/renderer/task_cost_estimator_unittest.cc',
-      'scheduler/renderer/throttling_helper_unittest.cc',
       'scheduler/renderer/user_model_unittest.cc',
       'scheduler/renderer/web_view_scheduler_impl_unittest.cc',
       'scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc',
diff --git a/components/scheduler/BUILD.gn b/components/scheduler/BUILD.gn
index c1070f1..5031a61 100644
--- a/components/scheduler/BUILD.gn
+++ b/components/scheduler/BUILD.gn
@@ -62,7 +62,6 @@
     "renderer/render_widget_signals_unittest.cpp",
     "renderer/renderer_scheduler_impl_unittest.cc",
     "renderer/task_cost_estimator_unittest.cc",
-    "renderer/throttling_helper_unittest.cc",
     "renderer/user_model_unittest.cc",
     "renderer/web_view_scheduler_impl_unittest.cc",
     "renderer/webthread_impl_for_renderer_scheduler_unittest.cc",
diff --git a/components/scheduler/base/real_time_domain.cc b/components/scheduler/base/real_time_domain.cc
index d894eca..c3b4505 100644
--- a/components/scheduler/base/real_time_domain.cc
+++ b/components/scheduler/base/real_time_domain.cc
@@ -10,7 +10,7 @@
 
 namespace scheduler {
 
-RealTimeDomain::RealTimeDomain() : TimeDomain(nullptr), weak_factory_(this) {}
+RealTimeDomain::RealTimeDomain() : weak_factory_(this) {}
 
 RealTimeDomain::~RealTimeDomain() {}
 
diff --git a/components/scheduler/base/real_time_domain.h b/components/scheduler/base/real_time_domain.h
index 82525637..f382fad 100644
--- a/components/scheduler/base/real_time_domain.h
+++ b/components/scheduler/base/real_time_domain.h
@@ -17,7 +17,6 @@
 class SCHEDULER_EXPORT RealTimeDomain : public TimeDomain {
  public:
   RealTimeDomain();
-  ~RealTimeDomain() override;
 
   // TimeDomain implementation:
   LazyNow CreateLazyNow() override;
@@ -41,6 +40,8 @@
   base::Closure do_work_closure_;
   base::WeakPtrFactory<RealTimeDomain> weak_factory_;
 
+  ~RealTimeDomain() override;
+
   DISALLOW_COPY_AND_ASSIGN(RealTimeDomain);
 };
 
diff --git a/components/scheduler/base/task_queue.cc b/components/scheduler/base/task_queue.cc
index 24aadb7..85786055 100644
--- a/components/scheduler/base/task_queue.cc
+++ b/components/scheduler/base/task_queue.cc
@@ -6,9 +6,8 @@
 
 namespace scheduler {
 
-bool TaskQueue::HasPendingImmediateTask() const {
-  QueueState state = GetQueueState();
-  return state == QueueState::NEEDS_PUMPING || state == QueueState::HAS_WORK;
+bool TaskQueue::IsQueueEmpty() const {
+  return GetQueueState() == QueueState::EMPTY;
 }
 
 }  // namespace scheduler
diff --git a/components/scheduler/base/task_queue.h b/components/scheduler/base/task_queue.h
index fbbbc25..393cc50 100644
--- a/components/scheduler/base/task_queue.h
+++ b/components/scheduler/base/task_queue.h
@@ -20,6 +20,12 @@
   // the TaskQueueManager's reference to it will be released soon.
   virtual void UnregisterTaskQueue() = 0;
 
+  // Post a delayed task at an absolute desired run time instead of a time
+  // delta from the current time.
+  virtual bool PostDelayedTaskAt(const tracked_objects::Location& from_here,
+                                 const base::Closure& task,
+                                 base::TimeTicks desired_run_time) = 0;
+
   enum QueuePriority {
     // Queues with control priority will run before any other queue, and will
     // explicitly starve other queues. Typically this should only be used for
@@ -85,9 +91,6 @@
     // A queue in the HAS_WORK state has tasks in the work task queue which
     // are runnable.
     HAS_WORK,
-    // The work and incomming queues are empty but there is delayed work
-    // scheduled.
-    NO_IMMEDIATE_WORK,
   };
 
   // Options for constructing a TaskQueue. Once set the |name|,
@@ -142,11 +145,10 @@
   virtual bool IsQueueEnabled() const = 0;
 
   // Returns true if there no tasks in either the work or incoming task queue.
-  // This method ignores delayed tasks that are scheduled to run in the future.
   // Note that this function involves taking a lock, so calling it has some
   // overhead. NOTE this must be called on the thread this TaskQueue was created
   // by.
-  virtual bool HasPendingImmediateTask() const;
+  virtual bool IsQueueEmpty() const;
 
   // Returns the QueueState. Note that this function involves taking a lock, so
   // calling it has some overhead.
@@ -181,7 +183,7 @@
 
   // Removes the task queue from the previous TimeDomain and adds it to
   // |domain|.  This is a moderately expensive operation.
-  virtual void SetTimeDomain(TimeDomain* domain) = 0;
+  virtual void SetTimeDomain(const scoped_refptr<TimeDomain>& domain) = 0;
 
  protected:
   ~TaskQueue() override {}
diff --git a/components/scheduler/base/task_queue_impl.cc b/components/scheduler/base/task_queue_impl.cc
index 14b6c314..0ad965b 100644
--- a/components/scheduler/base/task_queue_impl.cc
+++ b/components/scheduler/base/task_queue_impl.cc
@@ -13,7 +13,7 @@
 
 TaskQueueImpl::TaskQueueImpl(
     TaskQueueManager* task_queue_manager,
-    TimeDomain* time_domain,
+    const scoped_refptr<TimeDomain>& time_domain,
     const Spec& spec,
     const char* disabled_by_default_tracing_category,
     const char* disabled_by_default_verbose_tracing_category)
@@ -28,15 +28,10 @@
       wakeup_policy_(spec.wakeup_policy),
       should_monitor_quiescence_(spec.should_monitor_quiescence),
       should_notify_observers_(spec.should_notify_observers) {
-  DCHECK(time_domain);
-  time_domain->RegisterQueue(this);
+  DCHECK(time_domain.get());
 }
 
-TaskQueueImpl::~TaskQueueImpl() {
-  base::AutoLock lock(any_thread_lock_);
-  if (any_thread().time_domain)
-    any_thread().time_domain->UnregisterQueue(this);
-}
+TaskQueueImpl::~TaskQueueImpl() {}
 
 TaskQueueImpl::Task::Task()
     : PendingTask(tracked_objects::Location(),
@@ -62,9 +57,10 @@
   sequence_num = sequence_number;
 }
 
-TaskQueueImpl::AnyThread::AnyThread(TaskQueueManager* task_queue_manager,
-                                    PumpPolicy pump_policy,
-                                    TimeDomain* time_domain)
+TaskQueueImpl::AnyThread::AnyThread(
+    TaskQueueManager* task_queue_manager,
+    PumpPolicy pump_policy,
+    const scoped_refptr<TimeDomain>& time_domain)
     : task_queue_manager(task_queue_manager),
       pump_policy(pump_policy),
       time_domain(time_domain) {}
@@ -82,8 +78,7 @@
   base::AutoLock lock(any_thread_lock_);
   if (!any_thread().task_queue_manager)
     return;
-  if (any_thread().time_domain)
-    any_thread().time_domain->UnregisterQueue(this);
+  any_thread().time_domain->UnregisterQueue(this);
   any_thread().time_domain = nullptr;
   any_thread().task_queue_manager->UnregisterTaskQueue(this);
 
@@ -112,6 +107,18 @@
   return PostDelayedTaskImpl(from_here, task, delay, TaskType::NON_NESTABLE);
 }
 
+bool TaskQueueImpl::PostDelayedTaskAt(
+    const tracked_objects::Location& from_here,
+    const base::Closure& task,
+    base::TimeTicks desired_run_time) {
+  base::AutoLock lock(any_thread_lock_);
+  if (!any_thread().task_queue_manager)
+    return false;
+  LazyNow lazy_now(any_thread().time_domain->CreateLazyNow());
+  return PostDelayedTaskLocked(&lazy_now, from_here, task, desired_run_time,
+                               TaskType::NORMAL);
+}
+
 bool TaskQueueImpl::PostDelayedTaskImpl(
     const tracked_objects::Location& from_here,
     const base::Closure& task,
@@ -169,8 +176,9 @@
   return true;
 }
 
-void TaskQueueImpl::ScheduleDelayedWorkTask(TimeDomain* time_domain,
-                                            base::TimeTicks desired_run_time) {
+void TaskQueueImpl::ScheduleDelayedWorkTask(
+    const scoped_refptr<TimeDomain> time_domain,
+    base::TimeTicks desired_run_time) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
   LazyNow lazy_now(time_domain->CreateLazyNow());
   time_domain->ScheduleDelayedWork(this, desired_run_time, &lazy_now);
@@ -211,15 +219,13 @@
   {
     base::AutoLock lock(any_thread_lock_);
     if (any_thread().incoming_queue.empty()) {
-      if (any_thread().delayed_task_queue.empty())
-        return QueueState::EMPTY;
-      else
-        return QueueState::NO_IMMEDIATE_WORK;
+      return QueueState::EMPTY;
     } else {
       return QueueState::NEEDS_PUMPING;
     }
   }
 }
+
 bool TaskQueueImpl::TaskIsOlderThanQueuedTasks(const Task* task) {
   // A null task is passed when UpdateQueue is called before any task is run.
   // In this case we don't want to pump an after_wakeup queue, so return true
@@ -499,14 +505,14 @@
                     DidProcessTask(pending_task));
 }
 
-void TaskQueueImpl::SetTimeDomain(TimeDomain* time_domain) {
+void TaskQueueImpl::SetTimeDomain(
+    const scoped_refptr<TimeDomain>& time_domain) {
   base::AutoLock lock(any_thread_lock_);
   DCHECK(main_thread_checker_.CalledOnValidThread());
   if (time_domain == any_thread().time_domain)
     return;
 
-  if (time_domain)
-    any_thread().time_domain->MigrateQueue(this, time_domain);
+  any_thread().time_domain->MigrateQueue(this, time_domain.get());
   any_thread().time_domain = time_domain;
 }
 
diff --git a/components/scheduler/base/task_queue_impl.h b/components/scheduler/base/task_queue_impl.h
index aa846394..1f130e124 100644
--- a/components/scheduler/base/task_queue_impl.h
+++ b/components/scheduler/base/task_queue_impl.h
@@ -23,7 +23,7 @@
 class SCHEDULER_EXPORT TaskQueueImpl final : public TaskQueue {
  public:
   TaskQueueImpl(TaskQueueManager* task_queue_manager,
-                TimeDomain* time_domain,
+                const scoped_refptr<TimeDomain>& time_domain,
                 const Spec& spec,
                 const char* disabled_by_default_tracing_category,
                 const char* disabled_by_default_verbose_tracing_category);
@@ -70,6 +70,9 @@
   bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
                                   const base::Closure& task,
                                   base::TimeDelta delay) override;
+  bool PostDelayedTaskAt(const tracked_objects::Location& from_here,
+                         const base::Closure& task,
+                         base::TimeTicks desired_run_time) override;
 
   bool IsQueueEnabled() const override;
   QueueState GetQueueState() const override;
@@ -79,7 +82,7 @@
   void AddTaskObserver(base::MessageLoop::TaskObserver* task_observer) override;
   void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) override;
-  void SetTimeDomain(TimeDomain* time_domain) override;
+  void SetTimeDomain(const scoped_refptr<TimeDomain>& time_domain) override;
 
   void UpdateWorkQueue(LazyNow* lazy_now,
                        bool should_trigger_wakeup,
@@ -151,7 +154,7 @@
   struct AnyThread {
     AnyThread(TaskQueueManager* task_queue_manager,
               PumpPolicy pump_policy,
-              TimeDomain* time_domain);
+              const scoped_refptr<TimeDomain>& time_domain);
     ~AnyThread();
 
     // TaskQueueManager is maintained in two copies: inside AnyThread and inside
@@ -162,7 +165,7 @@
     std::queue<Task> incoming_queue;
     PumpPolicy pump_policy;
     std::priority_queue<Task> delayed_task_queue;
-    TimeDomain* time_domain;
+    scoped_refptr<TimeDomain> time_domain;
   };
 
   struct MainThreadOnly {
@@ -189,7 +192,7 @@
                              const base::Closure& task,
                              base::TimeTicks desired_run_time,
                              TaskType task_type);
-  void ScheduleDelayedWorkTask(TimeDomain* time_domain,
+  void ScheduleDelayedWorkTask(const scoped_refptr<TimeDomain> time_domain,
                                base::TimeTicks desired_run_time);
 
   // Enqueues any delayed tasks which should be run now on the incoming_queue_.
diff --git a/components/scheduler/base/task_queue_manager.cc b/components/scheduler/base/task_queue_manager.cc
index ec478db..b9df8756 100644
--- a/components/scheduler/base/task_queue_manager.cc
+++ b/components/scheduler/base/task_queue_manager.cc
@@ -21,8 +21,7 @@
     const char* tracing_category,
     const char* disabled_by_default_tracing_category,
     const char* disabled_by_default_verbose_tracing_category)
-    : real_time_domain_(new RealTimeDomain()),
-      delegate_(delegate),
+    : delegate_(delegate),
       task_was_run_on_quiescence_monitored_queue_(false),
       pending_dowork_count_(0),
       work_batch_size_(1),
@@ -45,7 +44,8 @@
       base::Bind(&TaskQueueManager::DoWork, weak_factory_.GetWeakPtr(), false);
 
   // TODO(alexclarke): Change this to be a parameter that's passed in.
-  RegisterTimeDomain(real_time_domain_.get());
+  real_time_domain_ = make_scoped_refptr(new RealTimeDomain());
+  RegisterTimeDomain(real_time_domain_);
 }
 
 TaskQueueManager::~TaskQueueManager() {
@@ -58,13 +58,15 @@
   selector_.SetTaskQueueSelectorObserver(nullptr);
 }
 
-void TaskQueueManager::RegisterTimeDomain(TimeDomain* time_domain) {
+void TaskQueueManager::RegisterTimeDomain(
+    const scoped_refptr<TimeDomain>& time_domain) {
   time_domains_.insert(time_domain);
   time_domain->OnRegisterWithTaskQueueManager(delegate_.get(),
                                               do_work_closure_);
 }
 
-void TaskQueueManager::UnregisterTimeDomain(TimeDomain* time_domain) {
+void TaskQueueManager::UnregisterTimeDomain(
+    const scoped_refptr<TimeDomain>& time_domain) {
   time_domains_.erase(time_domain);
 }
 
@@ -75,7 +77,8 @@
   DCHECK(main_thread_checker_.CalledOnValidThread());
   TimeDomain* time_domain =
       spec.time_domain ? spec.time_domain : real_time_domain_.get();
-  DCHECK(time_domains_.find(time_domain) != time_domains_.end());
+  DCHECK(time_domains_.find(make_scoped_refptr(time_domain)) !=
+         time_domains_.end());
   scoped_refptr<internal::TaskQueueImpl> queue(
       make_scoped_refptr(new internal::TaskQueueImpl(
           this, time_domain, spec, disabled_by_default_tracing_category_,
@@ -112,7 +115,7 @@
   TRACE_EVENT0(disabled_by_default_tracing_category_,
                "TaskQueueManager::UpdateWorkQueues");
 
-  for (TimeDomain* time_domain : time_domains_) {
+  for (const scoped_refptr<TimeDomain>& time_domain : time_domains_) {
     time_domain->UpdateWorkQueues(should_trigger_wakeup, previous_task);
   }
 }
@@ -186,7 +189,7 @@
 
 bool TaskQueueManager::TryAdvanceTimeDomains() {
   bool can_advance = false;
-  for (TimeDomain* time_domain : time_domains_) {
+  for (const scoped_refptr<TimeDomain>& time_domain : time_domains_) {
     can_advance |= time_domain->MaybeAdvanceTime();
   }
   return can_advance;
diff --git a/components/scheduler/base/task_queue_manager.h b/components/scheduler/base/task_queue_manager.h
index f7090bd..f6af854 100644
--- a/components/scheduler/base/task_queue_manager.h
+++ b/components/scheduler/base/task_queue_manager.h
@@ -100,10 +100,12 @@
   const scoped_refptr<TaskQueueManagerDelegate>& delegate() const;
 
   // Time domains must be registered for the task queues to get updated.
-  void RegisterTimeDomain(TimeDomain* time_domain);
-  void UnregisterTimeDomain(TimeDomain* time_domain);
+  void RegisterTimeDomain(const scoped_refptr<TimeDomain>& time_domain);
+  void UnregisterTimeDomain(const scoped_refptr<TimeDomain>& time_domain);
 
-  RealTimeDomain* real_time_domain() const { return real_time_domain_.get(); }
+  const scoped_refptr<RealTimeDomain>& real_time_domain() const {
+    return real_time_domain_;
+  }
 
  private:
   friend class LazyNow;
@@ -174,8 +176,8 @@
   AsValueWithSelectorResult(bool should_run,
                             internal::TaskQueueImpl* selected_queue) const;
 
-  std::set<TimeDomain*> time_domains_;
-  scoped_ptr<RealTimeDomain> real_time_domain_;
+  std::set<scoped_refptr<TimeDomain>> time_domains_;
+  scoped_refptr<RealTimeDomain> real_time_domain_;
 
   std::set<scoped_refptr<internal::TaskQueueImpl>> queues_;
 
@@ -210,6 +212,7 @@
 
   Observer* observer_;  // NOT OWNED
   scoped_refptr<DeletionSentinel> deletion_sentinel_;
+  scoped_refptr<TimeDomain> time_domain_;
   base::WeakPtrFactory<TaskQueueManager> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TaskQueueManager);
diff --git a/components/scheduler/base/task_queue_manager_unittest.cc b/components/scheduler/base/task_queue_manager_unittest.cc
index f1d9dc4..251ac56 100644
--- a/components/scheduler/base/task_queue_manager_unittest.cc
+++ b/components/scheduler/base/task_queue_manager_unittest.cc
@@ -216,12 +216,12 @@
   Initialize(1u);
 
   std::vector<int> run_order;
-  EXPECT_FALSE(runners_[0]->HasPendingImmediateTask());
+  EXPECT_TRUE(runners_[0]->IsQueueEmpty());
   runners_[0]->PostTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order));
-  EXPECT_TRUE(runners_[0]->HasPendingImmediateTask());
+  EXPECT_FALSE(runners_[0]->IsQueueEmpty());
 
   test_task_runner_->RunUntilIdle();
-  EXPECT_FALSE(runners_[0]->HasPendingImmediateTask());
+  EXPECT_TRUE(runners_[0]->IsQueueEmpty());
 }
 
 TEST_F(TaskQueueManagerTest, DelayedTaskPosting) {
@@ -232,8 +232,7 @@
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order),
                                delay);
   EXPECT_EQ(delay, test_task_runner_->DelayToNextTaskTime());
-  EXPECT_EQ(TaskQueue::QueueState::NO_IMMEDIATE_WORK,
-            runners_[0]->GetQueueState());
+  EXPECT_TRUE(runners_[0]->IsQueueEmpty());
   EXPECT_TRUE(run_order.empty());
 
   // The task doesn't run before the delay has completed.
@@ -243,7 +242,6 @@
   // After the delay has completed, the task runs normally.
   test_task_runner_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
   EXPECT_THAT(run_order, ElementsAre(1));
-  EXPECT_EQ(TaskQueue::QueueState::EMPTY, runners_[0]->GetQueueState());
 }
 
 bool MessageLoopTaskCounter(size_t* count) {
@@ -377,7 +375,7 @@
   EXPECT_FALSE(test_task_runner_->HasPendingTasks());
 
   // However polling still works.
-  EXPECT_TRUE(runners_[0]->HasPendingImmediateTask());
+  EXPECT_FALSE(runners_[0]->IsQueueEmpty());
 
   // After pumping the task runs normally.
   runners_[0]->PumpQueue();
@@ -1005,32 +1003,32 @@
   EXPECT_TRUE(manager_->GetAndClearSystemIsQuiescentBit());
 }
 
-TEST_F(TaskQueueManagerTest, HasPendingImmediateTask) {
+TEST_F(TaskQueueManagerTest, IsQueueEmpty) {
   Initialize(2u);
   internal::TaskQueueImpl* queue0 = runners_[0].get();
   internal::TaskQueueImpl* queue1 = runners_[1].get();
   queue0->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO);
   queue1->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL);
 
-  EXPECT_FALSE(queue0->HasPendingImmediateTask());
-  EXPECT_FALSE(queue1->HasPendingImmediateTask());
+  EXPECT_TRUE(queue0->IsQueueEmpty());
+  EXPECT_TRUE(queue1->IsQueueEmpty());
 
   queue0->PostTask(FROM_HERE, base::Bind(NullTask));
   queue1->PostTask(FROM_HERE, base::Bind(NullTask));
-  EXPECT_TRUE(queue0->HasPendingImmediateTask());
-  EXPECT_TRUE(queue1->HasPendingImmediateTask());
+  EXPECT_FALSE(queue0->IsQueueEmpty());
+  EXPECT_FALSE(queue1->IsQueueEmpty());
 
   test_task_runner_->RunUntilIdle();
-  EXPECT_FALSE(queue0->HasPendingImmediateTask());
-  EXPECT_TRUE(queue1->HasPendingImmediateTask());
+  EXPECT_TRUE(queue0->IsQueueEmpty());
+  EXPECT_FALSE(queue1->IsQueueEmpty());
 
   queue1->PumpQueue();
-  EXPECT_FALSE(queue0->HasPendingImmediateTask());
-  EXPECT_TRUE(queue1->HasPendingImmediateTask());
+  EXPECT_TRUE(queue0->IsQueueEmpty());
+  EXPECT_FALSE(queue1->IsQueueEmpty());
 
   test_task_runner_->RunUntilIdle();
-  EXPECT_FALSE(queue0->HasPendingImmediateTask());
-  EXPECT_FALSE(queue1->HasPendingImmediateTask());
+  EXPECT_TRUE(queue0->IsQueueEmpty());
+  EXPECT_TRUE(queue1->IsQueueEmpty());
 }
 
 TEST_F(TaskQueueManagerTest, GetQueueState) {
@@ -1104,6 +1102,33 @@
   EXPECT_THAT(run_order, ElementsAre(2, 1));
 }
 
+TEST_F(TaskQueueManagerTest, DelayedTaskWithAbsoluteRunTime) {
+  Initialize(1u);
+
+  // One task in the past, two with the exact same run time and one in the
+  // future.
+  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks runTime1 = now_src_->NowTicks() - delay;
+  base::TimeTicks runTime2 = now_src_->NowTicks();
+  base::TimeTicks runTime3 = now_src_->NowTicks();
+  base::TimeTicks runTime4 = now_src_->NowTicks() + delay;
+
+  std::vector<int> run_order;
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&TestTask, 1, &run_order), runTime1);
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&TestTask, 2, &run_order), runTime2);
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&TestTask, 3, &run_order), runTime3);
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&TestTask, 4, &run_order), runTime4);
+
+  now_src_->Advance(2 * delay);
+  test_task_runner_->RunUntilIdle();
+
+  EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4));
+}
+
 void CheckIsNested(bool* is_nested) {
   *is_nested = base::MessageLoop::current()->IsNested();
 }
@@ -1298,6 +1323,20 @@
   manager_->SetObserver(nullptr);
 }
 
+TEST_F(TaskQueueManagerTest, ScheduleDelayedWorkIsNotReEntrant) {
+  Initialize(1u);
+
+  // Post two tasks into the past. The second one used to trigger a deadlock
+  // because it tried to re-entrantly wake the first task in the same queue.
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&NullTask),
+      base::TimeTicks() + base::TimeDelta::FromMicroseconds(100));
+  runners_[0]->PostDelayedTaskAt(
+      FROM_HERE, base::Bind(&NullTask),
+      base::TimeTicks() + base::TimeDelta::FromMicroseconds(200));
+  test_task_runner_->RunUntilIdle();
+}
+
 void HasOneRefTask(std::vector<bool>* log, internal::TaskQueueImpl* tq) {
   log->push_back(tq->HasOneRef());
 }
@@ -1348,14 +1387,12 @@
   Initialize(2u);
 
   base::TimeTicks start_time = manager_->delegate()->NowTicks();
-  scoped_ptr<VirtualTimeDomain> domain_a(
-      new VirtualTimeDomain(nullptr, start_time));
-  scoped_ptr<VirtualTimeDomain> domain_b(
-      new VirtualTimeDomain(nullptr, start_time));
-  manager_->RegisterTimeDomain(domain_a.get());
-  manager_->RegisterTimeDomain(domain_b.get());
-  runners_[0]->SetTimeDomain(domain_a.get());
-  runners_[1]->SetTimeDomain(domain_b.get());
+  scoped_refptr<VirtualTimeDomain> domain_a(new VirtualTimeDomain(start_time));
+  scoped_refptr<VirtualTimeDomain> domain_b(new VirtualTimeDomain(start_time));
+  manager_->RegisterTimeDomain(domain_a);
+  manager_->RegisterTimeDomain(domain_b);
+  runners_[0]->SetTimeDomain(domain_a);
+  runners_[1]->SetTimeDomain(domain_b);
 
   std::vector<int> run_order;
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order),
@@ -1382,18 +1419,17 @@
   test_task_runner_->RunUntilIdle();
   EXPECT_THAT(run_order, ElementsAre(4, 5, 6, 1, 2, 3));
 
-  manager_->UnregisterTimeDomain(domain_a.get());
-  manager_->UnregisterTimeDomain(domain_b.get());
+  manager_->UnregisterTimeDomain(domain_a);
+  manager_->UnregisterTimeDomain(domain_b);
 }
 
 TEST_F(TaskQueueManagerTest, TimeDomainMigration) {
   Initialize(1u);
 
   base::TimeTicks start_time = manager_->delegate()->NowTicks();
-  scoped_ptr<VirtualTimeDomain> domain_a(
-      new VirtualTimeDomain(nullptr, start_time));
-  manager_->RegisterTimeDomain(domain_a.get());
-  runners_[0]->SetTimeDomain(domain_a.get());
+  scoped_refptr<VirtualTimeDomain> domain_a(new VirtualTimeDomain(start_time));
+  manager_->RegisterTimeDomain(domain_a);
+  runners_[0]->SetTimeDomain(domain_a);
 
   std::vector<int> run_order;
   runners_[0]->PostDelayedTask(FROM_HERE, base::Bind(&TestTask, 1, &run_order),
@@ -1409,18 +1445,17 @@
   test_task_runner_->RunUntilIdle();
   EXPECT_THAT(run_order, ElementsAre(1, 2));
 
-  scoped_ptr<VirtualTimeDomain> domain_b(
-      new VirtualTimeDomain(nullptr, start_time));
-  manager_->RegisterTimeDomain(domain_b.get());
-  runners_[0]->SetTimeDomain(domain_b.get());
+  scoped_refptr<VirtualTimeDomain> domain_b(new VirtualTimeDomain(start_time));
+  manager_->RegisterTimeDomain(domain_b);
+  runners_[0]->SetTimeDomain(domain_b);
 
   domain_b->AdvanceTo(start_time + base::TimeDelta::FromMilliseconds(50));
 
   test_task_runner_->RunUntilIdle();
   EXPECT_THAT(run_order, ElementsAre(1, 2, 3, 4));
 
-  manager_->UnregisterTimeDomain(domain_a.get());
-  manager_->UnregisterTimeDomain(domain_b.get());
+  manager_->UnregisterTimeDomain(domain_a);
+  manager_->UnregisterTimeDomain(domain_b);
 }
 
 namespace {
diff --git a/components/scheduler/base/task_queue_selector_unittest.cc b/components/scheduler/base/task_queue_selector_unittest.cc
index da64fac..382a440d 100644
--- a/components/scheduler/base/task_queue_selector_unittest.cc
+++ b/components/scheduler/base/task_queue_selector_unittest.cc
@@ -75,11 +75,11 @@
 
  protected:
   void SetUp() final {
-    virtual_time_domain_ = make_scoped_ptr<VirtualTimeDomain>(
-        new VirtualTimeDomain(nullptr, base::TimeTicks()));
+    virtual_time_domain_ = make_scoped_refptr<VirtualTimeDomain>(
+        new VirtualTimeDomain(base::TimeTicks()));
     for (size_t i = 0; i < kTaskQueueCount; i++) {
       scoped_refptr<TaskQueueImpl> task_queue = make_scoped_refptr(
-          new TaskQueueImpl(nullptr, virtual_time_domain_.get(),
+          new TaskQueueImpl(nullptr, virtual_time_domain_,
                             TaskQueue::Spec("test queue"), "test", "test"));
       selector_.AddQueue(task_queue.get());
       task_queues_.push_back(task_queue);
@@ -93,7 +93,7 @@
   const size_t kTaskQueueCount = 5;
   base::Closure test_closure_;
   TaskQueueSelector selector_;
-  scoped_ptr<VirtualTimeDomain> virtual_time_domain_;
+  scoped_refptr<VirtualTimeDomain> virtual_time_domain_;
   std::vector<scoped_refptr<TaskQueueImpl>> task_queues_;
   std::map<TaskQueueImpl*, size_t> queue_to_index_map_;
 };
diff --git a/components/scheduler/base/task_queue_sets_unittest.cc b/components/scheduler/base/task_queue_sets_unittest.cc
index 8d42503a..568c0d1 100644
--- a/components/scheduler/base/task_queue_sets_unittest.cc
+++ b/components/scheduler/base/task_queue_sets_unittest.cc
@@ -16,8 +16,8 @@
 class TaskQueueSetsTest : public testing::Test {
  public:
   void SetUp() override {
-    virtual_time_domain_ = make_scoped_ptr<VirtualTimeDomain>(
-        new VirtualTimeDomain(nullptr, base::TimeTicks()));
+    virtual_time_domain_ = make_scoped_refptr<VirtualTimeDomain>(
+        new VirtualTimeDomain(base::TimeTicks()));
     task_queue_sets_.reset(new TaskQueueSets(kNumSets));
   }
 
@@ -29,8 +29,8 @@
   TaskQueueImpl* NewTaskQueue(const char* queue_name) {
     scoped_refptr<internal::TaskQueueImpl> queue =
         make_scoped_refptr(new internal::TaskQueueImpl(
-            nullptr, virtual_time_domain_.get(), TaskQueue::Spec(queue_name),
-            "test", "test"));
+            nullptr, virtual_time_domain_, TaskQueue::Spec(queue_name), "test",
+            "test"));
     task_queues_.push_back(queue);
     return queue.get();
   }
@@ -41,7 +41,7 @@
     return fake_task;
   }
 
-  scoped_ptr<VirtualTimeDomain> virtual_time_domain_;
+  scoped_refptr<VirtualTimeDomain> virtual_time_domain_;
   std::vector<scoped_refptr<internal::TaskQueueImpl>> task_queues_;
   scoped_ptr<TaskQueueSets> task_queue_sets_;
 };
diff --git a/components/scheduler/base/time_domain.cc b/components/scheduler/base/time_domain.cc
index e64b4e9..2abfb52 100644
--- a/components/scheduler/base/time_domain.cc
+++ b/components/scheduler/base/time_domain.cc
@@ -12,21 +12,11 @@
 
 namespace scheduler {
 
-TimeDomain::TimeDomain(Observer* observer) : observer_(observer) {}
+TimeDomain::TimeDomain() {}
 
-TimeDomain::~TimeDomain() {
-  for (internal::TaskQueueImpl* queue : registered_task_queues_) {
-    queue->SetTimeDomain(nullptr);
-  }
-}
-
-void TimeDomain::RegisterQueue(internal::TaskQueueImpl* queue) {
-  registered_task_queues_.insert(queue);
-}
+TimeDomain::~TimeDomain() {}
 
 void TimeDomain::UnregisterQueue(internal::TaskQueueImpl* queue) {
-  registered_task_queues_.erase(queue);
-
   // We need to remove |task_queue| from delayed_wakeup_multimap_ which is a
   // little awkward since it's keyed by time. O(n) running time.
   for (DelayedWakeupMultimap::iterator iter = delayed_wakeup_multimap_.begin();
@@ -49,9 +39,6 @@
 
 void TimeDomain::MigrateQueue(internal::TaskQueueImpl* queue,
                               TimeDomain* destination_time_domain) {
-  DCHECK(destination_time_domain);
-  registered_task_queues_.erase(queue);
-
   LazyNow destination_lazy_now = destination_time_domain->CreateLazyNow();
   // We need to remove |task_queue| from delayed_wakeup_multimap_ which is a
   // little awkward since it's keyed by time. O(n) running time.
@@ -73,8 +60,6 @@
   // MoveNewlyUpdatableQueuesIntoUpdatableQueueSet to flush it out.
   MoveNewlyUpdatableQueuesIntoUpdatableQueueSet();
   updatable_queue_set_.erase(queue);
-
-  destination_time_domain->RegisterQueue(queue);
 }
 
 void TimeDomain::ScheduleDelayedWork(internal::TaskQueueImpl* queue,
@@ -82,26 +67,18 @@
                                      LazyNow* lazy_now) {
   DCHECK(main_thread_checker_.CalledOnValidThread());
 
-  bool delayed_wakeup_multimap_was_empty = delayed_wakeup_multimap_.empty();
-  if (delayed_wakeup_multimap_was_empty ||
+  if (delayed_wakeup_multimap_.empty() ||
       delayed_run_time < delayed_wakeup_multimap_.begin()->first) {
     base::TimeDelta delay =
         std::max(base::TimeDelta(), delayed_run_time - lazy_now->Now());
     RequestWakeup(lazy_now, delay);
   }
-
-  if (observer_ && delayed_wakeup_multimap_was_empty)
-    observer_->OnTimeDomainHasDelayedWork();
   delayed_wakeup_multimap_.insert(std::make_pair(delayed_run_time, queue));
 }
 
 void TimeDomain::RegisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue) {
-  {
-    base::AutoLock lock(newly_updatable_lock_);
-    newly_updatable_.push_back(queue);
-  }
-  if (observer_)
-    observer_->OnTimeDomainHasImmediateWork();
+  base::AutoLock lock(newly_updatable_lock_);
+  newly_updatable_.push_back(queue);
 }
 
 void TimeDomain::UnregisterAsUpdatableTaskQueue(
diff --git a/components/scheduler/base/time_domain.h b/components/scheduler/base/time_domain.h
index 5cf39b70..476a68e 100644
--- a/components/scheduler/base/time_domain.h
+++ b/components/scheduler/base/time_domain.h
@@ -23,24 +23,9 @@
 class TaskQueueManager;
 class TaskQueueManagerDelegate;
 
-class SCHEDULER_EXPORT TimeDomain {
+class SCHEDULER_EXPORT TimeDomain : public base::RefCounted<TimeDomain> {
  public:
-  class SCHEDULER_EXPORT Observer {
-   public:
-    virtual ~Observer() {}
-
-    // Called when an empty TaskQueue registered with this TimeDomain has a task
-    // enqueued.
-    virtual void OnTimeDomainHasImmediateWork() = 0;
-
-    // Called when a TaskQueue registered with this TimeDomain has a delayed
-    // task enqueued and no other delayed tasks associated with this TimeDomain
-    // are pending.
-    virtual void OnTimeDomainHasDelayedWork() = 0;
-  };
-
-  explicit TimeDomain(Observer* observer);
-  virtual ~TimeDomain();
+  TimeDomain();
 
   // Returns a LazyNow that evaluate this TimeDomain's Now.  Can be called from
   // any thread.
@@ -61,6 +46,9 @@
  protected:
   friend class internal::TaskQueueImpl;
   friend class TaskQueueManager;
+  friend class base::RefCounted<TimeDomain>;
+
+  virtual ~TimeDomain();
 
   void AsValueInto(base::trace_event::TracedValue* state) const;
 
@@ -82,9 +70,6 @@
                            base::TimeTicks delayed_run_time,
                            LazyNow* lazy_now);
 
-  // Registers the |queue|.
-  void RegisterQueue(internal::TaskQueueImpl* queue);
-
   // Removes |queue| from the set of task queues that UpdateWorkQueues calls
   // UpdateWorkQueue on.
   void UnregisterAsUpdatableTaskQueue(internal::TaskQueueImpl* queue);
@@ -130,10 +115,6 @@
   // only be accessed from the main thread.
   std::set<internal::TaskQueueImpl*> updatable_queue_set_;
 
-  std::set<internal::TaskQueueImpl*> registered_task_queues_;
-
-  Observer* observer_;
-
   base::ThreadChecker main_thread_checker_;
 
   DISALLOW_COPY_AND_ASSIGN(TimeDomain);
diff --git a/components/scheduler/base/time_domain_unittest.cc b/components/scheduler/base/time_domain_unittest.cc
index e699e18..d41305a 100644
--- a/components/scheduler/base/time_domain_unittest.cc
+++ b/components/scheduler/base/time_domain_unittest.cc
@@ -19,18 +19,14 @@
 
 class MockTimeDomain : public TimeDomain {
  public:
-  explicit MockTimeDomain(TimeDomain::Observer* observer)
-      : TimeDomain(observer),
-        now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {}
-
-  ~MockTimeDomain() override {}
+  MockTimeDomain()
+      : now_(base::TimeTicks() + base::TimeDelta::FromSeconds(1)) {}
 
   using TimeDomain::NextScheduledRunTime;
   using TimeDomain::NextScheduledTaskQueue;
   using TimeDomain::ScheduleDelayedWork;
   using TimeDomain::UnregisterQueue;
   using TimeDomain::UpdateWorkQueues;
-  using TimeDomain::RegisterAsUpdatableTaskQueue;
 
   // TimeSource implementation:
   LazyNow CreateLazyNow() override { return LazyNow(now_); }
@@ -53,23 +49,21 @@
  private:
   base::TimeTicks now_;
 
+  ~MockTimeDomain() override {}
+
   DISALLOW_COPY_AND_ASSIGN(MockTimeDomain);
 };
 
 class TimeDomainTest : public testing::Test {
  public:
   void SetUp() final {
-    time_domain_ = make_scoped_ptr(CreateMockTimeDomain());
+    time_domain_ = make_scoped_refptr(new MockTimeDomain());
     task_queue_ = make_scoped_refptr(new internal::TaskQueueImpl(
-        nullptr, time_domain_.get(), TaskQueue::Spec("test_queue"),
-        "test.category", "test.category"));
+        nullptr, time_domain_, TaskQueue::Spec("test_queue"), "test.category",
+        "test.category"));
   }
 
-  virtual MockTimeDomain* CreateMockTimeDomain() {
-    return new MockTimeDomain(nullptr);
-  }
-
-  scoped_ptr<MockTimeDomain> time_domain_;
+  scoped_refptr<MockTimeDomain> time_domain_;
   scoped_refptr<internal::TaskQueueImpl> task_queue_;
 };
 
@@ -121,7 +115,7 @@
 TEST_F(TimeDomainTest, UnregisterQueue) {
   scoped_refptr<internal::TaskQueueImpl> task_queue2_ =
       make_scoped_refptr(new internal::TaskQueueImpl(
-          nullptr, time_domain_.get(), TaskQueue::Spec("test_queue2"),
+          nullptr, time_domain_, TaskQueue::Spec("test_queue2"),
           "test.category", "test.category"));
 
   EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _)).Times(1);
@@ -146,7 +140,7 @@
 }
 
 TEST_F(TimeDomainTest, UpdateWorkQueues) {
-  scoped_ptr<MockTimeDomain> dummy_delegate(new MockTimeDomain(nullptr));
+  scoped_refptr<MockTimeDomain> dummy_delegate(new MockTimeDomain());
   base::SimpleTestTickClock dummy_time_source;
   scoped_refptr<cc::OrderedSimpleTaskRunner> dummy_task_runner(
       new cc::OrderedSimpleTaskRunner(&dummy_time_source, false));
@@ -182,38 +176,4 @@
   EXPECT_EQ(1UL, dummy_queue->IncomingQueueSizeForTest());
 }
 
-namespace {
-class MockObserver : public TimeDomain::Observer {
- public:
-  ~MockObserver() override {}
-
-  MOCK_METHOD0(OnTimeDomainHasImmediateWork, void());
-  MOCK_METHOD0(OnTimeDomainHasDelayedWork, void());
-};
-}  // namespace
-
-class TimeDomainWithObserverTest : public TimeDomainTest {
- public:
-  MockTimeDomain* CreateMockTimeDomain() override {
-    observer_.reset(new MockObserver());
-    return new MockTimeDomain(observer_.get());
-  }
-
-  scoped_ptr<MockObserver> observer_;
-};
-
-TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasImmediateWork) {
-  EXPECT_CALL(*observer_, OnTimeDomainHasImmediateWork());
-  time_domain_->RegisterAsUpdatableTaskQueue(task_queue_.get());
-}
-
-TEST_F(TimeDomainWithObserverTest, OnTimeDomainHasDelayedWork) {
-  EXPECT_CALL(*observer_, OnTimeDomainHasDelayedWork());
-  EXPECT_CALL(*time_domain_.get(), RequestWakeup(_, _));
-  LazyNow lazy_now = time_domain_->CreateLazyNow();
-  time_domain_->ScheduleDelayedWork(
-      task_queue_.get(),
-      time_domain_->Now() + base::TimeDelta::FromMilliseconds(10), &lazy_now);
-}
-
 }  // namespace scheduler
diff --git a/components/scheduler/base/virtual_time_domain.cc b/components/scheduler/base/virtual_time_domain.cc
index 0ed6abb..3251039 100644
--- a/components/scheduler/base/virtual_time_domain.cc
+++ b/components/scheduler/base/virtual_time_domain.cc
@@ -10,9 +10,8 @@
 
 namespace scheduler {
 
-VirtualTimeDomain::VirtualTimeDomain(TimeDomain::Observer* observer,
-                                     base::TimeTicks initial_time)
-    : TimeDomain(observer), now_(initial_time) {}
+VirtualTimeDomain::VirtualTimeDomain(base::TimeTicks initial_time)
+    : now_(initial_time) {}
 
 VirtualTimeDomain::~VirtualTimeDomain() {}
 
@@ -45,8 +44,6 @@
   base::AutoLock lock(lock_);
   DCHECK_GE(now, now_);
   now_ = now;
-  DCHECK(task_queue_manager_delegate_);
-
   task_queue_manager_delegate_->PostTask(FROM_HERE, do_work_closure_);
 }
 
diff --git a/components/scheduler/base/virtual_time_domain.h b/components/scheduler/base/virtual_time_domain.h
index 78239c1c..ce1f99a 100644
--- a/components/scheduler/base/virtual_time_domain.h
+++ b/components/scheduler/base/virtual_time_domain.h
@@ -15,9 +15,7 @@
 
 class SCHEDULER_EXPORT VirtualTimeDomain : public TimeDomain {
  public:
-  VirtualTimeDomain(TimeDomain::Observer* observer,
-                    base::TimeTicks initial_time);
-  ~VirtualTimeDomain() override;
+  VirtualTimeDomain(base::TimeTicks initial_time);
 
   // TimeDomain implementation:
   LazyNow CreateLazyNow() override;
@@ -43,6 +41,8 @@
   TaskQueueManagerDelegate* task_queue_manager_delegate_;  // NOT OWNED
   base::Closure do_work_closure_;
 
+  ~VirtualTimeDomain() override;
+
   DISALLOW_COPY_AND_ASSIGN(VirtualTimeDomain);
 };
 
diff --git a/components/scheduler/child/idle_helper.cc b/components/scheduler/child/idle_helper.cc
index b0fb06a..f007baec 100644
--- a/components/scheduler/child/idle_helper.cc
+++ b/components/scheduler/child/idle_helper.cc
@@ -93,7 +93,7 @@
   if (long_idle_period_duration >=
       base::TimeDelta::FromMilliseconds(kMinimumIdlePeriodDurationMillis)) {
     *next_long_idle_period_delay_out = long_idle_period_duration;
-    if (!idle_queue_->HasPendingImmediateTask()) {
+    if (idle_queue_->IsQueueEmpty()) {
       return IdlePeriodState::IN_LONG_IDLE_PERIOD_PAUSED;
     } else if (long_idle_period_duration == max_long_idle_period_duration) {
       return IdlePeriodState::IN_LONG_IDLE_PERIOD_WITH_MAX_DEADLINE;
diff --git a/components/scheduler/child/scheduler_helper.cc b/components/scheduler/child/scheduler_helper.cc
index 793f2599..f4081fd 100644
--- a/components/scheduler/child/scheduler_helper.cc
+++ b/components/scheduler/child/scheduler_helper.cc
@@ -124,28 +124,16 @@
   task_queue_manager_->SetObserver(this);
 }
 
-RealTimeDomain* SchedulerHelper::real_time_domain() const {
-  CheckOnValidThread();
-  DCHECK(task_queue_manager_);
-  return task_queue_manager_->real_time_domain();
-}
-
-void SchedulerHelper::RegisterTimeDomain(TimeDomain* time_domain) {
-  CheckOnValidThread();
-  DCHECK(task_queue_manager_);
-  task_queue_manager_->RegisterTimeDomain(time_domain);
-}
-
-void SchedulerHelper::UnregisterTimeDomain(TimeDomain* time_domain) {
-  CheckOnValidThread();
-  if (task_queue_manager_)
-    task_queue_manager_->UnregisterTimeDomain(time_domain);
-}
-
 void SchedulerHelper::OnUnregisterTaskQueue(
     const scoped_refptr<internal::TaskQueueImpl>& queue) {
   if (observer_)
     observer_->OnUnregisterTaskQueue(queue);
 }
 
+const scoped_refptr<RealTimeDomain>& SchedulerHelper::real_time_domain() const {
+  CheckOnValidThread();
+  DCHECK(task_queue_manager_);
+  return task_queue_manager_->real_time_domain();
+}
+
 }  // namespace scheduler
diff --git a/components/scheduler/child/scheduler_helper.h b/components/scheduler/child/scheduler_helper.h
index ee727f7e..27e33aa 100644
--- a/components/scheduler/child/scheduler_helper.h
+++ b/components/scheduler/child/scheduler_helper.h
@@ -83,9 +83,7 @@
   void SetObserver(Observer* observer);
 
   // Accessor methods.
-  RealTimeDomain* real_time_domain() const;
-  void RegisterTimeDomain(TimeDomain* time_domain);
-  void UnregisterTimeDomain(TimeDomain* time_domain);
+  const scoped_refptr<RealTimeDomain>& real_time_domain() const;
   const scoped_refptr<SchedulerTqmDelegate>& scheduler_tqm_delegate() const;
   bool GetAndClearSystemIsQuiescentBit();
 
diff --git a/components/scheduler/child/web_scheduler_impl.cc b/components/scheduler/child/web_scheduler_impl.cc
index cc08d38..ce5436e 100644
--- a/components/scheduler/child/web_scheduler_impl.cc
+++ b/components/scheduler/child/web_scheduler_impl.cc
@@ -87,6 +87,20 @@
   return timer_web_task_runner_.get();
 }
 
+void WebSchedulerImpl::postTimerTaskAt(
+    const blink::WebTraceLocation& web_location,
+    blink::WebTaskRunner::Task* task,
+    double monotonicTime) {
+  DCHECK(timer_task_runner_);
+  tracked_objects::Location location(web_location.functionName(),
+                                     web_location.fileName(), -1, nullptr);
+  timer_task_runner_->PostDelayedTaskAt(
+      location,
+      base::Bind(&WebTaskRunnerImpl::runTask,
+                 base::Passed(scoped_ptr<blink::WebTaskRunner::Task>(task))),
+      base::TimeTicks() + base::TimeDelta::FromSecondsD(monotonicTime));
+}
+
 blink::WebPassOwnPtr<blink::WebViewScheduler>
 WebSchedulerImpl::createWebViewScheduler(blink::WebView*) {
   NOTREACHED();
diff --git a/components/scheduler/child/web_scheduler_impl.h b/components/scheduler/child/web_scheduler_impl.h
index 724491f..84933ac 100644
--- a/components/scheduler/child/web_scheduler_impl.h
+++ b/components/scheduler/child/web_scheduler_impl.h
@@ -52,6 +52,11 @@
   void removePendingNavigation() override {}
   void onNavigationStarted() override {}
 
+  // TODO(alexclarke): Remove when possible.
+  void postTimerTaskAt(const blink::WebTraceLocation& location,
+                       blink::WebTaskRunner::Task* task,
+                       double monotonicTime) override;
+
  private:
   static void runIdleTask(scoped_ptr<blink::WebThread::IdleTask> task,
                           base::TimeTicks deadline);
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.cc b/components/scheduler/renderer/renderer_scheduler_impl.cc
index 0ef81cd..0ee5481d 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl.cc
+++ b/components/scheduler/renderer/renderer_scheduler_impl.cc
@@ -12,7 +12,6 @@
 #include "cc/output/begin_frame_args.h"
 #include "components/scheduler/base/task_queue_impl.h"
 #include "components/scheduler/base/task_queue_selector.h"
-#include "components/scheduler/base/virtual_time_domain.h"
 #include "components/scheduler/child/scheduler_tqm_delegate.h"
 #include "components/scheduler/renderer/webthread_impl_for_renderer_scheduler.h"
 
@@ -42,7 +41,6 @@
                    TRACE_DISABLED_BY_DEFAULT("renderer.scheduler"),
                    "RendererSchedulerIdlePeriod",
                    base::TimeDelta()),
-      throttling_helper_(this, "renderer.scheduler"),
       render_widget_scheduler_signals_(this),
       control_task_runner_(helper_.ControlTaskRunner()),
       compositor_task_runner_(
@@ -178,11 +176,6 @@
   return default_timer_task_runner_;
 }
 
-scoped_refptr<TaskQueue> RendererSchedulerImpl::ControlTaskRunner() {
-  helper_.CheckOnValidThread();
-  return helper_.ControlTaskRunner();
-}
-
 scoped_refptr<TaskQueue> RendererSchedulerImpl::NewLoadingTaskRunner(
     const char* name) {
   helper_.CheckOnValidThread();
@@ -542,7 +535,7 @@
 
     case UseCase::MAIN_THREAD_GESTURE:
     case UseCase::SYNCHRONIZED_GESTURE:
-      return compositor_task_runner_->HasPendingImmediateTask() ||
+      return !compositor_task_runner_->IsQueueEmpty() ||
              MainThreadOnly().touchstart_expected_soon;
 
     case UseCase::TOUCHSTART:
@@ -1053,16 +1046,4 @@
          static_cast<double>(base::Time::kMicrosecondsPerSecond);
 }
 
-void RendererSchedulerImpl::RegisterTimeDomain(TimeDomain* time_domain) {
-  helper_.RegisterTimeDomain(time_domain);
-}
-
-void RendererSchedulerImpl::UnregisterTimeDomain(TimeDomain* time_domain) {
-  helper_.UnregisterTimeDomain(time_domain);
-}
-
-base::TickClock* RendererSchedulerImpl::tick_clock() const {
-  return helper_.scheduler_tqm_delegate().get();
-}
-
 }  // namespace scheduler
diff --git a/components/scheduler/renderer/renderer_scheduler_impl.h b/components/scheduler/renderer/renderer_scheduler_impl.h
index d4025da53..6fb097a1 100644
--- a/components/scheduler/renderer/renderer_scheduler_impl.h
+++ b/components/scheduler/renderer/renderer_scheduler_impl.h
@@ -15,7 +15,6 @@
 #include "components/scheduler/renderer/render_widget_signals.h"
 #include "components/scheduler/renderer/renderer_scheduler.h"
 #include "components/scheduler/renderer/task_cost_estimator.h"
-#include "components/scheduler/renderer/throttling_helper.h"
 #include "components/scheduler/renderer/user_model.h"
 #include "components/scheduler/scheduler_export.h"
 
@@ -27,7 +26,6 @@
 
 namespace scheduler {
 class RenderWidgetSchedulingState;
-class ThrottlingHelper;
 
 class SCHEDULER_EXPORT RendererSchedulerImpl
     : public RendererScheduler,
@@ -84,12 +82,6 @@
   // TaskQueueManager::Observer implementation:
   void OnUnregisterTaskQueue(const scoped_refptr<TaskQueue>& queue) override;
 
-  // Returns a task runner where tasks run at the highest possible priority.
-  scoped_refptr<TaskQueue> ControlTaskRunner();
-
-  void RegisterTimeDomain(TimeDomain* time_domain);
-  void UnregisterTimeDomain(TimeDomain* time_domain);
-
   // Test helpers.
   SchedulerHelper* GetSchedulerHelperForTesting();
   TaskCostEstimator* GetLoadingTaskCostEstimatorForTesting();
@@ -97,14 +89,6 @@
   IdleTimeEstimator* GetIdleTimeEstimatorForTesting();
   base::TimeTicks CurrentIdleTaskDeadlineForTesting() const;
 
-  base::TickClock* tick_clock() const;
-
-  RealTimeDomain* real_time_domain() const {
-    return helper_.real_time_domain();
-  }
-
-  ThrottlingHelper* throttling_helper() { return &throttling_helper_; }
-
  private:
   friend class RendererSchedulerImplTest;
   friend class RendererSchedulerImplForTest;
@@ -249,7 +233,6 @@
 
   SchedulerHelper helper_;
   IdleHelper idle_helper_;
-  ThrottlingHelper throttling_helper_;
   RenderWidgetSignals render_widget_scheduler_signals_;
 
   const scoped_refptr<TaskQueue> control_task_runner_;
diff --git a/components/scheduler/renderer/throttling_helper.cc b/components/scheduler/renderer/throttling_helper.cc
deleted file mode 100644
index 88f9ae7..0000000
--- a/components/scheduler/renderer/throttling_helper.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2015 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 "components/scheduler/renderer/throttling_helper.h"
-
-#include "base/logging.h"
-#include "components/scheduler/base/real_time_domain.h"
-#include "components/scheduler/base/virtual_time_domain.h"
-#include "components/scheduler/child/scheduler_tqm_delegate.h"
-#include "components/scheduler/renderer/renderer_scheduler_impl.h"
-#include "components/scheduler/renderer/web_frame_scheduler_impl.h"
-#include "third_party/WebKit/public/platform/WebFrameScheduler.h"
-
-namespace scheduler {
-
-ThrottlingHelper::ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler,
-                                   const char* tracing_category)
-    : task_runner_(renderer_scheduler->ControlTaskRunner()),
-      renderer_scheduler_(renderer_scheduler),
-      tick_clock_(renderer_scheduler->tick_clock()),
-      tracing_category_(tracing_category),
-      time_domain_(new VirtualTimeDomain(this, tick_clock_->NowTicks())),
-      pending_pump_throttled_tasks_(false),
-      weak_factory_(this) {
-  pump_throttled_tasks_closure_ = base::Bind(
-      &ThrottlingHelper::PumpThrottledTasks, weak_factory_.GetWeakPtr());
-  forward_immediate_work_closure_ =
-      base::Bind(&ThrottlingHelper::OnTimeDomainHasImmediateWork,
-                 weak_factory_.GetWeakPtr());
-  renderer_scheduler_->RegisterTimeDomain(time_domain_.get());
-}
-
-ThrottlingHelper::~ThrottlingHelper() {
-  renderer_scheduler_->UnregisterTimeDomain(time_domain_.get());
-}
-
-void ThrottlingHelper::Throttle(TaskQueue* task_queue) {
-  throttled_queues_.insert(task_queue);
-
-  task_queue->SetTimeDomain(time_domain_.get());
-  task_queue->SetPumpPolicy(TaskQueue::PumpPolicy::MANUAL);
-
-  MaybeSchedulePumpThrottledTasksLocked();
-}
-
-void ThrottlingHelper::Unthrottle(TaskQueue* task_queue) {
-  throttled_queues_.erase(task_queue);
-
-  task_queue->SetTimeDomain(renderer_scheduler_->real_time_domain());
-  task_queue->SetPumpPolicy(TaskQueue::PumpPolicy::AUTO);
-}
-
-void ThrottlingHelper::OnTimeDomainHasImmediateWork() {
-  // Forward to the main thread if called from another thread.
-  if (!task_runner_->RunsTasksOnCurrentThread()) {
-    task_runner_->PostTask(FROM_HERE, forward_immediate_work_closure_);
-    return;
-  }
-  TRACE_EVENT0(tracing_category_,
-               "ThrottlingHelper::OnTimeDomainHasImmediateWork");
-  MaybeSchedulePumpThrottledTasksLocked();
-}
-
-void ThrottlingHelper::OnTimeDomainHasDelayedWork() {
-  TRACE_EVENT0(tracing_category_,
-               "ThrottlingHelper::OnTimeDomainHasDelayedWork");
-  MaybeSchedulePumpThrottledTasksLocked();
-}
-
-void ThrottlingHelper::PumpThrottledTasks() {
-  TRACE_EVENT0(tracing_category_, "ThrottlingHelper::PumpThrottledTasks");
-  pending_pump_throttled_tasks_ = false;
-
-  time_domain_->AdvanceTo(tick_clock_->NowTicks());
-  bool work_to_do = false;
-  for (TaskQueue* task_queue : throttled_queues_) {
-    if (task_queue->GetQueueState() == TaskQueue::QueueState::EMPTY)
-      continue;
-
-    work_to_do = true;
-    task_queue->PumpQueue();
-  }
-
-  if (work_to_do)
-    MaybeSchedulePumpThrottledTasksLocked();
-}
-
-/* static */
-base::TimeDelta ThrottlingHelper::DelayToNextRunTimeInSeconds(
-    base::TimeTicks now) {
-  const base::TimeDelta one_second = base::TimeDelta::FromSeconds(1);
-  return one_second - ((now - base::TimeTicks()) % one_second);
-}
-
-void ThrottlingHelper::MaybeSchedulePumpThrottledTasksLocked() {
-  if (pending_pump_throttled_tasks_)
-    return;
-
-  pending_pump_throttled_tasks_ = true;
-  task_runner_->PostDelayedTask(
-      FROM_HERE, pump_throttled_tasks_closure_,
-      DelayToNextRunTimeInSeconds(tick_clock_->NowTicks()));
-}
-
-}  // namespace scheduler
diff --git a/components/scheduler/renderer/throttling_helper.h b/components/scheduler/renderer/throttling_helper.h
deleted file mode 100644
index 8417d53..0000000
--- a/components/scheduler/renderer/throttling_helper.h
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2015 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.
-
-#ifndef COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_
-#define COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_
-
-#include <set>
-
-#include "base/macros.h"
-#include "components/scheduler/base/time_domain.h"
-#include "components/scheduler/scheduler_export.h"
-#include "third_party/WebKit/public/platform/WebViewScheduler.h"
-
-namespace scheduler {
-
-class RendererSchedulerImpl;
-class VirtualTimeDomain;
-class WebFrameSchedulerImpl;
-
-class SCHEDULER_EXPORT ThrottlingHelper : public TimeDomain::Observer {
- public:
-  ThrottlingHelper(RendererSchedulerImpl* renderer_scheduler,
-                   const char* tracing_category);
-
-  ~ThrottlingHelper() override;
-
-  // TimeDomain::Observer implementation:
-  void OnTimeDomainHasImmediateWork() override;
-  void OnTimeDomainHasDelayedWork() override;
-
-  void Throttle(TaskQueue* task_queue);
-  void Unthrottle(TaskQueue* task_queue);
-
-  const VirtualTimeDomain* time_domain() const { return time_domain_.get(); }
-
-  static base::TimeDelta DelayToNextRunTimeInSeconds(base::TimeTicks now);
-
- private:
-  void PumpThrottledTasks();
-  void MaybeSchedulePumpThrottledTasksLocked();
-
-  std::set<TaskQueue*> throttled_queues_;
-  base::Closure pump_throttled_tasks_closure_;
-  base::Closure forward_immediate_work_closure_;
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  RendererSchedulerImpl* renderer_scheduler_;  // NOT OWNED
-  base::TickClock* tick_clock_;                // NOT OWNED
-  const char* tracing_category_;               // NOT OWNED
-  scoped_ptr<VirtualTimeDomain> time_domain_;
-
-  bool pending_pump_throttled_tasks_;
-
-  base::WeakPtrFactory<ThrottlingHelper> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ThrottlingHelper);
-};
-
-}  // namespace scheduler
-
-#endif  // COMPONENTS_SCHEDULER_RENDERER_THROTTLING_HELPER_H_
diff --git a/components/scheduler/renderer/throttling_helper_unittest.cc b/components/scheduler/renderer/throttling_helper_unittest.cc
deleted file mode 100644
index 9526f06..0000000
--- a/components/scheduler/renderer/throttling_helper_unittest.cc
+++ /dev/null
@@ -1,203 +0,0 @@
-// Copyright 2015 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 "components/scheduler/renderer/throttling_helper.h"
-
-#include "base/callback.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "cc/test/ordered_simple_task_runner.h"
-#include "components/scheduler/base/test_time_source.h"
-#include "components/scheduler/child/scheduler_tqm_delegate_for_test.h"
-#include "components/scheduler/renderer/renderer_scheduler_impl.h"
-#include "components/scheduler/renderer/web_frame_scheduler_impl.h"
-#include "components/scheduler/renderer/web_view_scheduler_impl.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using testing::ElementsAre;
-
-namespace scheduler {
-
-class ThrottlingHelperTest : public testing::Test {
- public:
-  ThrottlingHelperTest() {}
-  ~ThrottlingHelperTest() override {}
-
-  void SetUp() override {
-    clock_.reset(new base::SimpleTestTickClock());
-    clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
-    mock_task_runner_ =
-        make_scoped_refptr(new cc::OrderedSimpleTaskRunner(clock_.get(), true));
-    delegate_ = SchedulerTqmDelegateForTest::Create(
-        mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get())));
-    scheduler_.reset(new RendererSchedulerImpl(delegate_));
-    throttling_helper_ = scheduler_->throttling_helper();
-    timer_queue_ = scheduler_->NewTimerTaskRunner("test_queue");
-  }
-
-  void TearDown() override {
-    scheduler_->Shutdown();
-    scheduler_.reset();
-  }
-
- protected:
-  scoped_ptr<base::SimpleTestTickClock> clock_;
-  scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
-  scoped_refptr<SchedulerTqmDelegate> delegate_;
-  scoped_ptr<RendererSchedulerImpl> scheduler_;
-  scoped_refptr<TaskQueue> timer_queue_;
-  ThrottlingHelper* throttling_helper_;  // NOT OWNED
-
-  DISALLOW_COPY_AND_ASSIGN(ThrottlingHelperTest);
-};
-
-TEST_F(ThrottlingHelperTest, DelayToNextRunTimeInSeconds) {
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.0)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.1)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.8),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.2)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.5),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.5)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.2),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.8)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.1),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(0.9)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(1.0)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(1.1)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(1.0),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(8.0)));
-
-  EXPECT_EQ(base::TimeDelta::FromSecondsD(0.9),
-            ThrottlingHelper::DelayToNextRunTimeInSeconds(
-                base::TimeTicks() + base::TimeDelta::FromSecondsD(8.1)));
-}
-
-namespace {
-void TestTask(std::vector<base::TimeTicks>* run_times,
-              base::SimpleTestTickClock* clock) {
-  run_times->push_back(clock->NowTicks());
-}
-}  // namespace
-
-TEST_F(ThrottlingHelperTest, TimerAlignment) {
-  std::vector<base::TimeTicks> run_times;
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(200.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(800.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(1200.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(8300.0));
-
-  throttling_helper_->Throttle(timer_queue_.get());
-
-  mock_task_runner_->RunUntilIdle();
-
-  // Times are aligned to a multipple of 1000 milliseconds.
-  EXPECT_THAT(
-      run_times,
-      ElementsAre(
-          base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
-          base::TimeTicks() + base::TimeDelta::FromMilliseconds(1000.0),
-          base::TimeTicks() + base::TimeDelta::FromMilliseconds(2000.0),
-          base::TimeTicks() + base::TimeDelta::FromMilliseconds(9000.0)));
-}
-
-TEST_F(ThrottlingHelperTest, TimerAlignment_Unthrottled) {
-  std::vector<base::TimeTicks> run_times;
-  base::TimeTicks start_time = clock_->NowTicks();
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(200.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(800.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(1200.0));
-
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(8300.0));
-
-  throttling_helper_->Throttle(timer_queue_.get());
-  throttling_helper_->Unthrottle(timer_queue_.get());
-
-  mock_task_runner_->RunUntilIdle();
-
-  // Times are not aligned.
-  EXPECT_THAT(
-      run_times,
-      ElementsAre(start_time + base::TimeDelta::FromMilliseconds(200.0),
-                  start_time + base::TimeDelta::FromMilliseconds(800.0),
-                  start_time + base::TimeDelta::FromMilliseconds(1200.0),
-                  start_time + base::TimeDelta::FromMilliseconds(8300.0)));
-}
-
-TEST_F(ThrottlingHelperTest, WakeUpForNonDelayedTask) {
-  std::vector<base::TimeTicks> run_times;
-
-  // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
-  throttling_helper_->Throttle(timer_queue_.get());
-
-  // Posting a task should trigger the pump.
-  timer_queue_->PostTask(FROM_HERE,
-                         base::Bind(&TestTask, &run_times, clock_.get()));
-
-  mock_task_runner_->RunUntilIdle();
-  EXPECT_THAT(run_times,
-              ElementsAre(base::TimeTicks() +
-                          base::TimeDelta::FromMilliseconds(1000.0)));
-}
-
-TEST_F(ThrottlingHelperTest, WakeUpForDelayedTask) {
-  std::vector<base::TimeTicks> run_times;
-
-  // Nothing is posted on timer_queue_ so PumpThrottledTasks will not tick.
-  throttling_helper_->Throttle(timer_queue_.get());
-
-  // Posting a task should trigger the pump.
-  timer_queue_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&TestTask, &run_times, clock_.get()),
-                                base::TimeDelta::FromMilliseconds(1200.0));
-
-  mock_task_runner_->RunUntilIdle();
-  EXPECT_THAT(run_times,
-              ElementsAre(base::TimeTicks() +
-                          base::TimeDelta::FromMilliseconds(2000.0)));
-}
-
-}  // namespace scheduler
diff --git a/components/scheduler/renderer/web_frame_scheduler_impl.cc b/components/scheduler/renderer/web_frame_scheduler_impl.cc
index b8c1c1a..15e4a7d 100644
--- a/components/scheduler/renderer/web_frame_scheduler_impl.cc
+++ b/components/scheduler/renderer/web_frame_scheduler_impl.cc
@@ -4,8 +4,6 @@
 
 #include "components/scheduler/renderer/web_frame_scheduler_impl.h"
 
-#include "components/scheduler/base/real_time_domain.h"
-#include "components/scheduler/base/virtual_time_domain.h"
 #include "components/scheduler/child/web_task_runner_impl.h"
 #include "components/scheduler/renderer/renderer_scheduler_impl.h"
 #include "components/scheduler/renderer/web_view_scheduler_impl.h"
@@ -18,18 +16,14 @@
     WebViewSchedulerImpl* parent_web_view_scheduler)
     : renderer_scheduler_(renderer_scheduler),
       parent_web_view_scheduler_(parent_web_view_scheduler),
-      visible_(true),
-      page_in_background_(false) {}
+      visible_(true) {}
 
 WebFrameSchedulerImpl::~WebFrameSchedulerImpl() {
   if (loading_task_queue_.get())
     loading_task_queue_->UnregisterTaskQueue();
 
-  if (timer_task_queue_.get()) {
-    renderer_scheduler_->throttling_helper()->Unthrottle(
-        timer_task_queue_.get());
+  if (timer_task_queue_.get())
     timer_task_queue_->UnregisterTaskQueue();
-  }
 
   if (parent_web_view_scheduler_)
     parent_web_view_scheduler_->Unregister(this);
@@ -57,10 +51,6 @@
   if (!timer_web_task_runner_) {
     timer_task_queue_ =
         renderer_scheduler_->NewTimerTaskRunner("frame_timer_tq");
-    if (page_in_background_) {
-      renderer_scheduler_->throttling_helper()->Throttle(
-          timer_task_queue_.get());
-    }
     timer_web_task_runner_.reset(new WebTaskRunnerImpl(timer_task_queue_));
   }
   return timer_web_task_runner_.get();
@@ -73,21 +63,4 @@
   // TODO(skyostil): Associate the task queues with this origin.
 }
 
-void WebFrameSchedulerImpl::SetPageInBackground(bool page_in_background) {
-  if (page_in_background_ == page_in_background)
-    return;
-
-  page_in_background_ = page_in_background;
-
-  if (!timer_web_task_runner_)
-    return;
-
-  if (page_in_background_) {
-    renderer_scheduler_->throttling_helper()->Throttle(timer_task_queue_.get());
-  } else {
-    renderer_scheduler_->throttling_helper()->Unthrottle(
-        timer_task_queue_.get());
-  }
-}
-
 }  // namespace scheduler
diff --git a/components/scheduler/renderer/web_frame_scheduler_impl.h b/components/scheduler/renderer/web_frame_scheduler_impl.h
index 96a9e19..eb14bc0 100644
--- a/components/scheduler/renderer/web_frame_scheduler_impl.h
+++ b/components/scheduler/renderer/web_frame_scheduler_impl.h
@@ -36,13 +36,10 @@
   blink::WebTaskRunner* timerTaskRunner() override;
   void setFrameOrigin(const blink::WebSecurityOrigin* origin) override;
 
-  void SetPageInBackground(bool page_in_background);
-
  private:
   friend class WebViewSchedulerImpl;
 
   void DetachFromWebViewScheduler();
-  void ApplyPolicyToTimerQueue();
 
   scoped_refptr<TaskQueue> loading_task_queue_;
   scoped_refptr<TaskQueue> timer_task_queue_;
@@ -52,7 +49,6 @@
   WebViewSchedulerImpl* parent_web_view_scheduler_;  // NOT OWNED
   blink::WebSecurityOrigin origin_;
   bool visible_;
-  bool page_in_background_;
 
   DISALLOW_COPY_AND_ASSIGN(WebFrameSchedulerImpl);
 };
diff --git a/components/scheduler/renderer/web_view_scheduler_impl.cc b/components/scheduler/renderer/web_view_scheduler_impl.cc
index 658fdbb..28bc216 100644
--- a/components/scheduler/renderer/web_view_scheduler_impl.cc
+++ b/components/scheduler/renderer/web_view_scheduler_impl.cc
@@ -5,9 +5,6 @@
 #include "components/scheduler/renderer/web_view_scheduler_impl.h"
 
 #include "base/logging.h"
-#include "components/scheduler/base/virtual_time_domain.h"
-#include "components/scheduler/child/scheduler_tqm_delegate.h"
-#include "components/scheduler/renderer/renderer_scheduler_impl.h"
 #include "components/scheduler/renderer/web_frame_scheduler_impl.h"
 #include "third_party/WebKit/public/platform/WebFrameScheduler.h"
 
@@ -18,7 +15,7 @@
     RendererSchedulerImpl* renderer_scheduler)
     : web_view_(web_view),
       renderer_scheduler_(renderer_scheduler),
-      page_in_background_(false) {}
+      background_(false) {}
 
 WebViewSchedulerImpl::~WebViewSchedulerImpl() {
   // TODO(alexclarke): Find out why we can't rely on the web view outliving the
@@ -28,29 +25,17 @@
   }
 }
 
-void WebViewSchedulerImpl::setPageInBackground(bool page_in_background) {
-  if (page_in_background_ == page_in_background)
-    return;
-
-  page_in_background_ = page_in_background;
-
-  for (WebFrameSchedulerImpl* frame_scheduler : frame_schedulers_) {
-    frame_scheduler->SetPageInBackground(page_in_background_);
-  }
-}
-
-scoped_ptr<WebFrameSchedulerImpl>
-WebViewSchedulerImpl::createWebFrameSchedulerImpl() {
-  scoped_ptr<WebFrameSchedulerImpl> frame_scheduler(
-      new WebFrameSchedulerImpl(renderer_scheduler_, this));
-  frame_scheduler->SetPageInBackground(page_in_background_);
-  frame_schedulers_.insert(frame_scheduler.get());
-  return frame_scheduler.Pass();
+void WebViewSchedulerImpl::setPageInBackground(bool background) {
+  background_ = background;
+  // TODO(alexclarke): Do something with this flag.
 }
 
 blink::WebPassOwnPtr<blink::WebFrameScheduler>
 WebViewSchedulerImpl::createFrameScheduler() {
-  return blink::adoptWebPtr(createWebFrameSchedulerImpl().release());
+  scoped_ptr<WebFrameSchedulerImpl> frame_scheduler(
+      new WebFrameSchedulerImpl(renderer_scheduler_, this));
+  frame_schedulers_.insert(frame_scheduler.get());
+  return blink::adoptWebPtr(frame_scheduler.release());
 }
 
 void WebViewSchedulerImpl::Unregister(WebFrameSchedulerImpl* frame_scheduler) {
diff --git a/components/scheduler/renderer/web_view_scheduler_impl.h b/components/scheduler/renderer/web_view_scheduler_impl.h
index a5dd7d7..647d6d9 100644
--- a/components/scheduler/renderer/web_view_scheduler_impl.h
+++ b/components/scheduler/renderer/web_view_scheduler_impl.h
@@ -8,7 +8,6 @@
 #include <set>
 
 #include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
 #include "components/scheduler/scheduler_export.h"
 #include "third_party/WebKit/public/platform/WebViewScheduler.h"
 
@@ -33,14 +32,12 @@
   ~WebViewSchedulerImpl() override;
 
   // blink::WebViewScheduler implementation:
-  void setPageInBackground(bool page_in_background) override;
+  void setPageInBackground(bool background) override;
   blink::WebPassOwnPtr<blink::WebFrameScheduler> createFrameScheduler()
       override;
 
   blink::WebView* web_view() const { return web_view_; }
 
-  scoped_ptr<WebFrameSchedulerImpl> createWebFrameSchedulerImpl();
-
  private:
   friend class WebFrameSchedulerImpl;
 
@@ -49,7 +46,7 @@
   std::set<WebFrameSchedulerImpl*> frame_schedulers_;
   blink::WebView* web_view_;
   RendererSchedulerImpl* renderer_scheduler_;
-  bool page_in_background_;
+  bool background_;
 
   DISALLOW_COPY_AND_ASSIGN(WebViewSchedulerImpl);
 };
diff --git a/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc b/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc
index 750811c..401b39a0 100644
--- a/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc
+++ b/components/scheduler/renderer/web_view_scheduler_impl_unittest.cc
@@ -13,8 +13,6 @@
 #include "components/scheduler/renderer/renderer_scheduler_impl.h"
 #include "components/scheduler/renderer/web_frame_scheduler_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/WebKit/public/platform/WebTaskRunner.h"
-#include "third_party/WebKit/public/platform/WebTraceLocation.h"
 
 namespace scheduler {
 
@@ -26,29 +24,22 @@
   void SetUp() override {
     clock_.reset(new base::SimpleTestTickClock());
     clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
-    mock_task_runner_ =
-        make_scoped_refptr(new cc::OrderedSimpleTaskRunner(clock_.get(), true));
-    delagate_ = SchedulerTqmDelegateForTest::Create(
+    mock_task_runner_ = make_scoped_refptr(
+        new cc::OrderedSimpleTaskRunner(clock_.get(), false));
+    main_task_runner_ = SchedulerTqmDelegateForTest::Create(
         mock_task_runner_, make_scoped_ptr(new TestTimeSource(clock_.get())));
-    scheduler_.reset(new RendererSchedulerImpl(delagate_));
+    scheduler_.reset(new RendererSchedulerImpl(main_task_runner_));
     web_view_scheduler_.reset(
         new WebViewSchedulerImpl(nullptr, scheduler_.get()));
-    web_frame_scheduler_ = web_view_scheduler_->createWebFrameSchedulerImpl();
   }
 
-  void TearDown() override {
-    web_frame_scheduler_.reset();
-    web_view_scheduler_.reset();
-    scheduler_->Shutdown();
-    scheduler_.reset();
-  }
+  void TearDown() override { scheduler_->Shutdown(); }
 
   scoped_ptr<base::SimpleTestTickClock> clock_;
   scoped_refptr<cc::OrderedSimpleTaskRunner> mock_task_runner_;
-  scoped_refptr<SchedulerTqmDelegate> delagate_;
+  scoped_refptr<SchedulerTqmDelegate> main_task_runner_;
   scoped_ptr<RendererSchedulerImpl> scheduler_;
   scoped_ptr<WebViewSchedulerImpl> web_view_scheduler_;
-  scoped_ptr<WebFrameSchedulerImpl> web_frame_scheduler_;
 };
 
 TEST_F(WebViewSchedulerImplTest, TestDestructionOfFrameSchedulersBefore) {
@@ -66,88 +57,4 @@
   web_view_scheduler_.reset();
 }
 
-namespace {
-class RepeatingTask : public blink::WebTaskRunner::Task {
- public:
-  RepeatingTask(blink::WebTaskRunner* web_task_runner, int* run_count)
-      : web_task_runner_(web_task_runner), run_count_(run_count) {}
-
-  ~RepeatingTask() override {}
-
-  void run() override {
-    (*run_count_)++;
-    web_task_runner_->postDelayedTask(
-        BLINK_FROM_HERE, new RepeatingTask(web_task_runner_, run_count_), 1.0);
-  }
-
- private:
-  blink::WebTaskRunner* web_task_runner_;  // NOT OWNED
-  int* run_count_;                         // NOT OWNED
-};
-}  // namespace
-
-TEST_F(WebViewSchedulerImplTest, RepeatingTimer_PageInForeground) {
-  web_view_scheduler_->setPageInBackground(false);
-
-  int run_count = 0;
-  web_frame_scheduler_->timerTaskRunner()->postDelayedTask(
-      BLINK_FROM_HERE,
-      new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count),
-      1.0);
-
-  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(1000, run_count);
-}
-
-TEST_F(WebViewSchedulerImplTest, RepeatingTimer_PageInBackground) {
-  web_view_scheduler_->setPageInBackground(true);
-
-  int run_count = 0;
-  web_frame_scheduler_->timerTaskRunner()->postDelayedTask(
-      BLINK_FROM_HERE,
-      new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count),
-      1.0);
-
-  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(1, run_count);
-}
-
-TEST_F(WebViewSchedulerImplTest, RepeatingLoadingTask_PageInBackground) {
-  web_view_scheduler_->setPageInBackground(true);
-
-  int run_count = 0;
-  web_frame_scheduler_->loadingTaskRunner()->postDelayedTask(
-      BLINK_FROM_HERE,
-      new RepeatingTask(web_frame_scheduler_->loadingTaskRunner(), &run_count),
-      1.0);
-
-  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(1000, run_count);  // Loading tasks should not be throttled
-}
-
-TEST_F(WebViewSchedulerImplTest, RepeatingTimers_OneBackgroundOneForeground) {
-  scoped_ptr<WebViewSchedulerImpl> web_view_scheduler2(
-      new WebViewSchedulerImpl(nullptr, scheduler_.get()));
-  scoped_ptr<WebFrameSchedulerImpl> web_frame_scheduler2 =
-      web_view_scheduler2->createWebFrameSchedulerImpl();
-
-  web_view_scheduler_->setPageInBackground(false);
-  web_view_scheduler2->setPageInBackground(true);
-
-  int run_count1 = 0;
-  int run_count2 = 0;
-  web_frame_scheduler_->timerTaskRunner()->postDelayedTask(
-      BLINK_FROM_HERE,
-      new RepeatingTask(web_frame_scheduler_->timerTaskRunner(), &run_count1),
-      1.0);
-  web_frame_scheduler2->timerTaskRunner()->postDelayedTask(
-      BLINK_FROM_HERE,
-      new RepeatingTask(web_frame_scheduler2->timerTaskRunner(), &run_count2),
-      1.0);
-
-  mock_task_runner_->RunForPeriod(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(1000, run_count1);
-  EXPECT_EQ(1, run_count2);
-}
-
 }  // namespace scheduler
diff --git a/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
index c19e75e5..52f4ee85 100644
--- a/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
+++ b/components/scheduler/renderer/webthread_impl_for_renderer_scheduler_unittest.cc
@@ -35,30 +35,27 @@
 
 class WebThreadImplForRendererSchedulerTest : public testing::Test {
  public:
-  WebThreadImplForRendererSchedulerTest() {}
-
-  void SetUp() override {
-    clock_.reset(new base::SimpleTestTickClock());
-    clock_->Advance(base::TimeDelta::FromMicroseconds(5000));
-    scheduler_.reset(new RendererSchedulerImpl(SchedulerTqmDelegateImpl::Create(
-        &message_loop_, make_scoped_ptr(new TestTimeSource(clock_.get())))));
-    default_task_runner_ = scheduler_->DefaultTaskRunner();
-    thread_ = scheduler_->CreateMainThread();
-  }
+  WebThreadImplForRendererSchedulerTest()
+      : clock_(new base::SimpleTestTickClock()),
+        scheduler_(SchedulerTqmDelegateImpl::Create(
+            &message_loop_,
+            make_scoped_ptr(new TestTimeSource(clock_.get())))),
+        default_task_runner_(scheduler_.DefaultTaskRunner()),
+        thread_(scheduler_.CreateMainThread()) {}
 
   ~WebThreadImplForRendererSchedulerTest() override {}
 
   void SetWorkBatchSizeForTesting(size_t work_batch_size) {
-    scheduler_->GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(
+    scheduler_.GetSchedulerHelperForTesting()->SetWorkBatchSizeForTesting(
         work_batch_size);
   }
 
-  void TearDown() override { scheduler_->Shutdown(); }
+  void TearDown() override { scheduler_.Shutdown(); }
 
  protected:
   base::MessageLoop message_loop_;
   scoped_ptr<base::SimpleTestTickClock> clock_;
-  scoped_ptr<RendererSchedulerImpl> scheduler_;
+  RendererSchedulerImpl scheduler_;
   scoped_refptr<base::SingleThreadTaskRunner> default_task_runner_;
   scoped_ptr<blink::WebThread> thread_;
 
diff --git a/components/scheduler/scheduler.gypi b/components/scheduler/scheduler.gypi
index c6a79b78..dbd40ce 100644
--- a/components/scheduler/scheduler.gypi
+++ b/components/scheduler/scheduler.gypi
@@ -72,14 +72,12 @@
       'renderer/render_widget_scheduling_state.h',
       'renderer/render_widget_signals.cc',
       'renderer/render_widget_signals.h',
-      'renderer/task_cost_estimator.cc',
-      'renderer/task_cost_estimator.h',
-      'renderer/throttling_helper.cc',
-      'renderer/throttling_helper.h',
       'renderer/web_frame_scheduler_impl.cc',
       'renderer/web_frame_scheduler_impl.h',
       'renderer/web_view_scheduler_impl.cc',
       'renderer/web_view_scheduler_impl.h',
+      'renderer/task_cost_estimator.cc',
+      'renderer/task_cost_estimator.h',
       'renderer/user_model.cc',
       'renderer/user_model.h',
       'renderer/webthread_impl_for_renderer_scheduler.cc',
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 795e85ed..8b824c9 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -442,7 +442,7 @@
     , m_timeline(AnimationTimeline::create(this))
     , m_templateDocumentHost(nullptr)
     , m_didAssociateFormControlsTimer(this, &Document::didAssociateFormControlsTimerFired)
-    , m_timers(timerTaskRunner()->adoptClone())
+    , m_timers(Platform::current()->currentThread()->scheduler()->timerTaskRunner()->adoptClone())
     , m_hasViewportUnits(false)
     , m_styleRecalcElementCounter(0)
     , m_parserSyncPolicy(AllowAsynchronousParsing)
@@ -453,6 +453,7 @@
         provideContextFeaturesToDocumentFrom(*this, *m_frame->page());
 
         m_fetcher = m_frame->loader().documentLoader()->fetcher();
+        m_timers.setTimerTaskRunner(m_frame->frameScheduler()->timerTaskRunner()->adoptClone());
         FrameFetchContext::provideDocumentToContext(m_fetcher->context(), this);
     } else if (m_importsController) {
         m_fetcher = FrameFetchContext::createContextAndFetcher(nullptr);
@@ -2876,6 +2877,14 @@
     return completeURL(url);
 }
 
+double Document::timerAlignmentInterval() const
+{
+    Page* p = page();
+    if (!p)
+        return DOMTimer::visiblePageAlignmentInterval();
+    return p->timerAlignmentInterval();
+}
+
 DOMTimerCoordinator* Document::timers()
 {
     return &m_timers;
@@ -3016,7 +3025,8 @@
 
 void Document::didLoadAllScriptBlockingResources()
 {
-    loadingTaskRunner()->postTask(BLINK_FROM_HERE, m_executeScriptsWaitingForResourcesTask->cancelAndCreate());
+    Platform::current()->currentThread()->scheduler()->loadingTaskRunner()->postTask(
+        BLINK_FROM_HERE, m_executeScriptsWaitingForResourcesTask->cancelAndCreate());
 
     if (frame())
         frame()->loader().client()->didRemoveAllPendingStylesheet();
@@ -5740,22 +5750,12 @@
 {
     if (frame())
         return frame()->frameScheduler()->loadingTaskRunner();
-    if (m_importsController)
-        return m_importsController->master()->loadingTaskRunner();
-    if (m_contextDocument)
-        return m_contextDocument->loadingTaskRunner();
     return Platform::current()->currentThread()->scheduler()->loadingTaskRunner();
 }
 
 WebTaskRunner* Document::timerTaskRunner() const
 {
-    if (frame())
-        return m_frame->frameScheduler()->timerTaskRunner();
-    if (m_importsController)
-        return m_importsController->master()->timerTaskRunner();
-    if (m_contextDocument)
-        return m_contextDocument->timerTaskRunner();
-    return Platform::current()->currentThread()->scheduler()->timerTaskRunner();
+    return m_timers.timerTaskRunner();
 }
 
 DEFINE_TRACE(Document)
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index e4af2d6..22555f4 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -1116,6 +1116,8 @@
 
     void reportBlockedScriptExecutionToInspector(const String& directiveText) final;
 
+    double timerAlignmentInterval() const final;
+
     void updateTitle(const String&);
     void updateFocusAppearanceTimerFired(Timer<Document>*);
     void updateBaseURL();
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.h b/third_party/WebKit/Source/core/dom/ExecutionContext.h
index 49b7e8f..ee4af7d 100644
--- a/third_party/WebKit/Source/core/dom/ExecutionContext.h
+++ b/third_party/WebKit/Source/core/dom/ExecutionContext.h
@@ -89,6 +89,7 @@
     virtual LocalDOMWindow* executingWindow() { return 0; }
     virtual String userAgent() const = 0;
     virtual void postTask(const WebTraceLocation&, PassOwnPtr<ExecutionContextTask>) = 0; // Executes the task on context's thread asynchronously.
+    virtual double timerAlignmentInterval() const = 0;
 
     // Gets the DOMTimerCoordinator which maintains the "active timer
     // list" of tasks created by setTimeout and setInterval. The
diff --git a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
index db7a582..09e5579 100644
--- a/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptRunnerTest.cpp
@@ -137,6 +137,7 @@
     void postIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override { }
     void postNonNestableIdleTask(const WebTraceLocation&, WebThread::IdleTask*) override { }
     void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*) override { }
+    void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task*, double monotonicTime) override { }
     WebPassOwnPtr<WebViewScheduler> createWebViewScheduler(blink::WebView*) override { return nullptr; }
     void suspendTimerQueue() override { }
     void resumeTimerQueue() override { }
diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.cpp b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
index cdfb3d8..b820cc6 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimer.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimer.cpp
@@ -52,6 +52,19 @@
         && nestingLevel == 1; // Gestures should not be forwarded to nested timers.
 }
 
+double DOMTimer::hiddenPageAlignmentInterval()
+{
+    // Timers on hidden pages are aligned so that they fire once per
+    // second at most.
+    return 1.0;
+}
+
+double DOMTimer::visiblePageAlignmentInterval()
+{
+    // Alignment does not apply to timers on visible pages.
+    return 0;
+}
+
 int DOMTimer::install(ExecutionContext* context, PassOwnPtrWillBeRawPtr<ScheduledAction> action, int timeout, bool singleShot)
 {
     int timeoutID = context->timers()->installNewTimeout(context, action, timeout, singleShot);
@@ -151,6 +164,42 @@
     m_action.clear();
 }
 
+double DOMTimer::alignedFireTime(double fireTime) const
+{
+    double alignmentInterval = executionContext()->timerAlignmentInterval();
+    if (alignmentInterval) {
+        double currentTime = monotonicallyIncreasingTime();
+        if (fireTime <= currentTime)
+            return fireTime;
+
+        // When a repeating timer is scheduled for exactly the
+        // background page alignment interval, because it's impossible
+        // for the timer to be rescheduled instantaneously, it misses
+        // every other fire time. Avoid this by looking at the next
+        // fire time rounded both down and up.
+
+        double alignedTimeRoundedDown = floor(fireTime / alignmentInterval) * alignmentInterval;
+        double alignedTimeRoundedUp = ceil(fireTime / alignmentInterval) * alignmentInterval;
+
+        // If the version rounded down is in the past, discard it
+        // immediately.
+
+        if (alignedTimeRoundedDown <= currentTime)
+            return alignedTimeRoundedUp;
+
+        // Only use the rounded-down time if it's within a certain
+        // tolerance of the fire time. This avoids speeding up timers
+        // on background pages in the common case.
+
+        if (fireTime - alignedTimeRoundedDown < minimumInterval)
+            return alignedTimeRoundedDown;
+
+        return alignedTimeRoundedUp;
+    }
+
+    return fireTime;
+}
+
 WebTaskRunner* DOMTimer::timerTaskRunner()
 {
     return executionContext()->timers()->timerTaskRunner();
diff --git a/third_party/WebKit/Source/core/frame/DOMTimer.h b/third_party/WebKit/Source/core/frame/DOMTimer.h
index 59236c8..4912ace9 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimer.h
+++ b/third_party/WebKit/Source/core/frame/DOMTimer.h
@@ -52,6 +52,10 @@
     // ActiveDOMObject
     void stop() override;
 
+    // The following are essentially constants. All intervals are in seconds.
+    static double hiddenPageAlignmentInterval();
+    static double visiblePageAlignmentInterval();
+
     // Eager finalization is needed to promptly stop this Timer object.
     // Otherwise timer events might fire at an object that's slated for destruction
     // (when lazily swept), but some of its members (m_action) may already have
@@ -72,6 +76,9 @@
     DOMTimer(ExecutionContext*, PassOwnPtrWillBeRawPtr<ScheduledAction>, int interval, bool singleShot, int timeoutID);
     void fired() override;
 
+    // Retuns timer fire time rounded to the next multiple of timer alignment interval.
+    double alignedFireTime(double) const override;
+
     WebTaskRunner* timerTaskRunner() override;
 
     int m_timeoutID;
diff --git a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
index 4401db92..240f8cb 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.cpp
@@ -42,6 +42,20 @@
     m_timers.remove(timeoutID);
 }
 
+void DOMTimerCoordinator::didChangeTimerAlignmentInterval()
+{
+    // Reschedule timers in increasing order of desired run time to maintain their relative order.
+    // TODO(skyostil): Move timer alignment into the scheduler.
+    WillBeHeapVector<RawPtrWillBeMember<DOMTimer>> timers;
+    timers.reserveCapacity(m_timers.size());
+    for (TimeoutMap::iterator iter = m_timers.begin(); iter != m_timers.end(); ++iter)
+        timers.append(iter->value.get());
+    std::sort(timers.begin(), timers.end(), TimerBase::Comparator());
+    double now = monotonicallyIncreasingTime();
+    for (DOMTimer* timer : timers)
+        timer->didChangeAlignmentInterval(now);
+}
+
 DEFINE_TRACE(DOMTimerCoordinator)
 {
 #if ENABLE(OILPAN)
diff --git a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h
index 2c7f0cde4..69c8d222 100644
--- a/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h
+++ b/third_party/WebKit/Source/core/frame/DOMTimerCoordinator.h
@@ -34,6 +34,11 @@
     // destroy the timer.
     void removeTimeoutByID(int id);
 
+    // Notifies registered timers that
+    // ExecutionContext::timerAlignmentInterval has changed so that
+    // timers can adjust their schedule to the new alignment interval.
+    void didChangeTimerAlignmentInterval();
+
     // Timers created during the execution of other timers, and
     // repeating timers, are throttled. Timer nesting level tracks the
     // number of linked timers or repetitions of a timer. See
diff --git a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
index 4d289e4..34fa2a0 100644
--- a/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalDOMWindow.cpp
@@ -149,7 +149,10 @@
         SuspendableTimer::trace(visitor);
     }
 
-    // TODO(alexclarke): Override timerTaskRunner() to pass in a document specific default task runner.
+    WebTaskRunner* timerTaskRunner() override
+    {
+        return m_window->document()->timerTaskRunner();
+    }
 
 private:
     void fired() override
diff --git a/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp b/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp
index b2d690a..8a9d048 100644
--- a/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp
+++ b/third_party/WebKit/Source/core/frame/SuspendableTimer.cpp
@@ -66,7 +66,7 @@
     m_suspended = true;
 #endif
     if (isActive()) {
-        m_nextFireInterval = nextFireInterval();
+        m_nextFireInterval = nextUnalignedFireInterval();
         ASSERT(m_nextFireInterval >= 0.0);
         m_repeatInterval = repeatInterval();
         TimerBase::stop();
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 3ec85f1..dac637d 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -117,6 +117,7 @@
     , m_tabKeyCyclesThroughElements(true)
     , m_defersLoading(false)
     , m_deviceScaleFactor(1)
+    , m_timerAlignmentInterval(DOMTimer::visiblePageAlignmentInterval())
     , m_visibilityState(PageVisibilityStateVisible)
     , m_isCursorVisible(true)
 #if ENABLE(ASSERT)
@@ -355,15 +356,41 @@
     }
 }
 
+void Page::setTimerAlignmentInterval(double interval)
+{
+    if (interval == m_timerAlignmentInterval)
+        return;
+
+    m_timerAlignmentInterval = interval;
+    for (Frame* frame = mainFrame(); frame; frame = frame->tree().traverseNextWithWrap(false)) {
+        if (!frame->isLocalFrame())
+            continue;
+
+        if (Document* document = toLocalFrame(frame)->document()) {
+            if (DOMTimerCoordinator* timers = document->timers()) {
+                timers->didChangeTimerAlignmentInterval();
+            }
+        }
+    }
+}
+
+double Page::timerAlignmentInterval() const
+{
+    return m_timerAlignmentInterval;
+}
+
 void Page::setVisibilityState(PageVisibilityState visibilityState, bool isInitialState)
 {
     if (m_visibilityState == visibilityState)
         return;
     m_visibilityState = visibilityState;
 
+    // TODO(alexclarke): Move throttling of timers to chromium.
     if (visibilityState == PageVisibilityStateVisible) {
+        setTimerAlignmentInterval(DOMTimer::visiblePageAlignmentInterval());
         memoryPurgeController().pageBecameActive();
     } else {
+        setTimerAlignmentInterval(DOMTimer::hiddenPageAlignmentInterval());
         if (!isInitialState)
             memoryPurgeController().pageBecameInactive();
     }
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index 1ce3024..15cfbc5 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -185,6 +185,8 @@
     bool isPainting() const { return m_isPainting; }
 #endif
 
+    double timerAlignmentInterval() const;
+
     class CORE_EXPORT MultisamplingChangedObserver : public WillBeGarbageCollectedMixin {
     public:
         virtual void multisamplingChanged(bool) = 0;
@@ -211,6 +213,8 @@
 private:
     void initGroup();
 
+    void setTimerAlignmentInterval(double);
+
     void setNeedsLayoutInAllFrames();
 
     // SettingsDelegate overrides.
@@ -257,6 +261,8 @@
 
     float m_deviceScaleFactor;
 
+    double m_timerAlignmentInterval;
+
     PageVisibilityState m_visibilityState;
 
     bool m_isCursorVisible;
diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp b/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp
index 46ab161..e3e79bc 100644
--- a/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp
+++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.cpp
@@ -34,6 +34,11 @@
 {
 }
 
+double NullExecutionContext::timerAlignmentInterval() const
+{
+    return DOMTimer::visiblePageAlignmentInterval();
+}
+
 bool NullExecutionContext::isSecureContext(String& errorMessage, const SecureContextCheck privilegeContextCheck) const
 {
     return true;
diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.h b/third_party/WebKit/Source/core/testing/NullExecutionContext.h
index 4838afd..9cb49be3 100644
--- a/third_party/WebKit/Source/core/testing/NullExecutionContext.h
+++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.h
@@ -37,6 +37,8 @@
     SecurityContext& securityContext() override { return *this; }
     DOMTimerCoordinator* timers() override { return nullptr; }
 
+    double timerAlignmentInterval() const;
+
     void addConsoleMessage(PassRefPtrWillBeRawPtr<ConsoleMessage>) override { }
     void logExceptionToConsole(const String& errorMessage, int scriptId, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>) override { }
 
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
index d5776efe..adfe55e 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -148,6 +148,11 @@
     m_script->disableEval(errorMessage);
 }
 
+double WorkerGlobalScope::timerAlignmentInterval() const
+{
+    return DOMTimer::visiblePageAlignmentInterval();
+}
+
 DOMTimerCoordinator* WorkerGlobalScope::timers()
 {
     return &m_timers;
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
index b03f823..0a0c4cb 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
@@ -120,6 +120,7 @@
     bool isContextThread() const final;
     bool isJSExecutionForbidden() const final;
 
+    double timerAlignmentInterval() const final;
     DOMTimerCoordinator* timers() final;
 
     WorkerInspectorController* workerInspectorController() { return m_workerInspectorController.get(); }
diff --git a/third_party/WebKit/Source/platform/Timer.cpp b/third_party/WebKit/Source/platform/Timer.cpp
index e27520d..ad619a3c 100644
--- a/third_party/WebKit/Source/platform/Timer.cpp
+++ b/third_party/WebKit/Source/platform/Timer.cpp
@@ -43,6 +43,7 @@
 
 TimerBase::TimerBase()
     : m_nextFireTime(0)
+    , m_unalignedNextFireTime(0)
     , m_repeatInterval(0)
     , m_cancellableTimerTask(nullptr)
     , m_webScheduler(Platform::current()->currentThread()->scheduler())
@@ -95,16 +96,23 @@
 {
     ASSERT(m_thread == currentThread());
 
-    double newTime = now + delay;
+    m_unalignedNextFireTime = now + delay;
 
+    double newTime = alignedFireTime(m_unalignedNextFireTime);
     if (m_nextFireTime != newTime) {
         m_nextFireTime = newTime;
         if (m_cancellableTimerTask)
             m_cancellableTimerTask->cancel();
         m_cancellableTimerTask = new CancellableTimerTask(this);
-
-        double delayMs = 1000.0 * (newTime - now);
-        timerTaskRunner()->postDelayedTask(m_location, m_cancellableTimerTask, delayMs);
+        if (newTime != m_unalignedNextFireTime) {
+            // If the timer is being aligned, use postTimerTaskAt() to schedule it
+            // so that the relative order of aligned timers is preserved.
+            // TODO(skyostil): Move timer alignment into the scheduler.
+            m_webScheduler->postTimerTaskAt(m_location, m_cancellableTimerTask, m_nextFireTime);
+        } else {
+            double delayMs = 1000.0 * (newTime - now);
+            m_webScheduler->timerTaskRunner()->postDelayedTask(m_location, m_cancellableTimerTask, delayMs);
+        }
     }
 }
 
@@ -118,24 +126,34 @@
     ASSERT_WITH_MESSAGE(m_thread == currentThread(), "Timer posted by %s %s was run on a different thread", m_location.functionName(), m_location.fileName());
     TRACE_EVENT_SET_SAMPLING_STATE("blink", "BlinkInternal");
 
+    m_nextFireTime = 0;
     if (m_repeatInterval) {
         double now = monotonicallyIncreasingTime();
         // This computation should be drift free, and it will cope if we miss a beat,
         // which can easily happen if the thread is busy.  It will also cope if we get
         // called slightly before m_unalignedNextFireTime, which can happen due to lack
         // of timer precision.
-        double intervalToNextFireTime = m_repeatInterval - fmod(now - m_nextFireTime, m_repeatInterval);
+        double intervalToNextFireTime = m_repeatInterval - fmod(now - m_unalignedNextFireTime, m_repeatInterval);
         setNextFireTime(monotonicallyIncreasingTime(), intervalToNextFireTime);
-    } else {
-        m_nextFireTime = 0;
     }
     fired();
     TRACE_EVENT_SET_SAMPLING_STATE("blink", "Sleeping");
 }
 
+void TimerBase::didChangeAlignmentInterval(double now)
+{
+    setNextFireTime(now, m_unalignedNextFireTime - now);
+}
+
+double TimerBase::nextUnalignedFireInterval() const
+{
+    ASSERT(isActive());
+    return std::max(m_unalignedNextFireTime - monotonicallyIncreasingTime(), 0.0);
+}
+
 bool TimerBase::Comparator::operator()(const TimerBase* a, const TimerBase* b) const
 {
-    return a->m_nextFireTime < b->m_nextFireTime;
+    return a->m_unalignedNextFireTime < b->m_unalignedNextFireTime;
 }
 
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/Timer.h b/third_party/WebKit/Source/platform/Timer.h
index d6616a8..f2c4aa3 100644
--- a/third_party/WebKit/Source/platform/Timer.h
+++ b/third_party/WebKit/Source/platform/Timer.h
@@ -61,6 +61,7 @@
     const WebTraceLocation& location() const { return m_location; }
 
     double nextFireInterval() const;
+    double nextUnalignedFireInterval() const;
     double repeatInterval() const { return m_repeatInterval; }
 
     void augmentRepeatInterval(double delta) {
@@ -69,6 +70,8 @@
         m_repeatInterval += delta;
     }
 
+    void didChangeAlignmentInterval(double now);
+
     struct PLATFORM_EXPORT Comparator {
         bool operator()(const TimerBase* a, const TimerBase* b) const;
     };
@@ -118,6 +121,7 @@
     };
 
     double m_nextFireTime; // 0 if inactive
+    double m_unalignedNextFireTime; // m_nextFireTime not considering alignment interval
     double m_repeatInterval; // 0 if not repeating
     WebTraceLocation m_location;
     CancellableTimerTask* m_cancellableTimerTask; // NOT OWNED
diff --git a/third_party/WebKit/Source/platform/TimerTest.cpp b/third_party/WebKit/Source/platform/TimerTest.cpp
index 9f7bd52..4585d950 100644
--- a/third_party/WebKit/Source/platform/TimerTest.cpp
+++ b/third_party/WebKit/Source/platform/TimerTest.cpp
@@ -136,6 +136,11 @@
         return nullptr;
     }
 
+    void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task* task, double monotonicTime) override
+    {
+        m_timerTasks.push(DelayedTask(task, (monotonicTime - monotonicallyIncreasingTime()) * 1000));
+    }
+
     void runUntilIdle()
     {
         while (m_timerTasks.size()) {
@@ -693,6 +698,80 @@
     EXPECT_THAT(m_runTimes, ElementsAre(m_startTime + 20.0, m_startTime + 40.0));
 }
 
+class MockTimerWithAlignment : public TimerBase {
+public:
+    MockTimerWithAlignment() : m_lastFireTime(0.0), m_alignedFireTime(0.0) { }
+
+    void fired() override
+    {
+    }
+
+    double alignedFireTime(double fireTime) const override
+    {
+        m_lastFireTime = fireTime;
+        return m_alignedFireTime;
+    }
+
+    void setAlignedFireTime(double alignedFireTime)
+    {
+        m_alignedFireTime = alignedFireTime;
+    }
+
+    double lastFireTime() const
+    {
+        return m_lastFireTime;
+    }
+
+private:
+    mutable double m_lastFireTime;
+    double m_alignedFireTime;
+};
+
+TEST_F(TimerTest, TimerAlignment_OneShotZero)
+{
+    MockTimerWithAlignment timer;
+    timer.setAlignedFireTime(m_startTime + 1.0);
+
+    timer.start(0.0, 0.0, BLINK_FROM_HERE);
+
+    // The nextFireInterval gets overrriden.
+    EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval());
+    EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval());
+    EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime());
+}
+
+TEST_F(TimerTest, TimerAlignment_OneShotNonZero)
+{
+    MockTimerWithAlignment timer;
+    timer.setAlignedFireTime(m_startTime + 1.0);
+
+    timer.start(0.5, 0.0, BLINK_FROM_HERE);
+
+    // The nextFireInterval gets overrriden.
+    EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval());
+    EXPECT_FLOAT_EQ(0.5, timer.nextUnalignedFireInterval());
+    EXPECT_FLOAT_EQ(m_startTime + 0.5, timer.lastFireTime());
+}
+
+TEST_F(TimerTest, DidChangeAlignmentInterval)
+{
+    MockTimerWithAlignment timer;
+    timer.setAlignedFireTime(m_startTime + 1.0);
+
+    timer.start(0.0, 0.0, BLINK_FROM_HERE);
+
+    EXPECT_FLOAT_EQ(1.0, timer.nextFireInterval());
+    EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval());
+    EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime());
+
+    timer.setAlignedFireTime(m_startTime);
+    timer.didChangeAlignmentInterval(monotonicallyIncreasingTime());
+
+    EXPECT_FLOAT_EQ(0.0, timer.nextFireInterval());
+    EXPECT_FLOAT_EQ(0.0, timer.nextUnalignedFireInterval());
+    EXPECT_FLOAT_EQ(m_startTime, timer.lastFireTime());
+}
+
 TEST_F(TimerTest, RepeatingTimerDoesNotDrift)
 {
     Timer<TimerTest> timer(this, &TimerTest::recordNextFireTimeTask);
diff --git a/third_party/WebKit/public/platform/WebScheduler.h b/third_party/WebKit/public/platform/WebScheduler.h
index aeeb8f4..0af83a2d 100644
--- a/third_party/WebKit/public/platform/WebScheduler.h
+++ b/third_party/WebKit/public/platform/WebScheduler.h
@@ -60,6 +60,15 @@
     // Takes ownership of |IdleTask|. Can be called from any thread.
     virtual void postIdleTaskAfterWakeup(const WebTraceLocation&, WebThread::IdleTask*) = 0;
 
+    // Schedule a timer task to be run on the the associated WebThread. Timer Tasks
+    // tasks usually have the default priority, but may be delayed
+    // when the user is interacting with the device.
+    // |monotonicTime| is in the timebase of WTF::monotonicallyIncreasingTime().
+    // Takes ownership of |WebTaskRunner::Task|. Can be called from any thread.
+    // TODO(alexclarke): Move timer throttling for background pages to the
+    // chromium side and remove this.
+    virtual void postTimerTaskAt(const WebTraceLocation&, WebTaskRunner::Task*, double monotonicTime) = 0;
+
     // Returns a WebTaskRunner for loading tasks. Can be called from any thread.
     virtual WebTaskRunner* loadingTaskRunner() = 0;