Bug 1719913 - Make sure transforms end up on a hit testing tree node corresponding to their ASR. r=tnikkel

Transforms stored in WebRenderLayerScrollData::mAncestorTransform would
always end up on the topmost hit-testing tree node generated by that
WebRenderLayerScrollData node. That topmost node may not correspond
to the ASR of the transform item(s) which are the source of the
mAncestorTransform. The transform being in the wrong place in the
hit-testing tree can in turn violate APZ's assumptions about different
nodes that scroll together having the same ancestor transform.

To resolve this, this patch stores the transform item's ASR in the
WebRenderLayerScrollData, and applies it to the matching hit-testing
tree node.

Differential Revision: https://phabricator.services.mozilla.com/D120567
This commit is contained in:
Botond Ballo 2021-07-28 03:20:17 +00:00
Родитель a57f7dcbdd
Коммит 65b7c16aa6
4 изменённых файлов: 59 добавлений и 16 удалений

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

@ -1832,6 +1832,13 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
// that we deferred, and a child WebRenderLayerScrollData item that
// holds the scroll metadata for the child's ASR.
Maybe<nsDisplayTransform*> deferred = aSc.GetDeferredTransformItem();
ScrollableLayerGuid::ViewID deferredId =
ScrollableLayerGuid::NULL_SCROLL_ID;
if (deferred) {
if (const auto* asr = (*deferred)->GetActiveScrolledRoot()) {
deferredId = asr->GetViewId();
}
}
if (deferred && (*deferred)->GetActiveScrolledRoot() !=
item->GetActiveScrolledRoot()) {
// This creates the child WebRenderLayerScrollData for |item|, but
@ -1842,7 +1849,8 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
mLayerScrollData.emplace_back();
mLayerScrollData.back().Initialize(
mManager->GetScrollData(), item, descendants,
(*deferred)->GetActiveScrolledRoot(), Nothing());
(*deferred)->GetActiveScrolledRoot(), Nothing(),
ScrollableLayerGuid::NULL_SCROLL_ID);
// The above WebRenderLayerScrollData will also be a descendant of
// the transform-holding WebRenderLayerScrollData we create below.
@ -1854,17 +1862,17 @@ void WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(
// stopAtAsr down to the deferred transform item's ASR, which must be
// "between" stopAtAsr and |item|'s ASR in the ASR tree).
mLayerScrollData.emplace_back();
mLayerScrollData.back().Initialize(mManager->GetScrollData(),
*deferred, descendants, stopAtAsr,
aSc.GetDeferredTransformMatrix());
mLayerScrollData.back().Initialize(
mManager->GetScrollData(), *deferred, descendants, stopAtAsr,
aSc.GetDeferredTransformMatrix(), deferredId);
} else {
// This is the "simple" case where we don't need to create two
// WebRenderLayerScrollData items; we can just create one that also
// holds the deferred transform matrix, if any.
mLayerScrollData.emplace_back();
mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
descendants, stopAtAsr,
aSc.GetDeferredTransformMatrix());
mLayerScrollData.back().Initialize(
mManager->GetScrollData(), item, descendants, stopAtAsr,
aSc.GetDeferredTransformMatrix(), deferredId);
}
}
}

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

@ -38,7 +38,8 @@ void WebRenderLayerScrollData::InitializeRoot(int32_t aDescendantCount) {
void WebRenderLayerScrollData::Initialize(
WebRenderScrollData& aOwner, nsDisplayItem* aItem, int32_t aDescendantCount,
const ActiveScrolledRoot* aStopAtAsr,
const Maybe<gfx::Matrix4x4>& aAncestorTransform) {
const Maybe<gfx::Matrix4x4>& aAncestorTransform,
const ViewID& aAncestorTransformId) {
MOZ_ASSERT(aDescendantCount >= 0); // Ensure value is valid
MOZ_ASSERT(mDescendantCount ==
-1); // Don't allow re-setting an already set value
@ -55,9 +56,13 @@ void WebRenderLayerScrollData::Initialize(
asr = nullptr;
}
bool seenAncestorTransformId = false;
while (asr && asr != aStopAtAsr) {
MOZ_ASSERT(aOwner.GetManager());
ScrollableLayerGuid::ViewID scrollId = asr->GetViewId();
if (aAncestorTransformId == scrollId) {
seenAncestorTransformId = true;
}
if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
mScrollIds.AppendElement(index.ref());
} else {
@ -75,6 +80,16 @@ void WebRenderLayerScrollData::Initialize(
asr = asr->mParent;
}
// Sanity check: if we have an ancestor transform, its scroll id should
// match one of the scroll metadatas on this node (WebRenderScrollDataWrapper
// will then use the ancestor transform at the level of that scroll metadata).
// One exception to this is if we have no scroll metadatas, which can happen
// if the scroll id of the transform is on an enclosing node.
MOZ_ASSERT(aAncestorTransformId == ScrollableLayerGuid::NULL_SCROLL_ID ||
mScrollIds.IsEmpty() || seenAncestorTransformId,
"The ancestor transform's view ID should match one of the metrics "
"on this node");
// See the comments on StackingContextHelper::mDeferredTransformItem for an
// overview of what deferred transforms are.
// aAncestorTransform, if present, is the transform from a deferred transform
@ -92,6 +107,7 @@ void WebRenderLayerScrollData::Initialize(
if (aAncestorTransform &&
mScrollbarData.mScrollbarLayerType != ScrollbarLayerType::Thumb) {
mAncestorTransform = *aAncestorTransform;
mAncestorTransformId = aAncestorTransformId;
}
}
@ -312,6 +328,7 @@ void ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Write(
WriteParam(aMsg, aParam.mDescendantCount);
WriteParam(aMsg, aParam.mScrollIds);
WriteParam(aMsg, aParam.mAncestorTransform);
WriteParam(aMsg, aParam.mAncestorTransformId);
WriteParam(aMsg, aParam.mTransform);
WriteParam(aMsg, aParam.mTransformIsPerspective);
WriteParam(aMsg, aParam.mResolution);
@ -337,6 +354,7 @@ bool ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Read(
return ReadParam(aMsg, aIter, &aResult->mDescendantCount) &&
ReadParam(aMsg, aIter, &aResult->mScrollIds) &&
ReadParam(aMsg, aIter, &aResult->mAncestorTransform) &&
ReadParam(aMsg, aIter, &aResult->mAncestorTransformId) &&
ReadParam(aMsg, aIter, &aResult->mTransform) &&
ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective) &&
ReadParam(aMsg, aIter, &aResult->mResolution) &&

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

@ -51,7 +51,8 @@ class WebRenderLayerScrollData final {
void Initialize(WebRenderScrollData& aOwner, nsDisplayItem* aItem,
int32_t aDescendantCount,
const ActiveScrolledRoot* aStopAtAsr,
const Maybe<gfx::Matrix4x4>& aAncestorTransform);
const Maybe<gfx::Matrix4x4>& aAncestorTransform,
const ViewID& aAncestorTransformId);
int32_t GetDescendantCount() const;
size_t GetScrollMetadataCount() const;
@ -66,6 +67,7 @@ class WebRenderLayerScrollData final {
size_t aIndex) const;
gfx::Matrix4x4 GetAncestorTransform() const { return mAncestorTransform; }
ViewID GetAncestorTransformId() const { return mAncestorTransformId; }
void SetTransform(const gfx::Matrix4x4& aTransform) {
mTransform = aTransform;
}
@ -186,6 +188,7 @@ class WebRenderLayerScrollData final {
// over IPC, and use on the parent side in APZ.
gfx::Matrix4x4 mAncestorTransform;
ViewID mAncestorTransformId;
gfx::Matrix4x4 mTransform;
bool mTransformIsPerspective;
float mResolution;

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

@ -205,17 +205,31 @@ class MOZ_STACK_CLASS WebRenderScrollDataWrapper final {
gfx::Matrix4x4 GetTransform() const {
MOZ_ASSERT(IsValid());
// See WebRenderLayerScrollData::Initialize for more context. The ancestor
// transform is associated with the "topmost" layer, and the transform is
// associated with the "bottommost" layer. If there is only one
// scrollmetadata on the layer, then it is both "topmost" and "bottommost"
// and we combine the two transforms.
// See WebRenderLayerScrollData::Initialize for more context.
// * The ancestor transform is associated with whichever layer has scroll
// id matching GetAncestorTransformId().
// * The resolution is associated with the "topmost" layer.
// * The transform is associated with the "bottommost" layer.
// Multiple transforms may apply to the same layer (e.g. if there is only
// one scrollmetadata on the layer, then it is both "topmost" and
// "bottommost"), so we may need to combine the transforms.
gfx::Matrix4x4 transform;
// The ancestor transform is usually emitted at the layer with the
// matching scroll id. However, sometimes the transform ends up on
// a node with no scroll metadata at all. In such cases we generate
// a single layer, and the ancestor transform needs to be on that layer,
// otherwise it will be lost.
bool emitAncestorTransform =
!Metrics().IsScrollable() ||
Metrics().GetScrollId() == mLayer->GetAncestorTransformId();
if (emitAncestorTransform) {
transform = mLayer->GetAncestorTransform();
}
if (AtTopLayer()) {
float resolution = mLayer->GetResolution();
transform = mLayer->GetAncestorTransform() *
gfx::Matrix4x4::Scaling(resolution, resolution, 1.f);
transform =
transform * gfx::Matrix4x4::Scaling(resolution, resolution, 1.f);
}
if (AtBottomLayer()) {
transform = mLayer->GetTransform() * transform;