Bug 1449620 - Extract an APZUpdater class from APZSampler. r?botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 28 Mar 2018 18:36:42 -0400
changeset 774452 a1e28a66ca2252134d87c0fb97d141a42cf6ba7e
parent 774393 38a320903a63fe2b226889e4d2c1bb2bab89aca7
push id104408
push userkgupta@mozilla.com
push dateWed, 28 Mar 2018 22:39:27 +0000
reviewersbotond
bugs1449620
milestone61.0a1
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,
@@ -1906,17 +1916,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);
   }
@@ -1955,33 +1965,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) {
@@ -2066,17 +2076,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.
@@ -3021,17 +3031,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;
 }
@@ -3208,16 +3218,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());
     }
   }