Bug 866232 - Add code to build the APZC tree on layer updates. r=BenWa, mattwoodrow
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 30 Jul 2013 14:03:40 -0400
changeset 152820 bda5c115afd924be55a9e2a0502428110bf5fc42
parent 152819 f856d234e9b27015ca1c89c82a76ee30edce38f5
child 152821 c3c079a743c02e70e5208302ca7ae34bec2be49a
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBenWa, mattwoodrow
bugs866232
milestone25.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 866232 - Add code to build the APZC tree on layer updates. r=BenWa, mattwoodrow
gfx/layers/composite/APZCTreeManager.cpp
gfx/layers/composite/APZCTreeManager.h
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/AsyncPanZoomController.h
gfx/layers/ipc/CompositorParent.cpp
gfx/tests/gtest/TestAsyncPanZoomController.cpp
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -11,58 +11,136 @@ namespace layers {
 
 APZCTreeManager::APZCTreeManager()
     : mTreeLock("APZCTreeLock")
 {
   MOZ_ASSERT(NS_IsMainThread());
   AsyncPanZoomController::InitializeGlobalState();
 }
 
+/* Flatten the tree of APZC instances into the given nsTArray */
+static void
+Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
+{
+  if (aApzc) {
+    aCollection->AppendElement(aApzc);
+    Collect(aApzc->GetLastChild(), aCollection);
+    Collect(aApzc->GetPrevSibling(), aCollection);
+  }
+}
+
 void
 APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
-                                             uint64_t aLayersId, bool aIsFirstPaint)
+                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
 {
   Compositor::AssertOnCompositorThread();
 
   MonitorAutoLock lock(mTreeLock);
-  AsyncPanZoomController* controller = nullptr;
-  if (aRoot && aRoot->AsContainerLayer()) {
-    ContainerLayer* container = aRoot->AsContainerLayer();
+
+  // We do this business with collecting the entire tree into an array because otherwise
+  // it's very hard to determine which APZC instances need to be destroyed. In the worst
+  // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
+  // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
+  // completely different place. In scenario (a) we would want to destroy the APZC while
+  // walking the layer tree and noticing that the layer/APZC is no longer there. But if
+  // we do that then we run into a problem in scenario (b) because we might encounter that
+  // layer later during the walk. To handle both of these we have to 'remember' that the
+  // layer was not found, and then do the destroy only at the end of the tree walk after
+  // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
+  // as part of a recursive tree walk is hard and so maintaining a list and removing
+  // APZCs that are still alive is much simpler.
+  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
+  Collect(mRootApzc, &apzcsToDestroy);
+  mRootApzc = nullptr;
+
+  if (aRoot) {
+    UpdatePanZoomControllerTree(aCompositor,
+                                aRoot, CompositorParent::ROOT_LAYER_TREE_ID,
+                                nullptr, nullptr,
+                                aIsFirstPaint, aFirstPaintLayersId,
+                                &apzcsToDestroy);
+  }
 
-    // If the layer is scrollable, it needs an APZC. If there is already an APZC on it, use that,
-    // otherwise create one if we can. We might not be able to create one if we don't have a
-    // GeckoContentController for it, in which case we just leave it as null.
-    controller = container->GetAsyncPanZoomController();
-    const FrameMetrics& metrics = container->GetFrameMetrics();
-    if (metrics.IsScrollable()) {
-      // Scrollable, create an APZC if it doesn't have one and we can create it
-      if (!controller) {
-        const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
-        if (state && state->mController.get()) {
-          controller = new AsyncPanZoomController(state->mController,
+  for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
+    apzcsToDestroy[i]->Destroy();
+  }
+}
+
+AsyncPanZoomController*
+APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
+                                             Layer* aLayer, uint64_t aLayersId,
+                                             AsyncPanZoomController* aParent,
+                                             AsyncPanZoomController* aNextSibling,
+                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
+                                             nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
+{
+  ContainerLayer* container = aLayer->AsContainerLayer();
+  AsyncPanZoomController* controller = nullptr;
+  if (container) {
+    if (container->GetFrameMetrics().IsScrollable()) {
+      const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
+      if (state && state->mController.get()) {
+        // If we get here, aLayer is a scrollable container layer and somebody
+        // has registered a GeckoContentController for it, so we need to ensure
+        // it has an APZC instance to manage its scrolling.
+
+        controller = container->GetAsyncPanZoomController();
+        if (!controller) {
+          controller = new AsyncPanZoomController(aLayersId, state->mController,
                                                   AsyncPanZoomController::USE_GESTURE_DETECTOR);
           controller->SetCompositorParent(aCompositor);
+        } else {
+          // If there was already an APZC for the layer clear the tree pointers
+          // so that it doesn't continue pointing to APZCs that should no longer
+          // be in the tree. These pointers will get reset properly as we continue
+          // building the tree. Also remove it from the set of APZCs that are going
+          // to be destroyed, because it's going to remain active.
+          aApzcsToDestroy->RemoveElement(controller);
+          controller->SetPrevSibling(nullptr);
+          controller->SetLastChild(nullptr);
         }
+
+        controller->NotifyLayersUpdated(container->GetFrameMetrics(),
+                                        aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
+
+        // Bind the APZC instance into the tree of APZCs
+        if (aNextSibling) {
+          aNextSibling->SetPrevSibling(controller);
+        } else if (aParent) {
+          aParent->SetLastChild(controller);
+        } else {
+          mRootApzc = controller;
+        }
+
+        // Let this controller be the parent of other controllers when we recurse downwards
+        aParent = controller;
       }
-    } else if (controller) {
-      // Not scrollable, so clear the APZC instance
-      controller = nullptr;
     }
+
     container->SetAsyncPanZoomController(controller);
-
-    if (controller) {
-      controller->NotifyLayersUpdated(container->GetFrameMetrics(), aIsFirstPaint);
-    }
   }
 
+  uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
+  AsyncPanZoomController* next = nullptr;
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aParent, next,
+                                       aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy);
+  }
+
+  // Return the APZC that should be the sibling of other APZCs as we continue
+  // moving towards the first child at this depth in the layer tree.
+  // If this layer doesn't have a controller, we promote any APZCs in the subtree
+  // upwards. Otherwise we fall back to the aNextSibling that was passed in.
   if (controller) {
-    mApzcs[aLayersId] = controller;
-  } else {
-    mApzcs.erase(aLayersId);
+    return controller;
   }
+  if (next) {
+    return next;
+  }
+  return aNextSibling;
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
 {
   nsRefPtr<AsyncPanZoomController> apzc;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
@@ -198,40 +276,62 @@ APZCTreeManager::CancelAnimation(const S
   }
 }
 
 void
 APZCTreeManager::ClearTree()
 {
   MonitorAutoLock lock(mTreeLock);
 
-  std::map< uint64_t, nsRefPtr<AsyncPanZoomController> >::iterator it = mApzcs.begin();
-  while (it != mApzcs.end()) {
-    nsRefPtr<AsyncPanZoomController> apzc = it->second;
-    apzc->Destroy();
-    it++;
+  // This can be done as part of a tree walk but it's easier to
+  // just re-use the Collect method that we need in other places.
+  // If this is too slow feel free to change it to a recursive walk.
+  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
+  Collect(mRootApzc, &apzcsToDestroy);
+  for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
+    apzcsToDestroy[i]->Destroy();
   }
-  mApzcs.clear();
+  mRootApzc = nullptr;
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
 {
   MonitorAutoLock lock(mTreeLock);
-  std::map< uint64_t, nsRefPtr<AsyncPanZoomController> >::iterator it = mApzcs.find(aGuid.mLayersId);
-  if (it == mApzcs.end()) {
-    return nullptr;
+  nsRefPtr<AsyncPanZoomController> target;
+  // The root may have siblings, check those too
+  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
+    target = FindTargetAPZC(apzc, aGuid);
+    if (target) {
+      break;
+    }
   }
-  nsRefPtr<AsyncPanZoomController> target = it->second;
   return target.forget();
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
 {
   MonitorAutoLock lock(mTreeLock);
   // TODO: Do a hit test on the tree of
   // APZC instances and return the right one.
   return nullptr;
 }
 
+AsyncPanZoomController*
+APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid) {
+  // This walks the tree in depth-first, reverse order, so that it encounters
+  // APZCs front-to-back on the screen.
+  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
+    AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
+    if (match) {
+      return match;
+    }
+  }
+
+  if (aApzc->Matches(aGuid)) {
+    return aApzc;
+  }
+  return nullptr;
+}
+
 }
 }
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -4,21 +4,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_APZCTreeManager_h
 #define mozilla_layers_APZCTreeManager_h
 
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "Layers.h"
 #include "CompositorParent.h"
-#include <map>
 
 namespace mozilla {
 namespace layers {
 
+class AsyncPanZoomController;
+class CompositorParent;
+
 /**
  * This class allows us to uniquely identify a scrollable layer. The
  * mLayersId identifies the layer tree (corresponding to a child process
  * and/or tab) that the scrollable layer belongs to. The mPresShellId
  * is a temporal identifier (corresponding to the document loaded that
  * contains the scrollable layer, which may change over time). The
  * mScrollId corresponds to the actual frame that is scrollable.
  */
@@ -101,19 +103,29 @@ public:
   APZCTreeManager();
 
   /**
    * Rebuild the APZC tree based on the layer update that just came up. Preserve
    * APZC instances where possible, but retire those whose layers are no longer
    * in the layer tree.
    *
    * This must be called on the compositor thread as it walks the layer tree.
+   *
+   * @param aCompositor A pointer to the compositor parent instance that owns
+   *                    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
+   *                      the tree that is affected by the first-paint flag is
+   *                      indicated by the aFirstPaintLayersId parameter.
    */
   void UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
-                                   uint64_t aLayersId, bool aIsFirstPaint);
+                                   bool aIsFirstPaint, uint64_t aFirstPaintLayersId);
 
   /**
    * General handler for incoming input events. Manipulates the frame metrics
    * based on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class.
    * HandleInputEvent() should be used internally.
    */
   nsEventStatus ReceiveInputEvent(const InputData& aEvent);
@@ -211,21 +223,42 @@ public:
 private:
   /* Some helper functions to find an APZC given some identifying input. These functions
      lock the tree of APZCs while they find the right one, and then return an addref'd
      pointer to it. This allows caller code to just use the target APZC without worrying
      about it going away.
   */
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint);
+  /* Recursive helper */
+  AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
+
+  /**
+   * Recursive helper function to build the APZC tree. The tree of APZC instances has
+   * the same shape as the layer tree, but excludes all the layers that are not scrollable.
+   * Note that this means APZCs corresponding to layers at different depths in the tree
+   * may end up becoming siblings. It also means that the "root" APZC may have siblings.
+   * This function walks the layer tree backwards through siblings and constructs the APZC
+   * tree also as a last-child-prev-sibling tree because that simplifies the hit detection
+   * code.
+   */
+  AsyncPanZoomController* UpdatePanZoomControllerTree(CompositorParent* aCompositor,
+                                                      Layer* aLayer, uint64_t aLayersId,
+                                                      AsyncPanZoomController* aParent,
+                                                      AsyncPanZoomController* aNextSibling,
+                                                      bool aIsFirstPaint,
+                                                      uint64_t aFirstPaintLayersId,
+                                                      nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy);
 
 private:
-  /* Whenever walking or mutating the map of APZC instances, mTreeLock must be held.
+  /* Whenever walking or mutating the tree rooted at mRootApzc, 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). */
+   * isolation (that is, if its tree pointers are not being accessed or mutated). The
+   * lock also needs to be held when accessing the mRootApzc instance variable, as that
+   * is considered part of the APZC tree management state. */
   mozilla::Monitor mTreeLock;
-  std::map< uint64_t, nsRefPtr<AsyncPanZoomController> > mApzcs;
+  nsRefPtr<AsyncPanZoomController> mRootApzc;
 };
 
 }
 }
 
 #endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -157,19 +157,21 @@ AsyncPanZoomController::InitializeGlobal
   Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout);
 
   gComputedTimingFunction = new ComputedTimingFunction();
   gComputedTimingFunction->Init(
     nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
   ClearOnShutdown(&gComputedTimingFunction);
 }
 
-AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController,
+AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
+                                               GeckoContentController* aGeckoContentController,
                                                GestureBehavior aGestures)
-  :  mPaintThrottler(GetFrameTime()),
+  :  mLayersId(aLayersId),
+     mPaintThrottler(GetFrameTime()),
      mGeckoContentController(aGeckoContentController),
      mRefPtrMonitor("RefPtrMonitor"),
      mTouchListenerTimeoutTask(nullptr),
      mX(this),
      mY(this),
      mAllowZoom(true),
      mMinZoom(MIN_ZOOM),
      mMaxZoom(MAX_ZOOM),
@@ -210,19 +212,23 @@ AsyncPanZoomController::GetGestureEventL
   MonitorAutoLock lock(mRefPtrMonitor);
   nsRefPtr<GestureEventListener> listener = mGestureEventListener;
   return listener.forget();
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
-  MonitorAutoLock lock(mRefPtrMonitor);
-  mGeckoContentController = nullptr;
-  mGestureEventListener = nullptr;
+  { // scope the lock
+    MonitorAutoLock lock(mRefPtrMonitor);
+    mGeckoContentController = nullptr;
+    mGestureEventListener = nullptr;
+  }
+  mPrevSibling = nullptr;
+  mLastChild = nullptr;
 }
 
 /* static */float
 AsyncPanZoomController::GetTouchStartTolerance()
 {
   return gTouchStartTolerance;
 }
 
@@ -1512,10 +1518,17 @@ void AsyncPanZoomController::GetAPZCAtPo
 }
 
 void AsyncPanZoomController::UpdateScrollOffset(const CSSPoint& aScrollOffset)
 {
   MonitorAutoLock monitor(mMonitor);
   mFrameMetrics.mScrollOffset = aScrollOffset;
 }
 
+bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
+{
+  // TODO: also check the presShellId and mScrollId, once those are
+  // fully propagated everywhere in RenderFrameParent and AndroidJNI.
+  return aGuid.mLayersId == mLayersId;
+}
+
 }
 }
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -9,22 +9,24 @@
 
 #include "GeckoContentController.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "InputData.h"
 #include "Axis.h"
 #include "TaskThrottler.h"
+#include "mozilla/layers/APZCTreeManager.h"
 
 #include "base/message_loop.h"
 
 namespace mozilla {
 namespace layers {
 
+struct ScrollableLayerGuid;
 class CompositorParent;
 class GestureEventListener;
 class ContainerLayer;
 class ViewTransform;
 
 /**
  * Controller for all panning and zooming logic. Any time a user input is
  * detected and it must be processed in some way to affect what the user sees,
@@ -63,17 +65,18 @@ public:
   /**
    * Constant describing the tolerance in distance we use, multiplied by the
    * device DPI, before we start panning the screen. This is to prevent us from
    * accidentally processing taps as touch moves, and from very short/accidental
    * touches moving the screen.
    */
   static float GetTouchStartTolerance();
 
-  AsyncPanZoomController(GeckoContentController* aController,
+  AsyncPanZoomController(uint64_t aLayersId,
+                         GeckoContentController* aController,
                          GestureBehavior aGestures = DEFAULT_GESTURES);
   ~AsyncPanZoomController();
 
   // --------------------------------------------------------------------------
   // These methods must only be called on the gecko thread.
   //
 
   /**
@@ -241,16 +244,21 @@ public:
 
   /**
    * Handler for events which should not be intercepted by the touch listener.
    * Does the work for ReceiveInputEvent().
    */
   nsEventStatus HandleInputEvent(const InputData& aEvent);
 
   /**
+   * Returns true if this APZC instance is for the layer identified by the guid.
+   */
+  bool Matches(const ScrollableLayerGuid& aGuid);
+
+  /**
    * Sync panning and zooming animation using a fixed frame time.
    * This will ensure that we animate the APZC correctly with other external
    * animations to the same timestamp.
    */
   static void SetFrameTime(const TimeStamp& aMilliseconds);
 
   /**
    * Transform and intersect aPoint with the layer tree returning the appropriate
@@ -498,16 +506,17 @@ private:
 
   /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it. If the monitor is already held by the current thread, it is safe to
    * instead use: |mState = NEWSTATE;|
    */
   void SetState(PanZoomState aState);
 
+  uint64_t mLayersId;
   nsRefPtr<CompositorParent> mCompositorParent;
   TaskThrottler mPaintThrottler;
 
   /* 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
      compositor thread. */
   nsRefPtr<GeckoContentController> mGeckoContentController;
   nsRefPtr<GestureEventListener> mGestureEventListener;
@@ -608,14 +617,28 @@ private:
 
   // Flag used to determine whether or not we should try scrolling by
   // BrowserElementScrolling first.  If set, we delay delivering
   // touchmove events to GestureListener until BrowserElementScrolling
   // decides whether it wants to handle panning for this touch series.
   bool mDelayPanning;
 
   friend class Axis;
+
+  /* The functions and members in this section are used to build a tree
+   * structure out of APZC instances. This tree can only be walked or
+   * manipulated while holding the lock in the associated APZCTreeManager
+   * instance.
+   */
+public:
+  void SetLastChild(AsyncPanZoomController* child) { mLastChild = child; }
+  void SetPrevSibling(AsyncPanZoomController* sibling) { mPrevSibling = sibling; }
+  AsyncPanZoomController* GetLastChild() const { return mLastChild; }
+  AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; }
+private:
+  nsRefPtr<AsyncPanZoomController> mLastChild;
+  nsRefPtr<AsyncPanZoomController> mPrevSibling;
 };
 
 }
 }
 
 #endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -404,17 +404,17 @@ CompositorParent::ScheduleTask(Cancelabl
   }
 }
 
 void
 CompositorParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint)
 {
   if (mApzcTreeManager) {
     AutoResolveRefLayers resolve(mCompositionManager);
-    mApzcTreeManager->UpdatePanZoomControllerTree(this, GetIndirectShadowTree(aId)->mRoot, aId, aIsFirstPaint);
+    mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId);
   }
 
   if (mLayerManager) {
     LayerManagerComposite* managerComposite = mLayerManager->AsLayerManagerComposite();
     if (managerComposite) {
       managerComposite->NotifyShadowTreeTransaction();
     }
   }
@@ -561,25 +561,20 @@ CompositorParent::ShadowLayersUpdated(La
   // change, dimension change would be done at the stage, update the size here is free of
   // race condition.
   mLayerManager->UpdateRenderBounds(aTargetConfig.clientBounds());
 
   mCompositionManager->Updated(isFirstPaint, aTargetConfig);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
-#ifdef MOZ_WIDGET_ANDROID
-  // Temporary hack for Fennec. This will be removed once UpdatePanZoomControllerTree actually
-  // does a full tree walk.
   if (mApzcTreeManager) {
     AutoResolveRefLayers resolve(mCompositionManager);
-    Layer* pzcRoot = mLayerManager->GetPrimaryScrollableLayer();
-    mApzcTreeManager->UpdatePanZoomControllerTree(this, pzcRoot, ROOT_LAYER_TREE_ID, isFirstPaint);
+    mApzcTreeManager->UpdatePanZoomControllerTree(this, root, isFirstPaint, ROOT_LAYER_TREE_ID);
   }
-#endif
 
   if (root) {
     SetShadowProperties(root);
     if (mIsTesting) {
       mCompositionManager->TransformShadowTree(mTestTime);
     }
   }
   ScheduleComposition();
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -36,18 +36,18 @@ class TestAPZCContainerLayer : public Co
   void RemoveChild(Layer* aChild) {}
   void InsertAfter(Layer* aChild, Layer* aAfter) {}
   void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {}
   void RepositionChild(Layer* aChild, Layer* aAfter) {}
 };
 
 class TestAsyncPanZoomController : public AsyncPanZoomController {
 public:
-  TestAsyncPanZoomController(MockContentController* mcc)
-    : AsyncPanZoomController(mcc)
+  TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* mcc)
+    : AsyncPanZoomController(aLayersId, mcc)
   {}
 
   void SetFrameMetrics(const FrameMetrics& metrics) {
     MonitorAutoLock lock(mMonitor);
     mFrameMetrics = metrics;
   }
 };
 
@@ -97,25 +97,25 @@ void ApzcPan(AsyncPanZoomController* apz
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
   status = apzc->HandleInputEvent(mti);
 }
 
 TEST(AsyncPanZoomController, Constructor) {
   // RefCounted class can't live in the stack
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
   apzc->SetFrameMetrics(TestFrameMetrics());
 }
 
 TEST(AsyncPanZoomController, SimpleTransform) {
   TimeStamp testStartTime = TimeStamp::Now();
   // RefCounted class can't live in the stack
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
   apzc->SetFrameMetrics(TestFrameMetrics());
 
   TestAPZCContainerLayer layer;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
   apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut);
 
   EXPECT_EQ(pointOut, ScreenPoint());
@@ -138,18 +138,18 @@ TEST(AsyncPanZoomController, ComplexTran
   // CSS transforms, the two layers are the same size in screen
   // pixels.
   //
   // The screen itself is 24x24 in screen pixels (therefore 4x4 in
   // CSS pixels). The displayport is 1 extra CSS pixel on all
   // sides.
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
-  nsRefPtr<TestAsyncPanZoomController> childApzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+  nsRefPtr<TestAsyncPanZoomController> childApzc = new TestAsyncPanZoomController(0, mcc);
 
   const char* layerTreeSyntax = "c(c)";
   // LayerID                     0 1
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0, 0, 300, 300)),
     nsIntRegion(nsIntRect(0, 0, 150, 300)),
   };
   gfx3DMatrix transforms[] = {
@@ -226,17 +226,17 @@ TEST(AsyncPanZoomController, ComplexTran
   EXPECT_EQ(ScreenPoint(135, 90), pointOut);
 }
 
 TEST(AsyncPanZoomController, Pan) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(4);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
@@ -259,17 +259,17 @@ TEST(AsyncPanZoomController, Pan) {
   EXPECT_EQ(viewTransformOut, ViewTransform());
 }
 
 TEST(AsyncPanZoomController, Fling) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(2);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
@@ -289,17 +289,17 @@ TEST(AsyncPanZoomController, Fling) {
   }
 }
 
 TEST(AsyncPanZoomController, OverScrollPanning) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
 
   apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
   EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(3);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   // Pan sufficiently to hit overscroll behavior