summaryrefslogtreecommitdiffstats
path: root/layout/painting
diff options
context:
space:
mode:
Diffstat (limited to 'layout/painting')
-rw-r--r--layout/painting/RetainedDisplayListBuilder.cpp19
-rw-r--r--layout/painting/RetainedDisplayListBuilder.h6
-rw-r--r--layout/painting/crashtests/crashtests.list2
-rw-r--r--layout/painting/nsCSSRenderingGradients.cpp30
-rw-r--r--layout/painting/nsCSSRenderingGradients.h37
-rw-r--r--layout/painting/nsDisplayList.cpp111
-rw-r--r--layout/painting/nsDisplayList.h24
7 files changed, 131 insertions, 98 deletions
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
@@ -200,11 +200,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<ColorStop>& aStops,
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
- float aOpacity, nsTArray<wr::GradientStop>& aResult)
- : ColorStopInterpolator(aStops, aStyleColorInterpolationMethod),
+ float aOpacity, nsTArray<wr::GradientStop>& 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<ColorStop>& 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<ColorStop>& 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<nsIDocShell> 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> presShell = GetFocusedPresShell();
- if (presShell) {
- RefPtr<nsCaret> 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<nsCaret> 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<TreeKind::Flat>(
+ return content1 != content2 &&
+ nsContentUtils::CompareTreePosition<TreeKind::Flat>(
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).
*
@@ -904,6 +904,11 @@ class nsDisplayListBuilder {
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
* DisplayItemClipChain objects.
@@ -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<nsDisplayItem*> mReuseableItems;
+ // Tracked carets used for retained display list.
+ AutoTArray<RefPtr<nsCaret>, 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<Item, 20> 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;