Bug 866232 - Update and re-enable the test for hit detection in the APZC tree. r=BenWa
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 30 Jul 2013 14:03:41 -0400
changeset 152824 a960ee129d9114b295c87596c552d0dcac74cb62
parent 152823 f69d34718660ee98696672542fb24b585dd4b947
child 152825 6323f687fa3c3eb5862b20e776ab58ed7bed6821
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
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 - Update and re-enable the test for hit detection in the APZC tree. r=BenWa
gfx/layers/composite/APZCTreeManager.cpp
gfx/layers/composite/APZCTreeManager.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/tests/gtest/TestAsyncPanZoomController.cpp
gfx/tests/gtest/TestLayers.cpp
--- a/gfx/layers/composite/APZCTreeManager.cpp
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -16,32 +16,38 @@ namespace layers {
 
 APZCTreeManager::APZCTreeManager()
     : mTreeLock("APZCTreeLock")
 {
   MOZ_ASSERT(NS_IsMainThread());
   AsyncPanZoomController::InitializeGlobalState();
 }
 
+void
+APZCTreeManager::AssertOnCompositorThread()
+{
+  Compositor::AssertOnCompositorThread();
+}
+
 /* 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,
                                              bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
 {
-  Compositor::AssertOnCompositorThread();
+  AssertOnCompositorThread();
 
   MonitorAutoLock lock(mTreeLock);
 
   // 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
--- a/gfx/layers/composite/APZCTreeManager.h
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -91,21 +91,22 @@ struct ScrollableLayerGuid {
  * The other functions on this class are used by various pieces of client code to
  * notify the APZC instances of events relevant to them. This includes, for example,
  * user input events that drive panning and zooming, changes to the scroll viewport
  * area, and changes to pan/zoom constraints.
  *
  * Note that the ClearTree function MUST be called when this class is no longer needed;
  * see the method documentation for details.
  */
-class APZCTreeManager MOZ_FINAL {
+class APZCTreeManager {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager)
 
 public:
   APZCTreeManager();
+  virtual ~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.
    *
@@ -215,24 +216,33 @@ public:
    * Calls Destroy() on all APZC instances attached to the tree, and resets the
    * tree back to empty. This function may be called multiple times during the
    * lifetime of this APZCTreeManager, but it must always be called at least once
    * when this APZCTreeManager is no longer needed. Failing to call this function
    * may prevent objects from being freed properly.
    */
   void ClearTree();
 
-private:
+protected:
+  /**
+   * Debug-build assertion that can be called to ensure code is running on the
+   * compositor thread.
+   */
+  virtual void AssertOnCompositorThread();
+
+public:
   /* 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.
+     about it going away. These are public for testing code and generally should not be
+     used by other production code.
   */
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint);
+private:
   /* Recursive helpers */
   AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
   AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint);
 
   /**
    * 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
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -741,16 +741,30 @@ static void
 UpdateControllerForLayersId(uint64_t aLayersId,
                             GeckoContentController* aController)
 {
   // Adopt ref given to us by SetControllerForLayerTree()
   sIndirectLayerTrees[aLayersId].mController =
     already_AddRefed<GeckoContentController>(aController);
 }
 
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(uint64_t aLayersId,
+                                                         Layer* aRoot,
+                                                         GeckoContentController* aController)
+    : mLayersId(aLayersId)
+{
+  sIndirectLayerTrees[aLayersId].mRoot = aRoot;
+  sIndirectLayerTrees[aLayersId].mController = aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
+{
+  sIndirectLayerTrees.erase(mLayersId);
+}
+
 /*static*/ void
 CompositorParent::SetControllerForLayerTree(uint64_t aLayersId,
                                             GeckoContentController* aController)
 {
   // This ref is adopted by UpdateControllerForLayersId().
   aController->AddRef();
   CompositorLoop()->PostTask(FROM_HERE,
                              NewRunnableFunction(&UpdateControllerForLayersId,
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -33,16 +33,27 @@ namespace mozilla {
 namespace layers {
 
 class APZCTreeManager;
 class Layer;
 class LayerManagerComposite;
 class AsyncCompositionManager;
 struct TextureFactoryIdentifier;
 
+struct ScopedLayerTreeRegistration
+{
+  ScopedLayerTreeRegistration(uint64_t aLayersId,
+                              Layer* aRoot,
+                              GeckoContentController* aController);
+  ~ScopedLayerTreeRegistration();
+
+private:
+  uint64_t mLayersId;
+};
+
 class CompositorParent : public PCompositorParent,
                          public ShadowLayersManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorParent)
 
 public:
   CompositorParent(nsIWidget* aWidget,
                    bool aUseExternalSurfaceSize = false,
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -5,16 +5,18 @@
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/CompositorParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "Layers.h"
 #include "TestLayers.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using ::testing::_;
 
@@ -46,16 +48,21 @@ public:
   {}
 
   void SetFrameMetrics(const FrameMetrics& metrics) {
     MonitorAutoLock lock(mMonitor);
     mFrameMetrics = metrics;
   }
 };
 
+class TestAPZCTreeManager : public APZCTreeManager {
+protected:
+  void AssertOnCompositorThread() MOZ_OVERRIDE { /* no-op */ }
+};
+
 static
 FrameMetrics TestFrameMetrics() {
   FrameMetrics fm;
 
   fm.mDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mCompositionBounds = ScreenIntRect(0, 0, 10, 10);
   fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mScrollableRect = CSSRect(0, 0, 100, 100);
@@ -334,148 +341,128 @@ CreateTestLayerTree(nsRefPtr<LayerManage
     gfx3DMatrix(),
     gfx3DMatrix(),
     gfx3DMatrix(),
     gfx3DMatrix(),
   };
   return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers);
 }
 
-//TEST(AsyncPanZoomController, GetAPZCAtPoint) {
-//  nsTArray<nsRefPtr<Layer> > layers;
-//  nsRefPtr<LayerManager> lm;
-//  nsRefPtr<Layer> root = CreateTestLayerTree(lm, layers);
-//
-//  TimeStamp testStartTime = TimeStamp::Now();
-//  AsyncPanZoomController::SetFrameTime(testStartTime);
-//
-//  nsRefPtr<MockContentController> mcc = new MockContentController();
-//  nsRefPtr<AsyncPanZoomController> apzcMain = new AsyncPanZoomController(mcc);
-//  nsRefPtr<AsyncPanZoomController> apzcSub3 = new AsyncPanZoomController(mcc);
-//  nsRefPtr<AsyncPanZoomController> apzcSub4 = new AsyncPanZoomController(mcc);
-//  nsRefPtr<AsyncPanZoomController> apzcSub7 = new AsyncPanZoomController(mcc);
-//  apzcMain->NotifyLayersUpdated(TestFrameMetrics(), true);
-//
-//  nsIntRect layerBound;
-//  ScreenIntPoint touchPoint(20, 20);
-//  AsyncPanZoomController* apzcOut;
-//  LayerIntPoint relativePointOut;
-//
-//  FrameMetrics scrollable;
-//
-//  // No APZC attached so hit testing will return no APZC at (20,20)
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//
-//  AsyncPanZoomController* nullAPZC = nullptr;
-//  EXPECT_EQ(apzcOut, nullAPZC);
-//
-//  // Now we have a root APZC that will match the page
-//  scrollable.mScrollId = FrameMetrics::ROOT_SCROLL_ID;
-//  layerBound = root->GetVisibleRegion().GetBounds();
-//  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-//                                 layerBound.width, layerBound.height);
-//  root->AsContainerLayer()->SetFrameMetrics(scrollable);
-//  root->AsContainerLayer()->SetAsyncPanZoomController(apzcMain);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcMain.get());
-//  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
-//
-//  // Now we have a sub APZC with a better fit
-//  scrollable.mScrollId = FrameMetrics::START_SCROLL_ID;
-//  layerBound = layers[3]->GetVisibleRegion().GetBounds();
-//  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-//                                 layerBound.width, layerBound.height);
-//  layers[3]->AsContainerLayer()->SetFrameMetrics(scrollable);
-//  layers[3]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub3);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub3.get());
-//  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
-//
-//  // Now test hit testing when we have two scrollable layers
-//  touchPoint = ScreenIntPoint(15,15);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub3.get()); // We haven't bound apzcSub4 yet
-//  scrollable.mScrollId++;
-//  layerBound = layers[4]->GetVisibleRegion().GetBounds();
-//  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-//                                 layerBound.width, layerBound.height);
-//  layers[4]->AsContainerLayer()->SetFrameMetrics(scrollable);
-//  layers[4]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub4);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub4.get());
-//  EXPECT_EQ(LayerIntPoint(15, 15), relativePointOut);
-//
-//  // Hit test ouside the reach of apzc3/4 but inside apzcMain
-//  touchPoint = ScreenIntPoint(90,90);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcMain.get());
-//  EXPECT_EQ(LayerIntPoint(90, 90), relativePointOut);
-//
-//  // Hit test ouside the reach of any layer
-//  touchPoint = ScreenIntPoint(1000,10);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, nullAPZC);
-//  touchPoint = ScreenIntPoint(-1000,10);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, nullAPZC);
-//
-//  // Test layer transform
-//  gfx3DMatrix transform;
-//  transform.ScalePost(0.1, 0.1, 1);
-//  root->SetBaseTransform(transform);
-//
-//  touchPoint = ScreenIntPoint(50,50); // This point is now outside the root layer
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, nullAPZC);
-//
-//  touchPoint = ScreenIntPoint(2,2);
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub4.get());
-//  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
-//
-//  // Scale layer[4] outside the range
-//  layers[4]->SetBaseTransform(transform);
-//  // layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
-//  // Does not contain (2, 2)
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub3.get());
-//  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
-//
-//  // Transformation chain to layer 7
-//  scrollable.mScrollId++;
-//  layerBound = layers[7]->GetVisibleRegion().GetBounds();
-//  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-//                                 layerBound.width, layerBound.height);
-//  layers[7]->AsContainerLayer()->SetFrameMetrics(scrollable);
-//  layers[7]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub7);
-//
-//  gfx3DMatrix translateTransform;
-//  translateTransform.Translate(gfxPoint3D(10, 10, 0));
-//  layers[5]->SetBaseTransform(translateTransform);
-//
-//  gfx3DMatrix translateTransform2;
-//  translateTransform2.Translate(gfxPoint3D(-20, 0, 0));
-//  layers[6]->SetBaseTransform(translateTransform2);
-//
-//  gfx3DMatrix translateTransform3;
-//  translateTransform3.ScalePost(1,15,1);
-//  layers[7]->SetBaseTransform(translateTransform3);
-//
-//  touchPoint = ScreenIntPoint(1,45);
-//  // layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
-//  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-//                 &apzcOut, &relativePointOut);
-//  EXPECT_EQ(apzcOut, apzcSub7.get());
-//  EXPECT_EQ(LayerIntPoint(20, 29), relativePointOut);
-//}
+static void
+SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId, MockContentController* mcc)
+{
+  ContainerLayer* container = aLayer->AsContainerLayer();
+  FrameMetrics metrics;
+  metrics.mScrollId = aScrollId;
+  nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
+  metrics.mCompositionBounds = ScreenIntRect(layerBound.x, layerBound.y,
+                                             layerBound.width, layerBound.height);
+  metrics.mViewport = CSSRect(layerBound.x, layerBound.y,
+                              layerBound.width, layerBound.height);
+  container->SetFrameMetrics(metrics);
+
+  // when we do the next tree update, a new APZC will be created for this layer,
+  // and that will invoke these functions once.
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(1);
+  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+}
+
+TEST(APZCTreeManager, GetAPZCAtPoint) {
+  nsTArray<nsRefPtr<Layer> > layers;
+  nsRefPtr<LayerManager> lm;
+  nsRefPtr<Layer> root = CreateTestLayerTree(lm, layers);
+
+  TimeStamp testStartTime = TimeStamp::Now();
+  AsyncPanZoomController::SetFrameTime(testStartTime);
+  nsRefPtr<MockContentController> mcc = new MockContentController();
+  ScopedLayerTreeRegistration controller(CompositorParent::ROOT_LAYER_TREE_ID, root, mcc);
+
+  nsRefPtr<APZCTreeManager> manager = new TestAPZCTreeManager();
+
+  // No APZC attached so hit testing will return no APZC at (20,20)
+  nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(20, 20));
+  AsyncPanZoomController* nullAPZC = nullptr;
+  EXPECT_EQ(nullAPZC, hit.get());
+
+  // Now we have a root APZC that will match the page
+  SetScrollableFrameMetrics(root, FrameMetrics::ROOT_SCROLL_ID, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
+
+  // Now we have a sub APZC with a better fit
+  SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController());
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
+
+  // Now test hit testing when we have two scrollable layers
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
+
+  // Hit test ouside the reach of layer[3,4] but inside root
+  hit = manager->GetTargetAPZC(ScreenPoint(90, 90));
+  EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(90, 90)
+
+  // Hit test ouside the reach of any layer
+  hit = manager->GetTargetAPZC(ScreenPoint(1000, 10));
+  EXPECT_EQ(nullAPZC, hit.get());
+  hit = manager->GetTargetAPZC(ScreenPoint(-1000, 10));
+  EXPECT_EQ(nullAPZC, hit.get());
+
+  // Test layer transform
+  gfx3DMatrix transform;
+  transform.ScalePost(0.1, 0.1, 1);
+  root->SetBaseTransform(transform);
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(50, 50)); // This point is now outside the root layer
+  EXPECT_EQ(nullAPZC, hit.get());
+
+  hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 20)
+
+  // Scale layer[4] outside the range
+  layers[4]->SetBaseTransform(transform);
+  // layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
+  // Does not contain (2, 2)
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 20)
+
+  // Transformation chain to layer 7
+  SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2, mcc);
+
+  gfx3DMatrix translateTransform;
+  translateTransform.Translate(gfxPoint3D(10, 10, 0));
+  layers[5]->SetBaseTransform(translateTransform);
+
+  gfx3DMatrix translateTransform2;
+  translateTransform2.Translate(gfxPoint3D(-20, 0, 0));
+  layers[6]->SetBaseTransform(translateTransform2);
+
+  gfx3DMatrix translateTransform3;
+  translateTransform3.ScalePost(1,15,1);
+  layers[7]->SetBaseTransform(translateTransform3);
+
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, CompositorParent::ROOT_LAYER_TREE_ID, false);
+  // layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
+  hit = manager->GetTargetAPZC(ScreenPoint(1, 45));
+  EXPECT_EQ(layers[7]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 29)
+
+  manager->ClearTree();
+}
 
 
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -43,16 +43,17 @@ public:
     return "TestContainerLayer";
   }
 
   virtual LayerType GetType() const {
     return TYPE_CONTAINER;
   }
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {
+    DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void RepositionChild(Layer* aChild, Layer* aAfter) {
     MOZ_CRASH();
   }
 
   virtual void InsertAfter(Layer* aChild, Layer* aAfter) {
     // Bad implementation but it should be fine for testing
@@ -87,20 +88,16 @@ public:
   virtual const char* Name() const {
     return "TestThebesLayer";
   }
 
   virtual LayerType GetType() const {
     return TYPE_THEBES;
   }
 
-  virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {
-    MOZ_CRASH();
-  }
-
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     MOZ_CRASH();
   }
 };
 
 class TestUserData: public LayerUserData {
 public:
   MOCK_METHOD0(Die, void());
@@ -231,16 +228,19 @@ already_AddRefed<Layer> CreateLayerTree(
       }
       if (parentContainerLayer) {
         parentContainerLayer->InsertAfter(layer, nullptr);
         layer->SetParent(parentContainerLayer);
       }
       lastLayer = layer;
     }
   }
+  if (rootLayer) {
+    rootLayer->ComputeEffectiveTransforms(gfx3DMatrix());
+  }
   return rootLayer.forget();
 }
 
 TEST(Layers, LayerTree) {
   const char* layerTreeSyntax = "c(c(tt))";
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
     nsIntRegion(nsIntRect(0,0,100,100)),