--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "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
#include "mozilla/dom/Touch.h" // for Touch
#include "mozilla/gfx/Point.h" // for Point
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/CompositorParent.h" // for CompositorParent, etc
#include "mozilla/layers/LayerMetricsWrapper.h"
@@ -53,20 +54,20 @@ struct APZCTreeManager::TreeBuildingStat
// State that doesn't change as we recurse in the tree building
CompositorParent* const mCompositor;
const bool mIsFirstPaint;
const uint64_t mOriginatingLayersId;
const APZPaintLogHelper mPaintLogger;
// 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
// 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
@@ -135,24 +136,24 @@ APZCTreeManager::GetAllowedTouchBehavior
void
APZCTreeManager::SetAllowedTouchBehavior(uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags> &aValues)
{
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);
}
}
void
APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
Layer* aRoot,
bool aIsFirstPaint,
uint64_t aOriginatingLayersId,
@@ -184,33 +185,35 @@ APZCTreeManager::UpdatePanZoomController
// completely different place. In scenario (a) we would want to destroy the APZC while
// walking the layer tree and noticing that the layer/APZC is no longer there. But if
// we do that then we run into a problem in scenario (b) because we might encounter that
// layer later during the walk. To handle both of these we have to 'remember' that the
// layer was not found, and then do the destroy only at the end of the tree walk after
// we are sure that the layer was removed and not just transplanted elsewhere. Doing that
// as part of a recursive tree walk is hard and so maintaining a list and removing
// APZCs that are still alive is much simpler.
- Collect(mRootApzc, &state.mApzcsToDestroy);
- mRootApzc = nullptr;
+ Collect(mRootNode, &state.mNodesToDestroy);
+ mRootNode = nullptr;
if (aRoot) {
mApzcTreeLog << "[start]\n";
LayerMetricsWrapper root(aRoot);
UpdatePanZoomControllerTree(state, root,
// aCompositor is null in gtest scenarios
aCompositor ? aCompositor->RootLayerTreeId() : 0,
Matrix4x4(), nullptr, nullptr, nsIntRegion());
mApzcTreeLog << "[end]\n";
}
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();
}
}
// Compute the touch-sensitive region of an APZC. This is used only when
// event-regions are disabled.
static nsIntRegion
ComputeTouchSensitiveRegion(GeckoContentController* aController,
const FrameMetrics& aMetrics,
@@ -252,24 +255,40 @@ APZCTreeManager::PrintAPZCInfo(const Lay
const FrameMetrics& metrics = aLayer.Metrics();
mApzcTreeLog << "APZC " << apzc->GetGuid() << "\tcb=" << metrics.mCompositionBounds
<< "\tsr=" << metrics.GetScrollableRect()
<< (aLayer.IsScrollInfoLayer() ? "\tscrollinfo" : "")
<< (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
<< 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()) {
return nullptr;
}
if (gfxPrefs::LayoutEventRegionsEnabled() && aLayer.IsScrollInfoLayer()) {
return nullptr;
}
@@ -294,92 +313,104 @@ APZCTreeManager::PrepareAPZCForLayer(con
apzc = insertResult.first->second;
PrintAPZCInfo(aLayer, apzc);
}
APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRId64 " %" PRId64 "\n", apzc, aLayer.GetLayer(), guid.mLayersId, guid.mScrollId);
// 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();
// If the content represented by the scrollable layer has changed (which may
// be possible because of DLBI heuristics) then we don't want to keep using
// the same old APZC for the new content. Null it out so we run through the
// code to find another one or create one.
if (apzc && !apzc->Matches(guid)) {
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);
apzc->SetCompositorParent(aState.mCompositor);
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);
}
// This initializes, among other things, the APZC's hit-region.
// If event-regions are disabled, this will initialize it to the empty
// region, but UpdatePanZoomControllerTree will add the hit-region from
// the event regions later.
apzc->SetLayerHitTestData(EventRegions(unobscured), aAncestorTransform);
APZCTM_LOG("Setting region %s as visible region for APZC %p\n",
Stringify(unobscured).c_str(), apzc);
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.
// Note that we currently only do this for APZCs in the layer tree
// that originated the update, because the only identifying information
// we are logging about APZCs is the scroll id, and otherwise we could
// confuse APZCs from different layer trees with the same scroll id.
if (aLayersId == aState.mOriginatingLayersId && apzc->GetParent()) {
@@ -418,42 +449,56 @@ APZCTreeManager::PrepareAPZCForLayer(con
// Consider the case where we have three layers: A, B, and C. A is at the top in
// z-order and C is at the bottom. A and C share a scrollid and scroll together; but
// B has a different scrollid and scrolls independently. Depending on how B moves
// and the async transform on it, a larger/smaller area of C may be unobscured.
// However, when we combine the hit regions of A and C here we are ignoring the
// async transform and so we basically assume the same amount of C is always visible
// on top of B. Fixing this doesn't appear to be very easy so I'm leaving it for
// now in the hopes that we won't run into this problem a lot.
+
+ // TODO(optimization): we could recycle one of the non-primary-holder nodes
+ // from mNodesToDestroy instead of creating a new one since those are going
+ // to get discarded anyway.
+ node = new HitTestingTreeNode(apzc, false);
+ AttachNodeToTree(node, aParent, aNextSibling);
+
+ // Even though different layers associated with a given APZC may be at
+ // different levels in the layer tree (e.g. one being an uncle of another),
+ // we require from Layout that the CSS transforms up to their common
+ // ancestor be the same.
+ MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
+
if (!gfxPrefs::LayoutEventRegionsEnabled()) {
nsIntRegion unobscured = ComputeTouchSensitiveRegion(state->mController, aMetrics, aObscured);
apzc->AddHitTestRegions(EventRegions(unobscured));
APZCTM_LOG("Adding region %s to visible region of APZC %p\n", Stringify(unobscured).c_str(), apzc);
}
}
- 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';
// Accumulate the CSS transform between layers that have an APZC.
// In the terminology of the big comment above APZCTreeManager::GetScreenToApzcTransform, if
// we are at layer M, then aAncestorTransform is NC * OC * PC, and we left-multiply MC and
// compute ancestorTransform to be MC * NC * OC * PC. This gets passed down as the ancestor
@@ -482,28 +527,25 @@ APZCTreeManager::UpdatePanZoomController
// have to check for mask layers and so on in order to properly handle
// that case.
obscured = aObscured;
obscured.Transform(To3DMatrix(transform).Inverse());
}
// 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
// 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.
@@ -599,22 +641,22 @@ APZCTreeManager::UpdatePanZoomController
} 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);
aState.mEventRegions.LastElement().OrWith(subtreeEventRegions);
}
}
- // 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;
}
return aNextSibling;
}
nsEventStatus
@@ -745,18 +787,18 @@ APZCTreeManager::GetTouchInputBlockAPZC(
// at the start of an input block for a number of reasons. One of the reasons is so that
// after we untransform the event into gecko space, it doesn't end up under something
// else. Another reason is that if we hit-test this event and end up on a layer's
// dispatch-to-content region we cannot be sure we actually got the correct layer. We
// have to fall back to the gecko hit-test to handle this case, but we can't untransform
// 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);
}
}
apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, aOutHitResult);
for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, aOutHitResult);
apzc = GetMultitouchTarget(apzc, apzc2);
APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
@@ -1029,64 +1071,68 @@ void
APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets)
{
nsRefPtr<AsyncPanZoomController> target = nullptr;
if (aTargets.Length() > 0) {
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);
}
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);
}
}
void
APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
{
nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
@@ -1098,22 +1144,29 @@ APZCTreeManager::CancelAnimation(const S
void
APZCTreeManager::ClearTree()
{
MonitorAutoLock lock(mTreeLock);
// 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;
}
/**
* Transform a displacement from the ParentLayer coordinates of a source APZC
* to the ParentLayer coordinates of a target APZC.
* @param aTreeManager the tree manager for the APZC tree containing |aSource|
* and |aTarget|
* @param aSource the source APZC
@@ -1242,53 +1295,79 @@ APZCTreeManager::DispatchFling(AsyncPanZ
bool
APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint)
{
nsRefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
return target != nullptr;
}
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;
}
}
return target.forget();
}
already_AddRefed<AsyncPanZoomController>
APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHitResult)
{
MonitorAutoLock lock(mTreeLock);
nsRefPtr<AsyncPanZoomController> target;
// The root may have siblings, so check those too
HitTestResult hitResult = NoApzcHit;
- for (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)) {
break;
}
}
// If we are in an overscrolled APZC, we should be returning nullptr.
MOZ_ASSERT(!(target && (hitResult == OverscrolledApzc)));
if (aOutHitResult) {
*aOutHitResult = hitResult;
}
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)
{
// Scroll grabbing is a mechanism that allows content to specify that
// the initial target of a pan should be not the innermost scrollable
// frame at the touch point (which is what GetTargetAPZC finds), but
// something higher up in the tree.
// It's not sufficient to just find the initial target, however, as
@@ -1330,17 +1409,19 @@ APZCTreeManager::BuildOverscrollHandoffC
// handoff parent, we don't actually need to do the search so we can
// just abort here.
if (parent->GetGuid().mScrollId == apzc->GetScrollHandoffParentId()) {
scrollParent = parent;
break;
}
}
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;
}
// Now adjust the chain to account for scroll grabbing. Sorting is a bit
// of an overkill here, but scroll grabbing will likely be generalized
// to scroll priorities, so we might as well do it this way.
result->SortByScrollPriority();
@@ -1348,127 +1429,121 @@ APZCTreeManager::BuildOverscrollHandoffC
// Print the overscroll chain for debugging.
for (uint32_t i = 0; i < result->Length(); ++i) {
APZCTM_LOG("OverscrollHandoffChain[%d] = %p\n", i, result->GetApzcAtIndex(i).get());
}
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
// which we hit test are in those coordinates).
Point4D hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
APZCTM_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
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;
}
if (match) {
result = match;
break;
}
}
}
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;
}
return result;
}
/* The methods GetScreenToApzcTransform() and GetApzcToGeckoTransform() return
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -43,16 +43,17 @@ class Layer;
class AsyncPanZoomController;
class CompositorParent;
class APZPaintLogHelper;
class OverscrollHandoffChain;
struct OverscrollHandoffState;
class LayerMetricsWrapper;
class InputQueue;
class GeckoContentController;
+class HitTestingTreeNode;
/**
* ****************** NOTE ON LOCK ORDERING IN APZ **************************
*
* There are two kinds of locks used by APZ: APZCTreeManager::mTreeLock
* ("the tree lock") and AsyncPanZoomController::mMonitor ("APZC locks").
*
* To avoid deadlock, we impose a lock ordering between these locks, which is:
@@ -400,54 +401,64 @@ public:
*/
enum HitTestResult {
NoApzcHit,
ApzcHitRegion,
ApzcContentRegion,
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;
already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const;
already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc) const;
already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
HitTestResult* aOutHitResult);
nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
nsEventStatus ProcessWheelEvent(WidgetWheelEvent& aEvent,
ScrollableLayerGuid* aOutTargetGuid,
uint64_t* aOutInputBlockId);
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
* 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
@@ -457,53 +468,53 @@ private:
* @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 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
* 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.
*/
- 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);
protected:
/* The input queue where input events are held until we know enough to
* figure out where they're going. Protected so gtests can access it.
*/
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
* input delivery thread, and so does not require locking.
*/
nsRefPtr<AsyncPanZoomController> mApzcForInputBlock;
/* The hit result for the current input event block; this should always be in
* sync with mApzcForInputBlock.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -911,17 +911,19 @@ AsyncPanZoomController::AsyncPanZoomCont
mSharedLock(nullptr),
mAsyncTransformAppliedToContent(false)
{
if (aGestures == USE_GESTURE_DETECTOR) {
mGestureEventListener = new GestureEventListener(this);
}
}
-AsyncPanZoomController::~AsyncPanZoomController() {
+AsyncPanZoomController::~AsyncPanZoomController()
+{
+ MOZ_ASSERT(IsDestroyed());
}
PCompositorParent*
AsyncPanZoomController::GetSharedFrameMetricsCompositor()
{
AssertOnCompositorThread();
if (mSharingFrameMetricsAcrossProcesses) {
@@ -958,18 +960,16 @@ AsyncPanZoomController::Destroy()
CancelAnimation();
{ // scope the lock
MonitorAutoLock lock(mRefPtrMonitor);
mGeckoContentController = nullptr;
mGestureEventListener = nullptr;
}
- mPrevSibling = nullptr;
- mLastChild = nullptr;
mParent = nullptr;
mTreeManager = nullptr;
PCompositorParent* compositor = GetSharedFrameMetricsCompositor();
// Only send the release message if the SharedFrameMetrics has been created.
if (compositor && mSharedFrameMetricsBuffer) {
unused << compositor->SendReleaseSharedCompositorFrameMetrics(mFrameMetrics.GetScrollId(), mAPZCId);
}
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -840,51 +840,27 @@ private:
bool aHandoff);
// Start an overscroll animation with the given initial velocity.
void StartOverscrollAnimation(const ParentLayerPoint& aVelocity);
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
* layers id.
*/
bool IsRootForLayersId() const {
return !mParent || (mParent->mLayersId != mLayersId);
}
@@ -892,18 +868,16 @@ public:
private:
// This is a raw pointer to avoid introducing a reference cycle between
// AsyncPanZoomController and APZCTreeManager. Since these objects don't
// live on the main thread, we can't use the cycle collector with them.
// The APZCTreeManager owns the lifetime of the APZCs, so nulling this
// pointer out in Destroy() will prevent accessing deleted memory.
Atomic<APZCTreeManager*> mTreeManager;
- nsRefPtr<AsyncPanZoomController> mLastChild;
- nsRefPtr<AsyncPanZoomController> mPrevSibling;
nsRefPtr<AsyncPanZoomController> mParent;
/* ===================================================================
* The functions and members in this section are used for scrolling,
* including handing off scroll to another APZC, and overscrolling.
*/
public:
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -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
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -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
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -232,16 +232,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
'ipc/FenceUtilsGonk.cpp',
]
UNIFIED_SOURCES += [
'apz/src/APZCTreeManager.cpp',
'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',
'apz/src/TaskThrottler.cpp',
'apz/testutil/APZTestData.cpp',
'apz/util/ActiveElementManager.cpp',
'apz/util/APZCCallbackHelper.cpp',
'apz/util/ChromeProcessController.cpp',
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -9,16 +9,17 @@
#include "mozilla/Attributes.h"
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/APZCTreeManager.h"
#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"
#include "gfxPrefs.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
@@ -870,16 +871,18 @@ TEST_F(APZCBasicTester, ComplexTransform
EXPECT_EQ(ViewTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
childMetrics.ZoomBy(1.5f);
childApzc->SetFrameMetrics(childMetrics);
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 {
protected:
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
{
if (aShouldTriggerScroll) {
EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(AtLeast(1));
@@ -2023,26 +2026,31 @@ TEST_F(APZCTreeManagerTester, Scrollable
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
}
TEST_F(APZCTreeManagerTester, Bug1068268) {
CreatePotentiallyLeakingTree();
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) {
CreateComplexMultiLayerTree();
ScopedLayerTreeRegistration registration(0, root, mcc);
manager->UpdatePanZoomControllerTree(nullptr, root, false, 0, 0);
TestAsyncPanZoomController* nullAPZC = nullptr;
@@ -2065,28 +2073,38 @@ TEST_F(APZHitTestingTester, ComplexMulti
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
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();
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
EXPECT_EQ(nullptr, layers1_2->GetParent());
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());
hit = GetTargetAPZC(ScreenPoint(275, 375));
EXPECT_EQ(ApzcOf(layers[9]), hit.get());
hit = GetTargetAPZC(ScreenPoint(250, 100));
EXPECT_EQ(ApzcOf(layers[7]), hit.get());
}