Bug 1449620 - Extract an APZUpdater class from APZSampler. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 28 Mar 2018 18:36:42 -0400
changeset 410527 2b4ca22e460c14da73c2efb5ae2f07f70ffc8a4b
parent 410526 9211c666bc439ebbb2d30fc261b7e6dbe1658d01
child 410528 e7b8aee95ed2e3e92e3873c9088f292bc702aaf1
push id33730
push userdluca@mozilla.com
push dateThu, 29 Mar 2018 09:49:41 +0000
treeherdermozilla-central@f9755fa986d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1449620
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1449620 - Extract an APZUpdater class from APZSampler. r=botond The APZUpdater class holds the methods that are to be run on the updater thread. Note that there are a few differences between the APZSampler and APZUpdater classes - most notably, APZSampler no longer has a "RunOnSamplerThread" function because there should never be any need to dispatch runnables to the sampler thread. There is still a RunOnUpdaterThread in APZUpdater, as well as a mechanism for dispatching runnables to the controller thread via the updater thread. MozReview-Commit-ID: LLVWzRyhYWl
gfx/layers/apz/public/APZSampler.h
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZSampler.cpp
gfx/layers/apz/src/APZUpdater.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/FocusState.cpp
gfx/layers/apz/src/FocusState.h
gfx/layers/apz/src/HitTestingTreeNode.cpp
gfx/layers/apz/test/gtest/APZCBasicTester.h
gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
gfx/layers/apz/testutil/APZTestData.h
gfx/layers/ipc/APZCTreeManagerParent.cpp
gfx/layers/ipc/APZCTreeManagerParent.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
gfx/layers/moz.build
gfx/layers/wr/WebRenderBridgeParent.cpp
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -2,87 +2,50 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZSampler_h
 #define mozilla_layers_APZSampler_h
 
-#include "base/message_loop.h"
-#include "LayersTypes.h"
-#include "mozilla/layers/APZTestData.h"
 #include "mozilla/layers/AsyncCompositionManager.h" // for AsyncTransform
-#include "mozilla/Maybe.h"
 #include "nsTArray.h"
 #include "Units.h"
 
 namespace mozilla {
 
 class TimeStamp;
 
 namespace wr {
 class TransactionBuilder;
 struct WrTransformProperty;
 } // namespace wr
 
 namespace layers {
 
 class APZCTreeManager;
-class FocusTarget;
-class Layer;
 class LayerMetricsWrapper;
 struct ScrollThumbData;
-class WebRenderScrollData;
 
 /**
- * This interface is used to interact with the APZ code from the compositor
- * thread. It internally redispatches the functions to the sampler thread
- * in the case where the two threads are not the same.
+ * This interface exposes APZ methods related to "sampling" (i.e. reading the
+ * async transforms produced by APZ). These methods should all be called on
+ * the sampler thread.
  */
 class APZSampler {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZSampler)
 
 public:
   explicit APZSampler(const RefPtr<APZCTreeManager>& aApz);
 
-  bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
-
-  void ClearTree();
-  void UpdateFocusState(LayersId aRootLayerTreeId,
-                        LayersId aOriginatingLayersId,
-                        const FocusTarget& aFocusTarget);
-  void UpdateHitTestingTree(LayersId aRootLayerTreeId,
-                            Layer* aRoot,
-                            bool aIsFirstPaint,
-                            LayersId aOriginatingLayersId,
-                            uint32_t aPaintSequenceNumber);
-  void UpdateHitTestingTree(LayersId aRootLayerTreeId,
-                            const WebRenderScrollData& aScrollData,
-                            bool aIsFirstPaint,
-                            LayersId aOriginatingLayersId,
-                            uint32_t aPaintSequenceNumber);
-
-  void NotifyLayerTreeAdopted(LayersId aLayersId,
-                              const RefPtr<APZSampler>& aOldSampler);
-  void NotifyLayerTreeRemoved(LayersId aLayersId);
-
   bool PushStateToWR(wr::TransactionBuilder& aTxn,
                      const TimeStamp& aSampleTime,
                      nsTArray<wr::WrTransformProperty>& aTransformArray);
 
-  bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
-
-  void SetTestAsyncScrollOffset(LayersId aLayersId,
-                                const FrameMetrics::ViewID& aScrollId,
-                                const CSSPoint& aOffset);
-  void SetTestAsyncZoom(LayersId aLayersId,
-                        const FrameMetrics::ViewID& aScrollId,
-                        const LayerToParentLayerScale& aZoom);
-
   bool SampleAnimations(const LayerMetricsWrapper& aLayer,
                         const TimeStamp& aSampleTime);
 
   /**
    * Compute the updated shadow transform for a scroll thumb layer that
    * reflects async scrolling of the associated scroll frame.
    *
    * Refer to APZCTreeManager::ComputeTransformForScrollThumb for the
@@ -110,37 +73,20 @@ public:
   /**
    * This can be used to assert that the current thread is the
    * sampler thread (which samples the async transform).
    * This does nothing if thread assertions are disabled.
    */
   void AssertOnSamplerThread();
 
   /**
-   * Runs the given task on the APZ "sampler thread" for this APZSampler. If
-   * this function is called from the sampler thread itself then the task is
-   * run immediately without getting queued.
-   */
-  void RunOnSamplerThread(already_AddRefed<Runnable> aTask);
-
-  /**
    * Returns true if currently on the APZSampler's "sampler thread".
    */
   bool IsSamplerThread();
 
-  /**
-   * Dispatches the given task to the APZ "controller thread", but does it *from*
-   * the sampler thread. That is, if the thread on which this function is called
-   * is not the sampler thread, the task is first dispatched to the sampler thread.
-   * When the sampler thread runs it (or if this is called directly on the sampler
-   * thread), that is when the task gets dispatched to the controller thread.
-   * The controller thread then actually runs the task.
-   */
-  void RunOnControllerThread(already_AddRefed<Runnable> aTask);
-
 protected:
   virtual ~APZSampler();
 
 private:
   RefPtr<APZCTreeManager> mApz;
 };
 
 } // namespace layers
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZUpdater_h
+#define mozilla_layers_APZUpdater_h
+
+#include "LayersTypes.h"
+#include "mozilla/layers/APZTestData.h"
+#include "nsThreadUtils.h"
+#include "Units.h"
+
+namespace mozilla {
+namespace layers {
+
+class APZCTreeManager;
+class FocusTarget;
+class Layer;
+class WebRenderScrollData;
+
+/**
+ * This interface is used to send updates or otherwise mutate APZ internal
+ * state. These functions is usually called from the compositor thread in
+ * response to IPC messages. The method implementations internally redispatch
+ * themselves to the updater thread in the case where the compositor thread
+ * is not the updater thread.
+ */
+class APZUpdater {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZUpdater)
+
+public:
+  explicit APZUpdater(const RefPtr<APZCTreeManager>& aApz);
+
+  bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
+
+  void ClearTree();
+  void UpdateFocusState(LayersId aRootLayerTreeId,
+                        LayersId aOriginatingLayersId,
+                        const FocusTarget& aFocusTarget);
+  void UpdateHitTestingTree(LayersId aRootLayerTreeId,
+                            Layer* aRoot,
+                            bool aIsFirstPaint,
+                            LayersId aOriginatingLayersId,
+                            uint32_t aPaintSequenceNumber);
+  void UpdateHitTestingTree(LayersId aRootLayerTreeId,
+                            const WebRenderScrollData& aScrollData,
+                            bool aIsFirstPaint,
+                            LayersId aOriginatingLayersId,
+                            uint32_t aPaintSequenceNumber);
+
+  void NotifyLayerTreeAdopted(LayersId aLayersId,
+                              const RefPtr<APZUpdater>& aOldUpdater);
+  void NotifyLayerTreeRemoved(LayersId aLayersId);
+
+  bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
+
+  void SetTestAsyncScrollOffset(LayersId aLayersId,
+                                const FrameMetrics::ViewID& aScrollId,
+                                const CSSPoint& aOffset);
+  void SetTestAsyncZoom(LayersId aLayersId,
+                        const FrameMetrics::ViewID& aScrollId,
+                        const LayerToParentLayerScale& aZoom);
+
+  /**
+   * This can be used to assert that the current thread is the
+   * updater thread (which samples the async transform).
+   * This does nothing if thread assertions are disabled.
+   */
+  void AssertOnUpdaterThread();
+
+  /**
+   * Runs the given task on the APZ "updater thread" for this APZUpdater. If
+   * this function is called from the updater thread itself then the task is
+   * run immediately without getting queued.
+   */
+  void RunOnUpdaterThread(already_AddRefed<Runnable> aTask);
+
+  /**
+   * Returns true if currently on the APZUpdater's "updater thread".
+   */
+  bool IsUpdaterThread();
+
+  /**
+   * Dispatches the given task to the APZ "controller thread", but does it *from*
+   * the updater thread. That is, if the thread on which this function is called
+   * is not the updater thread, the task is first dispatched to the updater thread.
+   * When the updater thread runs it (or if this is called directly on the updater
+   * thread), that is when the task gets dispatched to the controller thread.
+   * The controller thread then actually runs the task.
+   */
+  void RunOnControllerThread(already_AddRefed<Runnable> aTask);
+
+protected:
+  virtual ~APZUpdater();
+
+private:
+  RefPtr<APZCTreeManager> mApz;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/MouseEventBinding.h" // for MouseEvent constants
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "mozilla/gfx/GPUParent.h"      // for GPUParent
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/layers/APZSampler.h"  // for APZSampler
 #include "mozilla/layers/APZThreadUtils.h"  // for AssertOnControllerThread, etc
+#include "mozilla/layers/APZUpdater.h"  // for APZUpdater
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "mozilla/layers/WebRenderScrollDataWrapper.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/mozalloc.h"           // for operator new
 #include "mozilla/TouchEvents.h"
@@ -221,16 +222,17 @@ private:
   InputData& mEvent;
   bool mMayChangeFocus;
 };
 
 APZCTreeManager::APZCTreeManager(LayersId aRootLayersId)
     : mInputQueue(new InputQueue()),
       mRootLayersId(aRootLayersId),
       mSampler(nullptr),
+      mUpdater(nullptr),
       mTreeLock("APZCTreeLock"),
       mHitResultForInputBlock(CompositorHitTestInfo::eInvisibleToHitTest),
       mRetainedTouchIdentifier(-1),
       mInScrollbarTouchDrag(false),
       mApzcTreeLog("apzctree"),
       mTestDataLock("APZTestDataLock"),
       mDPI(160.0)
 {
@@ -252,20 +254,28 @@ void
 APZCTreeManager::SetSampler(APZSampler* aSampler)
 {
   // We're either setting the sampler or clearing it
   MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
   mSampler = aSampler;
 }
 
 void
+APZCTreeManager::SetUpdater(APZUpdater* aUpdater)
+{
+  // We're either setting the updater or clearing it
+  MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
+  mUpdater = aUpdater;
+}
+
+void
 APZCTreeManager::NotifyLayerTreeAdopted(LayersId aLayersId,
                                         const RefPtr<APZCTreeManager>& aOldApzcTreeManager)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   if (aOldApzcTreeManager) {
     aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
     // While we could move the focus target information from the old APZC tree
     // manager into this one, it's safer to not do that, as we'll probably have
     // that information repopulated soon anyway (on the next layers update).
   }
 
@@ -282,17 +292,17 @@ APZCTreeManager::NotifyLayerTreeAdopted(
     MutexAutoLock lock(mTestDataLock);
     mTestData[aLayersId] = Move(adoptedData);
   }
 }
 
 void
 APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   mFocusState.RemoveFocusTarget(aLayersId);
 
   { // scope lock
     MutexAutoLock lock(mTestDataLock);
     mTestData.erase(aLayersId);
   }
 }
@@ -477,17 +487,17 @@ APZCTreeManager::UpdateHitTestingTreeImp
 #endif
 }
 
 void
 APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
                                   LayersId aOriginatingLayersId,
                                   const FocusTarget& aFocusTarget)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   if (!gfxPrefs::APZKeyboardEnabled()) {
     return;
   }
 
   mFocusState.Update(aRootLayerTreeId,
                      aOriginatingLayersId,
                      aFocusTarget);
@@ -495,31 +505,31 @@ APZCTreeManager::UpdateFocusState(Layers
 
 void
 APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                       Layer* aRoot,
                                       bool aIsFirstPaint,
                                       LayersId aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   LayerMetricsWrapper root(aRoot);
   UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
                            aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 void
 APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                       const WebRenderScrollData& aScrollData,
                                       bool aIsFirstPaint,
                                       LayersId aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   WebRenderScrollDataWrapper wrapper(&aScrollData);
   UpdateHitTestingTreeImpl(aRootLayerTreeId, wrapper, aIsFirstPaint,
                            aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 bool
 APZCTreeManager::PushStateToWR(wr::TransactionBuilder& aTxn,
@@ -1908,17 +1918,17 @@ APZCTreeManager::SetKeyboardMap(const Ke
   mKeyboardMap = aKeyboardMap;
 }
 
 void
 APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
                             const CSSRect& aRect,
                             const uint32_t aFlags)
 {
-  // We could probably move this to run on the sampler thread if needed, but
+  // We could probably move this to run on the updater thread if needed, but
   // either way we should restrict it to a single thread. For now let's use the
   // controller thread.
   APZThreadUtils::AssertOnControllerThread();
 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   if (apzc) {
     apzc->ZoomToRect(aRect, aFlags);
   }
@@ -1957,33 +1967,33 @@ APZCTreeManager::SetTargetAPZC(uint64_t 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
   mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
 }
 
 void
 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                                        const Maybe<ZoomConstraints>& aConstraints)
 {
-  if (!GetSampler()->IsSamplerThread()) {
+  if (!GetUpdater()->IsUpdaterThread()) {
     // This can happen if we're in the UI process and got a call directly from
     // nsBaseWidget (as opposed to over PAPZCTreeManager). We want this function
-    // to run on the sampler thread, so bounce it over.
+    // to run on the updater thread, so bounce it over.
     MOZ_ASSERT(XRE_IsParentProcess());
 
-    GetSampler()->RunOnSamplerThread(
+    GetUpdater()->RunOnUpdaterThread(
         NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
             "APZCTreeManager::UpdateZoomConstraints",
             this,
             &APZCTreeManager::UpdateZoomConstraints,
             aGuid,
             aConstraints));
     return;
   }
 
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   RecursiveMutexAutoLock lock(mTreeLock);
   RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
 
   // Propagate the zoom constraints down to the subtree, stopping at APZCs
   // which have their own zoom constraints or are in a different layers id.
   if (aConstraints) {
@@ -2068,17 +2078,17 @@ APZCTreeManager::AdjustScrollForSurfaceS
   if (apzc) {
     apzc->AdjustScrollForSurfaceShift(aShift);
   }
 }
 
 void
 APZCTreeManager::ClearTree()
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
 #if defined(MOZ_WIDGET_ANDROID)
   mToolbarAnimator->ClearTreeManager();
 #endif
 
   // Ensure that no references to APZCs are alive in any lingering input
   // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
   // the InputQueue.
@@ -3023,17 +3033,17 @@ APZCTreeManager::GetContentController(La
     });
   return controller.forget();
 }
 
 bool
 APZCTreeManager::GetAPZTestData(LayersId aLayersId,
                                 APZTestData* aOutData)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
   MutexAutoLock lock(mTestDataLock);
   auto it = mTestData.find(aLayersId);
   if (it == mTestData.end()) {
     return false;
   }
   *aOutData = *(it->second);
   return true;
 }
@@ -3210,16 +3220,31 @@ APZCTreeManager::GetSampler() const
 }
 
 void
 APZCTreeManager::AssertOnSamplerThread()
 {
   GetSampler()->AssertOnSamplerThread();
 }
 
+APZUpdater*
+APZCTreeManager::GetUpdater() const
+{
+  // We should always have an updater here, since in practice the updater
+  // is destroyed at the same time that this APZCTreeManager instance is.
+  MOZ_ASSERT(mUpdater);
+  return mUpdater;
+}
+
+void
+APZCTreeManager::AssertOnUpdaterThread()
+{
+  GetUpdater()->AssertOnUpdaterThread();
+}
+
 void
 APZCTreeManager::SetDPI(float aDpiValue)
 {
   APZThreadUtils::AssertOnControllerThread();
   mDPI = aDpiValue;
 }
 
 float
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -41,16 +41,17 @@ struct WrTransformProperty;
 }
 
 namespace layers {
 
 class Layer;
 class AsyncPanZoomController;
 class APZCTreeManagerParent;
 class APZSampler;
+class APZUpdater;
 class CompositorBridgeParent;
 class OverscrollHandoffChain;
 struct OverscrollHandoffState;
 class FocusTarget;
 struct FlingHandoffState;
 struct ScrollableLayerGuidHash;
 class LayerMetricsWrapper;
 class InputQueue;
@@ -76,17 +77,17 @@ struct ScrollThumbData;
  *
  * **************************************************************************
  */
 
 /**
  * This class manages the tree of AsyncPanZoomController instances. There is one
  * instance of this class owned by each CompositorBridgeParent, and it contains as
  * many AsyncPanZoomController instances as there are scrollable container layers.
- * This class generally lives on the sampler thread, although some functions
+ * This class generally lives on the updater thread, although some functions
  * may be called from other threads as noted; thread safety is ensured internally.
  *
  * The bulk of the work of this class happens as part of the UpdateHitTestingTree
  * function, which is when a layer tree update is received by the compositor.
  * This function walks through the layer tree and creates a tree of
  * HitTestingTreeNode instances to match the layer tree and for use in
  * hit-testing on the controller thread. APZC instances may be preserved across
  * calls to this function if the corresponding layers are still present in the layer
@@ -113,57 +114,58 @@ class APZCTreeManager : public IAPZCTree
   // UpdateHitTestingTree. All the state that we don't need to
   // push on the stack during recursion and pop on unwind is stored here.
   struct TreeBuildingState;
 
 public:
   explicit APZCTreeManager(LayersId aRootLayersId);
 
   void SetSampler(APZSampler* aSampler);
+  void SetUpdater(APZUpdater* aUpdater);
 
   /**
    * Notifies this APZCTreeManager that the associated compositor is now
    * responsible for managing another layers id, which got moved over from
    * some other compositor. That other compositor's APZCTreeManager is also
    * provided. This allows APZCTreeManager to transfer any necessary state
    * from the old APZCTreeManager related to that layers id.
-   * This function must be called on the sampler thread.
+   * This function must be called on the updater thread.
    */
   void NotifyLayerTreeAdopted(LayersId aLayersId,
                               const RefPtr<APZCTreeManager>& aOldTreeManager);
 
   /**
    * Notifies this APZCTreeManager that a layer tree being managed by the
    * associated compositor has been removed/destroyed. Note that this does
    * NOT get called during shutdown situations, when the root layer tree is
    * also getting destroyed.
-   * This function must be called on the sampler thread.
+   * This function must be called on the updater thread.
    */
   void NotifyLayerTreeRemoved(LayersId aLayersId);
 
   /**
    * Rebuild the focus state based on the focus target from the layer tree update
    * that just occurred.
-   * This must be called on the sampler thread.
+   * This must be called on the updater thread.
    *
    * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
    *                         to this APZCTreeManager
    * @param aOriginatingLayersId The layer tree ID of the layer corresponding to
    *                             this layer tree update.
    */
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
                         const FocusTarget& aFocusTarget);
 
   /**
    * Rebuild the hit-testing tree based on the layer update that just came up.
    * Preserve nodes and APZC instances where possible, but retire those whose
    * layers are no longer in the layer tree.
    *
-   * This must be called on the sampler thread as it walks the layer tree.
+   * This must be called on the updater thread as it walks the layer tree.
    *
    * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
    *                         to this APZCTreeManager
    * @param aRoot The root of the (full) layer tree
    * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint
    *                            applies.
    * @param aIsFirstPaint True if the layers update that this is called in response
    *                      to included a first-paint. If this is true, the part of
@@ -331,17 +333,17 @@ public:
   void AdjustScrollForSurfaceShift(const ScreenPoint& aShift);
 
   /**
    * Calls Destroy() on all APZC instances attached to the tree, and resets the
    * tree back to empty. This function must be called exactly once during the
    * lifetime of this APZCTreeManager, when this APZCTreeManager is no longer
    * needed. Failing to call this function may prevent objects from being freed
    * properly.
-   * This must be called on the sampler thread.
+   * This must be called on the updater thread.
    */
   void ClearTree();
 
   /**
    * Tests if a screen point intersect an apz in the tree.
    */
   bool HitTestAPZC(const ScreenIntPoint& aPoint);
 
@@ -523,22 +525,25 @@ public:
       AsyncPanZoomController* aApzc,
       const FrameMetrics& aMetrics,
       const ScrollThumbData& aThumbData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
+  // Assert that the current thread is the updater thread for this APZCTM.
+  void AssertOnUpdaterThread();
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
+  APZUpdater* GetUpdater() const;
 
   // Protected hooks for gtests subclass
   virtual AsyncPanZoomController* NewAPZCInstance(LayersId aLayersId,
                                                   GeckoContentController* aController);
 public:
   // Public hooks for gtests subclass
   virtual TimeStamp GetFrameTime();
 
@@ -700,28 +705,34 @@ private:
   LayersId mRootLayersId;
 
   /* Pointer to the APZSampler instance that is bound to this APZCTreeManager.
    * The sampler has a RefPtr to this class, and this non-owning raw pointer
    * back to the APZSampler is nulled out in the sampler's destructor, so this
    * pointer should always be valid.
    */
   APZSampler* MOZ_NON_OWNING_REF mSampler;
+  /* Pointer to the APZUpdater instance that is bound to this APZCTreeManager.
+   * The updater has a RefPtr to this class, and this non-owning raw pointer
+   * back to the APZUpdater is nulled out in the updater's destructor, so this
+   * pointer should always be valid.
+   */
+  APZUpdater* MOZ_NON_OWNING_REF mUpdater;
 
   /* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
    * This lock does not need to be held while manipulating a single APZC instance in
    * isolation (that is, if its tree pointers are not being accessed or mutated). The
    * lock also needs to be held when accessing the mRootNode instance variable, as that
    * is considered part of the APZC tree management state.
    * IMPORTANT: See the note about lock ordering at the top of this file. */
   mutable mozilla::RecursiveMutex mTreeLock;
   RefPtr<HitTestingTreeNode> mRootNode;
 
   /* Holds the zoom constraints for scrollable layers, as determined by the
-   * the main-thread gecko code. This can only be accessed on the sampler
+   * the main-thread gecko code. This can only be accessed on the updater
    * thread. */
   std::unordered_map<ScrollableLayerGuid, ZoomConstraints, ScrollableLayerGuidHash> mZoomConstraints;
   /* A list of keyboard shortcuts to use for translating keyboard inputs into
    * keyboard actions. This is gathered on the main thread from XBL bindings.
    * This must only be accessed on the controller thread.
    */
   KeyboardMap mKeyboardMap;
   /* This tracks the focus targets of chrome and content and whether we have
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -25,187 +25,26 @@ APZSampler::APZSampler(const RefPtr<APZC
 }
 
 APZSampler::~APZSampler()
 {
   mApz->SetSampler(nullptr);
 }
 
 bool
-APZSampler::HasTreeManager(const RefPtr<APZCTreeManager>& aApz)
-{
-  return aApz.get() == mApz.get();
-}
-
-void
-APZSampler::ClearTree()
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnSamplerThread(NewRunnableMethod(
-      "APZSampler::ClearTree",
-      mApz,
-      &APZCTreeManager::ClearTree));
-}
-
-void
-APZSampler::UpdateFocusState(LayersId aRootLayerTreeId,
-                             LayersId aOriginatingLayersId,
-                             const FocusTarget& aFocusTarget)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnSamplerThread(NewRunnableMethod<LayersId, LayersId, FocusTarget>(
-      "APZSampler::UpdateFocusState",
-      mApz,
-      &APZCTreeManager::UpdateFocusState,
-      aRootLayerTreeId,
-      aOriginatingLayersId,
-      aFocusTarget));
-}
-
-void
-APZSampler::UpdateHitTestingTree(LayersId aRootLayerTreeId,
-                                 Layer* aRoot,
-                                 bool aIsFirstPaint,
-                                 LayersId aOriginatingLayersId,
-                                 uint32_t aPaintSequenceNumber)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  AssertOnSamplerThread();
-  mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
-      aOriginatingLayersId, aPaintSequenceNumber);
-}
-
-void
-APZSampler::UpdateHitTestingTree(LayersId aRootLayerTreeId,
-                                 const WebRenderScrollData& aScrollData,
-                                 bool aIsFirstPaint,
-                                 LayersId aOriginatingLayersId,
-                                 uint32_t aPaintSequenceNumber)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  // use the local variable to resolve the function overload.
-  auto func = static_cast<void (APZCTreeManager::*)(LayersId,
-                                                    const WebRenderScrollData&,
-                                                    bool,
-                                                    LayersId,
-                                                    uint32_t)>
-      (&APZCTreeManager::UpdateHitTestingTree);
-  RunOnSamplerThread(NewRunnableMethod<LayersId,
-                                       WebRenderScrollData,
-                                       bool,
-                                       LayersId,
-                                       uint32_t>(
-      "APZSampler::UpdateHitTestingTree",
-      mApz,
-      func,
-      aRootLayerTreeId,
-      aScrollData,
-      aIsFirstPaint,
-      aOriginatingLayersId,
-      aPaintSequenceNumber));
-}
-
-void
-APZSampler::NotifyLayerTreeAdopted(LayersId aLayersId,
-                                   const RefPtr<APZSampler>& aOldSampler)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnSamplerThread(NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
-      "APZSampler::NotifyLayerTreeAdopted",
-      mApz,
-      &APZCTreeManager::NotifyLayerTreeAdopted,
-      aLayersId,
-      aOldSampler ? aOldSampler->mApz : nullptr));
-}
-
-void
-APZSampler::NotifyLayerTreeRemoved(LayersId aLayersId)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnSamplerThread(NewRunnableMethod<LayersId>(
-      "APZSampler::NotifyLayerTreeRemoved",
-      mApz,
-      &APZCTreeManager::NotifyLayerTreeRemoved,
-      aLayersId));
-}
-
-bool
 APZSampler::PushStateToWR(wr::TransactionBuilder& aTxn,
                           const TimeStamp& aSampleTime,
                           nsTArray<wr::WrTransformProperty>& aTransformArray)
 {
   // This function will be removed eventually since we'll have WR pull
   // the transforms from APZ instead.
   return mApz->PushStateToWR(aTxn, aSampleTime, aTransformArray);
 }
 
 bool
-APZSampler::GetAPZTestData(LayersId aLayersId,
-                           APZTestData* aOutData)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-
-  RefPtr<APZCTreeManager> apz = mApz;
-  bool ret = false;
-  SynchronousTask waiter("APZSampler::GetAPZTestData");
-  RunOnSamplerThread(NS_NewRunnableFunction(
-    "APZSampler::GetAPZTestData",
-    [&]() {
-      AutoCompleteTask notifier(&waiter);
-      ret = apz->GetAPZTestData(aLayersId, aOutData);
-    }
-  ));
-
-  // Wait until the task posted above has run and populated aOutData and ret
-  waiter.Wait();
-
-  return ret;
-}
-
-void
-APZSampler::SetTestAsyncScrollOffset(LayersId aLayersId,
-                                     const FrameMetrics::ViewID& aScrollId,
-                                     const CSSPoint& aOffset)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RefPtr<APZCTreeManager> apz = mApz;
-  RunOnSamplerThread(NS_NewRunnableFunction(
-    "APZSampler::SetTestAsyncScrollOffset",
-    [=]() {
-      RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
-      if (apzc) {
-        apzc->SetTestAsyncScrollOffset(aOffset);
-      } else {
-        NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
-      }
-    }
-  ));
-}
-
-void
-APZSampler::SetTestAsyncZoom(LayersId aLayersId,
-                             const FrameMetrics::ViewID& aScrollId,
-                             const LayerToParentLayerScale& aZoom)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RefPtr<APZCTreeManager> apz = mApz;
-  RunOnSamplerThread(NS_NewRunnableFunction(
-    "APZSampler::SetTestAsyncZoom",
-    [=]() {
-      RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
-      if (apzc) {
-        apzc->SetTestAsyncZoom(aZoom);
-      } else {
-        NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
-      }
-    }
-  ));
-}
-
-bool
 APZSampler::SampleAnimations(const LayerMetricsWrapper& aLayer,
                              const TimeStamp& aSampleTime)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   // TODO: eventually we can drop the aLayer argument and just walk the APZ
   // tree directly in mApz.
@@ -309,46 +148,16 @@ APZSampler::HasUnusedAsyncTransform(cons
 void
 APZSampler::AssertOnSamplerThread()
 {
   if (APZThreadUtils::GetThreadAssertionsEnabled()) {
     MOZ_ASSERT(IsSamplerThread());
   }
 }
 
-void
-APZSampler::RunOnSamplerThread(already_AddRefed<Runnable> aTask)
-{
-  RefPtr<Runnable> task = aTask;
-
-  MessageLoop* loop = CompositorThreadHolder::Loop();
-  if (!loop) {
-    // Could happen during startup
-    NS_WARNING("Dropping task posted to sampler thread");
-    return;
-  }
-
-  if (IsSamplerThread()) {
-    task->Run();
-  } else {
-    loop->PostTask(task.forget());
-  }
-}
-
 bool
 APZSampler::IsSamplerThread()
 {
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
-void
-APZSampler::RunOnControllerThread(already_AddRefed<Runnable> aTask)
-{
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-
-  RunOnSamplerThread(NewRunnableFunction(
-      "APZSampler::RunOnControllerThread",
-      &APZThreadUtils::RunOnControllerThread,
-      Move(aTask)));
-}
-
 } // namespace layers
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/APZUpdater.h"
+
+#include "APZCTreeManager.h"
+#include "AsyncPanZoomController.h"
+#include "base/task.h"
+#include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/SynchronousTask.h"
+#include "mozilla/layers/WebRenderScrollData.h"
+
+namespace mozilla {
+namespace layers {
+
+APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
+  : mApz(aApz)
+{
+  MOZ_ASSERT(aApz);
+  mApz->SetUpdater(this);
+}
+
+APZUpdater::~APZUpdater()
+{
+  mApz->SetUpdater(nullptr);
+}
+
+bool
+APZUpdater::HasTreeManager(const RefPtr<APZCTreeManager>& aApz)
+{
+  return aApz.get() == mApz.get();
+}
+
+void
+APZUpdater::ClearTree()
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RunOnUpdaterThread(NewRunnableMethod(
+      "APZUpdater::ClearTree",
+      mApz,
+      &APZCTreeManager::ClearTree));
+}
+
+void
+APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
+                             LayersId aOriginatingLayersId,
+                             const FocusTarget& aFocusTarget)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RunOnUpdaterThread(NewRunnableMethod<LayersId, LayersId, FocusTarget>(
+      "APZUpdater::UpdateFocusState",
+      mApz,
+      &APZCTreeManager::UpdateFocusState,
+      aRootLayerTreeId,
+      aOriginatingLayersId,
+      aFocusTarget));
+}
+
+void
+APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId,
+                                 Layer* aRoot,
+                                 bool aIsFirstPaint,
+                                 LayersId aOriginatingLayersId,
+                                 uint32_t aPaintSequenceNumber)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  AssertOnUpdaterThread();
+  mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
+      aOriginatingLayersId, aPaintSequenceNumber);
+}
+
+void
+APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId,
+                                 const WebRenderScrollData& aScrollData,
+                                 bool aIsFirstPaint,
+                                 LayersId aOriginatingLayersId,
+                                 uint32_t aPaintSequenceNumber)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  // use the local variable to resolve the function overload.
+  auto func = static_cast<void (APZCTreeManager::*)(LayersId,
+                                                    const WebRenderScrollData&,
+                                                    bool,
+                                                    LayersId,
+                                                    uint32_t)>
+      (&APZCTreeManager::UpdateHitTestingTree);
+  RunOnUpdaterThread(NewRunnableMethod<LayersId,
+                                       WebRenderScrollData,
+                                       bool,
+                                       LayersId,
+                                       uint32_t>(
+      "APZUpdater::UpdateHitTestingTree",
+      mApz,
+      func,
+      aRootLayerTreeId,
+      aScrollData,
+      aIsFirstPaint,
+      aOriginatingLayersId,
+      aPaintSequenceNumber));
+}
+
+void
+APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
+                                   const RefPtr<APZUpdater>& aOldUpdater)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RunOnUpdaterThread(NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
+      "APZUpdater::NotifyLayerTreeAdopted",
+      mApz,
+      &APZCTreeManager::NotifyLayerTreeAdopted,
+      aLayersId,
+      aOldUpdater ? aOldUpdater->mApz : nullptr));
+}
+
+void
+APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RunOnUpdaterThread(NewRunnableMethod<LayersId>(
+      "APZUpdater::NotifyLayerTreeRemoved",
+      mApz,
+      &APZCTreeManager::NotifyLayerTreeRemoved,
+      aLayersId));
+}
+
+bool
+APZUpdater::GetAPZTestData(LayersId aLayersId,
+                           APZTestData* aOutData)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  RefPtr<APZCTreeManager> apz = mApz;
+  bool ret = false;
+  SynchronousTask waiter("APZUpdater::GetAPZTestData");
+  RunOnUpdaterThread(NS_NewRunnableFunction(
+    "APZUpdater::GetAPZTestData",
+    [&]() {
+      AutoCompleteTask notifier(&waiter);
+      ret = apz->GetAPZTestData(aLayersId, aOutData);
+    }
+  ));
+
+  // Wait until the task posted above has run and populated aOutData and ret
+  waiter.Wait();
+
+  return ret;
+}
+
+void
+APZUpdater::SetTestAsyncScrollOffset(LayersId aLayersId,
+                                     const FrameMetrics::ViewID& aScrollId,
+                                     const CSSPoint& aOffset)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RefPtr<APZCTreeManager> apz = mApz;
+  RunOnUpdaterThread(NS_NewRunnableFunction(
+    "APZUpdater::SetTestAsyncScrollOffset",
+    [=]() {
+      RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
+      if (apzc) {
+        apzc->SetTestAsyncScrollOffset(aOffset);
+      } else {
+        NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
+      }
+    }
+  ));
+}
+
+void
+APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
+                             const FrameMetrics::ViewID& aScrollId,
+                             const LayerToParentLayerScale& aZoom)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  RefPtr<APZCTreeManager> apz = mApz;
+  RunOnUpdaterThread(NS_NewRunnableFunction(
+    "APZUpdater::SetTestAsyncZoom",
+    [=]() {
+      RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
+      if (apzc) {
+        apzc->SetTestAsyncZoom(aZoom);
+      } else {
+        NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
+      }
+    }
+  ));
+}
+
+void
+APZUpdater::AssertOnUpdaterThread()
+{
+  if (APZThreadUtils::GetThreadAssertionsEnabled()) {
+    MOZ_ASSERT(IsUpdaterThread());
+  }
+}
+
+void
+APZUpdater::RunOnUpdaterThread(already_AddRefed<Runnable> aTask)
+{
+  RefPtr<Runnable> task = aTask;
+
+  MessageLoop* loop = CompositorThreadHolder::Loop();
+  if (!loop) {
+    // Could happen during startup
+    NS_WARNING("Dropping task posted to updater thread");
+    return;
+  }
+
+  if (IsUpdaterThread()) {
+    task->Run();
+  } else {
+    loop->PostTask(task.forget());
+  }
+}
+
+bool
+APZUpdater::IsUpdaterThread()
+{
+  return CompositorThreadHolder::IsInCompositorThread();
+}
+
+void
+APZUpdater::RunOnControllerThread(already_AddRefed<Runnable> aTask)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  RunOnUpdaterThread(NewRunnableFunction(
+      "APZUpdater::RunOnControllerThread",
+      &APZThreadUtils::RunOnControllerThread,
+      Move(aTask)));
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -843,17 +843,17 @@ AsyncPanZoomController::GetGestureEventL
 const RefPtr<InputQueue>&
 AsyncPanZoomController::GetInputQueue() const {
   return mInputQueue;
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   CancelAnimation(CancelAnimationFlags::ScrollSnap);
 
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
@@ -3814,17 +3814,17 @@ bool AsyncPanZoomController::IsCurrently
     this, Stringify(painted).c_str(), Stringify(visible).c_str());
   return true;
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata,
                                                  bool aIsFirstPaint,
                                                  bool aThisLayerTreeUpdated)
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   bool isDefault = mScrollMetadata.IsDefault();
 
   const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics();
 
   if ((aScrollMetadata == mLastContentPaintMetadata) && !isDefault) {
     // No new information here, skip it.
@@ -4086,16 +4086,24 @@ const ScrollMetadata& AsyncPanZoomContro
 void
 AsyncPanZoomController::AssertOnSamplerThread() const
 {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
     treeManagerLocal->AssertOnSamplerThread();
   }
 }
 
+void
+AsyncPanZoomController::AssertOnUpdaterThread() const
+{
+  if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+    treeManagerLocal->AssertOnUpdaterThread();
+  }
+}
+
 APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
   mRecursiveMutex.AssertNotCurrentThreadIn();
   return mTreeManager;
 }
 
 void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
   if (!aRect.IsFinite()) {
     NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...");
@@ -4440,17 +4448,17 @@ void AsyncPanZoomController::UpdateShare
     mSharedLock->Lock();
     *frame = mFrameMetrics;
     mSharedLock->Unlock();
   }
 }
 
 void AsyncPanZoomController::ShareCompositorFrameMetrics()
 {
-  AssertOnSamplerThread();
+  AssertOnUpdaterThread();
 
   // Only create the shared memory buffer if it hasn't already been created,
   // we are using progressive tile painting, and we have a
   // controller to pass the shared memory back to the content process/thread.
   if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
 
     // Create shared memory and initialize it with the current FrameMetrics value
     mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -235,16 +235,20 @@ public:
    * The return value indicates whether or not any currently running animation
    * should continue. If true, the compositor should schedule another composite.
    */
   bool AdvanceAnimations(const TimeStamp& aSampleTime);
 
   bool UpdateAnimation(const TimeStamp& aSampleTime,
                        nsTArray<RefPtr<Runnable>>* aOutDeferredTasks);
 
+  // --------------------------------------------------------------------------
+  // These methods must only be called on the updater thread.
+  //
+
   /**
    * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata
    * for the container layer corresponding to this APZC.
    * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the scroll metadata being sent with this call are
    * the initial metadata and the initial paint of the frame has just happened.
    */
   void NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata, bool aIsFirstPaint,
@@ -749,16 +753,17 @@ protected:
   /**
    * Gets the pointer to the apzc tree manager. All the access to tree manager
    * should be made via this method and not via private variable since this method
    * ensures that no lock is set.
    */
   APZCTreeManager* GetApzcTreeManager() const;
 
   void AssertOnSamplerThread() const;
+  void AssertOnUpdaterThread() const;
 
   /**
    * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
    * to the parent document. This excludes the transient compositor transform.
    * NOTE: This must be converted to LayoutDevicePoint relative to the child
    * document before sending over IPC to a child process.
    */
   bool ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut);
@@ -789,17 +794,17 @@ protected:
   void OnTouchEndOrCancel();
 
   LayersId mLayersId;
   RefPtr<CompositorController> mCompositorController;
   RefPtr<MetricsSharingController> mMetricsSharingController;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
-     sampler thread. */
+     updater thread. */
   RefPtr<GeckoContentController> mGeckoContentController;
   RefPtr<GestureEventListener> mGestureEventListener;
   mutable Monitor mRefPtrMonitor;
 
   // This is a raw pointer to avoid introducing a reference cycle between
   // AsyncPanZoomController and APZCTreeManager. Since these objects don't
   // live on the main thread, we can't use the cycle collector with them.
   // The APZCTreeManager owns the lifetime of the APZCs, so nulling this
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -65,17 +65,17 @@ FocusState::ReceiveFocusChangingEvent()
          mLastAPZProcessedEvent);
 }
 
 void
 FocusState::Update(LayersId aRootLayerTreeId,
                    LayersId aOriginatingLayersId,
                    const FocusTarget& aState)
 {
-  // This runs on the sampler thread, it's not worth passing around extra raw
+  // This runs on the updater thread, it's not worth passing around extra raw
   // pointers just to assert it.
 
   MutexAutoLock lock(mMutex);
 
   FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n",
          aRootLayerTreeId,
          aOriginatingLayersId,
          aState.Type(),
@@ -179,17 +179,17 @@ FocusState::Update(LayersId aRootLayerTr
       return;
     }
   }
 }
 
 void
 FocusState::RemoveFocusTarget(LayersId aLayersId)
 {
-  // This runs on the sampler thread, it's not worth passing around extra raw
+  // This runs on the updater thread, it's not worth passing around extra raw
   // pointers just to assert it.
   MutexAutoLock lock(mMutex);
 
   mFocusTree.erase(aLayersId);
 }
 
 Maybe<ScrollableLayerGuid>
 FocusState::GetHorizontalTarget() const
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -133,17 +133,17 @@ private:
    * update with a new confirmed target.
    * This can only be called by methods that have already acquired mMutex; they
    * have to pass their lock as compile-time proof.
    */
   bool IsCurrent(const MutexAutoLock& aLock) const;
 
 private:
   // All methods should hold this lock, since this class is accessed via both
-  // the sampler and controller threads.
+  // the updater and controller threads.
   mutable Mutex mMutex;
 
   // The set of focus targets received indexed by their layer tree ID
   std::unordered_map<LayersId,
                      FocusTarget,
                      LayersId::HashFn,
                      LayersId::EqualFn> mFocusTree;
 
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -52,17 +52,17 @@ HitTestingTreeNode::RecycleWith(AsyncPan
   // fields.
 }
 
 HitTestingTreeNode::~HitTestingTreeNode() = default;
 
 void
 HitTestingTreeNode::Destroy()
 {
-  // This runs on the sampler thread, it's not worth passing around extra raw
+  // This runs on the updater thread, it's not worth passing around extra raw
   // pointers just to assert it.
 
   mPrevSibling = nullptr;
   mLastChild = nullptr;
   mParent = nullptr;
 
   if (mApzc) {
     if (mIsPrimaryApzcHolder) {
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -9,32 +9,34 @@
 
 /**
  * Defines a test fixture used for testing a single APZC.
  */
 
 #include "APZTestCommon.h"
 #include "gfxPrefs.h"
 #include "mozilla/layers/APZSampler.h"
+#include "mozilla/layers/APZUpdater.h"
 
 class APZCBasicTester : public APZCTesterBase {
 public:
   explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
     : mGestureBehavior(aGestureBehavior)
   {
   }
 
 protected:
   virtual void SetUp()
   {
     gfxPrefs::GetSingleton();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     tm = new TestAPZCTreeManager(mcc);
+    updater = new APZUpdater(tm);
     sampler = new APZSampler(tm);
     apzc = new TestAsyncPanZoomController(LayersId{0}, mcc, tm, mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
     apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
   }
 
   /**
    * Get the APZC's scroll range in CSS pixels.
@@ -113,12 +115,13 @@ protected:
     apzc->AssertStateIsReset();
   }
 
   void TestOverscroll();
 
   AsyncPanZoomController::GestureBehavior mGestureBehavior;
   RefPtr<TestAPZCTreeManager> tm;
   RefPtr<APZSampler> sampler;
+  RefPtr<APZUpdater> updater;
   RefPtr<TestAsyncPanZoomController> apzc;
 };
 
 #endif // mozilla_layers_APZCBasicTester_h
--- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
+++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
@@ -11,26 +11,28 @@
  * Defines a test fixture used for testing multiple APZCs interacting in
  * an APZCTreeManager.
  */
 
 #include "APZTestCommon.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "mozilla/layers/APZSampler.h"
+#include "mozilla/layers/APZUpdater.h"
 
 class APZCTreeManagerTester : public APZCTesterBase {
 protected:
   virtual void SetUp() {
     gfxPrefs::GetSingleton();
     gfxPlatform::GetPlatform();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     manager = new TestAPZCTreeManager(mcc);
+    updater = new APZUpdater(manager);
     sampler = new APZSampler(manager);
   }
 
   virtual void TearDown() {
     while (mcc->RunThroughDelayedTasks());
     manager->ClearTree();
     manager->ClearContentController();
   }
@@ -52,16 +54,17 @@ protected:
   }
 
   nsTArray<RefPtr<Layer> > layers;
   RefPtr<LayerManager> lm;
   RefPtr<Layer> root;
 
   RefPtr<TestAPZCTreeManager> manager;
   RefPtr<APZSampler> sampler;
+  RefPtr<APZUpdater> updater;
 
 protected:
   static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,
                                             const CSSRect& aScrollableRect,
                                             const ParentLayerRect& aCompositionBounds)
   {
     ScrollMetadata metadata;
     FrameMetrics& metrics = metadata.GetMetrics();
--- a/gfx/layers/apz/testutil/APZTestData.h
+++ b/gfx/layers/apz/testutil/APZTestData.h
@@ -10,16 +10,17 @@
 #include <map>
 
 #include "gfxPrefs.h"
 #include "FrameMetrics.h"
 #include "nsDebug.h"             // for NS_WARNING
 #include "nsTArray.h"
 #include "mozilla/Assertions.h"  // for MOZ_ASSERT
 #include "mozilla/DebugOnly.h"   // for DebugOnly
+#include "mozilla/GfxMessageUtils.h" // for ParamTraits specializations
 #include "mozilla/ToString.h"    // for ToString
 #include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "ipc/IPCMessageUtils.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/ipc/APZCTreeManagerParent.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -2,53 +2,53 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/APZCTreeManagerParent.h"
 
 #include "apz/src/APZCTreeManager.h"
-#include "mozilla/layers/APZSampler.h"
 #include "mozilla/layers/APZThreadUtils.h"
+#include "mozilla/layers/APZUpdater.h"
 
 namespace mozilla {
 namespace layers {
 
 APZCTreeManagerParent::APZCTreeManagerParent(LayersId aLayersId,
                                              RefPtr<APZCTreeManager> aAPZCTreeManager,
-                                             RefPtr<APZSampler> aAPZSampler)
+                                             RefPtr<APZUpdater> aAPZUpdater)
   : mLayersId(aLayersId)
   , mTreeManager(Move(aAPZCTreeManager))
-  , mSampler(Move(aAPZSampler))
+  , mUpdater(Move(aAPZUpdater))
 {
   MOZ_ASSERT(mTreeManager != nullptr);
-  MOZ_ASSERT(mSampler != nullptr);
-  MOZ_ASSERT(mSampler->HasTreeManager(mTreeManager));
+  MOZ_ASSERT(mUpdater != nullptr);
+  MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
 }
 
 APZCTreeManagerParent::~APZCTreeManagerParent()
 {
 }
 
 void
 APZCTreeManagerParent::ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
-                                    RefPtr<APZSampler> aAPZSampler)
+                                    RefPtr<APZUpdater> aAPZUpdater)
 {
   MOZ_ASSERT(aAPZCTreeManager != nullptr);
-  MOZ_ASSERT(aAPZSampler != nullptr);
-  MOZ_ASSERT(aAPZSampler->HasTreeManager(aAPZCTreeManager));
+  MOZ_ASSERT(aAPZUpdater != nullptr);
+  MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
   mTreeManager = Move(aAPZCTreeManager);
-  mSampler = Move(aAPZSampler);
+  mUpdater = Move(aAPZUpdater);
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap)
 {
-  mSampler->RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
+  mUpdater->RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
     "layers::IAPZCTreeManager::SetKeyboardMap",
     mTreeManager,
     &IAPZCTreeManager::SetKeyboardMap,
     aKeyboardMap));
 
   return IPC_OK();
 }
 
@@ -59,31 +59,31 @@ APZCTreeManagerParent::RecvZoomToRect(
     const uint32_t& aFlags)
 {
   if (aGuid.mLayersId != mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
     NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
       "layers::IAPZCTreeManager::ZoomToRect",
       mTreeManager,
       &IAPZCTreeManager::ZoomToRect,
       aGuid, aRect, aFlags));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvContentReceivedInputBlock(
     const uint64_t& aInputBlockId,
     const bool& aPreventDefault)
 {
-  mSampler->RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
+  mUpdater->RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
     "layers::IAPZCTreeManager::ContentReceivedInputBlock",
     mTreeManager,
     &IAPZCTreeManager::ContentReceivedInputBlock,
     aInputBlockId,
     aPreventDefault));
 
   return IPC_OK();
 }
@@ -95,17 +95,17 @@ APZCTreeManagerParent::RecvSetTargetAPZC
 {
   for (size_t i = 0; i < aTargets.Length(); i++) {
     if (aTargets[i].mLayersId != mLayersId) {
       // Guard against bad data from hijacked child processes
       NS_ERROR("Unexpected layers id in RecvSetTargetAPZC; dropping message...");
       return IPC_FAIL_NO_REASON(this);
     }
   }
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
     NewRunnableMethod<uint64_t,
                       StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
       "layers::IAPZCTreeManager::SetTargetAPZC",
       mTreeManager,
       &IAPZCTreeManager::SetTargetAPZC,
       aInputBlockId,
       aTargets));
 
@@ -125,30 +125,30 @@ APZCTreeManagerParent::RecvUpdateZoomCon
 
   mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue)
 {
-  mSampler->RunOnControllerThread(NewRunnableMethod<float>(
+  mUpdater->RunOnControllerThread(NewRunnableMethod<float>(
     "layers::IAPZCTreeManager::SetDPI",
     mTreeManager,
     &IAPZCTreeManager::SetDPI,
     aDpiValue));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
     const uint64_t& aInputBlockId,
     nsTArray<TouchBehaviorFlags>&& aValues)
 {
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
     NewRunnableMethod<uint64_t,
                       StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
       "layers::IAPZCTreeManager::SetAllowedTouchBehavior",
       mTreeManager,
       &IAPZCTreeManager::SetAllowedTouchBehavior,
       aInputBlockId,
       Move(aValues)));
 
@@ -161,17 +161,17 @@ APZCTreeManagerParent::RecvStartScrollba
     const AsyncDragMetrics& aDragMetrics)
 {
   if (aGuid.mLayersId != mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
     NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
       "layers::IAPZCTreeManager::StartScrollbarDrag",
       mTreeManager,
       &IAPZCTreeManager::StartScrollbarDrag,
       aGuid,
       aDragMetrics));
 
   return IPC_OK();
@@ -184,45 +184,45 @@ APZCTreeManagerParent::RecvStartAutoscro
 {
   // Unlike RecvStartScrollbarDrag(), this message comes from the parent
   // process (via nsBaseWidget::mAPZC) rather than from the child process
   // (via TabChild::mApzcTreeManager), so there is no need to check the
   // layers id against mLayersId (and in any case, it wouldn't match, because
   // mLayersId stores the parent process's layers id, while nsBaseWidget is
   // sending the child process's layers id).
 
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
       NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
         "layers::IAPZCTreeManager::StartAutoscroll",
         mTreeManager,
         &IAPZCTreeManager::StartAutoscroll,
         aGuid, aAnchorLocation));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvStopAutoscroll(const ScrollableLayerGuid& aGuid)
 {
   // See RecvStartAutoscroll() for why we don't check the layers id.
 
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
       NewRunnableMethod<ScrollableLayerGuid>(
         "layers::IAPZCTreeManager::StopAutoscroll",
         mTreeManager,
         &IAPZCTreeManager::StopAutoscroll,
         aGuid));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aLongTapEnabled)
 {
-  mSampler->RunOnControllerThread(
+  mUpdater->RunOnControllerThread(
       NewRunnableMethod<bool>(
         "layers::IAPZCTreeManager::SetLongTapEnabled",
         mTreeManager,
         &IAPZCTreeManager::SetLongTapEnabled,
         aLongTapEnabled));
 
   return IPC_OK();
 }
--- a/gfx/layers/ipc/APZCTreeManagerParent.h
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -8,36 +8,36 @@
 #define mozilla_layers_APZCTreeManagerParent_h
 
 #include "mozilla/layers/PAPZCTreeManagerParent.h"
 
 namespace mozilla {
 namespace layers {
 
 class APZCTreeManager;
-class APZSampler;
+class APZUpdater;
 
 class APZCTreeManagerParent
     : public PAPZCTreeManagerParent
 {
 public:
 
   APZCTreeManagerParent(LayersId aLayersId,
                         RefPtr<APZCTreeManager> aAPZCTreeManager,
-                        RefPtr<APZSampler> mAPZSampler);
+                        RefPtr<APZUpdater> mAPZUpdater);
   virtual ~APZCTreeManagerParent();
 
   LayersId GetLayersId() const { return mLayersId; }
 
   /**
    * Called when the layer tree that this protocol is connected to
    * is adopted by another compositor, and we need to switch APZCTreeManagers.
    */
   void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
-                    RefPtr<APZSampler> aAPZSampler);
+                    RefPtr<APZUpdater> aAPZUpdater);
 
   mozilla::ipc::IPCResult
   RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
 
   mozilla::ipc::IPCResult
   RecvZoomToRect(
           const ScrollableLayerGuid& aGuid,
           const CSSRect& aRect,
@@ -83,15 +83,15 @@ public:
   RecvSetLongTapEnabled(const bool& aTapGestureEnabled) override;
 
   void
   ActorDestroy(ActorDestroyReason aWhy) override { }
 
 private:
   LayersId mLayersId;
   RefPtr<APZCTreeManager> mTreeManager;
-  RefPtr<APZSampler> mSampler;
+  RefPtr<APZUpdater> mUpdater;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_APZCTreeManagerParent_h
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -38,16 +38,17 @@
 #include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUParent.h"
 #include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage
 #include "mozilla/layers/APZCTreeManagerParent.h"  // for APZCTreeManagerParent
 #include "mozilla/layers/APZSampler.h"  // for APZSampler
 #include "mozilla/layers/APZThreadUtils.h"  // for APZThreadUtils
+#include "mozilla/layers/APZUpdater.h"  // for APZUpdater
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorManagerParent.h" // for CompositorManagerParent
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/CompositorVsyncScheduler.h"
@@ -376,18 +377,20 @@ void
 CompositorBridgeParent::Initialize()
 {
   MOZ_ASSERT(CompositorThread(),
              "The compositor thread must be Initialized before instanciating a CompositorBridgeParent.");
 
   if (mOptions.UseAPZ()) {
     MOZ_ASSERT(!mApzcTreeManager);
     MOZ_ASSERT(!mApzSampler);
+    MOZ_ASSERT(!mApzUpdater);
     mApzcTreeManager = new APZCTreeManager(mRootLayerTreeID);
     mApzSampler = new APZSampler(mApzcTreeManager);
+    mApzUpdater = new APZUpdater(mApzcTreeManager);
   }
 
   mCompositorBridgeID = 0;
   // FIXME: This holds on the the fact that right now the only thing that
   // can destroy this instance is initialized on the compositor thread after
   // this task has been processed.
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(NewRunnableFunction("AddCompositorRunnable",
@@ -628,19 +631,21 @@ CompositorBridgeParent::ActorDestroy(Act
 
   StopAndClearResources();
 
   RemoveCompositor(mCompositorBridgeID);
 
   mCompositionManager = nullptr;
 
   MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
-  if (mApzSampler) {
-    mApzSampler->ClearTree();
+  MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
+  if (mApzUpdater) {
     mApzSampler = nullptr;
+    mApzUpdater->ClearTree();
+    mApzUpdater = nullptr;
     mApzcTreeManager = nullptr;
   }
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees.erase(mRootLayerTreeID);
   }
 
@@ -854,20 +859,20 @@ CompositorBridgeParent::NotifyShadowTree
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
     // If plugins haven't been updated, stop waiting.
     if (!pluginsUpdatedFlag) {
       mWaitForPluginsUntil = TimeStamp();
       mHaveBlockedForPlugins = false;
     }
 #endif
 
-    if (mApzSampler) {
-      mApzSampler->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
+    if (mApzUpdater) {
+      mApzUpdater->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
       if (aHitTestUpdate) {
-        mApzSampler->UpdateHitTestingTree(mRootLayerTreeID,
+        mApzUpdater->UpdateHitTestingTree(mRootLayerTreeID,
             mLayerManager->GetRoot(), aIsFirstPaint, aId, aPaintSequenceNumber);
       }
     }
 
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
@@ -1091,27 +1096,27 @@ CompositorBridgeParent::ForceComposeToTa
 
 PAPZCTreeManagerParent*
 CompositorBridgeParent::AllocPAPZCTreeManagerParent(const LayersId& aLayersId)
 {
   // This should only ever get called in the GPU process.
   MOZ_ASSERT(XRE_IsGPUProcess());
   // We should only ever get this if APZ is enabled in this compositor.
   MOZ_ASSERT(mOptions.UseAPZ());
-  // The mApzcTreeManager and mApzSampler should have been created via RecvInitialize()
+  // The mApzcTreeManager and mApzUpdater should have been created via RecvInitialize()
   MOZ_ASSERT(mApzcTreeManager);
-  MOZ_ASSERT(mApzSampler);
+  MOZ_ASSERT(mApzUpdater);
   // The main process should pass in 0 because we assume mRootLayerTreeID
   MOZ_ASSERT(!aLayersId.IsValid());
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[mRootLayerTreeID];
   MOZ_ASSERT(state.mParent.get() == this);
   MOZ_ASSERT(!state.mApzcTreeManagerParent);
-  state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, mApzcTreeManager, mApzSampler);
+  state.mApzcTreeManagerParent = new APZCTreeManagerParent(mRootLayerTreeID, mApzcTreeManager, mApzUpdater);
 
   return state.mApzcTreeManagerParent;
 }
 
 bool
 CompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
 {
   delete aActor;
@@ -1120,19 +1125,19 @@ CompositorBridgeParent::DeallocPAPZCTree
 
 void
 CompositorBridgeParent::AllocateAPZCTreeManagerParent(const MonitorAutoLock& aProofOfLayerTreeStateLock,
                                                       const LayersId& aLayersId,
                                                       LayerTreeState& aState)
 {
   MOZ_ASSERT(aState.mParent == this);
   MOZ_ASSERT(mApzcTreeManager);
-  MOZ_ASSERT(mApzSampler);
+  MOZ_ASSERT(mApzUpdater);
   MOZ_ASSERT(!aState.mApzcTreeManagerParent);
-  aState.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzSampler);
+  aState.mApzcTreeManagerParent = new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater);
 }
 
 PAPZParent*
 CompositorBridgeParent::AllocPAPZParent(const LayersId& aLayersId)
 {
   // The main process should pass in 0 because we assume mRootLayerTreeID
   MOZ_ASSERT(!aLayersId.IsValid());
 
@@ -1167,16 +1172,22 @@ CompositorBridgeParent::GetAndroidDynami
 #endif
 
 RefPtr<APZSampler>
 CompositorBridgeParent::GetAPZSampler()
 {
   return mApzSampler;
 }
 
+RefPtr<APZUpdater>
+CompositorBridgeParent::GetAPZUpdater()
+{
+  return mApzUpdater;
+}
+
 CompositorBridgeParent*
 CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(const LayersId& aLayersId)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   return sIndirectLayerTrees[aLayersId].mParent;
 }
 
 bool
@@ -1225,25 +1236,25 @@ CompositorBridgeParent::ShadowLayersUpda
   if (mLayerManager->GetCompositor()) {
     mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
   }
 
   mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
-  if (mApzSampler && !aInfo.isRepeatTransaction()) {
-    mApzSampler->UpdateFocusState(mRootLayerTreeID,
+  if (mApzUpdater && !aInfo.isRepeatTransaction()) {
+    mApzUpdater->UpdateFocusState(mRootLayerTreeID,
                                   mRootLayerTreeID,
                                   aInfo.focusTarget());
 
     if (aHitTestUpdate) {
       AutoResolveRefLayers resolve(mCompositionManager);
 
-      mApzSampler->UpdateHitTestingTree(
+      mApzUpdater->UpdateHitTestingTree(
         mRootLayerTreeID, root, aInfo.isFirstPaint(),
         mRootLayerTreeID, aInfo.paintSequenceNumber());
     }
   }
 
   // The transaction ID might get reset to 1 if the page gets reloaded, see
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
   // Otherwise, it should be continually increasing.
@@ -1354,31 +1365,31 @@ CompositorBridgeParent::RecvGetFrameUnif
 }
 
 void
 CompositorBridgeParent::SetTestAsyncScrollOffset(
     const LayersId& aLayersId,
     const FrameMetrics::ViewID& aScrollId,
     const CSSPoint& aPoint)
 {
-  if (mApzSampler) {
+  if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzSampler->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+    mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
   }
 }
 
 void
 CompositorBridgeParent::SetTestAsyncZoom(
     const LayersId& aLayersId,
     const FrameMetrics::ViewID& aScrollId,
     const LayerToParentLayerScale& aZoom)
 {
-  if (mApzSampler) {
+  if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzSampler->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+    mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
   }
 }
 
 void
 CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId)
 {
   MOZ_ASSERT(mApzcTreeManager);
   MOZ_ASSERT(aLayersId.IsValid());
@@ -1387,19 +1398,19 @@ CompositorBridgeParent::FlushApzRepaints
     "layers::CompositorBridgeParent::FlushApzRepaints",
     [=]() { self->mApzcTreeManager->FlushApzRepaints(aLayersId); }));
 }
 
 void
 CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
                                        APZTestData* aOutData)
 {
-  if (mApzSampler) {
+  if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzSampler->GetAPZTestData(aLayersId, aOutData);
+    mApzUpdater->GetAPZTestData(aLayersId, aOutData);
   }
 }
 
 void
 CompositorBridgeParent::SetConfirmedTargetAPZC(const LayersId& aLayersId,
                                                const uint64_t& aInputBlockId,
                                                const nsTArray<ScrollableLayerGuid>& aTargets)
 {
@@ -1678,25 +1689,25 @@ CompositorBridgeParent::RecvMapAndNotify
   NotifyChildCreated(aChild);
   *aOptions = mOptions;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvAdoptChild(const LayersId& child)
 {
-  RefPtr<APZSampler> oldApzSampler;
+  RefPtr<APZUpdater> oldApzUpdater;
   APZCTreeManagerParent* parent;
   {
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     if (sIndirectLayerTrees[child].mParent) {
       // We currently don't support adopting children from one compositor to
       // another if the two compositors don't have the same options.
       MOZ_ASSERT(sIndirectLayerTrees[child].mParent->mOptions == mOptions);
-      oldApzSampler = sIndirectLayerTrees[child].mParent->mApzSampler;
+      oldApzUpdater = sIndirectLayerTrees[child].mParent->mApzUpdater;
     }
     NotifyChildCreated(child);
     if (sIndirectLayerTrees[child].mLayerTree) {
       sIndirectLayerTrees[child].mLayerTree->SetLayerManager(mLayerManager, GetAnimationStorage());
       // Trigger composition to handle a case that mLayerTree was not composited yet
       // by previous CompositorBridgeParent, since nsRefreshDriver might wait composition complete.
       ScheduleComposition();
     }
@@ -1712,31 +1723,31 @@ CompositorBridgeParent::RecvAdoptChild(c
       if (cpcp) {
         TimeStamp now = TimeStamp::Now();
         cpcp->DidCompositeLocked(child, now, now);
       }
     }
     parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
   }
 
-  if (oldApzSampler) {
+  if (oldApzUpdater) {
     // We don't support moving a child from an APZ-enabled compositor to a
     // APZ-disabled compositor. The mOptions assertion above should already
     // ensure this, since APZ-ness is one of the things in mOptions. Note
-    // however it is possible for mApzSampler to be non-null here with
-    // oldApzSampler null, because the child may not have been previously
+    // however it is possible for mApzUpdater to be non-null here with
+    // oldApzUpdater null, because the child may not have been previously
     // composited.
-    MOZ_ASSERT(mApzSampler);
+    MOZ_ASSERT(mApzUpdater);
   }
-  if (mApzSampler) {
+  if (mApzUpdater) {
     if (parent) {
       MOZ_ASSERT(mApzcTreeManager);
-      parent->ChildAdopted(mApzcTreeManager, mApzSampler);
+      parent->ChildAdopted(mApzcTreeManager, mApzUpdater);
     }
-    mApzSampler->NotifyLayerTreeAdopted(child, oldApzSampler);
+    mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
   }
   return IPC_OK();
 }
 
 PWebRenderBridgeParent*
 CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
                                                     const LayoutDeviceIntSize& aSize,
                                                     TextureFactoryIdentifier* aTextureFactoryIdentifier,
@@ -1818,17 +1829,17 @@ void
 EraseLayerState(LayersId aId)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
 
   auto iter = sIndirectLayerTrees.find(aId);
   if (iter != sIndirectLayerTrees.end()) {
     CompositorBridgeParent* parent = iter->second.mParent;
     if (parent) {
-      if (RefPtr<APZSampler> apz = parent->GetAPZSampler()) {
+      if (RefPtr<APZUpdater> apz = parent->GetAPZUpdater()) {
         apz->NotifyLayerTreeRemoved(aId);
       }
     }
 
     sIndirectLayerTrees.erase(iter);
   }
 }
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -59,16 +59,17 @@ namespace ipc {
 class Shmem;
 } // namespace ipc
 
 namespace layers {
 
 class APZCTreeManager;
 class APZCTreeManagerParent;
 class APZSampler;
+class APZUpdater;
 class AsyncCompositionManager;
 class AsyncImagePipelineManager;
 class Compositor;
 class CompositorAnimationStorage;
 class CompositorBridgeParent;
 class CompositorManagerParent;
 class CompositorVsyncScheduler;
 class HostLayerManager;
@@ -456,16 +457,17 @@ public:
 
   PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
   bool DeallocPAPZParent(PAPZParent* aActor) override;
 
 #if defined(MOZ_WIDGET_ANDROID)
   AndroidDynamicToolbarAnimator* GetAndroidDynamicToolbarAnimator();
 #endif
   RefPtr<APZSampler> GetAPZSampler();
+  RefPtr<APZUpdater> GetAPZUpdater();
 
   CompositorOptions GetOptions() const {
     return mOptions;
   }
 
   TimeDuration GetVsyncInterval() const {
     // the variable is called "rate" but really it's an interval
     return mVsyncRate;
@@ -618,16 +620,17 @@ protected:
   uint64_t mCompositorBridgeID;
   LayersId mRootLayerTreeID;
 
   bool mOverrideComposeReadiness;
   RefPtr<CancelableRunnable> mForceCompositionTask;
 
   RefPtr<APZCTreeManager> mApzcTreeManager;
   RefPtr<APZSampler> mApzSampler;
+  RefPtr<APZUpdater> mApzUpdater;
 
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   // This makes sure the compositorParent is not destroyed before receiving
   // confirmation that the channel is closed.
   // mSelfRef is cleared in DeferredDestroy which is scheduled by ActorDestroy.
   RefPtr<CompositorBridgeParent> mSelfRef;
   RefPtr<CompositorAnimationStorage> mAnimationStorage;
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -14,17 +14,17 @@
 #include "base/task.h"                  // for CancelableTask, etc
 #include "base/thread.h"                // for Thread
 #ifdef XP_WIN
 #include "mozilla/gfx/DeviceManagerDx.h" // for DeviceManagerDx
 #endif
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/AnimationHelper.h" // for CompositorAnimationStorage
 #include "mozilla/layers/APZCTreeManagerParent.h"  // for APZCTreeManagerParent
-#include "mozilla/layers/APZSampler.h"  // for APZSampler
+#include "mozilla/layers/APZUpdater.h"  // for APZUpdater
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/WebRenderBridgeParent.h"
@@ -129,19 +129,19 @@ CrossProcessCompositorBridgeParent::Allo
 
   // If the widget has shutdown its compositor, we may not have had a chance yet
   // to unmap our layers id, and we could get here without a parent compositor.
   // In this case return an empty APZCTM.
   if (!state.mParent) {
     // Note: we immediately call ClearTree since otherwise the APZCTM will
     // retain a reference to itself, through the checkerboard observer.
     RefPtr<APZCTreeManager> temp = new APZCTreeManager(LayersId{0});
-    RefPtr<APZSampler> tempSampler = new APZSampler(temp);
-    tempSampler->ClearTree();
-    return new APZCTreeManagerParent(aLayersId, temp, tempSampler);
+    RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp);
+    tempUpdater->ClearTree();
+    return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
   }
 
   state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
   return state.mApzcTreeManagerParent;
 }
 bool
 CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)
 {
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -89,16 +89,17 @@ EXPORTS.mozilla.dom += [
     'apz/util/CheckerboardReportService.h',
 ]
 
 EXPORTS.mozilla.layers += [
     'AnimationHelper.h',
     'AnimationInfo.h',
     'apz/public/APZInputBridge.h',
     'apz/public/APZSampler.h',
+    'apz/public/APZUpdater.h',
     'apz/public/CompositorController.h',
     'apz/public/GeckoContentController.h',
     'apz/public/IAPZCTreeManager.h',
     'apz/public/MetricsSharingController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZUtils.h',
     'apz/src/AsyncDragMetrics.h',
@@ -293,16 +294,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
     ]
 
 UNIFIED_SOURCES += [
     'AnimationHelper.cpp',
     'AnimationInfo.cpp',
     'apz/src/APZCTreeManager.cpp',
     'apz/src/APZInputBridge.cpp',
     'apz/src/APZSampler.cpp',
+    'apz/src/APZUpdater.cpp',
     'apz/src/APZUtils.cpp',
     'apz/src/AsyncPanZoomController.cpp',
     'apz/src/AutoscrollAnimation.cpp',
     'apz/src/Axis.cpp',
     'apz/src/CheckerboardEvent.cpp',
     'apz/src/DragTracker.cpp',
     'apz/src/FocusState.cpp',
     'apz/src/FocusTarget.cpp',
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -11,16 +11,17 @@
 #include "gfxPrefs.h"
 #include "gfxEnv.h"
 #include "GeckoProfiler.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/Range.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/APZSampler.h"
+#include "mozilla/layers/APZUpdater.h"
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorVsyncScheduler.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
 #include "mozilla/layers/SharedSurfacesParent.h"
@@ -511,17 +512,17 @@ WebRenderBridgeParent::UpdateAPZ(bool aU
   if (!cbp) {
     return;
   }
   LayersId rootLayersId = cbp->RootLayerTreeId();
   RefPtr<WebRenderBridgeParent> rootWrbp = cbp->GetWebRenderBridgeParent();
   if (!rootWrbp) {
     return;
   }
-  if (RefPtr<APZSampler> apz = cbp->GetAPZSampler()) {
+  if (RefPtr<APZUpdater> apz = cbp->GetAPZUpdater()) {
     apz->UpdateFocusState(rootLayersId, GetLayersId(),
                           mScrollData.GetFocusTarget());
     if (aUpdateHitTestingTree) {
       apz->UpdateHitTestingTree(rootLayersId, rootWrbp->GetScrollData(),
           mScrollData.IsFirstPaint(), GetLayersId(),
           mScrollData.GetPaintSequenceNumber());
     }
   }