diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index fcd7ee1c642..473d8991bd7 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -613,12 +613,12 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mCurrentAnimatedGeometryRoot(nullptr), mDirtyRect(-1,-1,-1,-1), mGlassDisplayItem(nullptr), - mScrollInfoItemsForHoisting(nullptr), + mPendingScrollInfoItems(nullptr), + mCommittedScrollInfoItems(nullptr), mMode(aMode), mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID), mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID), mCurrentScrollbarFlags(0), - mSVGEffectsBuildingDepth(0), mBuildCaret(aBuildCaret), mIgnoreSuppression(false), mHadToIgnoreSuppression(false), @@ -1251,35 +1251,27 @@ nsDisplayListBuilder::IsInWillChangeBudget(nsIFrame* aFrame, return onBudget; } -void -nsDisplayListBuilder::EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage) +nsDisplayList* +nsDisplayListBuilder::EnterScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage) { - MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0); - MOZ_ASSERT(aHoistedItemsStorage); - if (mSVGEffectsBuildingDepth == 0) { - MOZ_ASSERT(!mScrollInfoItemsForHoisting); - mScrollInfoItemsForHoisting = aHoistedItemsStorage; - } - mSVGEffectsBuildingDepth++; + MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting()); + nsDisplayList* old = mPendingScrollInfoItems; + mPendingScrollInfoItems = aScrollInfoItemStorage; + return old; } void -nsDisplayListBuilder::ExitSVGEffectsContents() +nsDisplayListBuilder::LeaveScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage) { - mSVGEffectsBuildingDepth--; - MOZ_ASSERT(mSVGEffectsBuildingDepth >= 0); - MOZ_ASSERT(mScrollInfoItemsForHoisting); - if (mSVGEffectsBuildingDepth == 0) { - mScrollInfoItemsForHoisting = nullptr; - } + MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting()); + mPendingScrollInfoItems = aScrollInfoItemStorage; } void nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem) { MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting()); - MOZ_ASSERT(mScrollInfoItemsForHoisting); - mScrollInfoItemsForHoisting->AppendNewToTop(aScrollInfoItem); + mPendingScrollInfoItems->AppendNewToTop(aScrollInfoItem); } void @@ -4448,6 +4440,7 @@ nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer( , mScrollFrame(aScrollFrame) , mScrolledFrame(aScrolledFrame) , mScrollParentId(aBuilder->GetCurrentScrollParentId()) + , mIgnoreIfCompositorSupportsBlending(false) { #ifdef NS_BUILD_REFCNT_LOGGING MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer); @@ -4471,6 +4464,16 @@ nsDisplayScrollInfoLayer::BuildLayer(nsDisplayListBuilder* aBuilder, // cannot be layerized, and so needs to scroll synchronously. To handle those // cases, we still want to generate scrollinfo layers. + if (mIgnoreIfCompositorSupportsBlending) { + // This item was created pessimistically because, during display list + // building, we encountered a mix blend mode. If our layer manager + // supports compositing this mix blend mode, we don't actually need to + // create a scroll info layer. + if (aManager->SupportsMixBlendModes(mContainedBlendModes)) { + return nullptr; + } + } + ContainerLayerParameters params = aContainerParameters; if (mScrolledFrame->GetContent() && nsLayoutUtils::GetCriticalDisplayPort(mScrolledFrame->GetContent(), nullptr)) { @@ -4512,7 +4515,24 @@ nsDisplayScrollInfoLayer::ComputeFrameMetrics(Layer* aLayer, mScrollParentId, viewport, Nothing(), false, params))); } +void +nsDisplayScrollInfoLayer::IgnoreIfCompositorSupportsBlending(BlendModeSet aBlendModes) +{ + mContainedBlendModes += aBlendModes; + mIgnoreIfCompositorSupportsBlending = true; +} +void +nsDisplayScrollInfoLayer::UnsetIgnoreIfCompositorSupportsBlending() +{ + mIgnoreIfCompositorSupportsBlending = false; +} + +bool +nsDisplayScrollInfoLayer::ContainedInMixBlendMode() const +{ + return mIgnoreIfCompositorSupportsBlending; +} void nsDisplayScrollInfoLayer::WriteDebugInfo(std::stringstream& aStream) diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 706d53192ce..e08fe0f7cc2 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -841,11 +841,26 @@ public: const nsIFrame* aStopAtAncestor, nsIFrame** aOutResult); - void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage); - void ExitSVGEffectsContents(); + void SetCommittedScrollInfoItemList(nsDisplayList* aScrollInfoItemStorage) { + mCommittedScrollInfoItems = aScrollInfoItemStorage; + } + nsDisplayList* CommittedScrollInfoItems() const { + return mCommittedScrollInfoItems; + } + bool ShouldBuildScrollInfoItemsForHoisting() const { + return IsPaintingToWindow(); + } - bool ShouldBuildScrollInfoItemsForHoisting() const - { return mSVGEffectsBuildingDepth > 0; } + // When building display lists for stacking contexts, we append scroll info + // items to a temporary list. If the stacking context would create an + // inactive layer, we commit these items to the final hoisted scroll items + // list. Otherwise, we propagate these items to the parent stacking + // context's list of pending scroll info items. + // + // EnterScrollInfoItemHoisting returns the parent stacking context's pending + // item list. + nsDisplayList* EnterScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage); + void LeaveScrollInfoItemHoisting(nsDisplayList* aScrollInfoItemStorage); void AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem); @@ -954,19 +969,20 @@ private: nsIntRegion mWindowDraggingRegion; // The display item for the Windows window glass background, if any nsDisplayItem* mGlassDisplayItem; - // A temporary list that we append scroll info items to while building - // display items for the contents of frames with SVG effects. - // Only non-null when ShouldBuildScrollInfoItemsForHoisting() is true. - // This is a pointer and not a real nsDisplayList value because the - // nsDisplayList class is defined below this class, so we can't use it here. - nsDisplayList* mScrollInfoItemsForHoisting; + // When encountering inactive layers, we need to hoist scroll info items + // above these layers so APZ can deliver events to content. Such scroll + // info items are considered "committed" to the final hoisting list. If + // no hoisting is needed immediately, it may be needed later if a blend + // mode is introduced in a higher stacking context, so we keep all scroll + // info items until the end of display list building. + nsDisplayList* mPendingScrollInfoItems; + nsDisplayList* mCommittedScrollInfoItems; nsTArray mDisplayItemClipsToDestroy; Mode mMode; ViewID mCurrentScrollParentId; ViewID mCurrentScrollbarTarget; uint32_t mCurrentScrollbarFlags; BlendModeSet mContainedBlendModes; - int32_t mSVGEffectsBuildingDepth; bool mBuildCaret; bool mIgnoreSuppression; bool mHadToIgnoreSuppression; @@ -3265,10 +3281,20 @@ public: mozilla::UniquePtr ComputeFrameMetrics(Layer* aLayer, const ContainerLayerParameters& aContainerParameters); + void IgnoreIfCompositorSupportsBlending(BlendModeSet aBlendModes); + void UnsetIgnoreIfCompositorSupportsBlending(); + bool ContainedInMixBlendMode() const; + protected: nsIFrame* mScrollFrame; nsIFrame* mScrolledFrame; ViewID mScrollParentId; + + // If the only reason for the ScrollInfoLayer is a blend mode, the blend + // mode may be supported in the compositor. We track it here to determine + // if so during layer construction. + BlendModeSet mContainedBlendModes; + bool mIgnoreIfCompositorSupportsBlending; }; /** diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index db7de8979f0..6d00f9875df 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3103,6 +3103,11 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram builder, rootScrollFrame, displayportBase, &displayport); } + nsDisplayList hoistedScrollItemStorage; + if (aFlags & (PAINT_WIDGET_LAYERS | PAINT_TO_WINDOW)) { + builder.SetCommittedScrollInfoItemList(&hoistedScrollItemStorage); + } + nsRegion visibleRegion; if (aFlags & PAINT_WIDGET_LAYERS) { // This layer tree will be reused, so we'll need to calculate it diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index bc9523f9b8a..9ff132a57fb 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1911,6 +1911,87 @@ public: } }; +class AutoHoistScrollInfoItems +{ + nsDisplayListBuilder& mBuilder; + nsDisplayList* mParentPendingList; + nsDisplayList mPendingList; + +public: + explicit AutoHoistScrollInfoItems(nsDisplayListBuilder& aBuilder) + : mBuilder(aBuilder), + mParentPendingList(nullptr) + { + if (!mBuilder.ShouldBuildScrollInfoItemsForHoisting()) { + return; + } + mParentPendingList = mBuilder.EnterScrollInfoItemHoisting(&mPendingList); + } + ~AutoHoistScrollInfoItems() { + if (!mParentPendingList) { + // If we have no parent stacking context, we will throw out any scroll + // info items that are pending (meaning, we can safely ignore them since + // the scrollable layers they represent will not be flattened). + return; + } + mParentPendingList->AppendToTop(&mPendingList); + mBuilder.LeaveScrollInfoItemHoisting(mParentPendingList); + } + + // The current stacking context will definitely be flattened, so commit all + // pending scroll info items and make sure they will not be optimized away + // in the case they were also inside a compositor-supported mix-blend-mode. + void Commit() { + nsDisplayItem* iter = nullptr; + while ((iter = mPendingList.RemoveBottom()) != nullptr) { + MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER); + auto item = static_cast(iter); + + item->UnsetIgnoreIfCompositorSupportsBlending(); + mBuilder.CommittedScrollInfoItems()->AppendToTop(item); + } + } + + // The current stacking context will only be flattened if the given mix-blend + // mode is not supported in the compositor. Annotate the scroll info items + // and keep them in the pending list. + void AnnotateForBlendModes(BlendModeSet aBlendModes) { + for (nsDisplayItem* iter = mPendingList.GetBottom(); iter; iter = iter->GetAbove()) { + MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER); + auto item = static_cast(iter); + + item->IgnoreIfCompositorSupportsBlending(aBlendModes); + } + } + + bool IsRootStackingContext() { + // We're only finished building the hoisted list if we have no parent + // stacking context. + return !mParentPendingList; + } + + // Any scroll info items which contain a mix-blend mode are moved into the + // parent display list. + void Finish(nsDisplayList* aResultList) { + MOZ_ASSERT(IsRootStackingContext()); + + nsDisplayItem* iter = nullptr; + while ((iter = mPendingList.RemoveBottom()) != nullptr) { + MOZ_ASSERT(iter->GetType() == nsDisplayItem::TYPE_SCROLL_INFO_LAYER); + nsDisplayScrollInfoLayer *item = static_cast(iter); + + if (!item->ContainedInMixBlendMode()) { + // Discard the item, it was not committed for having an SVG effect nor + // was it contained with a mix-blend mode. + item->~nsDisplayScrollInfoLayer(); + continue; + } + + aResultList->AppendToTop(item); + } + } +}; + static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) { @@ -1990,13 +2071,13 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, inTransform = true; } + AutoHoistScrollInfoItems hoistedScrollInfoItems(*aBuilder); + bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); nsRect dirtyRectOutsideSVGEffects = dirtyRect; - nsDisplayList hoistedScrollInfoItemsStorage; if (usingSVGEffects) { dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); - aBuilder->EnterSVGEffectsContents(&hoistedScrollInfoItemsStorage); } bool useOpacity = HasVisualOpacity() && !nsSVGUtils::CanOptimizeOpacity(this); @@ -2130,10 +2211,6 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, /* List now emptied, so add the new list to the top. */ resultList.AppendNewToTop( new (aBuilder) nsDisplaySVGEffects(aBuilder, this, &resultList)); - // Also add the hoisted scroll info items. We need those for APZ scrolling - // because nsDisplaySVGEffects items can't build active layers. - aBuilder->ExitSVGEffectsContents(); - resultList.AppendToTop(&hoistedScrollInfoItemsStorage); } /* Else, if the list is non-empty and there is CSS group opacity without SVG * effects, wrap it up in an opacity item. @@ -2202,8 +2279,25 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, */ if (aBuilder->ContainsBlendMode()) { - resultList.AppendNewToTop( - new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList, aBuilder->ContainedBlendModes())); + resultList.AppendNewToTop( + new (aBuilder) nsDisplayBlendContainer(aBuilder, this, &resultList, aBuilder->ContainedBlendModes())); + } + + if (aBuilder->ShouldBuildScrollInfoItemsForHoisting()) { + if (usingSVGEffects) { + // We know this stacking context will be flattened, so hoist any scroll + // info items we created. + hoistedScrollInfoItems.Commit(); + } else if (aBuilder->ContainsBlendMode()) { + hoistedScrollInfoItems.AnnotateForBlendModes(aBuilder->ContainedBlendModes()); + } + + if (hoistedScrollInfoItems.IsRootStackingContext()) { + // If we're the root stacking context, no more mix-blend modes can be + // introduced and it's safe to hoist scroll info items. + resultList.AppendToTop(aBuilder->CommittedScrollInfoItems()); + hoistedScrollInfoItems.Finish(&resultList); + } } /* If there's blending, wrap up the list in a blend-mode item. Note