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

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

@ -38,7 +38,8 @@ void WebRenderLayerScrollData::InitializeRoot(int32_t aDescendantCount) {
void WebRenderLayerScrollData::Initialize( void WebRenderLayerScrollData::Initialize(
WebRenderScrollData& aOwner, nsDisplayItem* aItem, int32_t aDescendantCount, WebRenderScrollData& aOwner, nsDisplayItem* aItem, int32_t aDescendantCount,
const ActiveScrolledRoot* aStopAtAsr, 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(aDescendantCount >= 0); // Ensure value is valid
MOZ_ASSERT(mDescendantCount == MOZ_ASSERT(mDescendantCount ==
-1); // Don't allow re-setting an already set value -1); // Don't allow re-setting an already set value
@ -55,9 +56,13 @@ void WebRenderLayerScrollData::Initialize(
asr = nullptr; asr = nullptr;
} }
bool seenAncestorTransformId = false;
while (asr && asr != aStopAtAsr) { while (asr && asr != aStopAtAsr) {
MOZ_ASSERT(aOwner.GetManager()); MOZ_ASSERT(aOwner.GetManager());
ScrollableLayerGuid::ViewID scrollId = asr->GetViewId(); ScrollableLayerGuid::ViewID scrollId = asr->GetViewId();
if (aAncestorTransformId == scrollId) {
seenAncestorTransformId = true;
}
if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) { if (Maybe<size_t> index = aOwner.HasMetadataFor(scrollId)) {
mScrollIds.AppendElement(index.ref()); mScrollIds.AppendElement(index.ref());
} else { } else {
@ -75,6 +80,16 @@ void WebRenderLayerScrollData::Initialize(
asr = asr->mParent; 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 // See the comments on StackingContextHelper::mDeferredTransformItem for an
// overview of what deferred transforms are. // overview of what deferred transforms are.
// aAncestorTransform, if present, is the transform from a deferred transform // aAncestorTransform, if present, is the transform from a deferred transform
@ -92,6 +107,7 @@ void WebRenderLayerScrollData::Initialize(
if (aAncestorTransform && if (aAncestorTransform &&
mScrollbarData.mScrollbarLayerType != ScrollbarLayerType::Thumb) { mScrollbarData.mScrollbarLayerType != ScrollbarLayerType::Thumb) {
mAncestorTransform = *aAncestorTransform; mAncestorTransform = *aAncestorTransform;
mAncestorTransformId = aAncestorTransformId;
} }
} }
@ -312,6 +328,7 @@ void ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Write(
WriteParam(aMsg, aParam.mDescendantCount); WriteParam(aMsg, aParam.mDescendantCount);
WriteParam(aMsg, aParam.mScrollIds); WriteParam(aMsg, aParam.mScrollIds);
WriteParam(aMsg, aParam.mAncestorTransform); WriteParam(aMsg, aParam.mAncestorTransform);
WriteParam(aMsg, aParam.mAncestorTransformId);
WriteParam(aMsg, aParam.mTransform); WriteParam(aMsg, aParam.mTransform);
WriteParam(aMsg, aParam.mTransformIsPerspective); WriteParam(aMsg, aParam.mTransformIsPerspective);
WriteParam(aMsg, aParam.mResolution); WriteParam(aMsg, aParam.mResolution);
@ -337,6 +354,7 @@ bool ParamTraits<mozilla::layers::WebRenderLayerScrollData>::Read(
return ReadParam(aMsg, aIter, &aResult->mDescendantCount) && return ReadParam(aMsg, aIter, &aResult->mDescendantCount) &&
ReadParam(aMsg, aIter, &aResult->mScrollIds) && ReadParam(aMsg, aIter, &aResult->mScrollIds) &&
ReadParam(aMsg, aIter, &aResult->mAncestorTransform) && ReadParam(aMsg, aIter, &aResult->mAncestorTransform) &&
ReadParam(aMsg, aIter, &aResult->mAncestorTransformId) &&
ReadParam(aMsg, aIter, &aResult->mTransform) && ReadParam(aMsg, aIter, &aResult->mTransform) &&
ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective) && ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective) &&
ReadParam(aMsg, aIter, &aResult->mResolution) && ReadParam(aMsg, aIter, &aResult->mResolution) &&

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

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

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

@ -205,17 +205,31 @@ class MOZ_STACK_CLASS WebRenderScrollDataWrapper final {
gfx::Matrix4x4 GetTransform() const { gfx::Matrix4x4 GetTransform() const {
MOZ_ASSERT(IsValid()); MOZ_ASSERT(IsValid());
// See WebRenderLayerScrollData::Initialize for more context. The ancestor // See WebRenderLayerScrollData::Initialize for more context.
// transform is associated with the "topmost" layer, and the transform is // * The ancestor transform is associated with whichever layer has scroll
// associated with the "bottommost" layer. If there is only one // id matching GetAncestorTransformId().
// scrollmetadata on the layer, then it is both "topmost" and "bottommost" // * The resolution is associated with the "topmost" layer.
// and we combine the two transforms. // * 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; 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()) { if (AtTopLayer()) {
float resolution = mLayer->GetResolution(); float resolution = mLayer->GetResolution();
transform = mLayer->GetAncestorTransform() * transform =
gfx::Matrix4x4::Scaling(resolution, resolution, 1.f); transform * gfx::Matrix4x4::Scaling(resolution, resolution, 1.f);
} }
if (AtBottomLayer()) { if (AtBottomLayer()) {
transform = mLayer->GetTransform() * transform; transform = mLayer->GetTransform() * transform;