--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -385,27 +385,23 @@ APZCTreeManager::PrepareNodeForLayer(con
apzc->NotifyLayersUpdated(aMetrics,
aState.mIsFirstPaint && (aLayersId == aState.mOriginatingLayersId));
// Since this is the first time we are encountering an APZC with this guid,
// the node holding it must be the primary holder. It may be newly-created
// or not, depending on whether it went through the newApzc branch above.
MOZ_ASSERT(node->IsPrimaryHolder() && node->Apzc()->Matches(guid));
- nsIntRegion unobscured;
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
- unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
+ nsIntRegion unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
+ node->SetHitTestData(EventRegions(), Matrix4x4(), unobscured);
+ APZCTM_LOG("Setting region %s as visible region for APZC %p (node %p)\n",
+ Stringify(unobscured).c_str(), apzc, node.get());
}
- // This initializes, among other things, the APZC's hit-region.
- // If event-regions are disabled, this will initialize it to the empty
- // region, but UpdatePanZoomControllerTree will add the hit-region from
- // the event regions later.
- apzc->SetLayerHitTestData(EventRegions(unobscured), aAncestorTransform);
- APZCTM_LOG("Setting region %s as visible region for APZC %p\n",
- Stringify(unobscured).c_str(), apzc);
+ apzc->SetAncestorTransform(aAncestorTransform);
PrintAPZCInfo(aLayer, apzc);
// Bind the APZC instance into the tree of APZCs
AttachNodeToTree(node, aParent, aNextSibling);
// For testing, log the parent scroll id of every APZC that has a
// parent. This allows test code to reconstruct the APZC tree.
@@ -437,45 +433,34 @@ APZCTreeManager::PrepareNodeForLayer(con
}
// Add a guid -> APZC mapping for the newly created APZC.
insertResult.first->second = apzc;
} else {
// We already built an APZC earlier in this tree walk, but we have another layer
// now that will also be using that APZC. The hit-test region on the APZC needs
// to be updated to deal with the new layer's hit region.
- // FIXME: Combining this hit test region to the existing hit test region has a bit
- // of a problem, because it assumes the z-index of this new region is the same as
- // the z-index of the old region (from the previous layer with the same scrollid)
- // when in fact that may not be the case.
- // Consider the case where we have three layers: A, B, and C. A is at the top in
- // z-order and C is at the bottom. A and C share a scrollid and scroll together; but
- // B has a different scrollid and scrolls independently. Depending on how B moves
- // and the async transform on it, a larger/smaller area of C may be unobscured.
- // However, when we combine the hit regions of A and C here we are ignoring the
- // async transform and so we basically assume the same amount of C is always visible
- // on top of B. Fixing this doesn't appear to be very easy so I'm leaving it for
- // now in the hopes that we won't run into this problem a lot.
// TODO(optimization): we could recycle one of the non-primary-holder nodes
// from mNodesToDestroy instead of creating a new one since those are going
// to get discarded anyway.
node = new HitTestingTreeNode(apzc, false);
AttachNodeToTree(node, aParent, aNextSibling);
// Even though different layers associated with a given APZC may be at
// different levels in the layer tree (e.g. one being an uncle of another),
// we require from Layout that the CSS transforms up to their common
// ancestor be the same.
MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
nsIntRegion unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
- apzc->AddHitTestRegions(EventRegions(unobscured));
- APZCTM_LOG("Adding region %s to visible region of APZC %p\n", Stringify(unobscured).c_str(), apzc);
+ node->SetHitTestData(EventRegions(), Matrix4x4(), unobscured);
+ APZCTM_LOG("Adding region %s to visible region of APZC %p (via node %p)\n",
+ Stringify(unobscured).c_str(), apzc, node.get());
}
}
return node;
}
HitTestingTreeNode*
APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
@@ -508,31 +493,32 @@ APZCTreeManager::UpdatePanZoomController
Matrix4x4 transform = aLayer.GetTransform();
Matrix4x4 ancestorTransform = transform;
if (!apzc) {
ancestorTransform = ancestorTransform * aAncestorTransform;
}
uint64_t childLayersId = (aLayer.AsRefLayer() ? aLayer.AsRefLayer()->GetReferentId() : aLayersId);
- nsIntRegion obscured;
+ nsIntRegion obscuredByUncles;
if (aLayersId == childLayersId) {
- // If the child layer is in the same process, transform
- // aObscured from aLayer's ParentLayerPixels to aLayer's LayerPixels,
- // which are the children layers' ParentLayerPixels.
+ // If the child layer is in the same process, keep a copy of |aObscured|.
+ // This value is in ParentLayerPixels and represents the area that is
+ // obscured by |aLayer|'s younger uncles (i.e. any next-siblings of any
+ // ancestor of |aLayer|) in the same process. We will need this value later.
+
// If we cross a process boundary, we assume that we can start with
// an empty obscured region because nothing in the parent process will
// obscure the child process. This may be false. However, not doing this
// definitely runs into a problematic case where the B2G notification
// bar and the keyboard get merged into a single layer that obscures
// all child processes, even though visually they do not. We'd probably
// have to check for mask layers and so on in order to properly handle
// that case.
- obscured = aObscured;
- obscured.Transform(To3DMatrix(transform).Inverse());
+ obscuredByUncles = aObscured;
}
// If there's no APZC at this level, any APZCs for our child layers will
// have our siblings as their siblings, and our parent as their parent.
HitTestingTreeNode* next = aNextSibling;
if (node) {
// Otherwise, use this APZC as the parent going downwards, and start off
// with its first child as the next sibling.
@@ -548,16 +534,19 @@ APZCTreeManager::UpdatePanZoomController
// which will accumulate the hit area of descendants of aLayer. In general,
// the mEventRegions stack is used to accumulate event regions from descendant
// layers because the event regions for a layer don't include those of its
// children.
if (gfxPrefs::LayoutEventRegionsEnabled() && (apzc || aState.mEventRegions.Length() > 0)) {
aState.mEventRegions.AppendElement(EventRegions());
}
+ // Convert the obscured region into this layer's LayerPixels.
+ nsIntRegion obscured = obscuredByUncles;
+ obscured.Transform(To3DMatrix(transform).Inverse());
for (LayerMetricsWrapper child = aLayer.GetLastChild(); child; child = child.GetPrevSibling()) {
gfx::TreeAutoIndent indent(mApzcTreeLog);
next = UpdatePanZoomControllerTree(aState, child, childLayersId,
ancestorTransform, aParent, next,
obscured);
// Each layer obscures its previous siblings, so we augment the obscured
// region as we loop backwards through the children.
@@ -580,73 +569,84 @@ APZCTreeManager::UpdatePanZoomController
// the accumulated regions of the non-APZC descendants of |aLayer|. This
// happened in the loop above while we iterated through the descendants of
// |aLayer|. Note that it only includes the non-APZC descendants, because
// if a layer has an APZC, we simply store the regions from that subtree on
// that APZC and don't propagate them upwards in the tree. Because of the
// way we do hit-testing (where the deepest matching APZC is used) it should
// still be ok if we did propagate those regions upwards and included them
// in all the ancestor APZCs.
- //
- // Also at this point in the code the |obscured| region includes the hit
- // regions of children of |aLayer| as well as the hit regions of |aLayer|'s
- // younger uncles (i.e. the next-sibling chains of |aLayer|'s ancestors).
- // When we compute the unobscured regions below, we subtract off the
- // |obscured| region, but it would also be ok to do this before the above
- // loop. At that point |obscured| would only have the uncles' hit regions
- // and not the children. The reason this is ok is again because of the way
- // we do hit-testing (where the deepest APZC is used) it doesn't matter if
- // we count the children as obscuring the parent or not.
-
- EventRegions unobscured;
- unobscured.Sub(aLayer.GetEventRegions(), obscured);
- APZCTM_LOG("Picking up unobscured hit region %s from layer %p\n", Stringify(unobscured).c_str(), aLayer.GetLayer());
// Take the hit region of the |aLayer|'s subtree (which has already been
// transformed into the coordinate space of |aLayer|) and...
EventRegions subtreeEventRegions = aState.mEventRegions.LastElement();
aState.mEventRegions.RemoveElementAt(aState.mEventRegions.Length() - 1);
- // ... combine it with the hit region for this layer, and then ...
- subtreeEventRegions.OrWith(unobscured);
- // ... transform it up to the parent layer's coordinate space.
- subtreeEventRegions.Transform(To3DMatrix(aLayer.GetTransform()));
- if (aLayer.GetClipRect()) {
- subtreeEventRegions.AndWith(*aLayer.GetClipRect());
- }
-
- APZCTM_LOG("After processing layer %p the subtree hit region is %s\n", aLayer.GetLayer(), Stringify(subtreeEventRegions).c_str());
+ // ... combine it with the event region for this layer.
+ subtreeEventRegions.OrWith(aLayer.GetEventRegions());
// If we have an APZC at this level, intersect the subtree hit region with
// the touch-sensitive region and add it to the APZ's hit test regions.
- if (apzc) {
- APZCTM_LOG("Adding region %s to visible region of APZC %p\n", Stringify(subtreeEventRegions).c_str(), apzc);
+ if (node) {
+ // At this point in the code we have two different "obscured" regions.
+ // There is |obscuredByUncles| which represents the hit regions of
+ // |aLayer|'s younger uncles (i.e. the next-sibling chains of |aLayer|'s
+ // ancestors). This obscured region does not move when aLayer is scrolled,
+ // and so is in the same ParentLayerPixel coordinate space as |aLayer|'s
+ // clip rect.
+ // We also have |obscured| which includes the hit regions of |aLayer|'s
+ // descendants. However we don't want to use this because those
+ // descendants (and their hit regions) move as |aLayer| scrolls.
+ // From |aLayer|'s point of view |obscured| is in a nonsensical coordinate
+ // space because it combines the uncle-obscured region and child-obscured
+ // regions.
+ // We combine the |obscuredByUncles| region with the clip rect of |aLayer|
+ // below, to "apply" the obscuration by the uncles.
+ nsIntRegion clipRegion;
+ if (aLayer.GetClipRect()) {
+ clipRegion = nsIntRegion(*aLayer.GetClipRect());
+ } else {
+ // if there is no clip on this layer (which should only happen for the
+ // root scrollable layer in a process) fall back to using the comp
+ // bounds which should be equivalent.
+ clipRegion = nsIntRegion(ParentLayerIntRect::ToUntyped(RoundedToInt(aLayer.Metrics().mCompositionBounds)));
+ }
+ clipRegion.SubOut(obscuredByUncles);
+
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
MOZ_ASSERT(state);
MOZ_ASSERT(state->mController.get());
CSSRect touchSensitiveRegion;
if (state->mController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
// Here we assume 'touchSensitiveRegion' is in the CSS pixels of the
// parent frame. To convert it to ParentLayer pixels, we therefore need
// the cumulative resolution of the parent frame. We approximate this as
// the quotient of our cumulative resolution and our pres shell
// resolution; this approximation may not be accurate in the presence of
// a css-driven resolution.
LayoutDeviceToParentLayerScale parentCumulativeResolution =
aLayer.Metrics().GetCumulativeResolution()
/ ParentLayerToLayerScale(aLayer.Metrics().mPresShellResolution);
- subtreeEventRegions.AndWith(ParentLayerIntRect::ToUntyped(
+ clipRegion.AndWith(ParentLayerIntRect::ToUntyped(
RoundedIn(touchSensitiveRegion
* aLayer.Metrics().GetDevPixelsPerCSSPixel()
* parentCumulativeResolution)));
}
- apzc->AddHitTestRegions(subtreeEventRegions);
+
+ node->SetHitTestData(subtreeEventRegions, aLayer.GetTransform(), clipRegion);
+ APZCTM_LOG("After processing layer %p the event regions for %p is %s\n",
+ aLayer.GetLayer(), node, Stringify(subtreeEventRegions).c_str());
} else {
// If we don't have an APZC at this level, carry the subtree hit region
// up to the parent.
MOZ_ASSERT(aState.mEventRegions.Length() > 0);
+ // transform it up to the parent layer's coordinate space.
+ subtreeEventRegions.Transform(To3DMatrix(aLayer.GetTransform()));
+ if (aLayer.GetClipRect()) {
+ subtreeEventRegions.AndWith(*aLayer.GetClipRect());
+ }
aState.mEventRegions.LastElement().OrWith(subtreeEventRegions);
}
}
// Return the node that should be the sibling of other nodes as we continue
// moving towards the first child at this depth in the layer tree.
// If this layer doesn't have an APZC, we promote any nodes in the subtree
// upwards. Otherwise we fall back to the aNextSibling that was passed in.
@@ -1328,23 +1328,29 @@ already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHitResult)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, so check those too
HitTestResult hitResult = NoApzcHit;
for (HitTestingTreeNode* node = mRootNode; node; node = node->GetPrevSibling()) {
target = GetAPZCAtPoint(node, aPoint.ToUnknownPoint(), &hitResult);
- if (target == node->Apzc() && node->GetPrevSibling() && target == node->GetPrevSibling()->Apzc()) {
- // We might be able to do better if we keep looping, so let's do that.
- // This happens because the hit-test data stored in "target" actually
- // includes the areas of all the nodes that have "target" as their APZC.
- // One of these nodes (that we haven't yet encountered) may have a child
- // that is on top of this area, so we need to check for that. A future
- // patch will deal with this more correctly.
+ if (!gfxPrefs::LayoutEventRegionsEnabled() && target == node->Apzc()
+ && node->GetPrevSibling() && target == node->GetPrevSibling()->Apzc()) {
+ // When event-regions are disabled, we treat scrollinfo layers as
+ // regular scrollable layers. Unfortunately, their "hit region" (which
+ // we create from the composition bounds) is their full area, and they
+ // sit on top of their non-scrollinfo siblings. This means they will get
+ // a HitTestingTreeNode with a hit region that will aggressively match
+ // any input events that might be directed to sub-APZCs of their non-
+ // scrollinfo siblings. This means that we need to keep looping through
+ // to see if there are any other non-scrollinfo siblings that have
+ // children that match this event. If so, they should take priority.
+ // Once we turn on event-regions and ignore scrollinfo layers, this
+ // will become unnecessary.
continue;
}
// If we hit an overscrolled APZC, 'target' will be nullptr but it's still
// a hit so we don't search further siblings.
if (target || (hitResult == OverscrolledApzc)) {
break;
}
}
@@ -1480,17 +1486,17 @@ APZCTreeManager::GetAPZCAtPoint(HitTesti
// It is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
// and RC.Inverse() * QC.Inverse() at recursion level for P.
Matrix4x4 ancestorUntransform = apzc->GetAncestorTransform().Inverse();
// Hit testing for this layer takes place in our parent layer coordinates,
// since the composition bounds (used to initialize the visible rect against
// which we hit test are in those coordinates).
Point4D hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
- APZCTM_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
+ APZCTM_LOG("Untransformed %f %f to parentlayer coordinates %f %f for hit-testing APZC %p\n",
aHitTestPoint.x, aHitTestPoint.y,
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, apzc);
// childUntransform takes points from apzc's parent APZC's CSS-transformed layer coordinates
// to apzc's CSS-transformed layer coordinates.
// It is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LA.Inverse() at L
// and RC.Inverse() * QC.Inverse() * PA.Inverse() at P.
Matrix4x4 childUntransform = ancestorUntransform * Matrix4x4(apzc->GetCurrentAsyncTransform()).Inverse();
@@ -1500,43 +1506,51 @@ APZCTreeManager::GetAPZCAtPoint(HitTesti
hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, apzc);
AsyncPanZoomController* result = nullptr;
// This walks the tree in depth-first, reverse order, so that it encounters
// APZCs front-to-back on the screen.
if (hitTestPointForChildLayers.HasPositiveWCoord()) {
for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) {
AsyncPanZoomController* match = GetAPZCAtPoint(child, hitTestPointForChildLayers.As2DPoint(), aOutHitResult);
- if (match == child->Apzc() && child->GetPrevSibling() && match == child->GetPrevSibling()->Apzc()) {
- // We might be able to do better if we keep looping, so let's do that.
- // This happens because the hit-test data stored in "target" actually
- // includes the areas of all the nodes that have "target" as their APZC.
- // One of these nodes (that we haven't yet encountered) may have a child
- // that is on top of this area, so we need to check for that. A future
- // patch will deal with this more correctly.
+ if (!gfxPrefs::LayoutEventRegionsEnabled() && match == aNode->Apzc()
+ && aNode->GetPrevSibling() && match == aNode->GetPrevSibling()->Apzc()) {
+ // When event-regions are disabled, we treat scrollinfo layers as
+ // regular scrollable layers. Unfortunately, their "hit region" (which
+ // we create from the composition bounds) is their full area, and they
+ // sit on top of their non-scrollinfo siblings. This means they will get
+ // a HitTestingTreeNode with a hit region that will aggressively match
+ // any input events that might be directed to sub-APZCs of their non-
+ // scrollinfo siblings. This means that we need to keep looping through
+ // to see if there are any other non-scrollinfo siblings that have
+ // children that match this event. If so, they should take priority.
+ // Once we turn on event-regions and ignore scrollinfo layers, this
+ // will become unnecessary.
continue;
}
if (*aOutHitResult == OverscrolledApzc) {
// We matched an overscrolled APZC, abort.
return nullptr;
}
if (match) {
result = match;
break;
}
}
}
if (!result && hitTestPointForThisLayer.HasPositiveWCoord()) {
ParentLayerPoint point = ParentLayerPoint::FromUnknownPoint(hitTestPointForThisLayer.As2DPoint());
- if (apzc->HitRegionContains(point)) {
- APZCTM_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n",
- hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, apzc);
+ HitTestResult hitResult = aNode->HitTest(point);
+ if (hitResult != HitTestResult::NoApzcHit) {
+ APZCTM_LOG("Successfully matched untransformed point %s to visible region for APZC %p via node %p\n",
+ Stringify(hitTestPointForThisLayer.As2DPoint()).c_str(), apzc, aNode);
result = apzc;
+ MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion);
// If event regions are disabled, *aOutHitResult will be ApzcHitRegion
- *aOutHitResult = (apzc->DispatchToContentRegionContains(point) ? ApzcContentRegion : ApzcHitRegion);
+ *aOutHitResult = hitResult;
}
}
// If we are overscrolled, and the point matches us or one of our children,
// the result is inside an overscrolled APZC, inform our caller of this
// (callers typically ignore events targeted at overscrolled APZCs).
if (result && apzc->IsOverscrolled()) {
*aOutHitResult = OverscrolledApzc;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -965,49 +965,29 @@ private:
/* ===================================================================
* The functions and members in this section are used to maintain the
* area that this APZC instance is responsible for. This is used when
* hit-testing to see which APZC instance should handle touch events.
*/
public:
- void SetLayerHitTestData(const EventRegions& aRegions, const Matrix4x4& aTransformToLayer) {
- mEventRegions = aRegions;
+ void SetAncestorTransform(const Matrix4x4& aTransformToLayer) {
mAncestorTransform = aTransformToLayer;
}
- void AddHitTestRegions(const EventRegions& aRegions) {
- mEventRegions.OrWith(aRegions);
- }
-
Matrix4x4 GetAncestorTransform() const {
return mAncestorTransform;
}
- bool HitRegionContains(const ParentLayerPoint& aPoint) const {
- ParentLayerIntPoint point = RoundedToInt(aPoint);
- return mEventRegions.mHitRegion.Contains(point.x, point.y);
- }
-
- bool DispatchToContentRegionContains(const ParentLayerPoint& aPoint) const {
- ParentLayerIntPoint point = RoundedToInt(aPoint);
- return mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y);
- }
-
bool IsOverscrolled() const {
return mX.IsOverscrolled() || mY.IsOverscrolled();
}
private:
- /* This is the union of the hit regions of the layers that this APZC
- * corresponds to, in the local screen pixels of those layers. (This is the
- * same coordinate system in which this APZC receives events in
- * ReceiveInputEvent()). */
- EventRegions mEventRegions;
/* This is the cumulative CSS transform for all the layers from (and including)
* the parent APZC down to (but excluding) this one. */
Matrix4x4 mAncestorTransform;
/* ===================================================================
* The functions and members in this section are used for sharing the
* FrameMetrics across processes for the progressive tiling code.
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -1,19 +1,22 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et 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 "HitTestingTreeNode.h"
-#include "AsyncPanZoomController.h"
-#include "LayersLogging.h"
-#include "nsPrintfCString.h"
+#include "AsyncPanZoomController.h" // for AsyncPanZoomController
+#include "LayersLogging.h" // for Stringify
+#include "mozilla/gfx/Point.h" // for Point4D
+#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform::operator Matrix4x4()
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "UnitTransforms.h" // for ViewAs
namespace mozilla {
namespace layers {
HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
bool aIsPrimaryHolder)
: mApzc(aApzc)
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
@@ -111,22 +114,71 @@ HitTestingTreeNode::Apzc() const
bool
HitTestingTreeNode::IsPrimaryHolder() const
{
return mIsPrimaryApzcHolder;
}
void
+HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
+ const gfx::Matrix4x4& aTransform,
+ const nsIntRegion& aClipRegion)
+{
+ mEventRegions = aRegions;
+ mTransform = aTransform;
+ mClipRegion = aClipRegion;
+}
+
+HitTestResult
+HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
+{
+ // When event regions are disabled, we are actually storing the
+ // touch-sensitive section of the composition bounds in the clip rect, and we
+ // don't need to use mTransform or mEventRegions.
+ if (!gfxPrefs::LayoutEventRegionsEnabled()) {
+ MOZ_ASSERT(mEventRegions == EventRegions());
+ MOZ_ASSERT(mTransform == gfx::Matrix4x4());
+ return mClipRegion.Contains(aPoint.x, aPoint.y)
+ ? HitTestResult::ApzcHitRegion
+ : HitTestResult::NoApzcHit;
+ }
+
+ // test against clip rect in ParentLayer coordinate space
+ if (!mClipRegion.Contains(aPoint.x, aPoint.y)) {
+ return HitTestResult::NoApzcHit;
+ }
+
+ // convert into Layer coordinate space
+ gfx::Matrix4x4 localTransform = mTransform * gfx::Matrix4x4(mApzc->GetCurrentAsyncTransform());
+ gfx::Point4D pointInLayerPixels = localTransform.Inverse().ProjectPoint(aPoint.ToUnknownPoint());
+ if (!pointInLayerPixels.HasPositiveWCoord()) {
+ return HitTestResult::NoApzcHit;
+ }
+ LayerIntPoint point = RoundedToInt(ViewAs<LayerPixel>(pointInLayerPixels.As2DPoint()));
+
+ // test against event regions in Layer coordinate space
+ if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
+ return HitTestResult::NoApzcHit;
+ }
+ if (mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y)) {
+ return HitTestResult::ApzcContentRegion;
+ }
+ return HitTestResult::ApzcHitRegion;
+}
+
+void
HitTestingTreeNode::Dump(const char* aPrefix) const
{
if (mPrevSibling) {
mPrevSibling->Dump(aPrefix);
}
- printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) guid (%s)\n",
- aPrefix, this, mApzc.get(), Stringify(mApzc->GetGuid()).c_str());
+ printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) r=(%s) t=(%s) c=(%s)\n",
+ aPrefix, this, mApzc.get(), Stringify(mApzc->GetGuid()).c_str(),
+ Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
+ Stringify(mClipRegion).c_str());
if (mLastChild) {
mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
}
}
} // namespace layers
} // namespace mozilla
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -2,18 +2,21 @@
/* vim: set sw=2 ts=8 et 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_HitTestingTreeNode_h
#define mozilla_layers_HitTestingTreeNode_h
-#include "FrameMetrics.h"
-#include "nsRefPtr.h"
+#include "APZUtils.h" // for HitTestResult
+#include "FrameMetrics.h" // for ScrollableLayerGuid
+#include "mozilla/gfx/Matrix.h" // for Matrix4x4
+#include "mozilla/layers/LayersTypes.h" // for EventRegions
+#include "nsRefPtr.h" // for nsRefPtr
namespace mozilla {
namespace layers {
class AsyncPanZoomController;
/**
* This class represents a node in a tree that is used by the APZCTreeManager
@@ -62,24 +65,56 @@ public:
HitTestingTreeNode* GetLastChild() const;
HitTestingTreeNode* GetPrevSibling() const;
HitTestingTreeNode* GetParent() const;
/* APZC related methods */
AsyncPanZoomController* Apzc() const;
bool IsPrimaryHolder() const;
+ /* Hit test related methods */
+ void SetHitTestData(const EventRegions& aRegions,
+ const gfx::Matrix4x4& aTransform,
+ const nsIntRegion& aClipRegion);
+ HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
+
/* Debug helpers */
void Dump(const char* aPrefix = "") const;
private:
nsRefPtr<HitTestingTreeNode> mLastChild;
nsRefPtr<HitTestingTreeNode> mPrevSibling;
nsRefPtr<HitTestingTreeNode> mParent;
nsRefPtr<AsyncPanZoomController> mApzc;
bool mIsPrimaryApzcHolder;
+
+ /* Let {L,M} be the {layer, scrollable metrics} pair that this node
+ * corresponds to in the layer tree. Then, mEventRegions contains the union
+ * of the event regions of all layers in L's subtree, excluding those layers
+ * which are contained in a descendant HitTestingTreeNode's mEventRegions.
+ * This value is stored in L's LayerPixel space.
+ * For example, if this HitTestingTreeNode maps to a ContainerLayer with
+ * scrollable metrics and which has two PaintedLayer children, the event
+ * regions stored here will be the union of the three event regions in the
+ * ContainerLayer's layer pixel space. This means the event regions from the
+ * PaintedLayer children will have been transformed and clipped according to
+ * the individual properties on those layers but the ContainerLayer's event
+ * regions will be used "raw". */
+ EventRegions mEventRegions;
+
+ /* This is the transform that the layer subtree corresponding to this node is
+ * subject to. In the terms of the comment on mEventRegions, it is the
+ * transform from the ContainerLayer. This does NOT include any async
+ * transforms. */
+ gfx::Matrix4x4 mTransform;
+
+ /* This is the clip rect that the layer subtree corresponding to this node
+ * is subject to. In the terms of the comment on mEventRegions, it is the clip
+ * rect of the ContainerLayer, and is in the ContainerLayer's ParentLayerPixel
+ * space. */
+ nsIntRegion mClipRegion;
};
}
}
#endif // mozilla_layers_HitTestingTreeNode_h
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1683,25 +1683,25 @@ protected:
FrameMetrics metrics;
metrics.SetScrollId(aScrollId);
nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
metrics.mCompositionBounds = ParentLayerRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height);
metrics.SetScrollableRect(aScrollableRect);
metrics.SetScrollOffset(CSSPoint(0, 0));
aLayer->SetFrameMetrics(metrics);
+ aLayer->SetClipRect(&layerBound);
if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
// The purpose of this is to roughly mimic what layout would do in the
// case of a scrollable frame with the event regions and clip. This lets
// us exercise the hit-testing code in APZCTreeManager
EventRegions er = aLayer->GetEventRegions();
nsIntRect scrollRect = LayerIntRect::ToUntyped(RoundedToInt(aScrollableRect * metrics.LayersPixelsPerCSSPixel()));
er.mHitRegion = nsIntRegion(nsIntRect(layerBound.TopLeft(), scrollRect.Size()));
aLayer->SetEventRegions(er);
- aLayer->SetClipRect(&layerBound);
}
}
void SetScrollHandoff(Layer* aChild, Layer* aParent) {
FrameMetrics metrics = aChild->GetFrameMetrics(0);
metrics.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId());
aChild->SetFrameMetrics(metrics);
}
@@ -2439,17 +2439,22 @@ TEST_F(APZOverscrollHandoffTester, Scrol
class APZEventRegionsTester : public APZCTreeManagerTester {
protected:
UniquePtr<ScopedLayerTreeRegistration> registration;
TestAsyncPanZoomController* rootApzc;
void CreateEventRegionsLayerTree1() {
const char* layerTreeSyntax = "c(tt)";
- root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(nsIntRect(0, 0, 200, 200)), // root
+ nsIntRegion(nsIntRect(0, 0, 100, 200)), // left half
+ nsIntRegion(nsIntRect(0, 100, 200, 100)), // bottom half
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2);
SetScrollHandoff(layers[1], root);
SetScrollHandoff(layers[2], root);
// Set up the event regions over a 200x200 area. The root layer has the
// whole 200x200 as the hit region; layers[1] has the left half and
@@ -2467,17 +2472,21 @@ protected:
registration = MakeUnique<ScopedLayerTreeRegistration>(0, root, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateEventRegionsLayerTree2() {
const char* layerTreeSyntax = "c(t)";
- root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ nsIntRegion layerVisibleRegions[] = {
+ nsIntRegion(nsIntRect(0, 0, 100, 500)),
+ nsIntRegion(nsIntRect(0, 150, 100, 100)),
+ };
+ root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
// Set up the event regions so that the child thebes layer is positioned far
// away from the scrolling container layer.
EventRegions regions(nsIntRegion(nsIntRect(0, 0, 100, 100)));
root->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(nsIntRect(0, 150, 100, 100));
layers[1]->SetEventRegions(regions);