commit 194e8b90fc7fcb23fa515c70f76c72c432097203
parent 0005d5ba37d31857fd4a193ff70e560791ca0cb6
Author: Botond Ballo <botond@mozilla.com>
Date: Tue, 25 Nov 2025 01:50:34 +0000
Bug 2000991 - Generalize ClipManager::FindStickyItem() to handle other items for the sticky frame (e.g. blend mode) wrapping the sticky item. r=mstange
Differential Revision: https://phabricator.services.mozilla.com/D273657
Diffstat:
4 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/gfx/layers/wr/ClipManager.cpp b/gfx/layers/wr/ClipManager.cpp
@@ -378,16 +378,53 @@ static nscoord NegativePart(nscoord min, nscoord max) {
return 0;
}
-const nsDisplayStickyPosition* ClipManager::FindStickyItemFromFrame(
- const nsIFrame* aStickyFrame) const {
- // Iterate in reverse order as the sticky item is more likely to be at
- // the top of the stack.
+const nsDisplayStickyPosition* ClipManager::FindStickyItem(
+ nsDisplayItem* aItemWithStickyASR, const nsIFrame* aStickyFrame) const {
+ // Most common case: the sticky item is the item with the sticky ASR.
+ if (aItemWithStickyASR->GetType() == DisplayItemType::TYPE_STICKY_POSITION &&
+ aItemWithStickyASR->Frame() == aStickyFrame) {
+ return static_cast<nsDisplayStickyPosition*>(aItemWithStickyASR);
+ }
+
+ // Next most common case: the item with the sticky ASR is a descendant of the
+ // sticky item. We've pushed sticky items we've entered onto a stack,
+ // so iterate it backwards to find our sticky item.
for (const nsDisplayStickyPosition* item :
mozilla::Reversed(mStickyItemStack)) {
if (item->Frame() == aStickyFrame) {
return item;
}
}
+
+ // Edge case: the item with the sticky ASR is a wrapper display item wrapping
+ // the sticky item (e.g. an nsDisplayBlendMode). Fish out the sticky item.
+ if (aItemWithStickyASR->Frame() == aStickyFrame) {
+ nsDisplayItem* item = aItemWithStickyASR;
+ while (item) {
+ nsDisplayList* children = item->GetChildren();
+ if (!children) {
+ return nullptr;
+ }
+ nsDisplayItem* onlyChild = nullptr;
+ for (nsDisplayItem* child : *children) {
+ if (!onlyChild) {
+ onlyChild = child;
+ } else {
+ // More than one child
+ return nullptr;
+ }
+ }
+ if (!onlyChild || onlyChild->Frame() != aStickyFrame) {
+ // Not a wrapping display item for the same frame.
+ return nullptr;
+ }
+ if (onlyChild->GetType() == DisplayItemType::TYPE_STICKY_POSITION) {
+ return static_cast<nsDisplayStickyPosition*>(onlyChild);
+ }
+ // Unwrap and keep looking.
+ item = onlyChild;
+ }
+ }
return nullptr;
}
@@ -420,7 +457,7 @@ Maybe<wr::WrSpatialId> ClipManager::DefineStickyNode(
nsPoint toReferenceFrame;
const nsDisplayStickyPosition* stickyItem =
- FindStickyItemFromFrame(stickyFrame);
+ FindStickyItem(aItem, stickyFrame);
if (stickyItem) {
bool snap;
itemBounds = stickyItem->GetBounds(aBuilder, &snap);
diff --git a/gfx/layers/wr/ClipManager.h b/gfx/layers/wr/ClipManager.h
@@ -84,8 +84,8 @@ class ClipManager {
Maybe<wr::WrSpatialId> DefineStickyNode(
nsDisplayListBuilder* aBuilder, Maybe<wr::WrSpatialId> aParentSpatialId,
const ActiveScrolledRoot* aASR, nsDisplayItem* aItem);
- const nsDisplayStickyPosition* FindStickyItemFromFrame(
- const nsIFrame* aStickyFrame) const;
+ const nsDisplayStickyPosition* FindStickyItem(
+ nsDisplayItem* aItemWithStickyASR, const nsIFrame* aStickyFrame) const;
Maybe<wr::WrClipChainId> DefineClipChain(const DisplayItemClipChain* aChain,
int32_t aAppUnitsPerDevPixel);
diff --git a/layout/base/crashtests/2000991.html b/layout/base/crashtests/2000991.html
@@ -0,0 +1,7 @@
+<style>
+* {
+ position: sticky;
+ mix-blend-mode: screen;
+}
+</style>
+
diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list
@@ -568,3 +568,4 @@ load 1929445.html
asserts(1-3) load 1931933.html
load 1984507.html
load 2000121.html
+load 2000991.html