зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1109873 - Implement the expanded HitTestingTree. r=botond
This commit is contained in:
Родитель
ceba82c3a0
Коммит
1eb610400a
|
@ -28,8 +28,14 @@
|
|||
#include "OverscrollHandoffState.h" // for OverscrollHandoffState
|
||||
#include "LayersLogging.h" // for Stringify
|
||||
|
||||
#define APZCTM_LOG(...)
|
||||
// #define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
|
||||
#define ENABLE_APZCTM_LOGGING 0
|
||||
// #define ENABLE_APZCTM_LOGGING 1
|
||||
|
||||
#if ENABLE_APZCTM_LOGGING
|
||||
# define APZCTM_LOG(...) printf_stderr("APZCTM: " __VA_ARGS__)
|
||||
#else
|
||||
# define APZCTM_LOG(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
@ -68,13 +74,6 @@ struct APZCTreeManager::TreeBuildingState {
|
|||
// to facilitate re-using the same APZC for different layers that scroll
|
||||
// together (and thus have the same ScrollableLayerGuid).
|
||||
std::map<ScrollableLayerGuid, AsyncPanZoomController*> mApzcMap;
|
||||
|
||||
// A stack of event regions which corresponds to the call stack of recursive
|
||||
// UpdatePanZoomController calls during tree-building, except that we don't
|
||||
// start populating this stack until we first encounter an APZC. At each level
|
||||
// we accumulate the event regions of non-APZC descendants of the layer at
|
||||
// that level.
|
||||
nsTArray<EventRegions> mEventRegions;
|
||||
};
|
||||
|
||||
/*static*/ const ScreenMargin
|
||||
|
@ -199,10 +198,9 @@ APZCTreeManager::UpdateHitTestingTree(CompositorParent* aCompositor,
|
|||
UpdateHitTestingTree(state, root,
|
||||
// aCompositor is null in gtest scenarios
|
||||
aCompositor ? aCompositor->RootLayerTreeId() : 0,
|
||||
Matrix4x4(), nullptr, nullptr, nsIntRegion());
|
||||
Matrix4x4(), nullptr, nullptr);
|
||||
mApzcTreeLog << "[end]\n";
|
||||
}
|
||||
MOZ_ASSERT(state.mEventRegions.Length() == 0);
|
||||
|
||||
for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
|
||||
APZCTM_LOG("Destroying node at %p with APZC %p\n",
|
||||
|
@ -210,20 +208,34 @@ APZCTreeManager::UpdateHitTestingTree(CompositorParent* aCompositor,
|
|||
state.mNodesToDestroy[i]->GetApzc());
|
||||
state.mNodesToDestroy[i]->Destroy();
|
||||
}
|
||||
|
||||
#if ENABLE_APZCTM_LOGGING
|
||||
// Make the hit-test tree line up with the layer dump
|
||||
printf_stderr("APZCTreeManager (%p)\n", this);
|
||||
mRootNode->Dump(" ");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compute the touch-sensitive region of an APZC. This is used only when
|
||||
// event-regions are disabled.
|
||||
// Compute the clip region to be used for a layer with an APZC. This function
|
||||
// is only called for layers which actually have scrollable metrics and an APZC.
|
||||
static nsIntRegion
|
||||
ComputeTouchSensitiveRegion(GeckoContentController* aController,
|
||||
const FrameMetrics& aMetrics,
|
||||
const nsIntRegion& aObscured)
|
||||
ComputeClipRegion(GeckoContentController* aController,
|
||||
const LayerMetricsWrapper& aLayer)
|
||||
{
|
||||
// Use the composition bounds as the hit test region.
|
||||
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)));
|
||||
}
|
||||
|
||||
// Optionally, the GeckoContentController can provide a touch-sensitive
|
||||
// region that constrains all frames associated with the controller.
|
||||
// In this case we intersect the composition bounds with that region.
|
||||
ParentLayerRect visible(aMetrics.mCompositionBounds);
|
||||
CSSRect touchSensitiveRegion;
|
||||
if (aController->GetTouchSensitiveRegion(&touchSensitiveRegion)) {
|
||||
// Here we assume 'touchSensitiveRegion' is in the CSS pixels of the
|
||||
|
@ -233,19 +245,19 @@ ComputeTouchSensitiveRegion(GeckoContentController* aController,
|
|||
// this approximation may not be accurate in the presence of a css-driven
|
||||
// resolution.
|
||||
LayoutDeviceToParentLayerScale parentCumulativeResolution =
|
||||
aMetrics.GetCumulativeResolution()
|
||||
/ ParentLayerToLayerScale(aMetrics.mPresShellResolution);
|
||||
visible = visible.Intersect(touchSensitiveRegion
|
||||
* aMetrics.GetDevPixelsPerCSSPixel()
|
||||
* parentCumulativeResolution);
|
||||
aLayer.Metrics().GetCumulativeResolution()
|
||||
/ ParentLayerToLayerScale(aLayer.Metrics().mPresShellResolution);
|
||||
// Not sure what rounding option is the most correct here, but if we ever
|
||||
// figure it out we can change this. For now I'm rounding in to minimize
|
||||
// the chances of getting a complex region.
|
||||
nsIntRect extraClip = ParentLayerIntRect::ToUntyped(RoundedIn(
|
||||
touchSensitiveRegion
|
||||
* aLayer.Metrics().GetDevPixelsPerCSSPixel()
|
||||
* parentCumulativeResolution));
|
||||
clipRegion.AndWith(extraClip);
|
||||
}
|
||||
|
||||
// Not sure what rounding option is the most correct here, but if we ever
|
||||
// figure it out we can change this. For now I'm rounding in to minimize
|
||||
// the chances of getting a complex region.
|
||||
nsIntRegion unobscured;
|
||||
unobscured.Sub(ParentLayerIntRect::ToUntyped(RoundedIn(visible)), aObscured);
|
||||
return unobscured;
|
||||
return clipRegion;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -276,26 +288,63 @@ APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
|
|||
}
|
||||
}
|
||||
|
||||
static EventRegions
|
||||
GetEventRegions(const LayerMetricsWrapper& aLayer)
|
||||
{
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
return aLayer.GetEventRegions();
|
||||
}
|
||||
return EventRegions(aLayer.GetVisibleRegion());
|
||||
}
|
||||
|
||||
already_AddRefed<HitTestingTreeNode>
|
||||
APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
|
||||
AsyncPanZoomController* aApzc)
|
||||
{
|
||||
// Find a node without an APZC and return it. Note that unless the layer tree
|
||||
// actually changes, this loop should generally do an early-return on the
|
||||
// first iteration, so it should be cheap in the common case.
|
||||
for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
|
||||
nsRefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
|
||||
if (!node->IsPrimaryHolder()) {
|
||||
aState.mNodesToDestroy.RemoveElement(node);
|
||||
node->RecycleWith(aApzc);
|
||||
return node.forget();
|
||||
}
|
||||
}
|
||||
nsRefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false);
|
||||
return node.forget();
|
||||
}
|
||||
|
||||
HitTestingTreeNode*
|
||||
APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
const FrameMetrics& aMetrics,
|
||||
uint64_t aLayersId,
|
||||
const gfx::Matrix4x4& aAncestorTransform,
|
||||
const nsIntRegion& aObscured,
|
||||
HitTestingTreeNode* aParent,
|
||||
HitTestingTreeNode* aNextSibling,
|
||||
TreeBuildingState& aState)
|
||||
{
|
||||
bool needsApzc = true;
|
||||
if (!aMetrics.IsScrollable()) {
|
||||
return nullptr;
|
||||
needsApzc = false;
|
||||
}
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled() && aLayer.IsScrollInfoLayer()) {
|
||||
return nullptr;
|
||||
needsApzc = false;
|
||||
}
|
||||
|
||||
const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
|
||||
if (!(state && state->mController.get())) {
|
||||
return nullptr;
|
||||
needsApzc = false;
|
||||
}
|
||||
|
||||
nsRefPtr<HitTestingTreeNode> node = nullptr;
|
||||
if (!needsApzc) {
|
||||
node = RecycleOrCreateNode(aState, nullptr);
|
||||
AttachNodeToTree(node, aParent, aNextSibling);
|
||||
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(),
|
||||
aLayer.GetClipRect() ? Some(nsIntRegion(*aLayer.GetClipRect())) : Nothing());
|
||||
return node;
|
||||
}
|
||||
|
||||
AsyncPanZoomController* apzc = nullptr;
|
||||
|
@ -318,7 +367,6 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
|||
// If we haven't encountered a layer already with the same metrics, then we need to
|
||||
// do the full reuse-or-make-an-APZC algorithm, which is contained inside the block
|
||||
// below.
|
||||
nsRefPtr<HitTestingTreeNode> node = nullptr;
|
||||
if (apzc == nullptr) {
|
||||
apzc = aLayer.GetApzc();
|
||||
|
||||
|
@ -390,12 +438,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
|||
// or not, depending on whether it went through the newApzc branch above.
|
||||
MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
|
||||
|
||||
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
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());
|
||||
}
|
||||
nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
|
||||
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
|
||||
apzc->SetAncestorTransform(aAncestorTransform);
|
||||
|
||||
PrintAPZCInfo(aLayer, apzc);
|
||||
|
@ -439,10 +483,7 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
|||
// 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.
|
||||
|
||||
// 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);
|
||||
node = RecycleOrCreateNode(aState, apzc);
|
||||
AttachNodeToTree(node, aParent, aNextSibling);
|
||||
|
||||
// Even though different layers associated with a given APZC may be at
|
||||
|
@ -451,12 +492,8 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
|||
// ancestor be the same.
|
||||
MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
|
||||
|
||||
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
nsIntRegion unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
|
||||
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());
|
||||
}
|
||||
nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
|
||||
node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
|
||||
}
|
||||
|
||||
return node;
|
||||
|
@ -468,8 +505,7 @@ APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
|
|||
uint64_t aLayersId,
|
||||
const gfx::Matrix4x4& aAncestorTransform,
|
||||
HitTestingTreeNode* aParent,
|
||||
HitTestingTreeNode* aNextSibling,
|
||||
const nsIntRegion& aObscured)
|
||||
HitTestingTreeNode* aNextSibling)
|
||||
{
|
||||
mTreeLock.AssertCurrentThreadOwns();
|
||||
|
||||
|
@ -477,8 +513,9 @@ APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
|
|||
|
||||
HitTestingTreeNode* node = PrepareNodeForLayer(aLayer,
|
||||
aLayer.Metrics(), aLayersId, aAncestorTransform,
|
||||
aObscured, aParent, aNextSibling, aState);
|
||||
AsyncPanZoomController* apzc = (node ? node->GetApzc() : nullptr);
|
||||
aParent, aNextSibling, aState);
|
||||
MOZ_ASSERT(node);
|
||||
AsyncPanZoomController* apzc = node->GetApzc();
|
||||
aLayer.SetApzc(apzc);
|
||||
|
||||
mApzcTreeLog << '\n';
|
||||
|
@ -496,167 +533,20 @@ APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
|
|||
ancestorTransform = ancestorTransform * aAncestorTransform;
|
||||
}
|
||||
|
||||
// Note that |node| at this point will not have any children, otherwise we
|
||||
// we would have to set next to node->GetFirstChild().
|
||||
MOZ_ASSERT(!node->GetFirstChild());
|
||||
aParent = node;
|
||||
HitTestingTreeNode* next = nullptr;
|
||||
|
||||
uint64_t childLayersId = (aLayer.AsRefLayer() ? aLayer.AsRefLayer()->GetReferentId() : aLayersId);
|
||||
|
||||
nsIntRegion obscuredByUncles;
|
||||
if (aLayersId == childLayersId) {
|
||||
// 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.
|
||||
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.
|
||||
// Note that |node| at this point will not have any children, otherwise we
|
||||
// we would have to set next to node->GetFirstChild().
|
||||
MOZ_ASSERT(!node->GetFirstChild());
|
||||
aParent = node;
|
||||
next = nullptr;
|
||||
}
|
||||
|
||||
// In our recursive downward traversal, track event regions for layers once
|
||||
// we encounter an APZC. Push a new empty region on the mEventRegions stack
|
||||
// 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 = UpdateHitTestingTree(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.
|
||||
nsIntRegion childRegion;
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled()) {
|
||||
childRegion = child.GetEventRegions().mHitRegion;
|
||||
} else {
|
||||
childRegion = child.GetVisibleRegion();
|
||||
}
|
||||
childRegion.Transform(gfx::To3DMatrix(child.GetTransform()));
|
||||
if (child.GetClipRect()) {
|
||||
childRegion.AndWith(*child.GetClipRect());
|
||||
}
|
||||
|
||||
obscured.OrWith(childRegion);
|
||||
ancestorTransform, aParent, next);
|
||||
}
|
||||
|
||||
if (gfxPrefs::LayoutEventRegionsEnabled() && aState.mEventRegions.Length() > 0) {
|
||||
// At this point in the code, aState.mEventRegions.LastElement() contains
|
||||
// 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.
|
||||
|
||||
// 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 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 (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);
|
||||
clipRegion.AndWith(ParentLayerIntRect::ToUntyped(
|
||||
RoundedIn(touchSensitiveRegion
|
||||
* aLayer.Metrics().GetDevPixelsPerCSSPixel()
|
||||
* parentCumulativeResolution)));
|
||||
}
|
||||
|
||||
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.
|
||||
if (node) {
|
||||
return node;
|
||||
}
|
||||
if (next) {
|
||||
return next;
|
||||
}
|
||||
return aNextSibling;
|
||||
return node;
|
||||
}
|
||||
|
||||
nsEventStatus
|
||||
|
@ -1326,7 +1216,9 @@ APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHit
|
|||
{
|
||||
MonitorAutoLock lock(mTreeLock);
|
||||
HitTestResult hitResult = NoApzcHit;
|
||||
nsRefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, aPoint.ToUnknownPoint(), &hitResult);
|
||||
ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
|
||||
PixelCastJustification::ScreenIsParentLayerForRoot);
|
||||
nsRefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point, &hitResult);
|
||||
|
||||
// If we are in an overscrolled APZC, we should be returning nullptr.
|
||||
MOZ_ASSERT(!(target && (hitResult == OverscrolledApzc)));
|
||||
|
@ -1446,7 +1338,7 @@ APZCTreeManager::FindTargetNode(HitTestingTreeNode* aNode,
|
|||
|
||||
AsyncPanZoomController*
|
||||
APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
||||
const Point& aHitTestPoint,
|
||||
const ParentLayerPoint& aHitTestPoint,
|
||||
HitTestResult* aOutHitResult)
|
||||
{
|
||||
mTreeLock.AssertCurrentThreadOwns();
|
||||
|
@ -1456,59 +1348,41 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
|||
for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) {
|
||||
AsyncPanZoomController* apzc = node->GetApzc();
|
||||
|
||||
// The comments below assume there is a chain of layers L..R with L and P
|
||||
// having APZC instances as explained in the comment above
|
||||
// GetScreenToApzcTransform. This function will recurse with apzc at L and
|
||||
// P, and the comments explain what values are stored in the variables at
|
||||
// these two levels. All the comments use standard matrix notation where the
|
||||
// leftmost matrix in a multiplication is applied first.
|
||||
|
||||
// ancestorUntransform takes points from apzc's parent APZC's CSS-
|
||||
// transformed layer coordinates to apzc's parent layer's layer coordinates.
|
||||
// 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;
|
||||
if (apzc) {
|
||||
ancestorUntransform = apzc->GetAncestorTransform().Inverse();
|
||||
if (node->IsOutsideClip(aHitTestPoint)) {
|
||||
// If the point being tested is outside the clip region for this node
|
||||
// then we don't need to test against this node or any of its children.
|
||||
// Just skip it and move on.
|
||||
APZCTM_LOG("Point %f %f outside clip for node %p\n",
|
||||
aHitTestPoint.x, aHitTestPoint.y, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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 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 asyncUntransform;
|
||||
if (apzc) {
|
||||
asyncUntransform = Matrix4x4(apzc->GetCurrentAsyncTransform()).Inverse();
|
||||
}
|
||||
Matrix4x4 childUntransform = ancestorUntransform * asyncUntransform;
|
||||
Point4D hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
|
||||
APZCTM_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
|
||||
aHitTestPoint.x, aHitTestPoint.y,
|
||||
hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, apzc);
|
||||
|
||||
AsyncPanZoomController* result = nullptr;
|
||||
if (hitTestPointForChildLayers.HasPositiveWCoord()) {
|
||||
result = GetAPZCAtPoint(node->GetLastChild(), hitTestPointForChildLayers.As2DPoint(), aOutHitResult);
|
||||
|
||||
// First check the subtree rooted at this node, because deeper nodes
|
||||
// are more "in front".
|
||||
Maybe<LayerPoint> hitTestPointForChildLayers = node->Untransform(aHitTestPoint);
|
||||
if (hitTestPointForChildLayers) {
|
||||
ParentLayerPoint childPoint = ViewAs<ParentLayerPixel>(hitTestPointForChildLayers.ref(),
|
||||
PixelCastJustification::MovingDownToChildren);
|
||||
result = GetAPZCAtPoint(node->GetLastChild(), childPoint, aOutHitResult);
|
||||
if (*aOutHitResult == OverscrolledApzc) {
|
||||
// We matched an overscrolled APZC, abort.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (!result && hitTestPointForThisLayer.HasPositiveWCoord()) {
|
||||
ParentLayerPoint point = ParentLayerPoint::FromUnknownPoint(hitTestPointForThisLayer.As2DPoint());
|
||||
HitTestResult hitResult = node->HitTest(point);
|
||||
|
||||
// If we didn't match anything in the subtree, check |node|.
|
||||
if (!result) {
|
||||
APZCTM_LOG("Testing ParentLayer point %f %f (Layer %f %f) against node %p\n",
|
||||
aHitTestPoint.x, aHitTestPoint.y,
|
||||
hitTestPointForChildLayers.x, hitTestPointForChildLayers.y,
|
||||
node);
|
||||
HitTestResult hitResult = node->HitTest(aHitTestPoint);
|
||||
if (hitResult != HitTestResult::NoApzcHit) {
|
||||
APZCTM_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p via node %p\n",
|
||||
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, apzc, node);
|
||||
result = apzc;
|
||||
result = node->GetNearestContainingApzc();
|
||||
APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
|
||||
result, node, hitResult);
|
||||
MOZ_ASSERT(hitResult == ApzcHitRegion || hitResult == ApzcContentRegion);
|
||||
// If event regions are disabled, *aOutHitResult will be ApzcHitRegion
|
||||
*aOutHitResult = hitResult;
|
||||
|
@ -1519,6 +1393,7 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
|||
// the result is inside an overscrolled APZC, inform our caller of this
|
||||
// (callers typically ignore events targeted at overscrolled APZCs).
|
||||
if (result && apzc && apzc->IsOverscrolled()) {
|
||||
APZCTM_LOG("Result is inside overscrolled APZC %p\n", apzc);
|
||||
*aOutHitResult = OverscrolledApzc;
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1531,10 +1406,11 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
|||
// 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 we need to keep looping through to
|
||||
// scrollinfo siblings. Therefore, we need to keep looping through to
|
||||
// see if there are any other non-scrollinfo siblings that have children
|
||||
// that match this input. If so, they should take priority. With event-
|
||||
// regions enabled we ignore scrollinfo layers and this is unnecessary.
|
||||
// regions enabled we use the actual regions from the layer, which are
|
||||
// empty, and so this is unnecessary.
|
||||
AsyncPanZoomController* prevSiblingApzc = nullptr;
|
||||
for (HitTestingTreeNode* n = node->GetPrevSibling(); n; n = n->GetPrevSibling()) {
|
||||
if (n->GetApzc()) {
|
||||
|
@ -1543,6 +1419,7 @@ APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
|||
}
|
||||
}
|
||||
if (result == prevSiblingApzc) {
|
||||
APZCTM_LOG("Continuing search past probable scrollinfo info layer\n");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,8 +76,9 @@ class HitTestingTreeNode;
|
|||
*
|
||||
* 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 APZC instances
|
||||
* to match the scrollable container layers. APZC instances may be preserved across
|
||||
* 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
|
||||
* tree.
|
||||
*
|
||||
|
@ -97,7 +98,7 @@ class APZCTreeManager {
|
|||
typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior;
|
||||
typedef uint32_t TouchBehaviorFlags;
|
||||
|
||||
// Helper struct to hold some state while we build the APZ tree. The
|
||||
// Helper struct to hold some state while we build the hit-testing tree. The
|
||||
// sole purpose of this struct is to shorten the argument list to
|
||||
// UpdateHitTestingTree. All the state that we don't need to
|
||||
// push on the stack during recursion and pop on unwind is stored here.
|
||||
|
@ -107,9 +108,9 @@ 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.
|
||||
* 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 compositor thread as it walks the layer tree.
|
||||
*
|
||||
|
@ -420,7 +421,7 @@ private:
|
|||
const ScrollableLayerGuid& aGuid,
|
||||
GuidComparator aComparator);
|
||||
AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode,
|
||||
const gfx::Point& aHitTestPoint,
|
||||
const ParentLayerPoint& aHitTestPoint,
|
||||
HitTestResult* aOutHitResult);
|
||||
already_AddRefed<AsyncPanZoomController> GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
|
||||
already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
|
||||
|
@ -440,50 +441,43 @@ private:
|
|||
const ZoomConstraints& aConstraints);
|
||||
void FlushRepaintsRecursively(HitTestingTreeNode* aNode);
|
||||
|
||||
already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState,
|
||||
AsyncPanZoomController* aApzc);
|
||||
HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
|
||||
const FrameMetrics& aMetrics,
|
||||
uint64_t aLayersId,
|
||||
const gfx::Matrix4x4& aAncestorTransform,
|
||||
const nsIntRegion& aObscured,
|
||||
HitTestingTreeNode* aParent,
|
||||
HitTestingTreeNode* aNextSibling,
|
||||
TreeBuildingState& aState);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Recursive helper function to build the hit-testing tree. See documentation
|
||||
* in HitTestingTreeNode.h for more details on the shape of the tree.
|
||||
* This function walks the layer tree backwards through siblings and
|
||||
* constructs the hit-testing tree also as a last-child-prev-sibling tree
|
||||
* because that simplifies the hit detection code.
|
||||
*
|
||||
* @param aState The current tree building state.
|
||||
* @param aLayer The (layer, metrics) pair which is the current
|
||||
* position in the recursive walk of the layer tree.
|
||||
* This calls builds an APZC subtree corresponding
|
||||
* to the layer subtree rooted at aLayer.
|
||||
* @param aLayersId The layers id of the layer in aLayer.
|
||||
* @param aState The current tree building state.
|
||||
* @param aLayer The (layer, metrics) pair which is the current position in
|
||||
* the recursive walk of the layer tree. This call builds a
|
||||
* hit-testing subtree corresponding to the layer subtree rooted
|
||||
* at aLayer.
|
||||
* @param aLayersId The layers id of the layer in aLayer.
|
||||
* @param aAncestorTransform The accumulated CSS transforms of all the
|
||||
* layers from aLayer up (via the parent chain)
|
||||
* to the next APZC-bearing layer.
|
||||
* @param aParent The parent of any node built at this level.
|
||||
* @param aNextSibling The next sibling of any node built at this level.
|
||||
* @param aObscured The region that is obscured by APZCs above
|
||||
* the one at this level (in practice, the
|
||||
* next-siblings of this one), in the ParentLayer
|
||||
* pixels of the APZC at this level.
|
||||
* @return The root of the APZC subtree at this level, or
|
||||
* aNextSibling if no APZCs were built for the
|
||||
* layer subtree at this level.
|
||||
* @param aParent The parent of any node built at this level.
|
||||
* @param aNextSibling The next sibling of any node built at this level.
|
||||
* @return The HitTestingTreeNode created at this level. This will always
|
||||
* be non-null.
|
||||
*/
|
||||
HitTestingTreeNode* UpdateHitTestingTree(TreeBuildingState& aState,
|
||||
const LayerMetricsWrapper& aLayer,
|
||||
uint64_t aLayersId,
|
||||
const gfx::Matrix4x4& aAncestorTransform,
|
||||
HitTestingTreeNode* aParent,
|
||||
HitTestingTreeNode* aNextSibling,
|
||||
const nsIntRegion& aObscured);
|
||||
HitTestingTreeNode* aNextSibling);
|
||||
|
||||
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
|
||||
const AsyncPanZoomController* apzc);
|
||||
|
|
|
@ -26,6 +26,16 @@ HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc)
|
||||
{
|
||||
MOZ_ASSERT(!mIsPrimaryApzcHolder);
|
||||
Destroy(); // clear out tree pointers
|
||||
mApzc = aApzc;
|
||||
// The caller is expected to call SetHitTestData to repopulate the hit-test
|
||||
// fields.
|
||||
}
|
||||
|
||||
HitTestingTreeNode::~HitTestingTreeNode()
|
||||
{
|
||||
}
|
||||
|
@ -144,42 +154,56 @@ HitTestingTreeNode::IsPrimaryHolder() const
|
|||
void
|
||||
HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const nsIntRegion& aClipRegion)
|
||||
const Maybe<nsIntRegion>& aClipRegion)
|
||||
{
|
||||
mEventRegions = aRegions;
|
||||
mTransform = aTransform;
|
||||
mClipRegion = aClipRegion;
|
||||
}
|
||||
|
||||
HitTestResult
|
||||
HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
|
||||
bool
|
||||
HitTestingTreeNode::IsOutsideClip(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;
|
||||
}
|
||||
return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
|
||||
}
|
||||
|
||||
Maybe<LayerPoint>
|
||||
HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
|
||||
{
|
||||
// convert into Layer coordinate space
|
||||
gfx::Matrix4x4 localTransform = mTransform;
|
||||
if (mApzc) {
|
||||
localTransform = localTransform * gfx::Matrix4x4(mApzc->GetCurrentAsyncTransform());
|
||||
}
|
||||
gfx::Point4D pointInLayerPixels = localTransform.Inverse().ProjectPoint(aPoint.ToUnknownPoint());
|
||||
if (!pointInLayerPixels.HasPositiveWCoord()) {
|
||||
gfx::Point4D point = localTransform.Inverse().ProjectPoint(aPoint.ToUnknownPoint());
|
||||
return point.HasPositiveWCoord()
|
||||
? Some(ViewAs<LayerPixel>(point.As2DPoint()))
|
||||
: Nothing();
|
||||
}
|
||||
|
||||
HitTestResult
|
||||
HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
|
||||
{
|
||||
// This should only ever get called if the point is inside the clip region
|
||||
// for this node.
|
||||
MOZ_ASSERT(!IsOutsideClip(aPoint));
|
||||
|
||||
// When event regions are disabled and we have an APZC on this node, we are
|
||||
// actually storing the touch-sensitive section of the composition bounds in
|
||||
// the clip region, and we don't need to check against the mEventRegions.
|
||||
// If there's no APZC, then we do need to check against the mEventRegions
|
||||
// (which contains the layer's visible region) for obscuration purposes.
|
||||
if (!gfxPrefs::LayoutEventRegionsEnabled() && GetApzc()) {
|
||||
return HitTestResult::ApzcHitRegion;
|
||||
}
|
||||
|
||||
// convert into Layer coordinate space
|
||||
Maybe<LayerPoint> pointInLayerPixels = Untransform(aPoint);
|
||||
if (!pointInLayerPixels) {
|
||||
return HitTestResult::NoApzcHit;
|
||||
}
|
||||
LayerIntPoint point = RoundedToInt(ViewAs<LayerPixel>(pointInLayerPixels.As2DPoint()));
|
||||
LayerIntPoint point = RoundedToInt(pointInLayerPixels.ref());
|
||||
|
||||
// test against event regions in Layer coordinate space
|
||||
if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
|
||||
|
@ -200,7 +224,7 @@ HitTestingTreeNode::Dump(const char* aPrefix) const
|
|||
printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) r=(%s) t=(%s) c=(%s)\n",
|
||||
aPrefix, this, mApzc.get(), mApzc ? Stringify(mApzc->GetGuid()).c_str() : "",
|
||||
Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
|
||||
Stringify(mClipRegion).c_str());
|
||||
mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
|
||||
if (mLastChild) {
|
||||
mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "FrameMetrics.h" // for ScrollableLayerGuid
|
||||
#include "mozilla/gfx/Matrix.h" // for Matrix4x4
|
||||
#include "mozilla/layers/LayersTypes.h" // for EventRegions
|
||||
#include "mozilla/Maybe.h" // for Maybe
|
||||
#include "nsRefPtr.h" // for nsRefPtr
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -20,16 +21,21 @@ class AsyncPanZoomController;
|
|||
|
||||
/**
|
||||
* This class represents a node in a tree that is used by the APZCTreeManager
|
||||
* to do hit testing. The tree is roughly a copy of the layer tree but only
|
||||
* contains nodes for layers that have scrollable metrics. There will be
|
||||
* exactly one node per {layer, scrollable metrics} pair (since a layer may have
|
||||
* multiple scrollable metrics). However, multiple HitTestingTreeNode instances
|
||||
* may share the same underlying APZC instance if the layers they represent
|
||||
* share the same scrollable metrics (i.e. are part of the same animated
|
||||
* geometry root). If this happens, exactly one of the HitTestingTreeNode
|
||||
* instances will be designated as the "primary holder" of the APZC. When
|
||||
* this primary holder is destroyed, it will destroy the APZC along with it;
|
||||
* in contrast, destroying non-primary-holder nodes will not destroy the APZC.
|
||||
* to do hit testing. The tree is roughly a copy of the layer tree, but will
|
||||
* contain multiple nodes in cases where the layer has multiple FrameMetrics.
|
||||
* In other words, the structure of this tree should be identical to the
|
||||
* LayerMetrics tree (see documentation in LayerMetricsWrapper.h).
|
||||
*
|
||||
* Not all HitTestingTreeNode instances will have an APZC associated with them;
|
||||
* only HitTestingTreeNodes that correspond to layers with scrollable metrics
|
||||
* have APZCs.
|
||||
* Multiple HitTestingTreeNode instances may share the same underlying APZC
|
||||
* instance if the layers they represent share the same scrollable metrics (i.e.
|
||||
* are part of the same animated geometry root). If this happens, exactly one of
|
||||
* the HitTestingTreeNode instances will be designated as the "primary holder"
|
||||
* of the APZC. When this primary holder is destroyed, it will destroy the APZC
|
||||
* along with it; in contrast, destroying non-primary-holder nodes will not
|
||||
* destroy the APZC.
|
||||
* Code should not make assumptions about which of the nodes will be the
|
||||
* primary holder, only that that there will be exactly one for each APZC in
|
||||
* the tree.
|
||||
|
@ -48,29 +54,40 @@ private:
|
|||
~HitTestingTreeNode();
|
||||
public:
|
||||
HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder);
|
||||
void RecycleWith(AsyncPanZoomController* aApzc);
|
||||
void Destroy();
|
||||
|
||||
/* Tree construction methods */
|
||||
|
||||
void SetLastChild(HitTestingTreeNode* aChild);
|
||||
void SetPrevSibling(HitTestingTreeNode* aSibling);
|
||||
void MakeRoot();
|
||||
|
||||
/* Tree walking methods. GetFirstChild is O(n) in the number of children. The
|
||||
* other tree walking methods are all O(1). */
|
||||
|
||||
HitTestingTreeNode* GetFirstChild() const;
|
||||
HitTestingTreeNode* GetLastChild() const;
|
||||
HitTestingTreeNode* GetPrevSibling() const;
|
||||
HitTestingTreeNode* GetParent() const;
|
||||
|
||||
/* APZC related methods */
|
||||
|
||||
AsyncPanZoomController* GetApzc() const;
|
||||
AsyncPanZoomController* GetNearestContainingApzc() const;
|
||||
bool IsPrimaryHolder() const;
|
||||
|
||||
/* Hit test related methods */
|
||||
|
||||
void SetHitTestData(const EventRegions& aRegions,
|
||||
const gfx::Matrix4x4& aTransform,
|
||||
const nsIntRegion& aClipRegion);
|
||||
const Maybe<nsIntRegion>& aClipRegion);
|
||||
bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
|
||||
/* Convert aPoint into the LayerPixel space for the layer corresponding to
|
||||
* this node. */
|
||||
Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
|
||||
/* Assuming aPoint is inside the clip region for this node, check which of the
|
||||
* event region spaces it falls inside. */
|
||||
HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
|
||||
|
||||
/* Debug helpers */
|
||||
|
@ -87,30 +104,24 @@ private:
|
|||
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". */
|
||||
* corresponds to in the layer tree. mEventRegions contains the event regions
|
||||
* from L, in the case where event-regions are enabled. If event-regions are
|
||||
* disabled, it will contain the visible region of L, which we use as an
|
||||
* approximation to the hit region for the purposes of obscuring other layers.
|
||||
* This value is in L's LayerPixels.
|
||||
*/
|
||||
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
|
||||
/* This is the transform from layer L. 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;
|
||||
/* This is clip rect for L that we wish to use for hit-testing purposes. Note
|
||||
* that this may not be exactly the same as the clip rect on layer L because
|
||||
* of the touch-sensitive region provided by the GeckoContentController, or
|
||||
* because we may use the composition bounds of the layer if the clip is not
|
||||
* present. This value is in L's ParentLayerPixels. */
|
||||
Maybe<nsIntRegion> mClipRegion;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2032,8 +2032,8 @@ TEST_F(APZCTreeManagerTester, Bug1068268) {
|
|||
|
||||
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
|
||||
nsRefPtr<HitTestingTreeNode> root = manager->GetRootNode();
|
||||
nsRefPtr<HitTestingTreeNode> node2 = root->GetFirstChild();
|
||||
nsRefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
|
||||
nsRefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
|
||||
nsRefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
|
||||
|
||||
EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
|
||||
EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
|
||||
|
@ -2053,6 +2053,22 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
|
|||
ScopedLayerTreeRegistration registration(0, root, mcc);
|
||||
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
|
||||
|
||||
/* The layer tree looks like this:
|
||||
|
||||
0
|
||||
|----|--+--|----|
|
||||
1 2 4 5
|
||||
| /|\
|
||||
3 6 8 9
|
||||
|
|
||||
7
|
||||
|
||||
Layers 1,2 have the same APZC
|
||||
Layers 4,6,8 have the same APZC
|
||||
Layer 7 has an APZC
|
||||
Layer 9 has an APZC
|
||||
*/
|
||||
|
||||
TestAsyncPanZoomController* nullAPZC = nullptr;
|
||||
// Ensure all the scrollable layers have an APZC
|
||||
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
|
||||
|
@ -2076,9 +2092,7 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
|
|||
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
|
||||
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
|
||||
EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
|
||||
|
||||
// Ensure the shape of the APZC tree is as expected
|
||||
nsRefPtr<HitTestingTreeNode> root = manager->GetRootNode();
|
||||
// Ensure the APZC parent chains are set up correctly
|
||||
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
|
||||
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
|
||||
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
|
||||
|
@ -2087,19 +2101,27 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
|
|||
EXPECT_EQ(nullptr, layers4_6_8->GetParent());
|
||||
EXPECT_EQ(layers4_6_8, layer7->GetParent());
|
||||
EXPECT_EQ(nullptr, layer9->GetParent());
|
||||
EXPECT_EQ(layer9, root->GetApzc());
|
||||
TestAsyncPanZoomController* expected[] = {
|
||||
layer9,
|
||||
layers4_6_8, layers4_6_8, layers4_6_8,
|
||||
layers1_2, layers1_2
|
||||
};
|
||||
int i = 0;
|
||||
for (HitTestingTreeNode *iter = root; iter; iter = iter->GetPrevSibling()) {
|
||||
EXPECT_EQ(expected[i++], iter->GetApzc());
|
||||
}
|
||||
HitTestingTreeNode* node6 = root->GetPrevSibling()->GetPrevSibling();
|
||||
EXPECT_EQ(layer7, node6->GetLastChild()->GetApzc());
|
||||
EXPECT_EQ(nullptr, node6->GetLastChild()->GetPrevSibling());
|
||||
// Ensure the hit-testing tree looks like the layer tree
|
||||
nsRefPtr<HitTestingTreeNode> root = manager->GetRootNode();
|
||||
nsRefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
|
||||
nsRefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
|
||||
nsRefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
|
||||
nsRefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
|
||||
nsRefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
|
||||
nsRefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
|
||||
nsRefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
|
||||
nsRefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
|
||||
nsRefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
|
||||
EXPECT_EQ(nullptr, node1->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node3->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node6->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node7->GetPrevSibling());
|
||||
EXPECT_EQ(nullptr, node1->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node3->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node4->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node7->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node8->GetLastChild());
|
||||
EXPECT_EQ(nullptr, node9->GetLastChild());
|
||||
|
||||
nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
|
||||
EXPECT_EQ(ApzcOf(layers[1]), hit.get());
|
||||
|
|
|
@ -24,6 +24,9 @@ MOZ_BEGIN_ENUM_CLASS(PixelCastJustification, uint8_t)
|
|||
ScreenIsParentLayerForRoot,
|
||||
// For the root composition size we want to view it as layer pixels in any layer
|
||||
ParentLayerToLayerForRootComposition,
|
||||
// The Layer coordinate space for one layer is the ParentLayer coordinate
|
||||
// space for its children
|
||||
MovingDownToChildren,
|
||||
// The transform that is usually used to convert between two coordinate
|
||||
// systems is not available (for example, because the object that stores it
|
||||
// is being destroyed), so fall back to the identity.
|
||||
|
|
Загрузка…
Ссылка в новой задаче