Bug 1109873 - Introduce the HitTestingTreeNode class. r=botond

This patch extracts a HitTestingTreeNode from AsyncPanZoomController. The tree
formed by these nodes is very similar in structure to the APZC tree that we had
previously. The main difference is that we can have multiple HitTestingTreeNode
instances per APZC; each HitTestingTreeNode corresponds to a different layer
with the same metrics.

This is a first step in extracting the hit-test data entirely from the
AsyncPanZoomController class and having a 1:1 mapping with the layer tree.
This commit is contained in:
Kartikaya Gupta 2015-01-08 09:40:01 -05:00
Родитель 17b504d90c
Коммит 66746c1cfb
8 изменённых файлов: 509 добавлений и 213 удалений

Просмотреть файл

@ -6,6 +6,7 @@
#include "APZCTreeManager.h"
#include "AsyncPanZoomController.h"
#include "Compositor.h" // for Compositor
#include "HitTestingTreeNode.h" // for HitTestingTreeNode
#include "InputBlockState.h" // for InputBlockState
#include "InputData.h" // for InputData, etc
#include "Layers.h" // for Layer, etc
@ -58,10 +59,10 @@ struct APZCTreeManager::TreeBuildingState {
// State that is updated as we perform the tree build
// A list of APZCs that need to be destroyed at the end of the tree building.
// This is initialized with all APZCs in the old tree, and APZCs are removed
// A list of nodes that need to be destroyed at the end of the tree building.
// This is initialized with all nodes in the old tree, and nodes are removed
// from it as we reuse them in the new tree.
nsTArray< nsRefPtr<AsyncPanZoomController> > mApzcsToDestroy;
nsTArray<nsRefPtr<HitTestingTreeNode>> mNodesToDestroy;
// This map is populated as we place APZCs into the new tree. Its purpose is
// to facilitate re-using the same APZC for different layers that scroll
@ -140,14 +141,14 @@ APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
}
/* Flatten the tree of APZC instances into the given nsTArray */
/* Flatten the tree of nodes into the given nsTArray */
static void
Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
Collect(HitTestingTreeNode* aNode, nsTArray<nsRefPtr<HitTestingTreeNode>>* aCollection)
{
if (aApzc) {
aCollection->AppendElement(aApzc);
Collect(aApzc->GetLastChild(), aCollection);
Collect(aApzc->GetPrevSibling(), aCollection);
if (aNode) {
aCollection->AppendElement(aNode);
Collect(aNode->GetLastChild(), aCollection);
Collect(aNode->GetPrevSibling(), aCollection);
}
}
@ -189,8 +190,8 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
// we are sure that the layer was removed and not just transplanted elsewhere. Doing that
// as part of a recursive tree walk is hard and so maintaining a list and removing
// APZCs that are still alive is much simpler.
Collect(mRootApzc, &state.mApzcsToDestroy);
mRootApzc = nullptr;
Collect(mRootNode, &state.mNodesToDestroy);
mRootNode = nullptr;
if (aRoot) {
mApzcTreeLog << "[start]\n";
@ -203,9 +204,11 @@ APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
}
MOZ_ASSERT(state.mEventRegions.Length() == 0);
for (size_t i = 0; i < state.mApzcsToDestroy.Length(); i++) {
APZCTM_LOG("Destroying APZC at %p\n", state.mApzcsToDestroy[i].get());
state.mApzcsToDestroy[i]->Destroy();
for (size_t i = 0; i < state.mNodesToDestroy.Length(); i++) {
APZCTM_LOG("Destroying node at %p with APZC %p\n",
state.mNodesToDestroy[i].get(),
state.mNodesToDestroy[i]->Apzc());
state.mNodesToDestroy[i]->Destroy();
}
}
@ -257,14 +260,30 @@ APZCTreeManager::PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
<< metrics.GetContentDescription().get();
}
AsyncPanZoomController*
APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
void
APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling)
{
if (aNextSibling) {
aNextSibling->SetPrevSibling(aNode);
} else if (aParent) {
aParent->SetLastChild(aNode);
} else {
MOZ_ASSERT(!mRootNode);
mRootNode = aNode;
aNode->MakeRoot();
}
}
HitTestingTreeNode*
APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
const FrameMetrics& aMetrics,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
const nsIntRegion& aObscured,
AsyncPanZoomController* aParent,
AsyncPanZoomController* aNextSibling,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling,
TreeBuildingState& aState)
{
if (!aMetrics.IsScrollable()) {
@ -299,6 +318,7 @@ APZCTreeManager::PrepareAPZCForLayer(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();
@ -310,25 +330,36 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
apzc = nullptr;
}
// If the layer doesn't have an APZC already, try to find one of our
// pre-existing ones that matches. In particular, if we find an APZC whose
// ScrollableLayerGuid is the same, then we know what happened is that the
// layout of the page changed causing the layer tree to be rebuilt, but the
// underlying content for which the APZC was originally created is still
// there. So it makes sense to pick up that APZC instance again and use it here.
if (apzc == nullptr) {
for (size_t i = 0; i < aState.mApzcsToDestroy.Length(); i++) {
if (aState.mApzcsToDestroy.ElementAt(i)->Matches(guid)) {
apzc = aState.mApzcsToDestroy.ElementAt(i);
break;
// See if we can find an APZC from the previous tree that matches the
// ScrollableLayerGuid from this layer. If there is one, then we know that
// the layout of the page changed causing the layer tree to be rebuilt, but
// the underlying content for the APZC is still there somewhere. Therefore,
// we want to find the APZC instance and continue using it here.
//
// We particularly want to find the primary-holder node from the previous
// tree that matches, because we don't want that node to get destroyed. If
// it does get destroyed, then the APZC will get destroyed along with it by
// definition, but we want to keep that APZC around in the new tree.
// We leave non-primary-holder nodes in the destroy list because we don't
// care about those nodes getting destroyed.
for (size_t i = 0; i < aState.mNodesToDestroy.Length(); i++) {
nsRefPtr<HitTestingTreeNode> n = aState.mNodesToDestroy[i];
if (n->IsPrimaryHolder() && n->Apzc()->Matches(guid)) {
node = n;
if (apzc != nullptr) {
// If there is an APZC already then it should match the one from the
// old primary-holder node
MOZ_ASSERT(apzc == node->Apzc());
}
apzc = node->Apzc();
break;
}
}
// The APZC we get off the layer may have been destroyed previously if the layer was inactive
// or omitted from the layer tree for whatever reason from a layers update. If it later comes
// back it will have a reference to a destroyed APZC and so we need to throw that out and make
// a new one.
// The APZC we get off the layer may have been destroyed previously if the
// layer was inactive or omitted from the layer tree for whatever reason
// from a layers update. If it later comes back it will have a reference to
// a destroyed APZC and so we need to throw that out and make a new one.
bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
if (newApzc) {
apzc = MakeAPZCInstance(aLayersId, state->mController);
@ -336,21 +367,29 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
if (state->mCrossProcessParent != nullptr) {
apzc->ShareFrameMetricsAcrossProcesses();
}
MOZ_ASSERT(node == nullptr);
node = new HitTestingTreeNode(apzc, true);
} else {
// If there was already an APZC for the layer clear the tree pointers
// so that it doesn't continue pointing to APZCs that should no longer
// If we are re-using a node for this layer clear the tree pointers
// so that it doesn't continue pointing to nodes that might no longer
// be in the tree. These pointers will get reset properly as we continue
// building the tree. Also remove it from the set of APZCs that are going
// building the tree. Also remove it from the set of nodes that are going
// to be destroyed, because it's going to remain active.
aState.mApzcsToDestroy.RemoveElement(apzc);
apzc->SetPrevSibling(nullptr);
apzc->SetLastChild(nullptr);
aState.mNodesToDestroy.RemoveElement(node);
node->SetPrevSibling(nullptr);
node->SetLastChild(nullptr);
}
APZCTM_LOG("Using APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), aLayersId, aMetrics.GetScrollId());
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);
@ -366,15 +405,7 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
PrintAPZCInfo(aLayer, apzc);
// Bind the APZC instance into the tree of APZCs
if (aNextSibling) {
aNextSibling->SetPrevSibling(apzc);
} else if (aParent) {
aParent->SetLastChild(apzc);
} else {
MOZ_ASSERT(!mRootApzc);
mRootApzc = apzc;
apzc->MakeRoot();
}
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.
@ -423,6 +454,19 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
// 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));
@ -430,25 +474,26 @@ APZCTreeManager::PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
}
}
return apzc;
return node;
}
AsyncPanZoomController*
HitTestingTreeNode*
APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
const LayerMetricsWrapper& aLayer,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
AsyncPanZoomController* aParent,
AsyncPanZoomController* aNextSibling,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling,
const nsIntRegion& aObscured)
{
mTreeLock.AssertCurrentThreadOwns();
mApzcTreeLog << aLayer.Name() << '\t';
AsyncPanZoomController* apzc = PrepareAPZCForLayer(aLayer,
HitTestingTreeNode* node = PrepareNodeForLayer(aLayer,
aLayer.Metrics(), aLayersId, aAncestorTransform,
aObscured, aParent, aNextSibling, aState);
AsyncPanZoomController* apzc = (node ? node->Apzc() : nullptr);
aLayer.SetApzc(apzc);
mApzcTreeLog << '\n';
@ -487,18 +532,15 @@ APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
// 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.
AsyncPanZoomController* next = aNextSibling;
if (apzc) {
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 |apzc| at this point will not have children corresponding to
// the subtree of aLayer (those children will be populated in the loop
// below). However, it might have children corresponding to the subtree of
// another layer that shares the same APZC and that has already been
// visited; the children added at this level will become prev-siblings
// of those.
aParent = apzc;
next = apzc->GetFirstChild();
// 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
@ -604,12 +646,12 @@ APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
}
}
// Return the APZC that should be the sibling of other APZCs as we continue
// 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 APZCs in the subtree
// 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 (apzc) {
return apzc;
if (node) {
return node;
}
if (next) {
return next;
@ -750,8 +792,8 @@ APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
// the event we send to gecko because we don't know the layer to untransform with
// respect to.
MonitorAutoLock lock(mTreeLock);
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
FlushRepaintsRecursively(apzc);
for (HitTestingTreeNode* node = mRootNode; node; node = node->GetPrevSibling()) {
FlushRepaintsRecursively(node);
}
}
@ -1034,8 +1076,8 @@ APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
target = GetTargetAPZC(aTargets[0]);
}
for (size_t i = 1; i < aTargets.Length(); i++) {
nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aTargets[i]);
target = GetMultitouchTarget(target, apzc2);
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
target = GetMultitouchTarget(target, apzc);
}
mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
}
@ -1043,45 +1085,49 @@ APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
void
APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId, const ScrollableLayerGuid& aTarget)
{
nsRefPtr<AsyncPanZoomController> target = GetTargetAPZC(aTarget);
mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
}
void
APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
const ZoomConstraints& aConstraints)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
nsRefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
// For a given layers id, non-root APZCs inherit the zoom constraints
// of their root.
if (apzc && apzc->IsRootForLayersId()) {
if (node && node->Apzc()->IsRootForLayersId()) {
MonitorAutoLock lock(mTreeLock);
UpdateZoomConstraintsRecursively(apzc.get(), aConstraints);
UpdateZoomConstraintsRecursively(node.get(), aConstraints);
}
}
void
APZCTreeManager::UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
APZCTreeManager::UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode,
const ZoomConstraints& aConstraints)
{
mTreeLock.AssertCurrentThreadOwns();
aApzc->UpdateZoomConstraints(aConstraints);
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
if (aNode->IsPrimaryHolder()) {
aNode->Apzc()->UpdateZoomConstraints(aConstraints);
}
for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) {
// We can have subtrees with their own layers id - leave those alone.
if (!child->IsRootForLayersId()) {
if (!child->Apzc()->IsRootForLayersId()) {
UpdateZoomConstraintsRecursively(child, aConstraints);
}
}
}
void
APZCTreeManager::FlushRepaintsRecursively(AsyncPanZoomController* aApzc)
APZCTreeManager::FlushRepaintsRecursively(HitTestingTreeNode* aNode)
{
mTreeLock.AssertCurrentThreadOwns();
aApzc->FlushRepaintForNewInputBlock();
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
if (aNode->IsPrimaryHolder()) {
aNode->Apzc()->FlushRepaintForNewInputBlock();
}
for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) {
FlushRepaintsRecursively(child);
}
}
@ -1103,12 +1149,19 @@ APZCTreeManager::ClearTree()
// This can be done as part of a tree walk but it's easier to
// just re-use the Collect method that we need in other places.
// If this is too slow feel free to change it to a recursive walk.
nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
Collect(mRootApzc, &apzcsToDestroy);
for (size_t i = 0; i < apzcsToDestroy.Length(); i++) {
apzcsToDestroy[i]->Destroy();
nsTArray<nsRefPtr<HitTestingTreeNode>> nodesToDestroy;
Collect(mRootNode, &nodesToDestroy);
for (size_t i = 0; i < nodesToDestroy.Length(); i++) {
nodesToDestroy[i]->Destroy();
}
mRootApzc = nullptr;
mRootNode = nullptr;
}
nsRefPtr<HitTestingTreeNode>
APZCTreeManager::GetRootNode() const
{
MonitorAutoLock lock(mTreeLock);
return mRootNode;
}
/**
@ -1247,13 +1300,23 @@ APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
}
already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid,
GuidComparator aComparator)
{
nsRefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, aComparator);
nsRefPtr<AsyncPanZoomController> apzc = node ? node->Apzc() : nullptr;
return apzc.forget();
}
already_AddRefed<HitTestingTreeNode>
APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
GuidComparator aComparator)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<AsyncPanZoomController> target;
nsRefPtr<HitTestingTreeNode> target;
// The root may have siblings, check those too
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
target = FindTargetAPZC(apzc, aGuid);
for (HitTestingTreeNode* node = mRootNode; node; node = node->GetPrevSibling()) {
target = FindTargetNode(node, aGuid, aComparator);
if (target) {
break;
}
@ -1268,8 +1331,17 @@ APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHit
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, so check those too
HitTestResult hitResult = NoApzcHit;
for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
target = GetAPZCAtPoint(apzc, aPoint.ToUnknownPoint(), &hitResult);
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.
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)) {
@ -1284,6 +1356,13 @@ APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHit
return target.forget();
}
static bool
GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne, const ScrollableLayerGuid& aTwo)
{
return aOne.mLayersId == aTwo.mLayersId
&& aOne.mScrollId == aTwo.mScrollId;
}
nsRefPtr<const OverscrollHandoffChain>
APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget)
{
@ -1335,7 +1414,9 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomControll
}
}
if (!scrollParent) {
scrollParent = FindTargetAPZC(parent, apzc->GetScrollHandoffParentId());
ScrollableLayerGuid guid(parent->GetGuid().mLayersId, 0, apzc->GetScrollHandoffParentId());
nsRefPtr<AsyncPanZoomController> scrollParentPtr = GetTargetAPZC(guid, &GuidComparatorIgnoringPresShell);
scrollParent = scrollParentPtr.get();
}
apzc = scrollParent;
}
@ -1353,67 +1434,52 @@ APZCTreeManager::BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomControll
return result;
}
/* Find the apzc in the subtree rooted at aApzc that has the same layers id as
aApzc, and that has the given scroll id. Generally this function should be called
with aApzc being the root of its layers id subtree. */
AsyncPanZoomController*
APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId)
{
mTreeLock.AssertCurrentThreadOwns();
if (aApzc->GetGuid().mScrollId == aScrollId) {
return aApzc;
}
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
if (child->GetGuid().mLayersId != aApzc->GetGuid().mLayersId) {
continue;
}
AsyncPanZoomController* match = FindTargetAPZC(child, aScrollId);
if (match) {
return match;
}
}
return nullptr;
}
AsyncPanZoomController*
APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid)
HitTestingTreeNode*
APZCTreeManager::FindTargetNode(HitTestingTreeNode* aNode,
const ScrollableLayerGuid& aGuid,
GuidComparator aComparator)
{
mTreeLock.AssertCurrentThreadOwns();
// This walks the tree in depth-first, reverse order, so that it encounters
// APZCs front-to-back on the screen.
for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
for (HitTestingTreeNode* child = aNode->GetLastChild(); child; child = child->GetPrevSibling()) {
HitTestingTreeNode* match = FindTargetNode(child, aGuid, aComparator);
if (match) {
return match;
}
}
if (aApzc->Matches(aGuid)) {
return aApzc;
bool matches = false;
if (aComparator) {
matches = aComparator(aGuid, aNode->Apzc()->GetGuid());
} else {
matches = aNode->Apzc()->Matches(aGuid);
}
if (matches) {
return aNode;
}
return nullptr;
}
AsyncPanZoomController*
APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
const Point& aHitTestPoint,
HitTestResult* aOutHitResult)
{
mTreeLock.AssertCurrentThreadOwns();
AsyncPanZoomController* apzc = aNode->Apzc();
// 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 aApzc at L and P, and the
// 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 aApzc's parent APZC's CSS-transformed layer coordinates
// to aApzc's parent layer's layer coordinates.
// 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 = aApzc->GetAncestorTransform().Inverse();
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
@ -1421,24 +1487,33 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
Point4D hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
APZCTM_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
aHitTestPoint.x, aHitTestPoint.y,
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, apzc);
// childUntransform takes points from aApzc's parent APZC's CSS-transformed layer coordinates
// to aApzc's CSS-transformed layer coordinates.
// 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(aApzc->GetCurrentAsyncTransform()).Inverse();
Matrix4x4 childUntransform = ancestorUntransform * Matrix4x4(apzc->GetCurrentAsyncTransform()).Inverse();
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, aApzc);
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 (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
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.
continue;
}
if (*aOutHitResult == OverscrolledApzc) {
// We matched an overscrolled APZC, abort.
return nullptr;
@ -1451,19 +1526,19 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc,
}
if (!result && hitTestPointForThisLayer.HasPositiveWCoord()) {
ParentLayerPoint point = ParentLayerPoint::FromUnknownPoint(hitTestPointForThisLayer.As2DPoint());
if (aApzc->HitRegionContains(point)) {
if (apzc->HitRegionContains(point)) {
APZCTM_LOG("Successfully matched untransformed point %f %f to visible region for APZC %p\n",
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
result = aApzc;
hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, apzc);
result = apzc;
// If event regions are disabled, *aOutHitResult will be ApzcHitRegion
*aOutHitResult = (aApzc->DispatchToContentRegionContains(point) ? ApzcContentRegion : ApzcHitRegion);
*aOutHitResult = (apzc->DispatchToContentRegionContains(point) ? ApzcContentRegion : ApzcHitRegion);
}
}
// 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 && aApzc->IsOverscrolled()) {
if (result && apzc->IsOverscrolled()) {
*aOutHitResult = OverscrolledApzc;
result = nullptr;
}

Просмотреть файл

@ -48,6 +48,7 @@ struct OverscrollHandoffState;
class LayerMetricsWrapper;
class InputQueue;
class GeckoContentController;
class HitTestingTreeNode;
/**
* ****************** NOTE ON LOCK ORDERING IN APZ **************************
@ -405,16 +406,26 @@ public:
OverscrolledApzc,
};
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
nsRefPtr<HitTestingTreeNode> GetRootNode() const;
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint,
HitTestResult* aOutHitResult);
gfx::Matrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
gfx::Matrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
private:
typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
/* Helpers */
AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId);
AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc,
void AttachNodeToTree(HitTestingTreeNode* aNode,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling);
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid,
GuidComparator aComparator = nullptr);
already_AddRefed<HitTestingTreeNode> GetTargetNode(const ScrollableLayerGuid& aGuid,
GuidComparator aComparator);
HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode,
const ScrollableLayerGuid& aGuid,
GuidComparator aComparator);
AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode,
const gfx::Point& aHitTestPoint,
HitTestResult* aOutHitResult);
already_AddRefed<AsyncPanZoomController> GetMultitouchTarget(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
@ -431,18 +442,18 @@ private:
nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
void UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode,
const ZoomConstraints& aConstraints);
void FlushRepaintsRecursively(AsyncPanZoomController* aApzc);
void FlushRepaintsRecursively(HitTestingTreeNode* aNode);
AsyncPanZoomController* PrepareAPZCForLayer(const LayerMetricsWrapper& aLayer,
const FrameMetrics& aMetrics,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
const nsIntRegion& aObscured,
AsyncPanZoomController* aParent,
AsyncPanZoomController* aNextSibling,
TreeBuildingState& aState);
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
@ -462,8 +473,8 @@ private:
* @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 APZC built at this level.
* @param aNextSibling The next sibling any APZC built 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.
* @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
@ -472,13 +483,13 @@ private:
* aNextSibling if no APZCs were built for the
* layer subtree at this level.
*/
AsyncPanZoomController* UpdatePanZoomControllerTree(TreeBuildingState& aState,
const LayerMetricsWrapper& aLayer,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
AsyncPanZoomController* aParent,
AsyncPanZoomController* aNextSibling,
const nsIntRegion& aObscured);
HitTestingTreeNode* UpdatePanZoomControllerTree(TreeBuildingState& aState,
const LayerMetricsWrapper& aLayer,
uint64_t aLayersId,
const gfx::Matrix4x4& aAncestorTransform,
HitTestingTreeNode* aParent,
HitTestingTreeNode* aNextSibling,
const nsIntRegion& aObscured);
void PrintAPZCInfo(const LayerMetricsWrapper& aLayer,
const AsyncPanZoomController* apzc);
@ -490,15 +501,15 @@ protected:
nsRefPtr<InputQueue> mInputQueue;
private:
/* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held.
/* Whenever walking or mutating the tree rooted at mRootNode, mTreeLock must be held.
* This lock does not need to be held while manipulating a single APZC instance in
* isolation (that is, if its tree pointers are not being accessed or mutated). The
* lock also needs to be held when accessing the mRootApzc instance variable, as that
* lock also needs to be held when accessing the mRootNode instance variable, as that
* is considered part of the APZC tree management state.
* Finally, the lock needs to be held when accessing mOverscrollHandoffChain.
* IMPORTANT: See the note about lock ordering at the top of this file. */
mutable mozilla::Monitor mTreeLock;
nsRefPtr<AsyncPanZoomController> mRootApzc;
nsRefPtr<HitTestingTreeNode> mRootNode;
/* This tracks the APZC that should receive all inputs for the current input event block.
* This allows touch points to move outside the thing they started on, but still have the
* touch events delivered to the same initial APZC. This will only ever be touched on the

Просмотреть файл

@ -916,7 +916,9 @@ AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
}
}
AsyncPanZoomController::~AsyncPanZoomController() {
AsyncPanZoomController::~AsyncPanZoomController()
{
MOZ_ASSERT(IsDestroyed());
}
PCompositorParent*
@ -963,8 +965,6 @@ AsyncPanZoomController::Destroy()
mGeckoContentController = nullptr;
mGestureEventListener = nullptr;
}
mPrevSibling = nullptr;
mLastChild = nullptr;
mParent = nullptr;
mTreeManager = nullptr;

Просмотреть файл

@ -845,41 +845,17 @@ private:
void StartSmoothScroll();
/* ===================================================================
* The functions and members in this section are used to build a tree
* structure out of APZC instances. This tree can only be walked or
* manipulated while holding the lock in the associated APZCTreeManager
* instance.
* The functions and members in this section are used to make ancestor chains
* out of APZC instances. These chains can only be walked or manipulated
* while holding the lock in the associated APZCTreeManager instance.
*/
public:
void SetLastChild(AsyncPanZoomController* child) {
mLastChild = child;
if (child) {
child->mParent = this;
}
void SetParent(AsyncPanZoomController* aParent) {
mParent = aParent;
}
void SetPrevSibling(AsyncPanZoomController* sibling) {
mPrevSibling = sibling;
if (sibling) {
sibling->mParent = mParent;
}
}
// Make this APZC the root of the APZC tree. Clears the parent pointer.
void MakeRoot() {
mParent = nullptr;
}
AsyncPanZoomController* GetLastChild() const { return mLastChild; }
AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; }
AsyncPanZoomController* GetParent() const { return mParent; }
AsyncPanZoomController* GetFirstChild() const {
AsyncPanZoomController* child = GetLastChild();
while (child && child->GetPrevSibling()) {
child = child->GetPrevSibling();
}
return child;
AsyncPanZoomController* GetParent() const {
return mParent;
}
/* Returns true if there is no APZC higher in the tree with the same
@ -897,8 +873,6 @@ private:
// pointer out in Destroy() will prevent accessing deleted memory.
Atomic<APZCTreeManager*> mTreeManager;
nsRefPtr<AsyncPanZoomController> mLastChild;
nsRefPtr<AsyncPanZoomController> mPrevSibling;
nsRefPtr<AsyncPanZoomController> mParent;

Просмотреть файл

@ -0,0 +1,132 @@
/* -*- 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"
namespace mozilla {
namespace layers {
HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
bool aIsPrimaryHolder)
: mApzc(aApzc)
, mIsPrimaryApzcHolder(aIsPrimaryHolder)
{
MOZ_ASSERT(mApzc);
}
HitTestingTreeNode::~HitTestingTreeNode()
{
}
void
HitTestingTreeNode::Destroy()
{
AsyncPanZoomController::AssertOnCompositorThread();
mPrevSibling = nullptr;
mLastChild = nullptr;
mParent = nullptr;
if (mApzc) {
if (mIsPrimaryApzcHolder) {
mApzc->Destroy();
}
mApzc = nullptr;
}
}
void
HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild)
{
mLastChild = aChild;
if (aChild) {
// We assume that HitTestingTreeNodes with an ancestor/descendant
// relationship cannot both point to the same APZC instance. This assertion
// only covers a subset of cases in which that might occur, but it's better
// than nothing.
MOZ_ASSERT(aChild->Apzc() != Apzc());
aChild->mParent = this;
aChild->Apzc()->SetParent(Apzc());
}
}
void
HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
{
mPrevSibling = aSibling;
if (aSibling) {
aSibling->mParent = mParent;
aSibling->Apzc()->SetParent(mParent ? mParent->Apzc() : nullptr);
}
}
void
HitTestingTreeNode::MakeRoot()
{
mParent = nullptr;
Apzc()->SetParent(nullptr);
}
HitTestingTreeNode*
HitTestingTreeNode::GetFirstChild() const
{
HitTestingTreeNode* child = GetLastChild();
while (child && child->GetPrevSibling()) {
child = child->GetPrevSibling();
}
return child;
}
HitTestingTreeNode*
HitTestingTreeNode::GetLastChild() const
{
return mLastChild;
}
HitTestingTreeNode*
HitTestingTreeNode::GetPrevSibling() const
{
return mPrevSibling;
}
HitTestingTreeNode*
HitTestingTreeNode::GetParent() const
{
return mParent;
}
AsyncPanZoomController*
HitTestingTreeNode::Apzc() const
{
return mApzc;
}
bool
HitTestingTreeNode::IsPrimaryHolder() const
{
return mIsPrimaryApzcHolder;
}
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());
if (mLastChild) {
mLastChild->Dump(nsPrintfCString("%s ", aPrefix).get());
}
}
} // namespace layers
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,85 @@
/* -*- 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/. */
#ifndef mozilla_layers_HitTestingTreeNode_h
#define mozilla_layers_HitTestingTreeNode_h
#include "FrameMetrics.h"
#include "nsRefPtr.h"
namespace mozilla {
namespace layers {
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.
* 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.
*
* Note that every HitTestingTreeNode instance will have a pointer to an APZC,
* and that pointer will be non-null. This will NOT be the case after the
* HitTestingTreeNode has been Destroy()'d.
*
* The reason this tree exists at all is so that we can do hit-testing on the
* thread that we receive input on (referred to the as the controller thread in
* APZ terminology), which may be different from the compositor thread.
* Accessing the compositor layer tree can only be done on the compositor
* thread, and so it is simpler to make a copy of the hit-testing related
* properties into a separate tree.
*/
class HitTestingTreeNode {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
private:
~HitTestingTreeNode();
public:
HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder);
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* Apzc() const;
bool IsPrimaryHolder() const;
/* Debug helpers */
void Dump(const char* aPrefix = "") const;
private:
nsRefPtr<HitTestingTreeNode> mLastChild;
nsRefPtr<HitTestingTreeNode> mPrevSibling;
nsRefPtr<HitTestingTreeNode> mParent;
nsRefPtr<AsyncPanZoomController> mApzc;
bool mIsPrimaryApzcHolder;
};
}
}
#endif // mozilla_layers_HitTestingTreeNode_h

Просмотреть файл

@ -237,6 +237,7 @@ UNIFIED_SOURCES += [
'apz/src/AsyncPanZoomController.cpp',
'apz/src/Axis.cpp',
'apz/src/GestureEventListener.cpp',
'apz/src/HitTestingTreeNode.cpp',
'apz/src/InputBlockState.cpp',
'apz/src/InputQueue.cpp',
'apz/src/OverscrollHandoffState.cpp',

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/layers/LayerMetricsWrapper.h"
#include "mozilla/UniquePtr.h"
#include "apz/src/AsyncPanZoomController.h"
#include "apz/src/HitTestingTreeNode.h"
#include "base/task.h"
#include "Layers.h"
#include "TestLayers.h"
@ -875,6 +876,8 @@ TEST_F(APZCBasicTester, ComplexTransform) {
childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
EXPECT_EQ(ViewTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
childApzc->Destroy();
}
class APZCPanningTester : public APZCBasicTester {
@ -2028,16 +2031,21 @@ TEST_F(APZCTreeManagerTester, Bug1068268) {
ScopedLayerTreeRegistration registration(0, root, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[0])->GetLastChild());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[0])->GetFirstChild());
nsRefPtr<HitTestingTreeNode> root = manager->GetRootNode();
nsRefPtr<HitTestingTreeNode> node2 = root->GetFirstChild();
nsRefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
EXPECT_EQ(ApzcOf(layers[2]), node5->Apzc());
EXPECT_EQ(ApzcOf(layers[2]), node2->Apzc());
EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
EXPECT_EQ(ApzcOf(layers[3]), ApzcOf(layers[2])->GetFirstChild());
EXPECT_EQ(ApzcOf(layers[6]), ApzcOf(layers[2])->GetLastChild());
EXPECT_EQ(ApzcOf(layers[3]), ApzcOf(layers[6])->GetPrevSibling());
EXPECT_EQ(node2->GetFirstChild(), node2->GetLastChild());
EXPECT_EQ(ApzcOf(layers[3]), node2->GetLastChild()->Apzc());
EXPECT_EQ(node5->GetFirstChild(), node5->GetLastChild());
EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->Apzc());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[6])->GetParent());
EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
}
TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
@ -2070,6 +2078,7 @@ TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
// Ensure the shape of the APZC tree is as expected
nsRefPtr<HitTestingTreeNode> root = manager->GetRootNode();
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
@ -2078,10 +2087,19 @@ 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(nullptr, layers1_2->GetPrevSibling());
EXPECT_EQ(layers1_2, layers4_6_8->GetPrevSibling());
EXPECT_EQ(nullptr, layer7->GetPrevSibling());
EXPECT_EQ(layers4_6_8, layer9->GetPrevSibling());
EXPECT_EQ(layer9, root->Apzc());
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->Apzc());
}
HitTestingTreeNode* node6 = root->GetPrevSibling()->GetPrevSibling();
EXPECT_EQ(layer7, node6->GetLastChild()->Apzc());
EXPECT_EQ(nullptr, node6->GetLastChild()->GetPrevSibling());
nsRefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(ApzcOf(layers[1]), hit.get());