From def92d1b8e9d373e2f6f27c366d578d97d8960c6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:50 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- layout/painting/RetainedDisplayListBuilder.cpp | 19 +---- layout/painting/RetainedDisplayListBuilder.h | 6 -- layout/painting/crashtests/crashtests.list | 2 +- layout/painting/nsCSSRenderingGradients.cpp | 30 +++++-- layout/painting/nsCSSRenderingGradients.h | 37 +++++++-- layout/painting/nsDisplayList.cpp | 111 ++++++++++++++----------- layout/painting/nsDisplayList.h | 24 ++++-- 7 files changed, 131 insertions(+), 98 deletions(-) (limited to 'layout/painting') diff --git a/layout/painting/RetainedDisplayListBuilder.cpp b/layout/painting/RetainedDisplayListBuilder.cpp index ca8f37a252..18e9612ff2 100644 --- a/layout/painting/RetainedDisplayListBuilder.cpp +++ b/layout/painting/RetainedDisplayListBuilder.cpp @@ -1313,23 +1313,6 @@ bool RetainedDisplayListBuilder::ShouldBuildPartial( return true; } -void RetainedDisplayListBuilder::InvalidateCaretFramesIfNeeded() { - if (mPreviousCaret == mBuilder.GetCaretFrame()) { - // The current caret frame is the same as the previous one. - return; - } - - if (mPreviousCaret) { - mPreviousCaret->MarkNeedsDisplayItemRebuild(); - } - - if (mBuilder.GetCaretFrame()) { - mBuilder.GetCaretFrame()->MarkNeedsDisplayItemRebuild(); - } - - mPreviousCaret = mBuilder.GetCaretFrame(); -} - class AutoClearFramePropsArray { public: explicit AutoClearFramePropsArray(size_t aCapacity) : mFrames(aCapacity) {} @@ -1585,7 +1568,7 @@ PartialUpdateResult RetainedDisplayListBuilder::AttemptPartialUpdate( MarkFramesWithItemsAndImagesModified(&mList); } - InvalidateCaretFramesIfNeeded(); + mBuilder.InvalidateCaretFramesIfNeeded(); // We set the override dirty regions during ComputeRebuildRegion or in // DisplayPortUtils::InvalidateForDisplayPortChange. The display port change diff --git a/layout/painting/RetainedDisplayListBuilder.h b/layout/painting/RetainedDisplayListBuilder.h index e9f5b56d1b..e255c4d3aa 100644 --- a/layout/painting/RetainedDisplayListBuilder.h +++ b/layout/painting/RetainedDisplayListBuilder.h @@ -199,11 +199,6 @@ class RetainedDisplayListBuilder { void IncrementSubDocPresShellPaintCount(nsDisplayItem* aItem); - /** - * Invalidates the current and previous caret frame if they have changed. - */ - void InvalidateCaretFramesIfNeeded(); - /** * A simple early exit heuristic to avoid slow partial display list rebuilds. * Returns true if a partial display list build should be attempted. @@ -272,7 +267,6 @@ class RetainedDisplayListBuilder { nsDisplayListBuilder mBuilder; RetainedDisplayList mList; - WeakFrame mPreviousCaret; RetainedDisplayListMetrics mMetrics; RetainedDisplayListData mData; }; diff --git a/layout/painting/crashtests/crashtests.list b/layout/painting/crashtests/crashtests.list index af2df7830c..954e2de6e2 100644 --- a/layout/painting/crashtests/crashtests.list +++ b/layout/painting/crashtests/crashtests.list @@ -18,7 +18,7 @@ load 1469472.html load 1477831-1.html load 1504033.html load 1514544-1.html -asserts(0-1) asserts-if(Android,0-2) load 1547420-1.html +asserts(0-5) load 1547420-1.html load 1549909.html load 1551389-1.html asserts(0-2) load 1555819-1.html diff --git a/layout/painting/nsCSSRenderingGradients.cpp b/layout/painting/nsCSSRenderingGradients.cpp index 2fb2f0b024..db30d4fefc 100644 --- a/layout/painting/nsCSSRenderingGradients.cpp +++ b/layout/painting/nsCSSRenderingGradients.cpp @@ -1195,15 +1195,16 @@ class MOZ_STACK_CLASS WrColorStopInterpolator WrColorStopInterpolator( const nsTArray& aStops, const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, - float aOpacity, nsTArray& aResult) - : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod), + float aOpacity, nsTArray& aResult, bool aExtendLastStop) + : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod, + aExtendLastStop), mResult(aResult), mOpacity(aOpacity), mOutputStop(0) {} void CreateStops() { mResult.SetLengthAndRetainStorage(0); - // we always emit at least two stops (start and end) for each input stop, + // We always emit at least two stops (start and end) for each input stop, // which avoids ambiguity with incomplete oklch/lch/hsv/hsb color stops for // the last stop pair, where the last color stop can't be interpreted on its // own because it actually depends on the previous stop. @@ -1257,11 +1258,26 @@ void nsCSSGradientRenderer::BuildWebRenderParameters( // * https://bugzilla.mozilla.org/show_bug.cgi?id=1248178 StyleColorInterpolationMethod styleColorInterpolationMethod = mGradient->ColorInterpolationMethod(); - if (mStops.Length() >= 2 && - (styleColorInterpolationMethod.space != StyleColorSpace::Srgb || - gfxPlatform::GetCMSMode() == CMSMode::All)) { + // For colorspaces supported by WebRender (Srgb, Hsl, Hwb) we technically do + // not need to add extra stops, but the only one of those colorspaces that + // appears frequently is Srgb, and Srgb still needs extra stops if CMS is + // enabled. Hsl/Hwb need extra stops if StyleHueInterpolationMethod is not + // Shorter, or if CMS is enabled. + // + // It's probably best to keep this logic as simple as possible, see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for an example of + // what can happen if we try to be clever here. + if (styleColorInterpolationMethod.space != StyleColorSpace::Srgb || + gfxPlatform::GetCMSMode() == CMSMode::All) { + // For the specific case of longer hue interpolation on a CSS non-repeating + // gradient, we have to pretend there is another stop at position=1.0 that + // duplicates the last stop, this is probably only used for things like a + // color wheel. No such problem for SVG as it doesn't have that complexity. + bool extendLastStop = aMode == wr::ExtendMode::Clamp && + styleColorInterpolationMethod.hue == + StyleHueInterpolationMethod::Longer; WrColorStopInterpolator interpolator(mStops, styleColorInterpolationMethod, - aOpacity, aStops); + aOpacity, aStops, extendLastStop); interpolator.CreateStops(); } else { aStops.SetLength(mStops.Length()); diff --git a/layout/painting/nsCSSRenderingGradients.h b/layout/painting/nsCSSRenderingGradients.h index 549cd8c4b6..68ecab07ae 100644 --- a/layout/painting/nsCSSRenderingGradients.h +++ b/layout/painting/nsCSSRenderingGradients.h @@ -44,23 +44,38 @@ class MOZ_STACK_CLASS ColorStopInterpolator { public: ColorStopInterpolator( const nsTArray& aStops, - const StyleColorInterpolationMethod& aStyleColorInterpolationMethod) + const StyleColorInterpolationMethod& aStyleColorInterpolationMethod, + bool aExtendLastStop) : mStyleColorInterpolationMethod(aStyleColorInterpolationMethod), - mStops(aStops) {} + mStops(aStops), + mExtendLastStop(aExtendLastStop) {} void CreateStops() { - for (uint32_t i = 0; i < mStops.Length() - 1; i++) { + // This loop intentionally iterates the last stop if extending. + uint32_t iterStops = mStops.Length() - (mExtendLastStop ? 0 : 1); + for (uint32_t i = 0; i < iterStops; i++) { + auto nextindex = i + 1 < mStops.Length() ? i + 1 : i; const auto& start = mStops[i]; - const auto& end = mStops[i + 1]; + const auto& end = mStops[nextindex]; + float startPosition = start.mPosition; + float endPosition = end.mPosition; + // For CSS non-repeating gradients with longer hue specified, we have to + // pretend there is a stop beyond the last stop. This is never the case + // on SVG gradients as they only use shorter hue. + // + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1885716 for more info. + if (i == mStops.Length() - 1 && mExtendLastStop) { + endPosition = 1.0f; + } uint32_t extraStops = - (uint32_t)(floor(end.mPosition * kFullRangeExtraStops) - - floor(start.mPosition * kFullRangeExtraStops)); + (uint32_t)(floor(endPosition * kFullRangeExtraStops) - + floor(startPosition * kFullRangeExtraStops)); extraStops = clamped(extraStops, 1U, kFullRangeExtraStops); float step = 1.0f / (float)extraStops; for (uint32_t extraStop = 0; extraStop <= extraStops; extraStop++) { auto progress = (float)extraStop * step; auto position = - start.mPosition + progress * (end.mPosition - start.mPosition); + startPosition + progress * (endPosition - startPosition); StyleAbsoluteColor color = Servo_InterpolateColor(mStyleColorInterpolationMethod, &end.mColor, &start.mColor, progress); @@ -73,11 +88,15 @@ class MOZ_STACK_CLASS ColorStopInterpolator { protected: StyleColorInterpolationMethod mStyleColorInterpolationMethod; const nsTArray& mStops; + // This indicates that we want to extend the endPosition on the last stop, + // which only matters if this is a CSS non-repeating gradient with + // StyleHueInterpolationMethod::Longer (only valid for hsl/hwb/lch/oklch). + bool mExtendLastStop; - // this could be made tunable, but at 1.0/128 the error is largely + // This could be made tunable, but at 1.0/128 the error is largely // irrelevant, as WebRender re-encodes it to 128 pairs of stops. // - // note that we don't attempt to place the positions of these stops + // Note that we don't attempt to place the positions of these stops // precisely at intervals, we just add this many extra stops across the // range where it is convenient. inline static const uint32_t kFullRangeExtraStops = 128; diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 78388d2185..2c2b7cb04c 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -383,6 +383,36 @@ nsDisplayWrapList* nsDisplayListBuilder::MergeItems( return merged; } +// FIXME(emilio): This whole business should ideally not be needed at all, but +// there are a variety of hard-to-deal-with caret invalidation issues, like +// bug 1888583, and caret changes are relatively uncommon, enough that it +// probably isn't worth chasing all them down. +void nsDisplayListBuilder::InvalidateCaretFramesIfNeeded() { + if (mPaintedCarets.IsEmpty()) { + return; + } + size_t i = mPaintedCarets.Length(); + while (i--) { + nsCaret* caret = mPaintedCarets[i]; + nsIFrame* oldCaret = caret->GetLastPaintedFrame(); + nsRect caretRect; + nsIFrame* currentCaret = caret->GetPaintGeometry(&caretRect); + if (oldCaret == currentCaret) { + // Keep tracking this caret, it hasn't changed. + continue; + } + if (oldCaret) { + oldCaret->MarkNeedsDisplayItemRebuild(); + } + if (currentCaret) { + currentCaret->MarkNeedsDisplayItemRebuild(); + } + // If / when we paint this caret, we'll track it again. + caret->SetLastPaintedFrame(nullptr); + mPaintedCarets.RemoveElementAt(i); + } +} + void nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter:: SetCurrentActiveScrolledRoot( const ActiveScrolledRoot* aActiveScrolledRoot) { @@ -650,7 +680,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mCurrentContainerASR(nullptr), mCurrentFrame(aReferenceFrame), mCurrentReferenceFrame(aReferenceFrame), - mCaretFrame(nullptr), mScrollInfoItemsForHoisting(nullptr), mFirstClipChainToDestroy(nullptr), mTableBackgroundSet(nullptr), @@ -718,21 +747,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, mRetainingDisplayList && StaticPrefs::layout_display_list_retain_sc(); } -static PresShell* GetFocusedPresShell() { - nsPIDOMWindowOuter* focusedWnd = - nsFocusManager::GetFocusManager()->GetFocusedWindow(); - if (!focusedWnd) { - return nullptr; - } - - nsCOMPtr focusedDocShell = focusedWnd->GetDocShell(); - if (!focusedDocShell) { - return nullptr; - } - - return focusedDocShell->GetPresShell(); -} - void nsDisplayListBuilder::BeginFrame() { nsCSSRendering::BeginFrameTreesLocked(); @@ -742,26 +756,6 @@ void nsDisplayListBuilder::BeginFrame() { mInTransform = false; mInFilter = false; mSyncDecodeImages = false; - - if (!mBuildCaret) { - return; - } - - RefPtr presShell = GetFocusedPresShell(); - if (presShell) { - RefPtr caret = presShell->GetCaret(); - mCaretFrame = caret->GetPaintGeometry(&mCaretRect); - - // The focused pres shell may not be in the document that we're - // painting, or be in a popup. Check if the display root for - // the caret matches the display root that we're painting, and - // only use it if it matches. - if (mCaretFrame && - nsLayoutUtils::GetDisplayRootFrame(mCaretFrame) != - nsLayoutUtils::GetDisplayRootFrame(mReferenceFrame)) { - mCaretFrame = nullptr; - } - } } void nsDisplayListBuilder::AddEffectUpdate(dom::RemoteBrowser* aBrowser, @@ -801,7 +795,6 @@ void nsDisplayListBuilder::EndFrame() { FreeClipChains(); FreeTemporaryItems(); nsCSSRendering::EndFrameTreesLocked(); - mCaretFrame = nullptr; } void nsDisplayListBuilder::MarkFrameForDisplay(nsIFrame* aFrame, @@ -1141,12 +1134,32 @@ void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame, return; } - // Caret frames add visual area to their frame, but we don't update the - // overflow area. Use flags to make sure we build display items for that frame - // instead. - if (mCaretFrame && mCaretFrame->PresShell() == state->mPresShell) { - MarkFrameForDisplay(mCaretFrame, aReferenceFrame); - } + state->mCaretFrame = [&]() -> nsIFrame* { + RefPtr caret = state->mPresShell->GetCaret(); + nsIFrame* currentCaret = caret->GetPaintGeometry(&mCaretRect); + if (!currentCaret) { + return nullptr; + } + + // Check if the display root for the caret matches the display root that + // we're painting, and only use it if it matches. Likely we only need this + // for carets inside popups. + if (nsLayoutUtils::GetDisplayRootFrame(currentCaret) != + nsLayoutUtils::GetDisplayRootFrame(aReferenceFrame)) { + return nullptr; + } + + // Caret frames add visual area to their frame, but we don't update the + // overflow area. Use flags to make sure we build display items for that + // frame instead. + MOZ_ASSERT(currentCaret->PresShell() == state->mPresShell); + MarkFrameForDisplay(currentCaret, aReferenceFrame); + caret->SetLastPaintedFrame(currentCaret); + if (!mPaintedCarets.Contains(caret)) { + mPaintedCarets.AppendElement(std::move(caret)); + } + return currentCaret; + }(); } // A non-blank paint is a paint that does not just contain the canvas @@ -2534,9 +2547,7 @@ struct ZSortItem { }; struct ZOrderComparator { - bool operator()(const ZSortItem& aLeft, const ZSortItem& aRight) const { - // Note that we can't just take the difference of the two - // z-indices here, because that might overflow a 32-bit int. + bool LessThan(const ZSortItem& aLeft, const ZSortItem& aRight) const { return aLeft.zIndex < aRight.zIndex; } }; @@ -2549,7 +2560,7 @@ struct ContentComparator { explicit ContentComparator(nsIContent* aCommonAncestor) : mCommonAncestor(aCommonAncestor) {} - bool operator()(nsDisplayItem* aLeft, nsDisplayItem* aRight) const { + bool LessThan(nsDisplayItem* aLeft, nsDisplayItem* aRight) const { // It's possible that the nsIContent for aItem1 or aItem2 is in a // subdocument of commonAncestor, because display items for subdocuments // have been mixed into the same list. Ensure that we're looking at content @@ -2562,7 +2573,8 @@ struct ContentComparator { // Something weird going on return true; } - return nsContentUtils::CompareTreePosition( + return content1 != content2 && + nsContentUtils::CompareTreePosition( content1, content2, mCommonAncestor) < 0; } }; @@ -4134,8 +4146,7 @@ bool nsDisplayCaret::CreateWebRenderCommands( nscolor caretColor; nsIFrame* frame = mCaret->GetPaintGeometry(&caretRect, &hookRect, &caretColor); - MOZ_ASSERT(frame == mFrame, "We're referring different frame"); - if (!frame) { + if (NS_WARN_IF(!frame) || NS_WARN_IF(frame != mFrame)) { return true; } diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 5064677cc7..acda6ea863 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -659,7 +659,7 @@ class nsDisplayListBuilder { * Get the frame that the caret is supposed to draw in. * If the caret is currently invisible, this will be null. */ - nsIFrame* GetCaretFrame() { return mCaretFrame; } + nsIFrame* GetCaretFrame() { return CurrentPresShellState()->mCaretFrame; } /** * Get the rectangle we're supposed to draw the caret into. */ @@ -852,9 +852,9 @@ class nsDisplayListBuilder { /** * Notifies the builder that a particular themed widget exists * at the given rectangle within the currently built display list. - * For certain appearance values (currently only StyleAppearance::Toolbar and - * StyleAppearance::WindowTitlebar) this gets called during every display list - * construction, for every themed widget of the right type within the + * For certain appearance values (currently only + * StyleAppearance::MozWindowTitlebar) this gets called during every display + * list construction, for every themed widget of the right type within the * display list, except for themed widgets which are transformed or have * effects applied to them (e.g. CSS opacity or filters). * @@ -903,6 +903,11 @@ class nsDisplayListBuilder { void AddEffectUpdate(dom::RemoteBrowser* aBrowser, const dom::EffectsInfo& aUpdate); + /** + * Invalidates the caret frames from previous paints, if they have changed. + */ + void InvalidateCaretFramesIfNeeded(); + /** * Allocate memory in our arena. It will only be freed when this display list * builder is destroyed. This memory holds nsDisplayItems and @@ -1729,6 +1734,7 @@ class nsDisplayListBuilder { bool mInsidePointerEventsNoneDoc; bool mTouchEventPrefEnabledDoc; nsIFrame* mPresShellIgnoreScrollFrame; + nsIFrame* mCaretFrame = nullptr; }; PresShellState* CurrentPresShellState() { @@ -1763,7 +1769,6 @@ class nsDisplayListBuilder { // The reference frame for mCurrentFrame. const nsIFrame* mCurrentReferenceFrame; - nsIFrame* mCaretFrame; // 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. @@ -1808,6 +1813,9 @@ class nsDisplayListBuilder { // Stores reusable items collected during display list preprocessing. nsTHashSet mReuseableItems; + // Tracked carets used for retained display list. + AutoTArray, 1> mPaintedCarets; + // Tracked regions used for retained display list. WeakFrameRegion mRetainedWindowDraggingRegion; WeakFrameRegion mRetainedWindowNoDraggingRegion; @@ -3195,12 +3203,13 @@ class nsDisplayList { // array of 20 items should be able to avoid a lot of dynamic allocations // here. AutoTArray items; + // Ensure we need just one alloc otherwise, no-op if enough. + items.SetCapacity(Length()); for (nsDisplayItem* item : TakeItems()) { items.AppendElement(Item(item)); } - - std::stable_sort(items.begin(), items.end(), aComparator); + items.StableSort(aComparator); for (Item& item : items) { AppendToTop(item); @@ -3208,6 +3217,7 @@ class nsDisplayList { } nsDisplayList TakeItems() { + // This std::move makes this a defined empty list, see assignment operator. nsDisplayList list = std::move(*this); #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED list.mAllowNonEmptyDestruction = true; -- cgit v1.2.3