Bug 1879449 [wpt PR 44489] - [wptrunner] Add `infrastructure/expected-fail/` test...
[gecko.git] / layout / style / nsStyleStruct.cpp
blob39f5b1a76002d61844e03c5e9466e6b678b3a9be
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * structs that contain the data provided by ComputedStyle, the
9 * internal API for computed style data for an element
12 #include "nsStyleStruct.h"
13 #include "nsStyleStructInlines.h"
14 #include "nsStyleConsts.h"
15 #include "nsString.h"
16 #include "nsPresContext.h"
17 #include "nsIWidget.h"
18 #include "nsCRTGlue.h"
19 #include "nsCSSProps.h"
20 #include "nsDeviceContext.h"
21 #include "nsStyleUtil.h"
22 #include "nsIURIMutator.h"
24 #include "nsCOMPtr.h"
26 #include "nsBidiUtils.h"
27 #include "nsLayoutUtils.h"
29 #include "imgIRequest.h"
30 #include "imgIContainer.h"
31 #include "CounterStyleManager.h"
33 #include "mozilla/dom/AnimationEffectBinding.h" // for PlaybackDirection
34 #include "mozilla/dom/BaseKeyframeTypesBinding.h" // for CompositeOperation
35 #include "mozilla/dom/DocGroup.h"
36 #include "mozilla/dom/ImageTracker.h"
37 #include "mozilla/CORSMode.h"
38 #include "mozilla/ClearOnShutdown.h"
39 #include "mozilla/GeckoBindings.h"
40 #include "mozilla/PreferenceSheet.h"
41 #include "mozilla/SchedulerGroup.h"
42 #include "mozilla/StaticPresData.h"
43 #include "mozilla/Likely.h"
44 #include "nsIURI.h"
45 #include "mozilla/dom/Document.h"
46 #include "mozilla/dom/DocumentInlines.h"
47 #include <algorithm>
48 #include "ImageLoader.h"
49 #include "mozilla/StaticPrefs_layout.h"
51 using namespace mozilla;
52 using namespace mozilla::dom;
54 static const nscoord kMediumBorderWidth = nsPresContext::CSSPixelsToAppUnits(3);
56 // We set the size limit of style structs to 504 bytes so that when they
57 // are allocated by Servo side with Arc, the total size doesn't exceed
58 // 512 bytes, which minimizes allocator slop.
59 static constexpr size_t kStyleStructSizeLimit = 504;
61 template <typename Struct, size_t Actual, size_t Limit>
62 struct AssertSizeIsLessThan {
63 static_assert(Actual == sizeof(Struct), "Bogus invocation");
64 static_assert(Actual <= Limit,
65 "Style struct became larger than the size limit");
66 static constexpr bool instantiate = true;
69 #define STYLE_STRUCT(name_) \
70 static_assert(AssertSizeIsLessThan<nsStyle##name_, sizeof(nsStyle##name_), \
71 kStyleStructSizeLimit>::instantiate, \
72 "");
73 #include "nsStyleStructList.h"
74 #undef STYLE_STRUCT
76 bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const {
77 // This very intentionally avoids comparing LoadData and such.
78 const auto& extra = extra_data.get();
79 const auto& otherExtra = aOther.extra_data.get();
80 if (extra.BaseURI() != otherExtra.BaseURI() ||
81 extra.Principal() != otherExtra.Principal() ||
82 cors_mode != aOther.cors_mode) {
83 // NOTE(emilio): This does pointer comparison, but it's what URLValue used
84 // to do. That's ok though since this is only used for style struct diffing.
85 return false;
87 return serialization == aOther.serialization;
90 StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); }
92 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
93 nsIURI* aBase) const {
94 nsCOMPtr<nsIURI> result = GetURI();
95 if (result && IsLocalRef()) {
96 nsCString ref;
97 result->GetRef(ref);
99 nsresult rv = NS_MutateURI(aBase).SetRef(ref).Finalize(result);
101 if (NS_FAILED(rv)) {
102 // If setting the ref failed, just return the original URI.
103 result = aBase;
106 return result.forget();
109 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
110 const nsIContent* aContent) const {
111 return ResolveLocalRef(aContent->GetBaseURI());
114 void StyleComputedUrl::ResolveImage(Document& aDocument,
115 const StyleComputedUrl* aOldImage) {
116 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
118 StyleLoadData& data = LoadData();
120 MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE));
122 data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE;
124 MOZ_ASSERT(NS_IsMainThread());
126 // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
127 // lack of non-http image caching in imagelib (bug 1406134), which causes
128 // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
129 // 1440305, but that seems too risky, and a lot of work to do before 60.
131 // Once that's fixed, the "old style" argument to TriggerImageLoads can go
132 // away, and same for mSharedCount in the image loader and so on.
133 const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) &&
134 aOldImage && aOldImage->IsImageResolved() &&
135 *this == *aOldImage;
137 RefPtr<imgRequestProxy> request;
138 if (reuseProxy) {
139 request = aOldImage->LoadData().resolved_image;
140 if (request) {
141 css::ImageLoader::NoteSharedLoad(request);
143 } else {
144 request = css::ImageLoader::LoadImage(*this, aDocument);
147 if (!request) {
148 return;
151 data.resolved_image = request.forget().take();
153 // Boost priority now that we know the image is present in the ComputedStyle
154 // of some frame.
155 data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
159 * Runnable to release the image request's mRequestProxy
160 * and mImageTracker on the main thread, and to perform
161 * any necessary unlocking and untracking of the image.
163 class StyleImageRequestCleanupTask final : public mozilla::Runnable {
164 public:
165 explicit StyleImageRequestCleanupTask(StyleLoadData& aData)
166 : mozilla::Runnable("StyleImageRequestCleanupTask"),
167 mRequestProxy(dont_AddRef(aData.resolved_image)) {
168 MOZ_ASSERT(mRequestProxy);
169 aData.resolved_image = nullptr;
172 NS_IMETHOD Run() final {
173 MOZ_ASSERT(NS_IsMainThread());
174 css::ImageLoader::UnloadImage(mRequestProxy);
175 return NS_OK;
178 protected:
179 virtual ~StyleImageRequestCleanupTask() {
180 MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
181 "mRequestProxy destructor need to run on the main thread!");
184 private:
185 // Since we always dispatch this runnable to the main thread, these will be
186 // released on the main thread when the runnable itself is released.
187 RefPtr<imgRequestProxy> mRequestProxy;
190 // This is defined here for parallelism with LoadURI.
191 void Gecko_LoadData_Drop(StyleLoadData* aData) {
192 if (aData->resolved_image) {
193 // We want to dispatch this async to prevent reentrancy issues, as
194 // imgRequestProxy going away can destroy documents, etc, see bug 1677555.
195 auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData);
196 SchedulerGroup::Dispatch(task.forget());
199 // URIs are safe to refcount from any thread.
200 NS_IF_RELEASE(aData->resolved_uri);
203 // --------------------
204 // nsStyleFont
206 nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
207 : mFont(aSrc.mFont),
208 mSize(aSrc.mSize),
209 mFontSizeFactor(aSrc.mFontSizeFactor),
210 mFontSizeOffset(aSrc.mFontSizeOffset),
211 mFontSizeKeyword(aSrc.mFontSizeKeyword),
212 mFontPalette(aSrc.mFontPalette),
213 mMathDepth(aSrc.mMathDepth),
214 mLineHeight(aSrc.mLineHeight),
215 mMathVariant(aSrc.mMathVariant),
216 mMathStyle(aSrc.mMathStyle),
217 mMinFontSizeRatio(aSrc.mMinFontSizeRatio),
218 mExplicitLanguage(aSrc.mExplicitLanguage),
219 mXTextScale(aSrc.mXTextScale),
220 mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
221 mScriptMinSize(aSrc.mScriptMinSize),
222 mLanguage(aSrc.mLanguage) {
223 MOZ_COUNT_CTOR(nsStyleFont);
226 static StyleXTextScale InitialTextScale(const Document& aDoc) {
227 if (nsContentUtils::IsChromeDoc(&aDoc) ||
228 nsContentUtils::IsPDFJS(aDoc.NodePrincipal())) {
229 return StyleXTextScale::ZoomOnly;
231 return StyleXTextScale::All;
234 nsStyleFont::nsStyleFont(const Document& aDocument)
235 : mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont(
236 StyleGenericFontFamily::None)),
237 mSize(ZoomText(aDocument, mFont.size)),
238 mFontSizeFactor(1.0),
239 mFontSizeOffset{0},
240 mFontSizeKeyword(StyleFontSizeKeyword::Medium),
241 mFontPalette(StyleFontPalette::Normal()),
242 mMathDepth(0),
243 mLineHeight(StyleLineHeight::Normal()),
244 mMathVariant(StyleMathVariant::None),
245 mMathStyle(StyleMathStyle::Normal),
246 mXTextScale(InitialTextScale(aDocument)),
247 mScriptUnconstrainedSize(mSize),
248 mScriptMinSize(Length::FromPixels(
249 CSSPixel::FromPoints(kMathMLDefaultScriptMinSizePt))),
250 mLanguage(aDocument.GetLanguageForStyle()) {
251 MOZ_COUNT_CTOR(nsStyleFont);
252 MOZ_ASSERT(NS_IsMainThread());
253 mFont.family.is_initial = true;
254 mFont.size = mSize;
255 if (MinFontSizeEnabled()) {
256 const Length minimumFontSize =
257 aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize;
258 mFont.size = Length::FromPixels(
259 std::max(mSize.ToCSSPixels(), minimumFontSize.ToCSSPixels()));
263 nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const {
264 MOZ_ASSERT(mXTextScale == aNewData.mXTextScale,
265 "expected -x-text-scale to be the same on both nsStyleFonts");
266 if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage ||
267 mExplicitLanguage != aNewData.mExplicitLanguage ||
268 mMathVariant != aNewData.mMathVariant ||
269 mMathStyle != aNewData.mMathStyle ||
270 mMinFontSizeRatio != aNewData.mMinFontSizeRatio ||
271 mLineHeight != aNewData.mLineHeight) {
272 return NS_STYLE_HINT_REFLOW;
275 switch (mFont.CalcDifference(aNewData.mFont)) {
276 case nsFont::MaxDifference::eLayoutAffecting:
277 return NS_STYLE_HINT_REFLOW;
279 case nsFont::MaxDifference::eVisual:
280 return NS_STYLE_HINT_VISUAL;
282 case nsFont::MaxDifference::eNone:
283 break;
286 if (mFontPalette != aNewData.mFontPalette) {
287 return NS_STYLE_HINT_VISUAL;
290 // XXX Should any of these cause a non-nsChangeHint_NeutralChange change?
291 if (mMathDepth != aNewData.mMathDepth ||
292 mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize ||
293 mScriptMinSize != aNewData.mScriptMinSize) {
294 return nsChangeHint_NeutralChange;
297 return nsChangeHint(0);
300 Length nsStyleFont::ZoomText(const Document& aDocument, Length aSize) {
301 if (auto* pc = aDocument.GetPresContext()) {
302 aSize.ScaleBy(pc->TextZoom());
304 return aSize;
307 template <typename T>
308 static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
309 return {aSide, aSide, aSide, aSide};
312 nsStyleMargin::nsStyleMargin()
313 : mMargin(StyleRectWithAllSides(
314 LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Zero()))),
315 mScrollMargin(StyleRectWithAllSides(StyleLength{0.})),
316 mOverflowClipMargin(StyleLength::Zero()) {
317 MOZ_COUNT_CTOR(nsStyleMargin);
320 nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
321 : mMargin(aSrc.mMargin),
322 mScrollMargin(aSrc.mScrollMargin),
323 mOverflowClipMargin(aSrc.mOverflowClipMargin) {
324 MOZ_COUNT_CTOR(nsStyleMargin);
327 nsChangeHint nsStyleMargin::CalcDifference(
328 const nsStyleMargin& aNewData) const {
329 nsChangeHint hint = nsChangeHint(0);
331 if (mMargin != aNewData.mMargin) {
332 // Margin differences can't affect descendant intrinsic sizes and
333 // don't need to force children to reflow.
334 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition |
335 nsChangeHint_ClearAncestorIntrinsics;
338 if (mScrollMargin != aNewData.mScrollMargin) {
339 // FIXME: Bug 1530253 Support re-snapping when scroll-margin changes.
340 hint |= nsChangeHint_NeutralChange;
343 if (mOverflowClipMargin != aNewData.mOverflowClipMargin) {
344 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
347 return hint;
350 nsStylePadding::nsStylePadding()
351 : mPadding(StyleRectWithAllSides(LengthPercentage::Zero())),
352 mScrollPadding(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())) {
353 MOZ_COUNT_CTOR(nsStylePadding);
356 nsStylePadding::nsStylePadding(const nsStylePadding& aSrc)
357 : mPadding(aSrc.mPadding), mScrollPadding(aSrc.mScrollPadding) {
358 MOZ_COUNT_CTOR(nsStylePadding);
361 nsChangeHint nsStylePadding::CalcDifference(
362 const nsStylePadding& aNewData) const {
363 nsChangeHint hint = nsChangeHint(0);
365 if (mPadding != aNewData.mPadding) {
366 // Padding differences can't affect descendant intrinsic sizes, but do need
367 // to force children to reflow so that we can reposition them, since their
368 // offsets are from our frame bounds but our content rect's position within
369 // those bounds is moving.
370 // FIXME: It would be good to return a weaker hint here that doesn't
371 // force reflow of all descendants, but the hint would need to force
372 // reflow of the frame's children (see how
373 // ReflowInput::InitResizeFlags initializes the inline-resize flag).
374 hint |= NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
377 if (mScrollPadding != aNewData.mScrollPadding) {
378 // FIXME: Bug 1530253 Support re-snapping when scroll-padding changes.
379 hint |= nsChangeHint_NeutralChange;
382 return hint;
385 static inline BorderRadius ZeroBorderRadius() {
386 auto zero = LengthPercentage::Zero();
387 return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}};
390 nsStyleBorder::nsStyleBorder()
391 : mBorderRadius(ZeroBorderRadius()),
392 mBorderImageSource(StyleImage::None()),
393 mBorderImageWidth(
394 StyleRectWithAllSides(StyleBorderImageSideWidth::Number(1.))),
395 mBorderImageOutset(
396 StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))),
397 mBorderImageSlice(
398 {StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})),
399 false}),
400 mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
401 mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
402 mFloatEdge(StyleFloatEdge::ContentBox),
403 mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
404 mBorderTopColor(StyleColor::CurrentColor()),
405 mBorderRightColor(StyleColor::CurrentColor()),
406 mBorderBottomColor(StyleColor::CurrentColor()),
407 mBorderLeftColor(StyleColor::CurrentColor()),
408 mComputedBorder(0, 0, 0, 0) {
409 MOZ_COUNT_CTOR(nsStyleBorder);
411 nscoord medium = kMediumBorderWidth;
412 for (const auto side : mozilla::AllPhysicalSides()) {
413 mBorder.Side(side) = medium;
414 mBorderStyle[side] = StyleBorderStyle::None;
418 nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
419 : mBorderRadius(aSrc.mBorderRadius),
420 mBorderImageSource(aSrc.mBorderImageSource),
421 mBorderImageWidth(aSrc.mBorderImageWidth),
422 mBorderImageOutset(aSrc.mBorderImageOutset),
423 mBorderImageSlice(aSrc.mBorderImageSlice),
424 mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
425 mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
426 mFloatEdge(aSrc.mFloatEdge),
427 mBoxDecorationBreak(aSrc.mBoxDecorationBreak),
428 mBorderTopColor(aSrc.mBorderTopColor),
429 mBorderRightColor(aSrc.mBorderRightColor),
430 mBorderBottomColor(aSrc.mBorderBottomColor),
431 mBorderLeftColor(aSrc.mBorderLeftColor),
432 mComputedBorder(aSrc.mComputedBorder),
433 mBorder(aSrc.mBorder) {
434 MOZ_COUNT_CTOR(nsStyleBorder);
435 for (const auto side : mozilla::AllPhysicalSides()) {
436 mBorderStyle[side] = aSrc.mBorderStyle[side];
440 void nsStyleBorder::TriggerImageLoads(Document& aDocument,
441 const nsStyleBorder* aOldStyle) {
442 MOZ_ASSERT(NS_IsMainThread());
444 mBorderImageSource.ResolveImage(
445 aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
448 nsMargin nsStyleBorder::GetImageOutset() const {
449 // We don't check whether there is a border-image (which is OK since
450 // the initial values yields 0 outset) so that we don't have to
451 // reflow to update overflow areas when an image loads.
452 nsMargin outset;
453 for (const auto s : mozilla::AllPhysicalSides()) {
454 const auto& coord = mBorderImageOutset.Get(s);
455 nscoord value;
456 if (coord.IsLength()) {
457 value = coord.AsLength().ToAppUnits();
458 } else {
459 MOZ_ASSERT(coord.IsNumber());
460 value = coord.AsNumber() * mComputedBorder.Side(s);
462 outset.Side(s) = value;
464 return outset;
467 nsChangeHint nsStyleBorder::CalcDifference(
468 const nsStyleBorder& aNewData) const {
469 // FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these
470 // differences should not need to clear descendant intrinsics.
471 // FIXME: It would be good to return a weaker hint for the
472 // GetComputedBorder() differences (and perhaps others) that doesn't
473 // force reflow of all descendants, but the hint would need to force
474 // reflow of the frame's children (see how
475 // ReflowInput::InitResizeFlags initializes the inline-resize flag).
476 if (GetComputedBorder() != aNewData.GetComputedBorder() ||
477 mFloatEdge != aNewData.mFloatEdge ||
478 mBorderImageOutset != aNewData.mBorderImageOutset ||
479 mBoxDecorationBreak != aNewData.mBoxDecorationBreak) {
480 return NS_STYLE_HINT_REFLOW;
483 for (const auto ix : mozilla::AllPhysicalSides()) {
484 // See the explanation in nsChangeHint.h of
485 // nsChangeHint_BorderStyleNoneChange .
486 // Furthermore, even though we know *this* side is 0 width, just
487 // assume a repaint hint for some other change rather than bother
488 // tracking this result through the rest of the function.
489 if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) {
490 return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange;
494 // Note that mBorderStyle stores not only the border style but also
495 // color-related flags. Given that we've already done an mComputedBorder
496 // comparison, border-style differences can only lead to a repaint hint. So
497 // it's OK to just compare the values directly -- if either the actual
498 // style or the color flags differ we want to repaint.
499 for (const auto ix : mozilla::AllPhysicalSides()) {
500 if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] ||
501 BorderColorFor(ix) != aNewData.BorderColorFor(ix)) {
502 return nsChangeHint_RepaintFrame;
506 // Note that border radius also controls the outline radius if the
507 // layout.css.outline-follows-border-radius.enabled pref is set. Any
508 // optimizations here should apply to both.
509 if (mBorderRadius != aNewData.mBorderRadius) {
510 return nsChangeHint_RepaintFrame;
513 // Loading status of the border image can be accessed in main thread only
514 // while CalcDifference might be executed on a background thread. As a
515 // result, we have to check mBorderImage* fields even before border image was
516 // actually loaded.
517 if (!mBorderImageSource.IsNone() || !aNewData.mBorderImageSource.IsNone()) {
518 if (mBorderImageSource != aNewData.mBorderImageSource ||
519 mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
520 mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
521 mBorderImageSlice != aNewData.mBorderImageSlice ||
522 mBorderImageWidth != aNewData.mBorderImageWidth) {
523 return nsChangeHint_RepaintFrame;
527 // mBorder is the specified border value. Changes to this don't
528 // need any change processing, since we operate on the computed
529 // border values instead.
530 if (mBorder != aNewData.mBorder) {
531 return nsChangeHint_NeutralChange;
534 // mBorderImage* fields are checked only when border-image is not 'none'.
535 if (mBorderImageSource != aNewData.mBorderImageSource ||
536 mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
537 mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
538 mBorderImageSlice != aNewData.mBorderImageSlice ||
539 mBorderImageWidth != aNewData.mBorderImageWidth) {
540 return nsChangeHint_NeutralChange;
543 return nsChangeHint(0);
546 nsStyleOutline::nsStyleOutline()
547 : mOutlineWidth(kMediumBorderWidth),
548 mOutlineOffset({0.0f}),
549 mOutlineColor(StyleColor::CurrentColor()),
550 mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
551 mActualOutlineWidth(0) {
552 MOZ_COUNT_CTOR(nsStyleOutline);
555 nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
556 : mOutlineWidth(aSrc.mOutlineWidth),
557 mOutlineOffset(aSrc.mOutlineOffset),
558 mOutlineColor(aSrc.mOutlineColor),
559 mOutlineStyle(aSrc.mOutlineStyle),
560 mActualOutlineWidth(aSrc.mActualOutlineWidth) {
561 MOZ_COUNT_CTOR(nsStyleOutline);
564 nsChangeHint nsStyleOutline::CalcDifference(
565 const nsStyleOutline& aNewData) const {
566 const bool shouldPaintOutline = ShouldPaintOutline();
567 // We need the explicit 'outline-style: auto' check because
568 // 'outline-style: auto' effectively also changes 'outline-width'.
569 if (shouldPaintOutline != aNewData.ShouldPaintOutline() ||
570 mActualOutlineWidth != aNewData.mActualOutlineWidth ||
571 mOutlineStyle.IsAuto() != aNewData.mOutlineStyle.IsAuto() ||
572 (shouldPaintOutline && mOutlineOffset != aNewData.mOutlineOffset)) {
573 return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
574 nsChangeHint_RepaintFrame;
577 if (mOutlineStyle != aNewData.mOutlineStyle ||
578 mOutlineColor != aNewData.mOutlineColor) {
579 return shouldPaintOutline ? nsChangeHint_RepaintFrame
580 : nsChangeHint_NeutralChange;
583 if (mOutlineWidth != aNewData.mOutlineWidth ||
584 mOutlineOffset != aNewData.mOutlineOffset) {
585 return nsChangeHint_NeutralChange;
588 return nsChangeHint(0);
591 nsSize nsStyleOutline::EffectiveOffsetFor(const nsRect& aRect) const {
592 const nscoord offset = mOutlineOffset.ToAppUnits();
594 if (offset >= 0) {
595 // Fast path for non-negative offset values
596 return nsSize(offset, offset);
599 return nsSize(std::max(offset, -(aRect.Width() / 2)),
600 std::max(offset, -(aRect.Height() / 2)));
603 // --------------------
604 // nsStyleList
606 nsStyleList::nsStyleList()
607 : mListStylePosition(StyleListStylePosition::Outside),
608 mQuotes(StyleQuotes::Auto()),
609 mListStyleImage(StyleImage::None()) {
610 MOZ_COUNT_CTOR(nsStyleList);
611 MOZ_ASSERT(NS_IsMainThread());
613 mCounterStyle = nsGkAtoms::disc;
616 nsStyleList::nsStyleList(const nsStyleList& aSource)
617 : mListStylePosition(aSource.mListStylePosition),
618 mCounterStyle(aSource.mCounterStyle),
619 mQuotes(aSource.mQuotes),
620 mListStyleImage(aSource.mListStyleImage) {
621 MOZ_COUNT_CTOR(nsStyleList);
624 void nsStyleList::TriggerImageLoads(Document& aDocument,
625 const nsStyleList* aOldStyle) {
626 MOZ_ASSERT(NS_IsMainThread());
627 mListStyleImage.ResolveImage(
628 aDocument, aOldStyle ? &aOldStyle->mListStyleImage : nullptr);
631 nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aNewData,
632 const ComputedStyle& aOldStyle) const {
633 // If the quotes implementation is ever going to change we might not need
634 // a framechange here and a reflow should be sufficient. See bug 35768.
635 if (mQuotes != aNewData.mQuotes) {
636 return nsChangeHint_ReconstructFrame;
638 nsChangeHint hint = nsChangeHint(0);
639 // Only elements whose display value is list-item can be affected by
640 // list-style-{position,type,image}. This also relies on that when the display
641 // value changes from something else to list-item, that change itself would
642 // cause ReconstructFrame.
643 if (mListStylePosition != aNewData.mListStylePosition ||
644 mCounterStyle != aNewData.mCounterStyle ||
645 mListStyleImage != aNewData.mListStyleImage) {
646 if (aOldStyle.StyleDisplay()->IsListItem()) {
647 return nsChangeHint_ReconstructFrame;
649 // list-style-image may affect nsImageFrame for XUL elements, but that is
650 // dealt with explicitly in nsImageFrame::DidSetComputedStyle.
651 hint = nsChangeHint_NeutralChange;
653 return hint;
656 already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
657 if (!mListStyleImage.IsUrl()) {
658 return nullptr;
661 return do_AddRef(mListStyleImage.AsUrl().GetURI());
664 // --------------------
665 // nsStyleXUL
667 nsStyleXUL::nsStyleXUL()
668 : mBoxFlex(0.0f),
669 mBoxOrdinal(1),
670 mBoxAlign(StyleBoxAlign::Stretch),
671 mBoxDirection(StyleBoxDirection::Normal),
672 mBoxOrient(StyleBoxOrient::Horizontal),
673 mBoxPack(StyleBoxPack::Start) {
674 MOZ_COUNT_CTOR(nsStyleXUL);
677 nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource)
678 : mBoxFlex(aSource.mBoxFlex),
679 mBoxOrdinal(aSource.mBoxOrdinal),
680 mBoxAlign(aSource.mBoxAlign),
681 mBoxDirection(aSource.mBoxDirection),
682 mBoxOrient(aSource.mBoxOrient),
683 mBoxPack(aSource.mBoxPack) {
684 MOZ_COUNT_CTOR(nsStyleXUL);
687 nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const {
688 if (mBoxAlign == aNewData.mBoxAlign &&
689 mBoxDirection == aNewData.mBoxDirection &&
690 mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient &&
691 mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal) {
692 return nsChangeHint(0);
694 if (mBoxOrdinal != aNewData.mBoxOrdinal) {
695 return nsChangeHint_ReconstructFrame;
697 return NS_STYLE_HINT_REFLOW;
700 // --------------------
701 // nsStyleColumn
703 /* static */ const uint32_t nsStyleColumn::kMaxColumnCount;
704 /* static */ const uint32_t nsStyleColumn::kColumnCountAuto;
706 nsStyleColumn::nsStyleColumn()
707 : mColumnWidth(LengthOrAuto::Auto()),
708 mColumnRuleColor(StyleColor::CurrentColor()),
709 mColumnRuleStyle(StyleBorderStyle::None),
710 mColumnRuleWidth(kMediumBorderWidth),
711 mActualColumnRuleWidth(0) {
712 MOZ_COUNT_CTOR(nsStyleColumn);
715 nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource)
716 : mColumnCount(aSource.mColumnCount),
717 mColumnWidth(aSource.mColumnWidth),
718 mColumnRuleColor(aSource.mColumnRuleColor),
719 mColumnRuleStyle(aSource.mColumnRuleStyle),
720 mColumnFill(aSource.mColumnFill),
721 mColumnSpan(aSource.mColumnSpan),
722 mColumnRuleWidth(aSource.mColumnRuleWidth),
723 mActualColumnRuleWidth(aSource.mActualColumnRuleWidth) {
724 MOZ_COUNT_CTOR(nsStyleColumn);
727 nsChangeHint nsStyleColumn::CalcDifference(
728 const nsStyleColumn& aNewData) const {
729 if (mColumnWidth.IsAuto() != aNewData.mColumnWidth.IsAuto() ||
730 mColumnCount != aNewData.mColumnCount ||
731 mColumnSpan != aNewData.mColumnSpan) {
732 // We force column count changes to do a reframe, because it's tricky to
733 // handle some edge cases where the column count gets smaller and content
734 // overflows.
735 // XXX not ideal
736 return nsChangeHint_ReconstructFrame;
739 if (mColumnWidth != aNewData.mColumnWidth ||
740 mColumnFill != aNewData.mColumnFill) {
741 return NS_STYLE_HINT_REFLOW;
744 if (mActualColumnRuleWidth != aNewData.mActualColumnRuleWidth ||
745 mColumnRuleStyle != aNewData.mColumnRuleStyle ||
746 mColumnRuleColor != aNewData.mColumnRuleColor) {
747 return NS_STYLE_HINT_VISUAL;
750 if (mColumnRuleWidth != aNewData.mColumnRuleWidth) {
751 return nsChangeHint_NeutralChange;
754 return nsChangeHint(0);
757 using SVGPaintFallback = StyleGenericSVGPaintFallback<StyleColor>;
759 // --------------------
760 // nsStyleSVG
762 nsStyleSVG::nsStyleSVG()
763 : mFill{StyleSVGPaintKind::Color(StyleColor::Black()),
764 SVGPaintFallback::Unset()},
765 mStroke{StyleSVGPaintKind::None(), SVGPaintFallback::Unset()},
766 mMarkerEnd(StyleUrlOrNone::None()),
767 mMarkerMid(StyleUrlOrNone::None()),
768 mMarkerStart(StyleUrlOrNone::None()),
769 mMozContextProperties{{}, {0}},
770 mStrokeDasharray(StyleSVGStrokeDashArray::Values({})),
771 mStrokeDashoffset(
772 StyleSVGLength::LengthPercentage(LengthPercentage::Zero())),
773 mStrokeWidth(
774 StyleSVGWidth::LengthPercentage(LengthPercentage::FromPixels(1.0f))),
775 mFillOpacity(StyleSVGOpacity::Opacity(1.0f)),
776 mStrokeMiterlimit(4.0f),
777 mStrokeOpacity(StyleSVGOpacity::Opacity(1.0f)),
778 mClipRule(StyleFillRule::Nonzero),
779 mColorInterpolation(StyleColorInterpolation::Srgb),
780 mColorInterpolationFilters(StyleColorInterpolation::Linearrgb),
781 mFillRule(StyleFillRule::Nonzero),
782 mPaintOrder(0),
783 mShapeRendering(StyleShapeRendering::Auto),
784 mStrokeLinecap(StyleStrokeLinecap::Butt),
785 mStrokeLinejoin(StyleStrokeLinejoin::Miter),
786 mDominantBaseline(StyleDominantBaseline::Auto),
787 mTextAnchor(StyleTextAnchor::Start) {
788 MOZ_COUNT_CTOR(nsStyleSVG);
791 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
792 : mFill(aSource.mFill),
793 mStroke(aSource.mStroke),
794 mMarkerEnd(aSource.mMarkerEnd),
795 mMarkerMid(aSource.mMarkerMid),
796 mMarkerStart(aSource.mMarkerStart),
797 mMozContextProperties(aSource.mMozContextProperties),
798 mStrokeDasharray(aSource.mStrokeDasharray),
799 mStrokeDashoffset(aSource.mStrokeDashoffset),
800 mStrokeWidth(aSource.mStrokeWidth),
801 mFillOpacity(aSource.mFillOpacity),
802 mStrokeMiterlimit(aSource.mStrokeMiterlimit),
803 mStrokeOpacity(aSource.mStrokeOpacity),
804 mClipRule(aSource.mClipRule),
805 mColorInterpolation(aSource.mColorInterpolation),
806 mColorInterpolationFilters(aSource.mColorInterpolationFilters),
807 mFillRule(aSource.mFillRule),
808 mPaintOrder(aSource.mPaintOrder),
809 mShapeRendering(aSource.mShapeRendering),
810 mStrokeLinecap(aSource.mStrokeLinecap),
811 mStrokeLinejoin(aSource.mStrokeLinejoin),
812 mDominantBaseline(aSource.mDominantBaseline),
813 mTextAnchor(aSource.mTextAnchor) {
814 MOZ_COUNT_CTOR(nsStyleSVG);
817 static bool PaintURIChanged(const StyleSVGPaint& aPaint1,
818 const StyleSVGPaint& aPaint2) {
819 if (aPaint1.kind.IsPaintServer() != aPaint2.kind.IsPaintServer()) {
820 return true;
822 return aPaint1.kind.IsPaintServer() &&
823 aPaint1.kind.AsPaintServer() != aPaint2.kind.AsPaintServer();
826 nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const {
827 nsChangeHint hint = nsChangeHint(0);
829 if (mMarkerEnd != aNewData.mMarkerEnd || mMarkerMid != aNewData.mMarkerMid ||
830 mMarkerStart != aNewData.mMarkerStart) {
831 // Markers currently contribute to SVGGeometryFrame::mRect,
832 // so we need a reflow as well as a repaint. No intrinsic sizes need
833 // to change, so nsChangeHint_NeedReflow is sufficient.
834 return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow |
835 nsChangeHint_RepaintFrame;
838 if (mFill != aNewData.mFill || mStroke != aNewData.mStroke ||
839 mFillOpacity != aNewData.mFillOpacity ||
840 mStrokeOpacity != aNewData.mStrokeOpacity) {
841 hint |= nsChangeHint_RepaintFrame;
842 if (HasStroke() != aNewData.HasStroke() ||
843 (!HasStroke() && HasFill() != aNewData.HasFill())) {
844 // Frame bounds and overflow rects depend on whether we "have" fill or
845 // stroke. Whether we have stroke or not just changed, or else we have no
846 // stroke (in which case whether we have fill or not is significant to
847 // frame bounds) and whether we have fill or not just changed. In either
848 // case we need to reflow so the frame rect is updated.
849 // XXXperf this is a waste on non SVGGeometryFrames.
850 hint |= nsChangeHint_NeedReflow;
852 if (PaintURIChanged(mFill, aNewData.mFill) ||
853 PaintURIChanged(mStroke, aNewData.mStroke)) {
854 hint |= nsChangeHint_UpdateEffects;
858 // Stroke currently contributes to SVGGeometryFrame::mRect, so
859 // we need a reflow here. No intrinsic sizes need to change, so
860 // nsChangeHint_NeedReflow is sufficient.
861 // Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect.
862 // text-anchor and dominant-baseline changes also require a reflow since
863 // they change frames' rects.
864 if (mStrokeWidth != aNewData.mStrokeWidth ||
865 mStrokeMiterlimit != aNewData.mStrokeMiterlimit ||
866 mStrokeLinecap != aNewData.mStrokeLinecap ||
867 mStrokeLinejoin != aNewData.mStrokeLinejoin ||
868 mDominantBaseline != aNewData.mDominantBaseline ||
869 mTextAnchor != aNewData.mTextAnchor) {
870 return hint | nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame;
873 if (hint & nsChangeHint_RepaintFrame) {
874 return hint; // we don't add anything else below
877 if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
878 mClipRule != aNewData.mClipRule ||
879 mColorInterpolation != aNewData.mColorInterpolation ||
880 mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
881 mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
882 mShapeRendering != aNewData.mShapeRendering ||
883 mStrokeDasharray != aNewData.mStrokeDasharray ||
884 mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
885 return hint | nsChangeHint_RepaintFrame;
888 if (!hint) {
889 if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
890 hint = nsChangeHint_NeutralChange;
894 return hint;
897 // --------------------
898 // nsStyleSVGReset
900 nsStyleSVGReset::nsStyleSVGReset()
901 : mX(LengthPercentage::Zero()),
902 mY(LengthPercentage::Zero()),
903 mCx(LengthPercentage::Zero()),
904 mCy(LengthPercentage::Zero()),
905 mRx(NonNegativeLengthPercentageOrAuto::Auto()),
906 mRy(NonNegativeLengthPercentageOrAuto::Auto()),
907 mR(NonNegativeLengthPercentage::Zero()),
908 mMask(nsStyleImageLayers::LayerType::Mask),
909 mClipPath(StyleClipPath::None()),
910 mStopColor(StyleColor::Black()),
911 mFloodColor(StyleColor::Black()),
912 mLightingColor(StyleColor::White()),
913 mStopOpacity(1.0f),
914 mFloodOpacity(1.0f),
915 mVectorEffect(StyleVectorEffect::None),
916 mMaskType(StyleMaskType::Luminance),
917 mD(StyleDProperty::None()) {
918 MOZ_COUNT_CTOR(nsStyleSVGReset);
921 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
922 : mX(aSource.mX),
923 mY(aSource.mY),
924 mCx(aSource.mCx),
925 mCy(aSource.mCy),
926 mRx(aSource.mRx),
927 mRy(aSource.mRy),
928 mR(aSource.mR),
929 mMask(aSource.mMask),
930 mClipPath(aSource.mClipPath),
931 mStopColor(aSource.mStopColor),
932 mFloodColor(aSource.mFloodColor),
933 mLightingColor(aSource.mLightingColor),
934 mStopOpacity(aSource.mStopOpacity),
935 mFloodOpacity(aSource.mFloodOpacity),
936 mVectorEffect(aSource.mVectorEffect),
937 mMaskType(aSource.mMaskType),
938 mD(aSource.mD) {
939 MOZ_COUNT_CTOR(nsStyleSVGReset);
942 void nsStyleSVGReset::TriggerImageLoads(Document& aDocument,
943 const nsStyleSVGReset* aOldStyle) {
944 MOZ_ASSERT(NS_IsMainThread());
945 // NOTE(emilio): we intentionally don't call TriggerImageLoads for clip-path.
947 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
948 auto& image = mMask.mLayers[i].mImage;
949 if (!image.IsImageRequestType()) {
950 continue;
952 const auto* url = image.GetImageRequestURLValue();
953 // If the url is a local ref, it must be a <mask-resource>, so we don't
954 // need to resolve the style image.
955 if (url->IsLocalRef()) {
956 continue;
958 #if 0
959 // XXX The old style system also checks whether this is a reference to
960 // the current document with reference, but it doesn't seem to be a
961 // behavior mentioned anywhere, so we comment out the code for now.
962 nsIURI* docURI = aPresContext->Document()->GetDocumentURI();
963 if (url->EqualsExceptRef(docURI)) {
964 continue;
966 #endif
967 // Otherwise, we may need the image even if it has a reference, in case
968 // the referenced element isn't a valid SVG <mask> element.
969 const auto* oldImage = (aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
970 ? &aOldStyle->mMask.mLayers[i].mImage
971 : nullptr;
973 image.ResolveImage(aDocument, oldImage);
977 nsChangeHint nsStyleSVGReset::CalcDifference(
978 const nsStyleSVGReset& aNewData) const {
979 nsChangeHint hint = nsChangeHint(0);
981 if (mX != aNewData.mX || mY != aNewData.mY || mCx != aNewData.mCx ||
982 mCy != aNewData.mCy || mR != aNewData.mR || mRx != aNewData.mRx ||
983 mRy != aNewData.mRy || mD != aNewData.mD) {
984 hint |= nsChangeHint_InvalidateRenderingObservers | nsChangeHint_NeedReflow;
987 if (mClipPath != aNewData.mClipPath) {
988 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
991 if (mVectorEffect != aNewData.mVectorEffect) {
992 // Stroke currently affects SVGGeometryFrame::mRect, and
993 // vector-effect affect stroke. As a result we need to reflow if
994 // vector-effect changes in order to have SVGGeometryFrame::
995 // ReflowSVG called to update its mRect. No intrinsic sizes need
996 // to change so nsChangeHint_NeedReflow is sufficient.
997 hint |= nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame;
998 } else if (mStopColor != aNewData.mStopColor ||
999 mFloodColor != aNewData.mFloodColor ||
1000 mLightingColor != aNewData.mLightingColor ||
1001 mStopOpacity != aNewData.mStopOpacity ||
1002 mFloodOpacity != aNewData.mFloodOpacity ||
1003 mMaskType != aNewData.mMaskType || mD != aNewData.mD) {
1004 hint |= nsChangeHint_RepaintFrame;
1007 hint |=
1008 mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask);
1010 return hint;
1013 bool nsStyleSVGReset::HasMask() const {
1014 for (uint32_t i = 0; i < mMask.mImageCount; i++) {
1015 if (!mMask.mLayers[i].mImage.IsNone()) {
1016 return true;
1020 return false;
1023 // --------------------
1024 // nsStylePage
1027 nsStylePage::nsStylePage(const nsStylePage& aSrc)
1028 : mSize(aSrc.mSize),
1029 mPage(aSrc.mPage),
1030 mPageOrientation(aSrc.mPageOrientation) {
1031 MOZ_COUNT_CTOR(nsStylePage);
1034 nsChangeHint nsStylePage::CalcDifference(const nsStylePage& aNewData) const {
1035 // Page rule styling only matters when printing or using print preview.
1036 if (aNewData.mSize != mSize || aNewData.mPage != mPage ||
1037 aNewData.mPageOrientation != mPageOrientation) {
1038 return nsChangeHint_NeutralChange;
1040 return nsChangeHint_Empty;
1043 // --------------------
1044 // nsStylePosition
1046 nsStylePosition::nsStylePosition()
1047 : mObjectPosition(Position::FromPercentage(0.5f)),
1048 mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
1049 mWidth(StyleSize::Auto()),
1050 mMinWidth(StyleSize::Auto()),
1051 mMaxWidth(StyleMaxSize::None()),
1052 mHeight(StyleSize::Auto()),
1053 mMinHeight(StyleSize::Auto()),
1054 mMaxHeight(StyleMaxSize::None()),
1055 mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
1056 mAspectRatio(StyleAspectRatio::Auto()),
1057 mGridAutoFlow(StyleGridAutoFlow::ROW),
1058 mMasonryAutoFlow(
1059 {StyleMasonryPlacement::Pack, StyleMasonryItemOrder::DefiniteFirst}),
1060 mAlignContent({StyleAlignFlags::NORMAL}),
1061 mAlignItems({StyleAlignFlags::NORMAL}),
1062 mAlignSelf({StyleAlignFlags::AUTO}),
1063 mJustifyContent({StyleAlignFlags::NORMAL}),
1064 mJustifyItems({{StyleAlignFlags::LEGACY}, {StyleAlignFlags::NORMAL}}),
1065 mJustifySelf({StyleAlignFlags::AUTO}),
1066 mFlexDirection(StyleFlexDirection::Row),
1067 mFlexWrap(StyleFlexWrap::Nowrap),
1068 mObjectFit(StyleObjectFit::Fill),
1069 mBoxSizing(StyleBoxSizing::Content),
1070 mOrder(0),
1071 mFlexGrow(0.0f),
1072 mFlexShrink(1.0f),
1073 mZIndex(StyleZIndex::Auto()),
1074 mGridTemplateColumns(StyleGridTemplateComponent::None()),
1075 mGridTemplateRows(StyleGridTemplateComponent::None()),
1076 mGridTemplateAreas(StyleGridTemplateAreas::None()),
1077 mColumnGap(NonNegativeLengthPercentageOrNormal::Normal()),
1078 mRowGap(NonNegativeLengthPercentageOrNormal::Normal()),
1079 mContainIntrinsicWidth(StyleContainIntrinsicSize::None()),
1080 mContainIntrinsicHeight(StyleContainIntrinsicSize::None()) {
1081 MOZ_COUNT_CTOR(nsStylePosition);
1083 // The initial value of grid-auto-columns and grid-auto-rows is 'auto',
1084 // which computes to 'minmax(auto, auto)'.
1086 // Other members get their default constructors
1087 // which initialize them to representations of their respective initial value.
1088 // mGridTemplate{Rows,Columns}: false and empty arrays for 'none'
1089 // mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto'
1092 nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
1093 : mAlignTracks(aSource.mAlignTracks),
1094 mJustifyTracks(aSource.mJustifyTracks),
1095 mObjectPosition(aSource.mObjectPosition),
1096 mOffset(aSource.mOffset),
1097 mWidth(aSource.mWidth),
1098 mMinWidth(aSource.mMinWidth),
1099 mMaxWidth(aSource.mMaxWidth),
1100 mHeight(aSource.mHeight),
1101 mMinHeight(aSource.mMinHeight),
1102 mMaxHeight(aSource.mMaxHeight),
1103 mFlexBasis(aSource.mFlexBasis),
1104 mGridAutoColumns(aSource.mGridAutoColumns),
1105 mGridAutoRows(aSource.mGridAutoRows),
1106 mAspectRatio(aSource.mAspectRatio),
1107 mGridAutoFlow(aSource.mGridAutoFlow),
1108 mMasonryAutoFlow(aSource.mMasonryAutoFlow),
1109 mAlignContent(aSource.mAlignContent),
1110 mAlignItems(aSource.mAlignItems),
1111 mAlignSelf(aSource.mAlignSelf),
1112 mJustifyContent(aSource.mJustifyContent),
1113 mJustifyItems(aSource.mJustifyItems),
1114 mJustifySelf(aSource.mJustifySelf),
1115 mFlexDirection(aSource.mFlexDirection),
1116 mFlexWrap(aSource.mFlexWrap),
1117 mObjectFit(aSource.mObjectFit),
1118 mBoxSizing(aSource.mBoxSizing),
1119 mOrder(aSource.mOrder),
1120 mFlexGrow(aSource.mFlexGrow),
1121 mFlexShrink(aSource.mFlexShrink),
1122 mZIndex(aSource.mZIndex),
1123 mGridTemplateColumns(aSource.mGridTemplateColumns),
1124 mGridTemplateRows(aSource.mGridTemplateRows),
1125 mGridTemplateAreas(aSource.mGridTemplateAreas),
1126 mGridColumnStart(aSource.mGridColumnStart),
1127 mGridColumnEnd(aSource.mGridColumnEnd),
1128 mGridRowStart(aSource.mGridRowStart),
1129 mGridRowEnd(aSource.mGridRowEnd),
1130 mColumnGap(aSource.mColumnGap),
1131 mRowGap(aSource.mRowGap),
1132 mContainIntrinsicWidth(aSource.mContainIntrinsicWidth),
1133 mContainIntrinsicHeight(aSource.mContainIntrinsicHeight) {
1134 MOZ_COUNT_CTOR(nsStylePosition);
1137 static bool IsAutonessEqual(const StyleRect<LengthPercentageOrAuto>& aSides1,
1138 const StyleRect<LengthPercentageOrAuto>& aSides2) {
1139 for (const auto side : mozilla::AllPhysicalSides()) {
1140 if (aSides1.Get(side).IsAuto() != aSides2.Get(side).IsAuto()) {
1141 return false;
1144 return true;
1147 nsChangeHint nsStylePosition::CalcDifference(
1148 const nsStylePosition& aNewData, const ComputedStyle& aOldStyle) const {
1149 if (mGridTemplateColumns.IsMasonry() !=
1150 aNewData.mGridTemplateColumns.IsMasonry() ||
1151 mGridTemplateRows.IsMasonry() != aNewData.mGridTemplateRows.IsMasonry()) {
1152 // XXXmats this could be optimized to AllReflowHints with a bit of work,
1153 // but I'll assume this is a very rare use case in practice. (bug 1623886)
1154 return nsChangeHint_ReconstructFrame;
1157 nsChangeHint hint = nsChangeHint(0);
1159 // Changes to "z-index" require a repaint.
1160 if (mZIndex != aNewData.mZIndex) {
1161 hint |= nsChangeHint_RepaintFrame;
1164 // Changes to "object-fit" & "object-position" require a repaint. They
1165 // may also require a reflow, if we have a nsSubDocumentFrame, so that we
1166 // can adjust the size & position of the subdocument.
1167 if (mObjectFit != aNewData.mObjectFit ||
1168 mObjectPosition != aNewData.mObjectPosition) {
1169 hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow;
1172 if (mContainIntrinsicWidth != aNewData.mContainIntrinsicWidth ||
1173 mContainIntrinsicHeight != aNewData.mContainIntrinsicHeight) {
1174 hint |= NS_STYLE_HINT_REFLOW;
1177 if (mOrder != aNewData.mOrder) {
1178 // "order" impacts both layout order and stacking order, so we need both a
1179 // reflow and a repaint when it changes. (Technically, we only need a
1180 // reflow if we're in a multi-line flexbox (which we can't be sure about,
1181 // since that's determined by styling on our parent) -- there, "order" can
1182 // affect which flex line we end up on, & hence can affect our sizing by
1183 // changing the group of flex items we're competing with for space.)
1184 return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints;
1187 if (mBoxSizing != aNewData.mBoxSizing) {
1188 // Can affect both widths and heights; just a bad scene.
1189 return hint | nsChangeHint_AllReflowHints;
1192 if (mAlignItems != aNewData.mAlignItems ||
1193 mAlignSelf != aNewData.mAlignSelf ||
1194 mJustifyTracks != aNewData.mJustifyTracks ||
1195 mAlignTracks != aNewData.mAlignTracks) {
1196 return hint | nsChangeHint_AllReflowHints;
1199 // Properties that apply to flex items:
1200 // XXXdholbert These should probably be more targeted (bug 819536)
1201 if (mFlexBasis != aNewData.mFlexBasis || mFlexGrow != aNewData.mFlexGrow ||
1202 mFlexShrink != aNewData.mFlexShrink) {
1203 return hint | nsChangeHint_AllReflowHints;
1206 // Properties that apply to flex containers:
1207 // - flex-direction can swap a flex container between vertical & horizontal.
1208 // - flex-wrap changes whether a flex container's children are wrapped, which
1209 // impacts their sizing/positioning and hence impacts the container's size.
1210 if (mFlexDirection != aNewData.mFlexDirection ||
1211 mFlexWrap != aNewData.mFlexWrap) {
1212 return hint | nsChangeHint_AllReflowHints;
1215 // Properties that apply to grid containers:
1216 // FIXME: only for grid containers
1217 // (ie. 'display: grid' or 'display: inline-grid')
1218 if (mGridTemplateColumns != aNewData.mGridTemplateColumns ||
1219 mGridTemplateRows != aNewData.mGridTemplateRows ||
1220 mGridTemplateAreas != aNewData.mGridTemplateAreas ||
1221 mGridAutoColumns != aNewData.mGridAutoColumns ||
1222 mGridAutoRows != aNewData.mGridAutoRows ||
1223 mGridAutoFlow != aNewData.mGridAutoFlow ||
1224 mMasonryAutoFlow != aNewData.mMasonryAutoFlow) {
1225 return hint | nsChangeHint_AllReflowHints;
1228 // Properties that apply to grid items:
1229 // FIXME: only for grid items
1230 // (ie. parent frame is 'display: grid' or 'display: inline-grid')
1231 if (mGridColumnStart != aNewData.mGridColumnStart ||
1232 mGridColumnEnd != aNewData.mGridColumnEnd ||
1233 mGridRowStart != aNewData.mGridRowStart ||
1234 mGridRowEnd != aNewData.mGridRowEnd ||
1235 mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) {
1236 return hint | nsChangeHint_AllReflowHints;
1239 // Changing 'justify-content/items/self' might affect the positioning,
1240 // but it won't affect any sizing.
1241 if (mJustifyContent != aNewData.mJustifyContent ||
1242 mJustifyItems.computed != aNewData.mJustifyItems.computed ||
1243 mJustifySelf != aNewData.mJustifySelf) {
1244 hint |= nsChangeHint_NeedReflow;
1247 // No need to do anything if specified justify-items changes, as long as the
1248 // computed one (tested above) is unchanged.
1249 if (mJustifyItems.specified != aNewData.mJustifyItems.specified) {
1250 hint |= nsChangeHint_NeutralChange;
1253 // 'align-content' doesn't apply to a single-line flexbox but we don't know
1254 // if we're a flex container at this point so we can't optimize for that.
1255 if (mAlignContent != aNewData.mAlignContent) {
1256 hint |= nsChangeHint_NeedReflow;
1259 bool widthChanged = mWidth != aNewData.mWidth ||
1260 mMinWidth != aNewData.mMinWidth ||
1261 mMaxWidth != aNewData.mMaxWidth;
1262 bool heightChanged = mHeight != aNewData.mHeight ||
1263 mMinHeight != aNewData.mMinHeight ||
1264 mMaxHeight != aNewData.mMaxHeight;
1266 if (widthChanged || heightChanged) {
1267 // It doesn't matter whether we're looking at the old or new visibility
1268 // struct, since a change between vertical and horizontal writing-mode will
1269 // cause a reframe.
1270 const bool isVertical = aOldStyle.StyleVisibility()->mWritingMode !=
1271 StyleWritingModeProperty::HorizontalTb;
1272 if (isVertical ? widthChanged : heightChanged) {
1273 hint |= nsChangeHint_ReflowHintsForBSizeChange;
1275 if (isVertical ? heightChanged : widthChanged) {
1276 hint |= nsChangeHint_ReflowHintsForISizeChange;
1280 if (mAspectRatio != aNewData.mAspectRatio) {
1281 hint |= nsChangeHint_ReflowHintsForISizeChange |
1282 nsChangeHint_ReflowHintsForBSizeChange;
1285 // If any of the offsets have changed, then return the respective hints
1286 // so that we would hopefully be able to avoid reflowing.
1287 // Note that it is possible that we'll need to reflow when processing
1288 // restyles, but we don't have enough information to make a good decision
1289 // right now.
1290 // Don't try to handle changes between "auto" and non-auto efficiently;
1291 // that's tricky to do and will hardly ever be able to avoid a reflow.
1292 if (mOffset != aNewData.mOffset) {
1293 if (IsAutonessEqual(mOffset, aNewData.mOffset)) {
1294 hint |=
1295 nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow;
1296 } else {
1297 hint |=
1298 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
1301 return hint;
1304 const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicBSize(
1305 const WritingMode& aWM) const {
1306 return aWM.IsVertical() ? mContainIntrinsicWidth : mContainIntrinsicHeight;
1309 const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicISize(
1310 const WritingMode& aWM) const {
1311 return aWM.IsVertical() ? mContainIntrinsicHeight : mContainIntrinsicWidth;
1314 StyleAlignSelf nsStylePosition::UsedAlignSelf(
1315 const ComputedStyle* aParent) const {
1316 if (mAlignSelf._0 != StyleAlignFlags::AUTO) {
1317 return mAlignSelf;
1319 if (MOZ_LIKELY(aParent)) {
1320 auto parentAlignItems = aParent->StylePosition()->mAlignItems;
1321 MOZ_ASSERT(!(parentAlignItems._0 & StyleAlignFlags::LEGACY),
1322 "align-items can't have 'legacy'");
1323 return {parentAlignItems._0};
1325 return {StyleAlignFlags::NORMAL};
1328 StyleJustifySelf nsStylePosition::UsedJustifySelf(
1329 const ComputedStyle* aParent) const {
1330 if (mJustifySelf._0 != StyleAlignFlags::AUTO) {
1331 return mJustifySelf;
1333 if (MOZ_LIKELY(aParent)) {
1334 const auto& inheritedJustifyItems =
1335 aParent->StylePosition()->mJustifyItems.computed;
1336 return {inheritedJustifyItems._0 & ~StyleAlignFlags::LEGACY};
1338 return {StyleAlignFlags::NORMAL};
1341 // --------------------
1342 // nsStyleTable
1345 nsStyleTable::nsStyleTable()
1346 : mLayoutStrategy(StyleTableLayout::Auto), mXSpan(1) {
1347 MOZ_COUNT_CTOR(nsStyleTable);
1350 nsStyleTable::nsStyleTable(const nsStyleTable& aSource)
1351 : mLayoutStrategy(aSource.mLayoutStrategy), mXSpan(aSource.mXSpan) {
1352 MOZ_COUNT_CTOR(nsStyleTable);
1355 nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const {
1356 if (mXSpan != aNewData.mXSpan ||
1357 mLayoutStrategy != aNewData.mLayoutStrategy) {
1358 return nsChangeHint_ReconstructFrame;
1360 return nsChangeHint(0);
1363 // -----------------------
1364 // nsStyleTableBorder
1366 nsStyleTableBorder::nsStyleTableBorder()
1367 : mBorderSpacingCol(0),
1368 mBorderSpacingRow(0),
1369 mBorderCollapse(StyleBorderCollapse::Separate),
1370 mCaptionSide(StyleCaptionSide::Top),
1371 mEmptyCells(StyleEmptyCells::Show) {
1372 MOZ_COUNT_CTOR(nsStyleTableBorder);
1375 nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource)
1376 : mBorderSpacingCol(aSource.mBorderSpacingCol),
1377 mBorderSpacingRow(aSource.mBorderSpacingRow),
1378 mBorderCollapse(aSource.mBorderCollapse),
1379 mCaptionSide(aSource.mCaptionSide),
1380 mEmptyCells(aSource.mEmptyCells) {
1381 MOZ_COUNT_CTOR(nsStyleTableBorder);
1384 nsChangeHint nsStyleTableBorder::CalcDifference(
1385 const nsStyleTableBorder& aNewData) const {
1386 // Border-collapse changes need a reframe, because we use a different frame
1387 // class for table cells in the collapsed border model. This is used to
1388 // conserve memory when using the separated border model (collapsed borders
1389 // require extra state to be stored).
1390 if (mBorderCollapse != aNewData.mBorderCollapse) {
1391 return nsChangeHint_ReconstructFrame;
1394 if (mCaptionSide == aNewData.mCaptionSide &&
1395 mBorderSpacingCol == aNewData.mBorderSpacingCol &&
1396 mBorderSpacingRow == aNewData.mBorderSpacingRow) {
1397 if (mEmptyCells == aNewData.mEmptyCells) {
1398 return nsChangeHint(0);
1400 return NS_STYLE_HINT_VISUAL;
1402 return NS_STYLE_HINT_REFLOW;
1405 template <typename T>
1406 static bool GradientItemsAreOpaque(
1407 Span<const StyleGenericGradientItem<StyleColor, T>> aItems) {
1408 for (auto& stop : aItems) {
1409 if (stop.IsInterpolationHint()) {
1410 continue;
1413 auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop()
1414 : stop.AsComplexColorStop().color;
1415 if (color.MaybeTransparent()) {
1416 // We don't know the foreground color here, so if it's being used
1417 // we must assume it might be transparent.
1418 return false;
1422 return true;
1425 template <>
1426 bool StyleGradient::IsOpaque() const {
1427 if (IsLinear()) {
1428 return GradientItemsAreOpaque(AsLinear().items.AsSpan());
1430 if (IsRadial()) {
1431 return GradientItemsAreOpaque(AsRadial().items.AsSpan());
1433 return GradientItemsAreOpaque(AsConic().items.AsSpan());
1436 template <>
1437 bool StyleImage::IsOpaque() const {
1438 if (IsImageSet()) {
1439 return FinalImage().IsOpaque();
1442 if (!IsComplete()) {
1443 return false;
1446 if (IsGradient()) {
1447 return AsGradient()->IsOpaque();
1450 if (IsElement()) {
1451 return false;
1454 MOZ_ASSERT(IsImageRequestType(), "unexpected image type");
1455 MOZ_ASSERT(GetImageRequest(), "should've returned earlier above");
1457 nsCOMPtr<imgIContainer> imageContainer;
1458 GetImageRequest()->GetImage(getter_AddRefs(imageContainer));
1459 MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
1461 return imageContainer->WillDrawOpaqueNow();
1464 template <>
1465 bool StyleImage::IsComplete() const {
1466 switch (tag) {
1467 case Tag::None:
1468 return false;
1469 case Tag::Gradient:
1470 case Tag::Element:
1471 return true;
1472 case Tag::Url: {
1473 if (!IsResolved()) {
1474 return false;
1476 imgRequestProxy* req = GetImageRequest();
1477 if (!req) {
1478 return false;
1480 uint32_t status = imgIRequest::STATUS_ERROR;
1481 return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1482 (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
1483 (status & imgIRequest::STATUS_FRAME_COMPLETE);
1485 case Tag::ImageSet:
1486 return FinalImage().IsComplete();
1487 // Bug 546052 cross-fade not yet implemented.
1488 case Tag::CrossFade:
1489 return true;
1491 MOZ_ASSERT_UNREACHABLE("unexpected image type");
1492 return false;
1495 template <>
1496 bool StyleImage::IsSizeAvailable() const {
1497 switch (tag) {
1498 case Tag::None:
1499 return false;
1500 case Tag::Gradient:
1501 case Tag::Element:
1502 return true;
1503 case Tag::Url: {
1504 imgRequestProxy* req = GetImageRequest();
1505 if (!req) {
1506 return false;
1508 uint32_t status = imgIRequest::STATUS_ERROR;
1509 return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1510 !(status & imgIRequest::STATUS_ERROR) &&
1511 (status & imgIRequest::STATUS_SIZE_AVAILABLE);
1513 case Tag::ImageSet:
1514 return FinalImage().IsSizeAvailable();
1515 case Tag::CrossFade:
1516 // TODO: Bug 546052 cross-fade not yet implemented.
1517 return true;
1519 MOZ_ASSERT_UNREACHABLE("unexpected image type");
1520 return false;
1523 template <>
1524 void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
1525 if (IsResolved()) {
1526 return;
1528 const auto* old = aOld ? aOld->GetImageRequestURLValue() : nullptr;
1529 const auto* url = GetImageRequestURLValue();
1530 // We could avoid this const_cast generating more code but it's not really
1531 // worth it.
1532 const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
1535 template <>
1536 ImageResolution StyleImage::GetResolution(const ComputedStyle& aStyle) const {
1537 ImageResolution resolution;
1538 if (imgRequestProxy* request = GetImageRequest()) {
1539 RefPtr<imgIContainer> image;
1540 request->GetImage(getter_AddRefs(image));
1541 if (image) {
1542 resolution = image->GetResolution();
1545 if (IsImageSet()) {
1546 const auto& set = *AsImageSet();
1547 auto items = set.items.AsSpan();
1548 if (MOZ_LIKELY(set.selected_index < items.Length())) {
1549 float r = items[set.selected_index].resolution._0;
1550 resolution.ScaleBy(r);
1553 if (aStyle.EffectiveZoom() != StyleZoom::ONE) {
1554 resolution.ScaleBy(1.0f / aStyle.EffectiveZoom().ToFloat());
1556 return resolution;
1559 // --------------------
1560 // nsStyleImageLayers
1563 const nsCSSPropertyID nsStyleImageLayers::kBackgroundLayerTable[] = {
1564 eCSSProperty_background, // shorthand
1565 eCSSProperty_background_color, // color
1566 eCSSProperty_background_image, // image
1567 eCSSProperty_background_repeat, // repeat
1568 eCSSProperty_background_position_x, // positionX
1569 eCSSProperty_background_position_y, // positionY
1570 eCSSProperty_background_clip, // clip
1571 eCSSProperty_background_origin, // origin
1572 eCSSProperty_background_size, // size
1573 eCSSProperty_background_attachment, // attachment
1574 eCSSProperty_UNKNOWN, // maskMode
1575 eCSSProperty_UNKNOWN // composite
1578 const nsCSSPropertyID nsStyleImageLayers::kMaskLayerTable[] = {
1579 eCSSProperty_mask, // shorthand
1580 eCSSProperty_UNKNOWN, // color
1581 eCSSProperty_mask_image, // image
1582 eCSSProperty_mask_repeat, // repeat
1583 eCSSProperty_mask_position_x, // positionX
1584 eCSSProperty_mask_position_y, // positionY
1585 eCSSProperty_mask_clip, // clip
1586 eCSSProperty_mask_origin, // origin
1587 eCSSProperty_mask_size, // size
1588 eCSSProperty_UNKNOWN, // attachment
1589 eCSSProperty_mask_mode, // maskMode
1590 eCSSProperty_mask_composite // composite
1593 nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType)
1594 : mAttachmentCount(1),
1595 mClipCount(1),
1596 mOriginCount(1),
1597 mRepeatCount(1),
1598 mPositionXCount(1),
1599 mPositionYCount(1),
1600 mImageCount(1),
1601 mSizeCount(1),
1602 mMaskModeCount(1),
1603 mBlendModeCount(1),
1604 mCompositeCount(1),
1605 mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT) {
1606 // Ensure first layer is initialized as specified layer type
1607 mLayers[0].Initialize(aType);
1610 nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers& aSource)
1611 : mAttachmentCount(aSource.mAttachmentCount),
1612 mClipCount(aSource.mClipCount),
1613 mOriginCount(aSource.mOriginCount),
1614 mRepeatCount(aSource.mRepeatCount),
1615 mPositionXCount(aSource.mPositionXCount),
1616 mPositionYCount(aSource.mPositionYCount),
1617 mImageCount(aSource.mImageCount),
1618 mSizeCount(aSource.mSizeCount),
1619 mMaskModeCount(aSource.mMaskModeCount),
1620 mBlendModeCount(aSource.mBlendModeCount),
1621 mCompositeCount(aSource.mCompositeCount),
1622 mLayers(aSource.mLayers.Clone()) {}
1624 static bool AnyLayerIsElementImage(const nsStyleImageLayers& aLayers) {
1625 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aLayers) {
1626 if (aLayers.mLayers[i].mImage.FinalImage().IsElement()) {
1627 return true;
1630 return false;
1633 nsChangeHint nsStyleImageLayers::CalcDifference(
1634 const nsStyleImageLayers& aNewLayers, LayerType aType) const {
1635 nsChangeHint hint = nsChangeHint(0);
1637 // If the number of visible images changes, then it's easy-peasy.
1638 if (mImageCount != aNewLayers.mImageCount) {
1639 hint |= nsChangeHint_RepaintFrame;
1640 if (aType == nsStyleImageLayers::LayerType::Mask ||
1641 AnyLayerIsElementImage(*this) || AnyLayerIsElementImage(aNewLayers)) {
1642 hint |= nsChangeHint_UpdateEffects;
1644 return hint;
1647 const nsStyleImageLayers& moreLayers =
1648 mLayers.Length() > aNewLayers.mLayers.Length() ? *this : aNewLayers;
1649 const nsStyleImageLayers& lessLayers =
1650 mLayers.Length() > aNewLayers.mLayers.Length() ? aNewLayers : *this;
1652 for (size_t i = 0; i < moreLayers.mLayers.Length(); ++i) {
1653 const Layer& moreLayersLayer = moreLayers.mLayers[i];
1654 if (i < moreLayers.mImageCount) {
1655 // This is a visible image we're diffing, we may need to repaint.
1656 const Layer& lessLayersLayer = lessLayers.mLayers[i];
1657 nsChangeHint layerDifference =
1658 moreLayersLayer.CalcDifference(lessLayersLayer);
1659 if (layerDifference &&
1660 (moreLayersLayer.mImage.FinalImage().IsElement() ||
1661 lessLayersLayer.mImage.FinalImage().IsElement())) {
1662 layerDifference |=
1663 nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
1665 hint |= layerDifference;
1666 continue;
1668 if (hint) {
1669 // If they're different by now, we're done.
1670 return hint;
1672 if (i >= lessLayers.mLayers.Length()) {
1673 // The layer count differs, we know some property has changed, but if we
1674 // got here we know it won't affect rendering.
1675 return nsChangeHint_NeutralChange;
1678 const Layer& lessLayersLayer = lessLayers.mLayers[i];
1679 MOZ_ASSERT(moreLayersLayer.mImage.IsNone());
1680 MOZ_ASSERT(lessLayersLayer.mImage.IsNone());
1681 if (moreLayersLayer.CalcDifference(lessLayersLayer)) {
1682 // We don't care about the difference returned, we know it's not visible,
1683 // but if something changed, then we need to return the neutral change.
1684 return nsChangeHint_NeutralChange;
1688 if (hint) {
1689 // If they're different by now, we're done.
1690 return hint;
1693 // We could have same layers and values, but different count still.
1694 if (mAttachmentCount != aNewLayers.mAttachmentCount ||
1695 mBlendModeCount != aNewLayers.mBlendModeCount ||
1696 mClipCount != aNewLayers.mClipCount ||
1697 mCompositeCount != aNewLayers.mCompositeCount ||
1698 mMaskModeCount != aNewLayers.mMaskModeCount ||
1699 mOriginCount != aNewLayers.mOriginCount ||
1700 mRepeatCount != aNewLayers.mRepeatCount ||
1701 mPositionXCount != aNewLayers.mPositionXCount ||
1702 mPositionYCount != aNewLayers.mPositionYCount ||
1703 mSizeCount != aNewLayers.mSizeCount) {
1704 hint |= nsChangeHint_NeutralChange;
1707 return hint;
1710 nsStyleImageLayers& nsStyleImageLayers::operator=(
1711 const nsStyleImageLayers& aOther) {
1712 mAttachmentCount = aOther.mAttachmentCount;
1713 mClipCount = aOther.mClipCount;
1714 mOriginCount = aOther.mOriginCount;
1715 mRepeatCount = aOther.mRepeatCount;
1716 mPositionXCount = aOther.mPositionXCount;
1717 mPositionYCount = aOther.mPositionYCount;
1718 mImageCount = aOther.mImageCount;
1719 mSizeCount = aOther.mSizeCount;
1720 mMaskModeCount = aOther.mMaskModeCount;
1721 mBlendModeCount = aOther.mBlendModeCount;
1722 mCompositeCount = aOther.mCompositeCount;
1723 mLayers = aOther.mLayers.Clone();
1725 return *this;
1728 bool nsStyleImageLayers::operator==(const nsStyleImageLayers& aOther) const {
1729 if (mAttachmentCount != aOther.mAttachmentCount ||
1730 mClipCount != aOther.mClipCount || mOriginCount != aOther.mOriginCount ||
1731 mRepeatCount != aOther.mRepeatCount ||
1732 mPositionXCount != aOther.mPositionXCount ||
1733 mPositionYCount != aOther.mPositionYCount ||
1734 mImageCount != aOther.mImageCount || mSizeCount != aOther.mSizeCount ||
1735 mMaskModeCount != aOther.mMaskModeCount ||
1736 mBlendModeCount != aOther.mBlendModeCount) {
1737 return false;
1740 if (mLayers.Length() != aOther.mLayers.Length()) {
1741 return false;
1744 for (uint32_t i = 0; i < mLayers.Length(); i++) {
1745 if (mLayers[i].mPosition != aOther.mLayers[i].mPosition ||
1746 mLayers[i].mImage != aOther.mLayers[i].mImage ||
1747 mLayers[i].mSize != aOther.mLayers[i].mSize ||
1748 mLayers[i].mClip != aOther.mLayers[i].mClip ||
1749 mLayers[i].mOrigin != aOther.mLayers[i].mOrigin ||
1750 mLayers[i].mAttachment != aOther.mLayers[i].mAttachment ||
1751 mLayers[i].mBlendMode != aOther.mLayers[i].mBlendMode ||
1752 mLayers[i].mComposite != aOther.mLayers[i].mComposite ||
1753 mLayers[i].mMaskMode != aOther.mLayers[i].mMaskMode ||
1754 mLayers[i].mRepeat != aOther.mLayers[i].mRepeat) {
1755 return false;
1759 return true;
1762 static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
1763 const StyleImage& aImage) {
1764 MOZ_ASSERT(!aImage.IsNone(), "caller should have handled this");
1765 MOZ_ASSERT(!aImage.IsImageSet(), "caller should have handled this");
1767 // Contain and cover straightforwardly depend on frame size.
1768 if (aSize.IsCover() || aSize.IsContain()) {
1769 return true;
1772 MOZ_ASSERT(aSize.IsExplicitSize());
1773 const auto& size = aSize.AsExplicitSize();
1775 // If either dimension contains a non-zero percentage, rendering for that
1776 // dimension straightforwardly depends on frame size.
1777 if (size.width.HasPercent() || size.height.HasPercent()) {
1778 return true;
1781 // If both dimensions are fixed lengths, there's no dependency.
1782 if (!size.width.IsAuto() && !size.height.IsAuto()) {
1783 return false;
1786 // Gradient rendering depends on frame size when auto is involved because
1787 // gradients have no intrinsic ratio or dimensions, and therefore the relevant
1788 // dimension is "treat[ed] as 100%".
1789 if (aImage.IsGradient()) {
1790 return true;
1793 // XXX Element rendering for auto or fixed length doesn't depend on frame size
1794 // according to the spec. However, we don't implement the spec yet, so
1795 // for now we bail and say element() plus auto affects ultimate size.
1796 if (aImage.IsElement()) {
1797 return true;
1800 MOZ_ASSERT(aImage.IsImageRequestType(), "Missed some image");
1801 if (auto* request = aImage.GetImageRequest()) {
1802 nsCOMPtr<imgIContainer> imgContainer;
1803 request->GetImage(getter_AddRefs(imgContainer));
1804 if (imgContainer) {
1805 CSSIntSize imageSize;
1806 AspectRatio imageRatio;
1807 bool hasWidth, hasHeight;
1808 // We could bother getting the right resolution here but it doesn't matter
1809 // since we ignore `imageSize`.
1810 nsLayoutUtils::ComputeSizeForDrawing(imgContainer, ImageResolution(),
1811 imageSize, imageRatio, hasWidth,
1812 hasHeight);
1814 // If the image has a fixed width and height, rendering never depends on
1815 // the frame size.
1816 if (hasWidth && hasHeight) {
1817 return false;
1820 // If the image has an intrinsic ratio, rendering will depend on frame
1821 // size when background-size is all auto.
1822 if (imageRatio) {
1823 return size.width.IsAuto() == size.height.IsAuto();
1826 // Otherwise, rendering depends on frame size when the image dimensions
1827 // and background-size don't complement each other.
1828 return !(hasWidth && size.width.IsLengthPercentage()) &&
1829 !(hasHeight && size.height.IsLengthPercentage());
1833 // Passed the gauntlet: no dependency.
1834 return false;
1837 nsStyleImageLayers::Layer::Layer()
1838 : mImage(StyleImage::None()),
1839 mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(),
1840 LengthPercentageOrAuto::Auto())),
1842 mClip(StyleGeometryBox::BorderBox),
1843 mAttachment(StyleImageLayerAttachment::Scroll),
1844 mBlendMode(StyleBlend::Normal),
1845 mComposite(StyleMaskComposite::Add),
1846 mMaskMode(StyleMaskMode::MatchSource) {}
1848 nsStyleImageLayers::Layer::~Layer() = default;
1850 void nsStyleImageLayers::Layer::Initialize(
1851 nsStyleImageLayers::LayerType aType) {
1852 mPosition = Position::FromPercentage(0.);
1854 if (aType == LayerType::Background) {
1855 mOrigin = StyleGeometryBox::PaddingBox;
1856 } else {
1857 MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type.");
1858 mOrigin = StyleGeometryBox::BorderBox;
1862 bool nsStyleImageLayers::Layer::
1863 RenderingMightDependOnPositioningAreaSizeChange() const {
1864 // Do we even have an image?
1865 if (mImage.IsNone()) {
1866 return false;
1869 return mPosition.DependsOnPositioningAreaSize() ||
1870 SizeDependsOnPositioningAreaSize(mSize, mImage.FinalImage()) ||
1871 mRepeat.DependsOnPositioningAreaSize();
1874 bool nsStyleImageLayers::Layer::operator==(const Layer& aOther) const {
1875 return mAttachment == aOther.mAttachment && mClip == aOther.mClip &&
1876 mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat &&
1877 mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition &&
1878 mSize == aOther.mSize && mImage == aOther.mImage &&
1879 mMaskMode == aOther.mMaskMode && mComposite == aOther.mComposite;
1882 template <class ComputedValueItem>
1883 static void FillImageLayerList(
1884 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
1885 ComputedValueItem nsStyleImageLayers::Layer::*aResultLocation,
1886 uint32_t aItemCount, uint32_t aFillCount) {
1887 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
1888 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
1889 ++sourceLayer, ++destLayer) {
1890 aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation;
1894 // The same as FillImageLayerList, but for values stored in
1895 // layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
1896 static void FillImageLayerPositionCoordList(
1897 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
1898 LengthPercentage Position::*aResultLocation, uint32_t aItemCount,
1899 uint32_t aFillCount) {
1900 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
1901 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
1902 ++sourceLayer, ++destLayer) {
1903 aLayers[destLayer].mPosition.*aResultLocation =
1904 aLayers[sourceLayer].mPosition.*aResultLocation;
1908 void nsStyleImageLayers::FillAllLayers(uint32_t aMaxItemCount) {
1909 // Delete any extra items. We need to keep layers in which any
1910 // property was specified.
1911 mLayers.TruncateLengthNonZero(aMaxItemCount);
1913 uint32_t fillCount = mImageCount;
1914 FillImageLayerList(mLayers, &Layer::mImage, mImageCount, fillCount);
1915 FillImageLayerList(mLayers, &Layer::mRepeat, mRepeatCount, fillCount);
1916 FillImageLayerList(mLayers, &Layer::mAttachment, mAttachmentCount, fillCount);
1917 FillImageLayerList(mLayers, &Layer::mClip, mClipCount, fillCount);
1918 FillImageLayerList(mLayers, &Layer::mBlendMode, mBlendModeCount, fillCount);
1919 FillImageLayerList(mLayers, &Layer::mOrigin, mOriginCount, fillCount);
1920 FillImageLayerPositionCoordList(mLayers, &Position::horizontal,
1921 mPositionXCount, fillCount);
1922 FillImageLayerPositionCoordList(mLayers, &Position::vertical, mPositionYCount,
1923 fillCount);
1924 FillImageLayerList(mLayers, &Layer::mSize, mSizeCount, fillCount);
1925 FillImageLayerList(mLayers, &Layer::mMaskMode, mMaskModeCount, fillCount);
1926 FillImageLayerList(mLayers, &Layer::mComposite, mCompositeCount, fillCount);
1929 static bool UrlValuesEqual(const StyleImage& aImage,
1930 const StyleImage& aOtherImage) {
1931 const auto* url = aImage.GetImageRequestURLValue();
1932 const auto* other = aOtherImage.GetImageRequestURLValue();
1933 return url == other || (url && other && *url == *other);
1936 nsChangeHint nsStyleImageLayers::Layer::CalcDifference(
1937 const nsStyleImageLayers::Layer& aNewLayer) const {
1938 nsChangeHint hint = nsChangeHint(0);
1939 if (!UrlValuesEqual(mImage, aNewLayer.mImage)) {
1940 hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects;
1941 } else if (mAttachment != aNewLayer.mAttachment || mClip != aNewLayer.mClip ||
1942 mOrigin != aNewLayer.mOrigin || mRepeat != aNewLayer.mRepeat ||
1943 mBlendMode != aNewLayer.mBlendMode || mSize != aNewLayer.mSize ||
1944 mImage != aNewLayer.mImage || mMaskMode != aNewLayer.mMaskMode ||
1945 mComposite != aNewLayer.mComposite) {
1946 hint |= nsChangeHint_RepaintFrame;
1949 if (mPosition != aNewLayer.mPosition) {
1950 hint |= nsChangeHint_UpdateBackgroundPosition;
1953 return hint;
1956 // --------------------
1957 // nsStyleBackground
1960 nsStyleBackground::nsStyleBackground()
1961 : mImage(nsStyleImageLayers::LayerType::Background),
1962 mBackgroundColor(StyleColor::Transparent()) {
1963 MOZ_COUNT_CTOR(nsStyleBackground);
1966 nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource)
1967 : mImage(aSource.mImage), mBackgroundColor(aSource.mBackgroundColor) {
1968 MOZ_COUNT_CTOR(nsStyleBackground);
1971 void nsStyleBackground::TriggerImageLoads(Document& aDocument,
1972 const nsStyleBackground* aOldStyle) {
1973 MOZ_ASSERT(NS_IsMainThread());
1974 mImage.ResolveImages(aDocument, aOldStyle ? &aOldStyle->mImage : nullptr);
1977 nsChangeHint nsStyleBackground::CalcDifference(
1978 const nsStyleBackground& aNewData) const {
1979 nsChangeHint hint = nsChangeHint(0);
1980 if (mBackgroundColor != aNewData.mBackgroundColor) {
1981 hint |= nsChangeHint_RepaintFrame;
1984 hint |= mImage.CalcDifference(aNewData.mImage,
1985 nsStyleImageLayers::LayerType::Background);
1987 return hint;
1990 bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const {
1991 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
1992 const nsStyleImageLayers::Layer& layer = mImage.mLayers[i];
1993 if (layer.mAttachment == StyleImageLayerAttachment::Fixed &&
1994 !layer.mImage.IsNone() && !nsLayoutUtils::IsTransformed(aFrame)) {
1995 return true;
1998 return false;
2001 nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const {
2002 return mBackgroundColor.CalcColor(aFrame);
2005 nscolor nsStyleBackground::BackgroundColor(const ComputedStyle* aStyle) const {
2006 return mBackgroundColor.CalcColor(*aStyle);
2009 bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
2010 return IsTransparent(aFrame->Style());
2013 bool nsStyleBackground::IsTransparent(const ComputedStyle* aStyle) const {
2014 return BottomLayer().mImage.IsNone() && mImage.mImageCount == 1 &&
2015 NS_GET_A(BackgroundColor(aStyle)) == 0;
2018 StyleTransition::StyleTransition(const StyleTransition& aCopy) = default;
2020 bool StyleTransition::operator==(const StyleTransition& aOther) const {
2021 return mTimingFunction == aOther.mTimingFunction &&
2022 mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2023 mProperty == aOther.mProperty && mBehavior == aOther.mBehavior;
2026 StyleAnimation::StyleAnimation(const StyleAnimation& aCopy) = default;
2028 bool StyleAnimation::operator==(const StyleAnimation& aOther) const {
2029 return mTimingFunction == aOther.mTimingFunction &&
2030 mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2031 mName == aOther.mName && mDirection == aOther.mDirection &&
2032 mFillMode == aOther.mFillMode && mPlayState == aOther.mPlayState &&
2033 mIterationCount == aOther.mIterationCount &&
2034 mComposition == aOther.mComposition && mTimeline == aOther.mTimeline;
2037 // --------------------
2038 // nsStyleDisplay
2040 nsStyleDisplay::nsStyleDisplay()
2041 : mDisplay(StyleDisplay::Inline),
2042 mOriginalDisplay(StyleDisplay::Inline),
2043 mContentVisibility(StyleContentVisibility::Visible),
2044 mContainerType(StyleContainerType::Normal),
2045 mAppearance(StyleAppearance::None),
2046 mContain(StyleContain::NONE),
2047 mEffectiveContainment(StyleContain::NONE),
2048 mDefaultAppearance(StyleAppearance::None),
2049 mPosition(StylePositionProperty::Static),
2050 mFloat(StyleFloat::None),
2051 mClear(StyleClear::None),
2052 mBreakInside(StyleBreakWithin::Auto),
2053 mBreakBefore(StyleBreakBetween::Auto),
2054 mBreakAfter(StyleBreakBetween::Auto),
2055 mOverflowX(StyleOverflow::Visible),
2056 mOverflowY(StyleOverflow::Visible),
2057 mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
2058 mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
2059 mScrollbarGutter(StyleScrollbarGutter::AUTO),
2060 mResize(StyleResize::None),
2061 mOrient(StyleOrient::Inline),
2062 mIsolation(StyleIsolation::Auto),
2063 mTopLayer(StyleTopLayer::None),
2064 mTouchAction(StyleTouchAction::AUTO),
2065 mScrollBehavior(StyleScrollBehavior::Auto),
2066 mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
2067 mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
2068 mOverflowAnchor(StyleOverflowAnchor::Auto),
2069 mScrollSnapAlign{StyleScrollSnapAlignKeyword::None,
2070 StyleScrollSnapAlignKeyword::None},
2071 mScrollSnapStop{StyleScrollSnapStop::Normal},
2072 mScrollSnapType{StyleScrollSnapAxis::Both,
2073 StyleScrollSnapStrictness::None},
2074 mBackfaceVisibility(StyleBackfaceVisibility::Visible),
2075 mTransformStyle(StyleTransformStyle::Flat),
2076 mTransformBox(StyleTransformBox::ViewBox),
2077 mRotate(StyleRotate::None()),
2078 mTranslate(StyleTranslate::None()),
2079 mScale(StyleScale::None()),
2080 mWillChange{{}, {0}},
2081 mOffsetPath(StyleOffsetPath::None()),
2082 mOffsetDistance(LengthPercentage::Zero()),
2083 mOffsetRotate{true, StyleAngle{0.0}},
2084 mOffsetAnchor(StylePositionOrAuto::Auto()),
2085 mOffsetPosition(StyleOffsetPosition::Normal()),
2086 mTransformOrigin{LengthPercentage::FromPercentage(0.5),
2087 LengthPercentage::FromPercentage(0.5),
2088 {0.}},
2089 mChildPerspective(StylePerspective::None()),
2090 mPerspectiveOrigin(Position::FromPercentage(0.5f)),
2091 mVerticalAlign(
2092 StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)),
2093 mBaselineSource(StyleBaselineSource::Auto),
2094 mWebkitLineClamp(0),
2095 mShapeMargin(LengthPercentage::Zero()),
2096 mShapeOutside(StyleShapeOutside::None()) {
2097 MOZ_COUNT_CTOR(nsStyleDisplay);
2100 nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
2101 : mDisplay(aSource.mDisplay),
2102 mOriginalDisplay(aSource.mOriginalDisplay),
2103 mContentVisibility(aSource.mContentVisibility),
2104 mContainerType(aSource.mContainerType),
2105 mAppearance(aSource.mAppearance),
2106 mContain(aSource.mContain),
2107 mEffectiveContainment(aSource.mEffectiveContainment),
2108 mDefaultAppearance(aSource.mDefaultAppearance),
2109 mPosition(aSource.mPosition),
2110 mFloat(aSource.mFloat),
2111 mClear(aSource.mClear),
2112 mBreakInside(aSource.mBreakInside),
2113 mBreakBefore(aSource.mBreakBefore),
2114 mBreakAfter(aSource.mBreakAfter),
2115 mOverflowX(aSource.mOverflowX),
2116 mOverflowY(aSource.mOverflowY),
2117 mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
2118 mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
2119 mScrollbarGutter(aSource.mScrollbarGutter),
2120 mResize(aSource.mResize),
2121 mOrient(aSource.mOrient),
2122 mIsolation(aSource.mIsolation),
2123 mTopLayer(aSource.mTopLayer),
2124 mTouchAction(aSource.mTouchAction),
2125 mScrollBehavior(aSource.mScrollBehavior),
2126 mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
2127 mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
2128 mOverflowAnchor(aSource.mOverflowAnchor),
2129 mScrollSnapAlign(aSource.mScrollSnapAlign),
2130 mScrollSnapStop(aSource.mScrollSnapStop),
2131 mScrollSnapType(aSource.mScrollSnapType),
2132 mBackfaceVisibility(aSource.mBackfaceVisibility),
2133 mTransformStyle(aSource.mTransformStyle),
2134 mTransformBox(aSource.mTransformBox),
2135 mTransform(aSource.mTransform),
2136 mRotate(aSource.mRotate),
2137 mTranslate(aSource.mTranslate),
2138 mScale(aSource.mScale),
2139 mContainerName(aSource.mContainerName),
2140 mWillChange(aSource.mWillChange),
2141 mOffsetPath(aSource.mOffsetPath),
2142 mOffsetDistance(aSource.mOffsetDistance),
2143 mOffsetRotate(aSource.mOffsetRotate),
2144 mOffsetAnchor(aSource.mOffsetAnchor),
2145 mOffsetPosition(aSource.mOffsetPosition),
2146 mTransformOrigin(aSource.mTransformOrigin),
2147 mChildPerspective(aSource.mChildPerspective),
2148 mPerspectiveOrigin(aSource.mPerspectiveOrigin),
2149 mVerticalAlign(aSource.mVerticalAlign),
2150 mBaselineSource(aSource.mBaselineSource),
2151 mWebkitLineClamp(aSource.mWebkitLineClamp),
2152 mShapeImageThreshold(aSource.mShapeImageThreshold),
2153 mShapeMargin(aSource.mShapeMargin),
2154 mShapeOutside(aSource.mShapeOutside) {
2155 MOZ_COUNT_CTOR(nsStyleDisplay);
2158 void nsStyleDisplay::TriggerImageLoads(Document& aDocument,
2159 const nsStyleDisplay* aOldStyle) {
2160 MOZ_ASSERT(NS_IsMainThread());
2162 if (mShapeOutside.IsImage()) {
2163 const auto* old = aOldStyle && aOldStyle->mShapeOutside.IsImage()
2164 ? &aOldStyle->mShapeOutside.AsImage()
2165 : nullptr;
2166 // Const-cast is ugly but legit, we could avoid it by generating mut-casts
2167 // with cbindgen.
2168 const_cast<StyleImage&>(mShapeOutside.AsImage())
2169 .ResolveImage(aDocument, old);
2173 template <typename TransformLike>
2174 static inline nsChangeHint CompareTransformValues(
2175 const TransformLike& aOldTransform, const TransformLike& aNewTransform) {
2176 nsChangeHint result = nsChangeHint(0);
2178 // Note: If we add a new change hint for transform changes here, we have to
2179 // modify KeyframeEffect::CalculateCumulativeChangeHint too!
2180 if (aOldTransform != aNewTransform) {
2181 result |= nsChangeHint_UpdateTransformLayer;
2182 if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) {
2183 result |= nsChangeHint_UpdatePostTransformOverflow;
2184 } else {
2185 result |= nsChangeHint_UpdateOverflow;
2189 return result;
2192 static inline nsChangeHint CompareMotionValues(
2193 const nsStyleDisplay& aDisplay, const nsStyleDisplay& aNewDisplay) {
2194 if (aDisplay.mOffsetPath == aNewDisplay.mOffsetPath) {
2195 if (aDisplay.mOffsetDistance == aNewDisplay.mOffsetDistance &&
2196 aDisplay.mOffsetRotate == aNewDisplay.mOffsetRotate &&
2197 aDisplay.mOffsetAnchor == aNewDisplay.mOffsetAnchor &&
2198 aDisplay.mOffsetPosition == aNewDisplay.mOffsetPosition) {
2199 return nsChangeHint(0);
2202 // No motion path transform is applied.
2203 if (aDisplay.mOffsetPath.IsNone()) {
2204 return nsChangeHint_NeutralChange;
2208 // TODO: Bug 1482737: This probably doesn't need to UpdateOverflow
2209 // (or UpdateTransformLayer) if there's already a transform.
2210 // Set the same hints as what we use for transform because motion path is
2211 // a kind of transform and will be combined with other transforms.
2212 nsChangeHint result = nsChangeHint_UpdateTransformLayer;
2213 if (!aDisplay.mOffsetPath.IsNone() && !aNewDisplay.mOffsetPath.IsNone()) {
2214 result |= nsChangeHint_UpdatePostTransformOverflow;
2215 } else {
2216 result |= nsChangeHint_UpdateOverflow;
2218 return result;
2221 static bool ScrollbarGenerationChanged(const nsStyleDisplay& aOld,
2222 const nsStyleDisplay& aNew) {
2223 auto changed = [](StyleOverflow aOld, StyleOverflow aNew) {
2224 return aOld != aNew &&
2225 (aOld == StyleOverflow::Hidden || aNew == StyleOverflow::Hidden);
2227 return changed(aOld.mOverflowX, aNew.mOverflowX) ||
2228 changed(aOld.mOverflowY, aNew.mOverflowY);
2231 static bool AppearanceValueAffectsFrames(StyleAppearance aAppearance,
2232 StyleAppearance aDefaultAppearance) {
2233 switch (aAppearance) {
2234 case StyleAppearance::Textfield:
2235 // This is for <input type=number/search> where we allow authors to
2236 // specify a |-moz-appearance:textfield| to get a control without buttons.
2237 // We need to reframe since this affects the spinbox creation in
2238 // nsNumber/SearchControlFrame::CreateAnonymousContent.
2239 return aDefaultAppearance == StyleAppearance::NumberInput ||
2240 aDefaultAppearance == StyleAppearance::Searchfield;
2241 case StyleAppearance::Menulist:
2242 // This affects the menulist button creation.
2243 return aDefaultAppearance == StyleAppearance::Menulist;
2244 default:
2245 return false;
2249 nsChangeHint nsStyleDisplay::CalcDifference(
2250 const nsStyleDisplay& aNewData, const ComputedStyle& aOldStyle) const {
2251 if (mDisplay != aNewData.mDisplay ||
2252 (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None) ||
2253 mTopLayer != aNewData.mTopLayer || mResize != aNewData.mResize) {
2254 return nsChangeHint_ReconstructFrame;
2257 auto oldAppearance = EffectiveAppearance();
2258 auto newAppearance = aNewData.EffectiveAppearance();
2259 if (oldAppearance != newAppearance) {
2260 // Changes to the relevant default appearance changes in
2261 // AppearanceValueRequiresFrameReconstruction require reconstruction on
2262 // their own, so we can just pick either the new or the old.
2263 if (AppearanceValueAffectsFrames(oldAppearance, mDefaultAppearance) ||
2264 AppearanceValueAffectsFrames(newAppearance, mDefaultAppearance)) {
2265 return nsChangeHint_ReconstructFrame;
2269 auto hint = nsChangeHint(0);
2270 const auto containmentDiff =
2271 mEffectiveContainment ^ aNewData.mEffectiveContainment;
2272 if (containmentDiff) {
2273 if (containmentDiff & StyleContain::STYLE) {
2274 // Style containment affects counters so we need to re-frame.
2275 return nsChangeHint_ReconstructFrame;
2277 if (containmentDiff & (StyleContain::PAINT | StyleContain::LAYOUT)) {
2278 // Paint and layout containment boxes are absolutely/fixed positioning
2279 // containers.
2280 hint |= nsChangeHint_UpdateContainingBlock;
2282 // The other container types only need a reflow.
2283 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2285 if (mPosition != aNewData.mPosition) {
2286 if (IsAbsolutelyPositionedStyle() ||
2287 aNewData.IsAbsolutelyPositionedStyle()) {
2288 // This changes our parent relationship on the frame tree and / or needs
2289 // to create a placeholder, so gotta reframe. There are some cases (when
2290 // switching from fixed to absolute or viceversa, if our containing block
2291 // happens to remain the same, i.e., if it has a transform or such) where
2292 // this wouldn't really be needed (though we'd still need to move the
2293 // frame from one child list to another). In any case we don't have a hand
2294 // to that information from here, and it doesn't seem like a case worth
2295 // optimizing for.
2296 return nsChangeHint_ReconstructFrame;
2298 // We start or stop being a containing block for abspos descendants. This
2299 // also causes painting to change, as we'd become a pseudo-stacking context.
2300 if (IsRelativelyOrStickyPositionedStyle() !=
2301 aNewData.IsRelativelyOrStickyPositionedStyle()) {
2302 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_RepaintFrame;
2304 if (IsPositionForcingStackingContext() !=
2305 aNewData.IsPositionForcingStackingContext()) {
2306 hint |= nsChangeHint_RepaintFrame;
2308 // On top of that: if the above ends up not reframing, we need a reflow to
2309 // compute our relative, static or sticky position.
2310 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2313 if (mScrollSnapAlign != aNewData.mScrollSnapAlign ||
2314 mScrollSnapType != aNewData.mScrollSnapType ||
2315 mScrollSnapStop != aNewData.mScrollSnapStop) {
2316 hint |= nsChangeHint_RepaintFrame;
2318 if (mScrollBehavior != aNewData.mScrollBehavior) {
2319 hint |= nsChangeHint_NeutralChange;
2322 if (mOverflowX != aNewData.mOverflowX || mOverflowY != aNewData.mOverflowY) {
2323 const bool isScrollable = IsScrollableOverflow();
2324 if (isScrollable != aNewData.IsScrollableOverflow()) {
2325 // We may need to construct or destroy a scroll frame as a result of this
2326 // change. If we don't, we still need to update our overflow in some cases
2327 // (like svg:foreignObject), which ignore the scrollable-ness of our
2328 // overflow.
2329 hint |= nsChangeHint_ScrollbarChange | nsChangeHint_UpdateOverflow |
2330 nsChangeHint_RepaintFrame;
2331 } else if (isScrollable) {
2332 if (ScrollbarGenerationChanged(*this, aNewData)) {
2333 // We might need to reframe in the case of hidden -> non-hidden case
2334 // though, since nsHTMLScrollFrame::CreateAnonymousContent avoids
2335 // creating scrollbars altogether for overflow: hidden. That seems it
2336 // could create some interesting perf cliffs...
2337 hint |= nsChangeHint_ScrollbarChange;
2338 } else {
2339 // Otherwise, for changes where both overflow values are scrollable,
2340 // means that scrollbars may appear or disappear. We need to reflow,
2341 // since reflow is what determines which scrollbars if any are visible.
2342 hint |= nsChangeHint_ReflowHintsForScrollbarChange;
2344 } else {
2345 // Otherwise this is a change between 'visible' and 'clip'.
2346 // Here only whether we have a 'clip' changes, so just repaint and
2347 // update our overflow areas in that case.
2348 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2352 if (mScrollbarGutter != aNewData.mScrollbarGutter) {
2353 if (IsScrollableOverflow() || aOldStyle.IsRootElementStyle()) {
2354 // Changing scrollbar-gutter affects available inline-size of a inner
2355 // scrolled frame, so we need a reflow for scrollbar change. Note that the
2356 // root is always scrollable in HTML, even if its style doesn't say so.
2357 hint |= nsChangeHint_ReflowHintsForScrollbarChange;
2358 } else {
2359 // scrollbar-gutter only applies to scroll containers.
2360 hint |= nsChangeHint_NeutralChange;
2364 if (mFloat != aNewData.mFloat) {
2365 // Changing which side we're floating on (float:none was handled above).
2366 hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2369 if (mShapeOutside != aNewData.mShapeOutside ||
2370 mShapeMargin != aNewData.mShapeMargin ||
2371 mShapeImageThreshold != aNewData.mShapeImageThreshold) {
2372 if (aNewData.mFloat != StyleFloat::None) {
2373 // If we are floating, and our shape-outside, shape-margin, or
2374 // shape-image-threshold are changed, our descendants are not impacted,
2375 // but our ancestor and siblings are.
2376 hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2377 } else {
2378 // shape-outside or shape-margin or shape-image-threshold changed,
2379 // but we don't need to reflow because we're not floating.
2380 hint |= nsChangeHint_NeutralChange;
2384 if (mWebkitLineClamp != aNewData.mWebkitLineClamp ||
2385 mVerticalAlign != aNewData.mVerticalAlign ||
2386 mBaselineSource != aNewData.mBaselineSource) {
2387 // XXX Can this just be AllReflowHints + RepaintFrame, and be included in
2388 // the block below?
2389 hint |= NS_STYLE_HINT_REFLOW;
2392 // XXX the following is conservative, for now: changing float breaking
2393 // shouldn't necessarily require a repaint, reflow should suffice.
2395 // FIXME(emilio): We definitely change the frame tree in nsCSSFrameConstructor
2396 // based on break-before / break-after... Shouldn't that reframe?
2397 if (mClear != aNewData.mClear || mBreakInside != aNewData.mBreakInside ||
2398 mBreakBefore != aNewData.mBreakBefore ||
2399 mBreakAfter != aNewData.mBreakAfter ||
2400 mAppearance != aNewData.mAppearance ||
2401 mDefaultAppearance != aNewData.mDefaultAppearance ||
2402 mOrient != aNewData.mOrient ||
2403 mOverflowClipBoxBlock != aNewData.mOverflowClipBoxBlock ||
2404 mOverflowClipBoxInline != aNewData.mOverflowClipBoxInline) {
2405 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2408 if (mIsolation != aNewData.mIsolation) {
2409 hint |= nsChangeHint_RepaintFrame;
2412 /* If we've added or removed the transform property, we need to reconstruct
2413 * the frame to add or remove the view object, and also to handle abs-pos and
2414 * fixed-pos containers.
2416 if (HasTransformStyle() != aNewData.HasTransformStyle()) {
2417 hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
2418 } else {
2419 /* Otherwise, if we've kept the property lying around and we already had a
2420 * transform, we need to see whether or not we've changed the transform.
2421 * If so, we need to recompute its overflow rect (which probably changed
2422 * if the transform changed) and to redraw within the bounds of that new
2423 * overflow rect.
2425 * If the property isn't present in either style struct, we still do the
2426 * comparisons but turn all the resulting change hints into
2427 * nsChangeHint_NeutralChange.
2429 nsChangeHint transformHint = CalcTransformPropertyDifference(aNewData);
2431 if (transformHint) {
2432 if (HasTransformStyle()) {
2433 hint |= transformHint;
2434 } else {
2435 hint |= nsChangeHint_NeutralChange;
2440 if (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) {
2441 // A change from/to being a containing block for position:fixed.
2442 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_UpdateOverflow |
2443 nsChangeHint_RepaintFrame;
2444 } else if (mChildPerspective != aNewData.mChildPerspective) {
2445 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2448 // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
2449 // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM
2450 // bit, which in turn handles frame reconstruction for changes in the
2451 // containing block of fixed-positioned elements.
2452 auto willChangeBitsChanged = mWillChange.bits ^ aNewData.mWillChange.bits;
2454 if (willChangeBitsChanged &
2455 (StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL |
2456 StyleWillChangeBits::SCROLL | StyleWillChangeBits::OPACITY |
2457 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::TRANSFORM |
2458 StyleWillChangeBits::Z_INDEX)) {
2459 hint |= nsChangeHint_RepaintFrame;
2462 if (willChangeBitsChanged &
2463 (StyleWillChangeBits::FIXPOS_CB_NON_SVG | StyleWillChangeBits::TRANSFORM |
2464 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::POSITION |
2465 StyleWillChangeBits::CONTAIN)) {
2466 hint |= nsChangeHint_UpdateContainingBlock;
2469 // If touch-action is changed, we need to regenerate the event regions on
2470 // the layers and send it over to the compositor for APZ to handle.
2471 if (mTouchAction != aNewData.mTouchAction) {
2472 hint |= nsChangeHint_RepaintFrame;
2475 // If overscroll-behavior has changed, the changes are picked up
2476 // during a repaint.
2477 if (mOverscrollBehaviorX != aNewData.mOverscrollBehaviorX ||
2478 mOverscrollBehaviorY != aNewData.mOverscrollBehaviorY) {
2479 hint |= nsChangeHint_SchedulePaint;
2482 if (mOriginalDisplay != aNewData.mOriginalDisplay) {
2483 // Our hypothetical box position may have changed.
2485 // Note that it doesn't matter if we look at the old or the new struct,
2486 // since a change on whether we need a hypothetical position would trigger
2487 // reflow anyway.
2488 if (IsAbsolutelyPositionedStyle() &&
2489 aOldStyle.StylePosition()->NeedsHypotheticalPositionIfAbsPos()) {
2490 hint |=
2491 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2492 } else {
2493 hint |= nsChangeHint_NeutralChange;
2497 // Note: Our current behavior for handling changes to the
2498 // transition-duration, transition-delay, and transition-timing-function
2499 // properties is to do nothing. In other words, the transition
2500 // property that matters is what it is when the transition begins, and
2501 // we don't stop a transition later because the transition property
2502 // changed.
2503 // We do handle changes to transition-property, but we don't need to
2504 // bother with anything here, since the transition manager is notified
2505 // of any ComputedStyle change anyway.
2507 // Note: Likewise, for animation-*, the animation manager gets
2508 // notified about every new ComputedStyle constructed, and it uses
2509 // that opportunity to handle dynamic changes appropriately.
2511 // But we still need to return nsChangeHint_NeutralChange for these
2512 // properties, since some data did change in the style struct.
2514 // TODO(emilio): Figure out change hints for container-name, maybe it needs to
2515 // be handled by the style system as a special-case (since it changes
2516 // container-query selection on descendants).
2517 // container-type / contain / content-visibility are handled by the
2518 // mEffectiveContainment check.
2519 if (!hint && (mWillChange != aNewData.mWillChange ||
2520 mOverflowAnchor != aNewData.mOverflowAnchor ||
2521 mContentVisibility != aNewData.mContentVisibility ||
2522 mContainerType != aNewData.mContainerType ||
2523 mContain != aNewData.mContain ||
2524 mContainerName != aNewData.mContainerName)) {
2525 hint |= nsChangeHint_NeutralChange;
2528 return hint;
2531 nsChangeHint nsStyleDisplay::CalcTransformPropertyDifference(
2532 const nsStyleDisplay& aNewData) const {
2533 nsChangeHint transformHint = nsChangeHint(0);
2535 transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
2536 transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
2537 transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
2538 transformHint |= CompareTransformValues(mScale, aNewData.mScale);
2539 transformHint |= CompareMotionValues(*this, aNewData);
2541 if (mTransformOrigin != aNewData.mTransformOrigin) {
2542 transformHint |= nsChangeHint_UpdateTransformLayer |
2543 nsChangeHint_UpdatePostTransformOverflow;
2546 if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
2547 mTransformStyle != aNewData.mTransformStyle ||
2548 mTransformBox != aNewData.mTransformBox) {
2549 transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2552 if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
2553 transformHint |= nsChangeHint_RepaintFrame;
2556 return transformHint;
2559 // --------------------
2560 // nsStyleVisibility
2563 nsStyleVisibility::nsStyleVisibility(const Document& aDocument)
2564 : mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL
2565 ? StyleDirection::Rtl
2566 : StyleDirection::Ltr),
2567 mVisible(StyleVisibility::Visible),
2568 mImageRendering(StyleImageRendering::Auto),
2569 mWritingMode(StyleWritingModeProperty::HorizontalTb),
2570 mTextOrientation(StyleTextOrientation::Mixed),
2571 mMozBoxCollapse(StyleMozBoxCollapse::Flex),
2572 mPrintColorAdjust(StylePrintColorAdjust::Economy),
2573 mImageOrientation(StyleImageOrientation::FromImage) {
2574 MOZ_COUNT_CTOR(nsStyleVisibility);
2577 nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource)
2578 : mDirection(aSource.mDirection),
2579 mVisible(aSource.mVisible),
2580 mImageRendering(aSource.mImageRendering),
2581 mWritingMode(aSource.mWritingMode),
2582 mTextOrientation(aSource.mTextOrientation),
2583 mMozBoxCollapse(aSource.mMozBoxCollapse),
2584 mPrintColorAdjust(aSource.mPrintColorAdjust),
2585 mImageOrientation(aSource.mImageOrientation) {
2586 MOZ_COUNT_CTOR(nsStyleVisibility);
2589 nsChangeHint nsStyleVisibility::CalcDifference(
2590 const nsStyleVisibility& aNewData) const {
2591 nsChangeHint hint = nsChangeHint(0);
2593 if (mDirection != aNewData.mDirection ||
2594 mWritingMode != aNewData.mWritingMode) {
2595 // It's important that a change in mWritingMode results in frame
2596 // reconstruction, because it may affect intrinsic size (see
2597 // nsSubDocumentFrame::GetIntrinsicISize/BSize).
2598 // Also, the used writing-mode value is now a field on nsIFrame and some
2599 // classes (e.g. table rows/cells) copy their value from an ancestor.
2600 return nsChangeHint_ReconstructFrame;
2602 if (mImageOrientation != aNewData.mImageOrientation) {
2603 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2605 if (mVisible != aNewData.mVisible) {
2606 if (mVisible == StyleVisibility::Visible ||
2607 aNewData.mVisible == StyleVisibility::Visible) {
2608 hint |= nsChangeHint_VisibilityChange;
2610 if (mVisible == StyleVisibility::Collapse ||
2611 aNewData.mVisible == StyleVisibility::Collapse) {
2612 hint |= NS_STYLE_HINT_REFLOW;
2613 } else {
2614 hint |= NS_STYLE_HINT_VISUAL;
2617 if (mTextOrientation != aNewData.mTextOrientation ||
2618 mMozBoxCollapse != aNewData.mMozBoxCollapse) {
2619 hint |= NS_STYLE_HINT_REFLOW;
2621 if (mImageRendering != aNewData.mImageRendering) {
2622 hint |= nsChangeHint_RepaintFrame;
2624 if (mPrintColorAdjust != aNewData.mPrintColorAdjust) {
2625 // color-adjust only affects media where dynamic changes can't happen.
2626 hint |= nsChangeHint_NeutralChange;
2628 return hint;
2631 StyleImageOrientation nsStyleVisibility::UsedImageOrientation(
2632 imgIRequest* aRequest, StyleImageOrientation aOrientation) {
2633 if (aOrientation == StyleImageOrientation::FromImage || !aRequest) {
2634 return aOrientation;
2637 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
2638 aRequest->GetTriggeringPrincipal();
2640 // If the request was for a blob, the request may not have a triggering
2641 // principal and we should use the input orientation.
2642 if (!triggeringPrincipal) {
2643 return aOrientation;
2646 nsCOMPtr<nsIURI> uri = aRequest->GetURI();
2647 // If the image request is a data uri, then treat the request as a
2648 // same origin request.
2649 bool isSameOrigin =
2650 uri->SchemeIs("data") || triggeringPrincipal->IsSameOrigin(uri);
2652 // If the image request is a cross-origin request that does not use CORS,
2653 // do not enforce the image orientation found in the style. Use the image
2654 // orientation found in the exif data.
2655 if (!isSameOrigin && !nsLayoutUtils::ImageRequestUsesCORS(aRequest)) {
2656 return StyleImageOrientation::FromImage;
2659 return aOrientation;
2662 //-----------------------
2663 // nsStyleContent
2666 nsStyleContent::nsStyleContent() : mContent(StyleContent::Normal()) {
2667 MOZ_COUNT_CTOR(nsStyleContent);
2670 nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
2671 : mContent(aSource.mContent),
2672 mCounterIncrement(aSource.mCounterIncrement),
2673 mCounterReset(aSource.mCounterReset),
2674 mCounterSet(aSource.mCounterSet) {
2675 MOZ_COUNT_CTOR(nsStyleContent);
2678 nsChangeHint nsStyleContent::CalcDifference(
2679 const nsStyleContent& aNewData) const {
2680 // Unfortunately we need to reframe even if the content lengths are the same;
2681 // a simple reflow will not pick up different text or different image URLs,
2682 // since we set all that up in the CSSFrameConstructor
2683 if (mContent != aNewData.mContent ||
2684 mCounterIncrement != aNewData.mCounterIncrement ||
2685 mCounterReset != aNewData.mCounterReset ||
2686 mCounterSet != aNewData.mCounterSet) {
2687 return nsChangeHint_ReconstructFrame;
2690 return nsChangeHint(0);
2693 void nsStyleContent::TriggerImageLoads(Document& aDoc,
2694 const nsStyleContent* aOld) {
2695 if (!mContent.IsItems()) {
2696 return;
2699 Span<const StyleContentItem> oldItems;
2700 if (aOld && aOld->mContent.IsItems()) {
2701 oldItems = aOld->mContent.AsItems().AsSpan();
2704 auto items = mContent.AsItems().AsSpan();
2706 for (size_t i = 0; i < items.Length(); ++i) {
2707 const auto& item = items[i];
2708 if (!item.IsImage()) {
2709 continue;
2711 const auto& image = item.AsImage();
2712 const auto* oldImage = i < oldItems.Length() && oldItems[i].IsImage()
2713 ? &oldItems[i].AsImage()
2714 : nullptr;
2715 const_cast<StyleImage&>(image).ResolveImage(aDoc, oldImage);
2719 // --------------------
2720 // nsStyleTextReset
2723 nsStyleTextReset::nsStyleTextReset()
2724 : mTextDecorationLine(StyleTextDecorationLine::NONE),
2725 mTextDecorationStyle(StyleTextDecorationStyle::Solid),
2726 mUnicodeBidi(StyleUnicodeBidi::Normal),
2727 mInitialLetterSink(0),
2728 mInitialLetterSize(0.0f),
2729 mTextDecorationColor(StyleColor::CurrentColor()),
2730 mTextDecorationThickness(StyleTextDecorationLength::Auto()) {
2731 MOZ_COUNT_CTOR(nsStyleTextReset);
2734 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
2735 : mTextOverflow(aSource.mTextOverflow),
2736 mTextDecorationLine(aSource.mTextDecorationLine),
2737 mTextDecorationStyle(aSource.mTextDecorationStyle),
2738 mUnicodeBidi(aSource.mUnicodeBidi),
2739 mInitialLetterSink(aSource.mInitialLetterSink),
2740 mInitialLetterSize(aSource.mInitialLetterSize),
2741 mTextDecorationColor(aSource.mTextDecorationColor),
2742 mTextDecorationThickness(aSource.mTextDecorationThickness) {
2743 MOZ_COUNT_CTOR(nsStyleTextReset);
2746 nsChangeHint nsStyleTextReset::CalcDifference(
2747 const nsStyleTextReset& aNewData) const {
2748 if (mUnicodeBidi != aNewData.mUnicodeBidi ||
2749 mInitialLetterSink != aNewData.mInitialLetterSink ||
2750 mInitialLetterSize != aNewData.mInitialLetterSize) {
2751 return NS_STYLE_HINT_REFLOW;
2754 if (mTextDecorationLine != aNewData.mTextDecorationLine ||
2755 mTextDecorationStyle != aNewData.mTextDecorationStyle ||
2756 mTextDecorationThickness != aNewData.mTextDecorationThickness) {
2757 // Changes to our text-decoration line can impact our overflow area &
2758 // also our descendants' overflow areas (particularly for text-frame
2759 // descendants). So, we update those areas & trigger a repaint.
2760 return nsChangeHint_RepaintFrame | nsChangeHint_UpdateSubtreeOverflow |
2761 nsChangeHint_SchedulePaint;
2764 // Repaint for decoration color changes
2765 if (mTextDecorationColor != aNewData.mTextDecorationColor) {
2766 return nsChangeHint_RepaintFrame;
2769 if (mTextOverflow != aNewData.mTextOverflow) {
2770 return nsChangeHint_RepaintFrame;
2773 return nsChangeHint(0);
2776 // --------------------
2777 // nsStyleText
2780 static StyleAbsoluteColor DefaultColor(const Document& aDocument) {
2781 return StyleAbsoluteColor::FromColor(
2782 PreferenceSheet::PrefsFor(aDocument)
2783 .ColorsFor(aDocument.DefaultColorScheme())
2784 .mDefault);
2787 nsStyleText::nsStyleText(const Document& aDocument)
2788 : mColor(DefaultColor(aDocument)),
2789 mForcedColorAdjust(StyleForcedColorAdjust::Auto),
2790 mTextTransform(StyleTextTransform::None()),
2791 mTextAlign(StyleTextAlign::Start),
2792 mTextAlignLast(StyleTextAlignLast::Auto),
2793 mTextJustify(StyleTextJustify::Auto),
2794 mHyphens(StyleHyphens::Manual),
2795 mRubyAlign(StyleRubyAlign::SpaceAround),
2796 mRubyPosition(StyleRubyPosition::AlternateOver),
2797 mTextSizeAdjust(StyleTextSizeAdjust::Auto),
2798 mTextCombineUpright(StyleTextCombineUpright::None),
2799 mMozControlCharacterVisibility(
2800 StaticPrefs::layout_css_control_characters_visible()
2801 ? StyleMozControlCharacterVisibility::Visible
2802 : StyleMozControlCharacterVisibility::Hidden),
2803 mTextRendering(StyleTextRendering::Auto),
2804 mTextEmphasisColor(StyleColor::CurrentColor()),
2805 mWebkitTextFillColor(StyleColor::CurrentColor()),
2806 mWebkitTextStrokeColor(StyleColor::CurrentColor()),
2807 mTabSize(StyleNonNegativeLengthOrNumber::Number(8.f)),
2808 mWordSpacing(LengthPercentage::Zero()),
2809 mLetterSpacing({0.}),
2810 mTextUnderlineOffset(LengthPercentageOrAuto::Auto()),
2811 mTextDecorationSkipInk(StyleTextDecorationSkipInk::Auto),
2812 mTextUnderlinePosition(StyleTextUnderlinePosition::AUTO),
2813 mWebkitTextStrokeWidth(0),
2814 mTextEmphasisStyle(StyleTextEmphasisStyle::None()) {
2815 MOZ_COUNT_CTOR(nsStyleText);
2816 RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
2817 mTextEmphasisPosition =
2818 language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
2819 ? StyleTextEmphasisPosition::UNDER
2820 : StyleTextEmphasisPosition::OVER;
2823 nsStyleText::nsStyleText(const nsStyleText& aSource)
2824 : mColor(aSource.mColor),
2825 mForcedColorAdjust(aSource.mForcedColorAdjust),
2826 mTextTransform(aSource.mTextTransform),
2827 mTextAlign(aSource.mTextAlign),
2828 mTextAlignLast(aSource.mTextAlignLast),
2829 mTextJustify(aSource.mTextJustify),
2830 mWhiteSpaceCollapse(aSource.mWhiteSpaceCollapse),
2831 mTextWrapMode(aSource.mTextWrapMode),
2832 mLineBreak(aSource.mLineBreak),
2833 mWordBreak(aSource.mWordBreak),
2834 mOverflowWrap(aSource.mOverflowWrap),
2835 mHyphens(aSource.mHyphens),
2836 mRubyAlign(aSource.mRubyAlign),
2837 mRubyPosition(aSource.mRubyPosition),
2838 mTextSizeAdjust(aSource.mTextSizeAdjust),
2839 mTextCombineUpright(aSource.mTextCombineUpright),
2840 mMozControlCharacterVisibility(aSource.mMozControlCharacterVisibility),
2841 mTextEmphasisPosition(aSource.mTextEmphasisPosition),
2842 mTextRendering(aSource.mTextRendering),
2843 mTextEmphasisColor(aSource.mTextEmphasisColor),
2844 mWebkitTextFillColor(aSource.mWebkitTextFillColor),
2845 mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor),
2846 mTabSize(aSource.mTabSize),
2847 mWordSpacing(aSource.mWordSpacing),
2848 mLetterSpacing(aSource.mLetterSpacing),
2849 mTextIndent(aSource.mTextIndent),
2850 mTextUnderlineOffset(aSource.mTextUnderlineOffset),
2851 mTextDecorationSkipInk(aSource.mTextDecorationSkipInk),
2852 mTextUnderlinePosition(aSource.mTextUnderlinePosition),
2853 mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth),
2854 mTextShadow(aSource.mTextShadow),
2855 mTextEmphasisStyle(aSource.mTextEmphasisStyle),
2856 mHyphenateCharacter(aSource.mHyphenateCharacter),
2857 mWebkitTextSecurity(aSource.mWebkitTextSecurity),
2858 mTextWrapStyle(aSource.mTextWrapStyle) {
2859 MOZ_COUNT_CTOR(nsStyleText);
2862 nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
2863 if (WhiteSpaceOrNewlineIsSignificant() !=
2864 aNewData.WhiteSpaceOrNewlineIsSignificant()) {
2865 // This may require construction of suppressed text frames
2866 return nsChangeHint_ReconstructFrame;
2869 if (mTextCombineUpright != aNewData.mTextCombineUpright ||
2870 mMozControlCharacterVisibility !=
2871 aNewData.mMozControlCharacterVisibility) {
2872 return nsChangeHint_ReconstructFrame;
2875 if ((mTextAlign != aNewData.mTextAlign) ||
2876 (mTextAlignLast != aNewData.mTextAlignLast) ||
2877 (mTextTransform != aNewData.mTextTransform) ||
2878 (mWhiteSpaceCollapse != aNewData.mWhiteSpaceCollapse) ||
2879 (mTextWrapMode != aNewData.mTextWrapMode) ||
2880 (mLineBreak != aNewData.mLineBreak) ||
2881 (mWordBreak != aNewData.mWordBreak) ||
2882 (mOverflowWrap != aNewData.mOverflowWrap) ||
2883 (mHyphens != aNewData.mHyphens) || (mRubyAlign != aNewData.mRubyAlign) ||
2884 (mRubyPosition != aNewData.mRubyPosition) ||
2885 (mTextSizeAdjust != aNewData.mTextSizeAdjust) ||
2886 (mLetterSpacing != aNewData.mLetterSpacing) ||
2887 (mTextIndent != aNewData.mTextIndent) ||
2888 (mTextJustify != aNewData.mTextJustify) ||
2889 (mWordSpacing != aNewData.mWordSpacing) ||
2890 (mTabSize != aNewData.mTabSize) ||
2891 (mHyphenateCharacter != aNewData.mHyphenateCharacter) ||
2892 (mWebkitTextSecurity != aNewData.mWebkitTextSecurity) ||
2893 (mTextWrapStyle != aNewData.mTextWrapStyle)) {
2894 return NS_STYLE_HINT_REFLOW;
2897 if (HasEffectiveTextEmphasis() != aNewData.HasEffectiveTextEmphasis() ||
2898 (HasEffectiveTextEmphasis() &&
2899 mTextEmphasisPosition != aNewData.mTextEmphasisPosition)) {
2900 // Text emphasis position change could affect line height calculation.
2901 return nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2904 nsChangeHint hint = nsChangeHint(0);
2906 // text-rendering changes require a reflow since they change SVG
2907 // frames' rects.
2908 if (mTextRendering != aNewData.mTextRendering) {
2909 hint |= nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame;
2912 if (mTextShadow != aNewData.mTextShadow ||
2913 mTextEmphasisStyle != aNewData.mTextEmphasisStyle ||
2914 mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth ||
2915 mTextUnderlineOffset != aNewData.mTextUnderlineOffset ||
2916 mTextDecorationSkipInk != aNewData.mTextDecorationSkipInk ||
2917 mTextUnderlinePosition != aNewData.mTextUnderlinePosition) {
2918 hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint |
2919 nsChangeHint_RepaintFrame;
2921 // We don't add any other hints below.
2922 return hint;
2925 if (mColor != aNewData.mColor) {
2926 hint |= nsChangeHint_RepaintFrame;
2929 if (mTextEmphasisColor != aNewData.mTextEmphasisColor ||
2930 mWebkitTextFillColor != aNewData.mWebkitTextFillColor ||
2931 mWebkitTextStrokeColor != aNewData.mWebkitTextStrokeColor) {
2932 hint |= nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame;
2935 if (hint) {
2936 return hint;
2939 if (mTextEmphasisPosition != aNewData.mTextEmphasisPosition ||
2940 mForcedColorAdjust != aNewData.mForcedColorAdjust) {
2941 return nsChangeHint_NeutralChange;
2944 return nsChangeHint(0);
2947 LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
2948 bool noLeftBit = !(mTextEmphasisPosition & StyleTextEmphasisPosition::LEFT);
2949 DebugOnly<bool> noRightBit =
2950 !(mTextEmphasisPosition & StyleTextEmphasisPosition::RIGHT);
2951 bool noOverBit = !(mTextEmphasisPosition & StyleTextEmphasisPosition::OVER);
2952 DebugOnly<bool> noUnderBit =
2953 !(mTextEmphasisPosition & StyleTextEmphasisPosition::UNDER);
2955 MOZ_ASSERT((noOverBit != noUnderBit) &&
2956 ((noLeftBit != noRightBit) || noRightBit));
2957 mozilla::Side side = aWM.IsVertical() ? (noLeftBit ? eSideRight : eSideLeft)
2958 : (noOverBit ? eSideBottom : eSideTop);
2959 LogicalSide result = aWM.LogicalSideForPhysicalSide(side);
2960 MOZ_ASSERT(IsBlock(result));
2961 return result;
2964 //-----------------------
2965 // nsStyleUI
2968 nsStyleUI::nsStyleUI()
2969 : mInert(StyleInert::None),
2970 mMozTheme(StyleMozTheme::Auto),
2971 mUserInput(StyleUserInput::Auto),
2972 mUserModify(StyleUserModify::ReadOnly),
2973 mUserFocus(StyleUserFocus::Normal),
2974 mPointerEvents(StylePointerEvents::Auto),
2975 mCursor{{}, StyleCursorKind::Auto},
2976 mAccentColor(StyleColorOrAuto::Auto()),
2977 mCaretColor(StyleColorOrAuto::Auto()),
2978 mScrollbarColor(StyleScrollbarColor::Auto()),
2979 mColorScheme(StyleColorScheme{{}, {}}) {
2980 MOZ_COUNT_CTOR(nsStyleUI);
2983 nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
2984 : mInert(aSource.mInert),
2985 mMozTheme(aSource.mMozTheme),
2986 mUserInput(aSource.mUserInput),
2987 mUserModify(aSource.mUserModify),
2988 mUserFocus(aSource.mUserFocus),
2989 mPointerEvents(aSource.mPointerEvents),
2990 mCursor(aSource.mCursor),
2991 mAccentColor(aSource.mAccentColor),
2992 mCaretColor(aSource.mCaretColor),
2993 mScrollbarColor(aSource.mScrollbarColor),
2994 mColorScheme(aSource.mColorScheme) {
2995 MOZ_COUNT_CTOR(nsStyleUI);
2998 void nsStyleUI::TriggerImageLoads(Document& aDocument,
2999 const nsStyleUI* aOldStyle) {
3000 MOZ_ASSERT(NS_IsMainThread());
3002 auto cursorImages = mCursor.images.AsSpan();
3003 auto oldCursorImages = aOldStyle ? aOldStyle->mCursor.images.AsSpan()
3004 : Span<const StyleCursorImage>();
3005 for (size_t i = 0; i < cursorImages.Length(); ++i) {
3006 const auto& cursor = cursorImages[i];
3007 const auto* oldCursorImage =
3008 oldCursorImages.Length() > i ? &oldCursorImages[i].image : nullptr;
3009 const_cast<StyleCursorImage&>(cursor).image.ResolveImage(aDocument,
3010 oldCursorImage);
3014 nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const {
3015 // SVGGeometryFrame's mRect depends on stroke _and_ on the value of
3016 // pointer-events. See SVGGeometryFrame::ReflowSVG's use of GetHitTestFlags.
3017 // (Only a reflow, no visual change.)
3019 // pointer-events changes can change event regions overrides on layers and
3020 // so needs a repaint.
3021 const auto kPointerEventsHint =
3022 nsChangeHint_NeedReflow | nsChangeHint_SchedulePaint;
3024 nsChangeHint hint = nsChangeHint(0);
3025 if (mCursor != aNewData.mCursor) {
3026 hint |= nsChangeHint_UpdateCursor;
3029 if (mPointerEvents != aNewData.mPointerEvents) {
3030 hint |= kPointerEventsHint;
3033 if (mUserModify != aNewData.mUserModify) {
3034 hint |= NS_STYLE_HINT_VISUAL;
3037 if (mInert != aNewData.mInert) {
3038 // inert affects pointer-events, user-modify, user-select, user-focus and
3039 // -moz-user-input, do the union of all them (minus
3040 // nsChangeHint_NeutralChange which isn't needed if there's any other hint).
3041 hint |= NS_STYLE_HINT_VISUAL | kPointerEventsHint;
3044 if (mUserFocus != aNewData.mUserFocus || mUserInput != aNewData.mUserInput) {
3045 hint |= nsChangeHint_NeutralChange;
3048 if (mCaretColor != aNewData.mCaretColor ||
3049 mAccentColor != aNewData.mAccentColor ||
3050 mScrollbarColor != aNewData.mScrollbarColor ||
3051 mMozTheme != aNewData.mMozTheme ||
3052 mColorScheme != aNewData.mColorScheme) {
3053 hint |= nsChangeHint_RepaintFrame;
3056 return hint;
3059 //-----------------------
3060 // nsStyleUIReset
3063 nsStyleUIReset::nsStyleUIReset()
3064 : mUserSelect(StyleUserSelect::Auto),
3065 mScrollbarWidth(StyleScrollbarWidth::Auto),
3066 mMozForceBrokenImageIcon(false),
3067 mMozSubtreeHiddenOnlyVisually(false),
3068 mIMEMode(StyleImeMode::Auto),
3069 mWindowDragging(StyleWindowDragging::Default),
3070 mWindowShadow(StyleWindowShadow::Auto),
3071 mWindowOpacity(1.0),
3072 mMozWindowInputRegionMargin(StyleLength::Zero()),
3073 mWindowTransformOrigin{LengthPercentage::FromPercentage(0.5),
3074 LengthPercentage::FromPercentage(0.5),
3075 {0.}},
3076 mTransitions(
3077 nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
3078 mTransitionTimingFunctionCount(1),
3079 mTransitionDurationCount(1),
3080 mTransitionDelayCount(1),
3081 mTransitionPropertyCount(1),
3082 mTransitionBehaviorCount(1),
3083 mAnimations(
3084 nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
3085 mAnimationTimingFunctionCount(1),
3086 mAnimationDurationCount(1),
3087 mAnimationDelayCount(1),
3088 mAnimationNameCount(1),
3089 mAnimationDirectionCount(1),
3090 mAnimationFillModeCount(1),
3091 mAnimationPlayStateCount(1),
3092 mAnimationIterationCountCount(1),
3093 mAnimationCompositionCount(1),
3094 mAnimationTimelineCount(1),
3095 mScrollTimelines(
3096 nsStyleAutoArray<StyleScrollTimeline>::WITH_SINGLE_INITIAL_ELEMENT),
3097 mScrollTimelineNameCount(1),
3098 mScrollTimelineAxisCount(1),
3099 mViewTimelines(
3100 nsStyleAutoArray<StyleViewTimeline>::WITH_SINGLE_INITIAL_ELEMENT),
3101 mViewTimelineNameCount(1),
3102 mViewTimelineAxisCount(1),
3103 mViewTimelineInsetCount(1) {
3104 MOZ_COUNT_CTOR(nsStyleUIReset);
3107 nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
3108 : mUserSelect(aSource.mUserSelect),
3109 mScrollbarWidth(aSource.mScrollbarWidth),
3110 mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon),
3111 mMozSubtreeHiddenOnlyVisually(aSource.mMozSubtreeHiddenOnlyVisually),
3112 mIMEMode(aSource.mIMEMode),
3113 mWindowDragging(aSource.mWindowDragging),
3114 mWindowShadow(aSource.mWindowShadow),
3115 mWindowOpacity(aSource.mWindowOpacity),
3116 mMozWindowInputRegionMargin(aSource.mMozWindowInputRegionMargin),
3117 mMozWindowTransform(aSource.mMozWindowTransform),
3118 mWindowTransformOrigin(aSource.mWindowTransformOrigin),
3119 mTransitions(aSource.mTransitions.Clone()),
3120 mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount),
3121 mTransitionDurationCount(aSource.mTransitionDurationCount),
3122 mTransitionDelayCount(aSource.mTransitionDelayCount),
3123 mTransitionPropertyCount(aSource.mTransitionPropertyCount),
3124 mTransitionBehaviorCount(aSource.mTransitionBehaviorCount),
3125 mAnimations(aSource.mAnimations.Clone()),
3126 mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount),
3127 mAnimationDurationCount(aSource.mAnimationDurationCount),
3128 mAnimationDelayCount(aSource.mAnimationDelayCount),
3129 mAnimationNameCount(aSource.mAnimationNameCount),
3130 mAnimationDirectionCount(aSource.mAnimationDirectionCount),
3131 mAnimationFillModeCount(aSource.mAnimationFillModeCount),
3132 mAnimationPlayStateCount(aSource.mAnimationPlayStateCount),
3133 mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
3134 mAnimationCompositionCount(aSource.mAnimationCompositionCount),
3135 mAnimationTimelineCount(aSource.mAnimationTimelineCount),
3136 mScrollTimelines(aSource.mScrollTimelines.Clone()),
3137 mScrollTimelineNameCount(aSource.mScrollTimelineNameCount),
3138 mScrollTimelineAxisCount(aSource.mScrollTimelineAxisCount),
3139 mViewTimelines(aSource.mViewTimelines.Clone()),
3140 mViewTimelineNameCount(aSource.mViewTimelineNameCount),
3141 mViewTimelineAxisCount(aSource.mViewTimelineAxisCount),
3142 mViewTimelineInsetCount(aSource.mViewTimelineInsetCount) {
3143 MOZ_COUNT_CTOR(nsStyleUIReset);
3146 nsChangeHint nsStyleUIReset::CalcDifference(
3147 const nsStyleUIReset& aNewData) const {
3148 nsChangeHint hint = nsChangeHint(0);
3150 if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) {
3151 hint |= nsChangeHint_ReconstructFrame;
3153 if (mMozSubtreeHiddenOnlyVisually != aNewData.mMozSubtreeHiddenOnlyVisually) {
3154 hint |= nsChangeHint_RepaintFrame;
3156 if (mScrollbarWidth != aNewData.mScrollbarWidth) {
3157 // For scrollbar-width change, we need some special handling similar
3158 // to overflow properties. Specifically, we may need to reconstruct
3159 // the scrollbar or force reflow of the viewport scrollbar.
3160 hint |= nsChangeHint_ScrollbarChange;
3162 if (mWindowShadow != aNewData.mWindowShadow) {
3163 // We really need just an nsChangeHint_SyncFrameView, except
3164 // on an ancestor of the frame, so we get that by doing a
3165 // reflow.
3166 hint |= NS_STYLE_HINT_REFLOW;
3168 if (mUserSelect != aNewData.mUserSelect) {
3169 hint |= NS_STYLE_HINT_VISUAL;
3172 if (mWindowDragging != aNewData.mWindowDragging) {
3173 hint |= nsChangeHint_SchedulePaint;
3176 if (!hint &&
3177 (mTransitions != aNewData.mTransitions ||
3178 mTransitionTimingFunctionCount !=
3179 aNewData.mTransitionTimingFunctionCount ||
3180 mTransitionDurationCount != aNewData.mTransitionDurationCount ||
3181 mTransitionDelayCount != aNewData.mTransitionDelayCount ||
3182 mTransitionPropertyCount != aNewData.mTransitionPropertyCount ||
3183 mTransitionBehaviorCount != aNewData.mTransitionBehaviorCount ||
3184 mAnimations != aNewData.mAnimations ||
3185 mAnimationTimingFunctionCount !=
3186 aNewData.mAnimationTimingFunctionCount ||
3187 mAnimationDurationCount != aNewData.mAnimationDurationCount ||
3188 mAnimationDelayCount != aNewData.mAnimationDelayCount ||
3189 mAnimationNameCount != aNewData.mAnimationNameCount ||
3190 mAnimationDirectionCount != aNewData.mAnimationDirectionCount ||
3191 mAnimationFillModeCount != aNewData.mAnimationFillModeCount ||
3192 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount ||
3193 mAnimationIterationCountCount !=
3194 aNewData.mAnimationIterationCountCount ||
3195 mAnimationCompositionCount != aNewData.mAnimationCompositionCount ||
3196 mAnimationTimelineCount != aNewData.mAnimationTimelineCount ||
3197 mIMEMode != aNewData.mIMEMode ||
3198 mWindowOpacity != aNewData.mWindowOpacity ||
3199 mMozWindowInputRegionMargin != aNewData.mMozWindowInputRegionMargin ||
3200 mMozWindowTransform != aNewData.mMozWindowTransform ||
3201 mScrollTimelines != aNewData.mScrollTimelines ||
3202 mScrollTimelineNameCount != aNewData.mScrollTimelineNameCount ||
3203 mScrollTimelineAxisCount != aNewData.mScrollTimelineAxisCount ||
3204 mViewTimelines != aNewData.mViewTimelines ||
3205 mViewTimelineNameCount != aNewData.mViewTimelineNameCount ||
3206 mViewTimelineAxisCount != aNewData.mViewTimelineAxisCount ||
3207 mViewTimelineInsetCount != aNewData.mViewTimelineInsetCount)) {
3208 hint |= nsChangeHint_NeutralChange;
3211 return hint;
3214 StyleScrollbarWidth nsStyleUIReset::ScrollbarWidth() const {
3215 if (MOZ_UNLIKELY(StaticPrefs::layout_css_scrollbar_width_thin_disabled())) {
3216 if (mScrollbarWidth == StyleScrollbarWidth::Thin) {
3217 return StyleScrollbarWidth::Auto;
3220 return mScrollbarWidth;
3223 //-----------------------
3224 // nsStyleEffects
3227 nsStyleEffects::nsStyleEffects()
3228 : mClip(StyleClipRectOrAuto::Auto()),
3229 mOpacity(1.0f),
3230 mMixBlendMode(StyleBlend::Normal) {
3231 MOZ_COUNT_CTOR(nsStyleEffects);
3234 nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource)
3235 : mFilters(aSource.mFilters),
3236 mBoxShadow(aSource.mBoxShadow),
3237 mBackdropFilters(aSource.mBackdropFilters),
3238 mClip(aSource.mClip),
3239 mOpacity(aSource.mOpacity),
3240 mMixBlendMode(aSource.mMixBlendMode) {
3241 MOZ_COUNT_CTOR(nsStyleEffects);
3244 static bool AnyAutonessChanged(const StyleClipRectOrAuto& aOld,
3245 const StyleClipRectOrAuto& aNew) {
3246 if (aOld.IsAuto() != aNew.IsAuto()) {
3247 return true;
3249 if (aOld.IsAuto()) {
3250 return false;
3252 const auto& oldRect = aOld.AsRect();
3253 const auto& newRect = aNew.AsRect();
3254 return oldRect.top.IsAuto() != newRect.top.IsAuto() ||
3255 oldRect.right.IsAuto() != newRect.right.IsAuto() ||
3256 oldRect.bottom.IsAuto() != newRect.bottom.IsAuto() ||
3257 oldRect.left.IsAuto() != newRect.left.IsAuto();
3260 nsChangeHint nsStyleEffects::CalcDifference(
3261 const nsStyleEffects& aNewData) const {
3262 nsChangeHint hint = nsChangeHint(0);
3264 if (mBoxShadow != aNewData.mBoxShadow) {
3265 // Update overflow regions & trigger DLBI to be sure it's noticed.
3266 // Also request a repaint, since it's possible that only the color
3267 // of the shadow is changing (and UpdateOverflow/SchedulePaint won't
3268 // repaint for that, since they won't know what needs invalidating.)
3269 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
3270 nsChangeHint_RepaintFrame;
3273 if (AnyAutonessChanged(mClip, aNewData.mClip)) {
3274 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
3275 } else if (mClip != aNewData.mClip) {
3276 // If the clip has changed, we just need to update overflow areas. DLBI
3277 // will handle the invalidation.
3278 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint;
3281 if (mOpacity != aNewData.mOpacity) {
3282 hint |= nsChangeHint_UpdateOpacityLayer;
3284 // If we're going from the optimized >=0.99 opacity value to 1.0 or back,
3285 // then repaint the frame because DLBI will not catch the invalidation.
3286 // Otherwise, just update the opacity layer.
3287 if ((mOpacity >= 0.99f && mOpacity < 1.0f && aNewData.mOpacity == 1.0f) ||
3288 (aNewData.mOpacity >= 0.99f && aNewData.mOpacity < 1.0f &&
3289 mOpacity == 1.0f)) {
3290 hint |= nsChangeHint_RepaintFrame;
3291 } else {
3292 if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) {
3293 hint |= nsChangeHint_UpdateUsesOpacity;
3298 if (HasFilters() != aNewData.HasFilters()) {
3299 // A change from/to being a containing block for position:fixed.
3300 hint |= nsChangeHint_UpdateContainingBlock;
3303 if (mFilters != aNewData.mFilters) {
3304 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame |
3305 nsChangeHint_UpdateOverflow;
3308 if (mMixBlendMode != aNewData.mMixBlendMode) {
3309 hint |= nsChangeHint_RepaintFrame;
3312 if (HasBackdropFilters() != aNewData.HasBackdropFilters()) {
3313 // A change from/to being a containing block for position:fixed.
3314 hint |= nsChangeHint_UpdateContainingBlock;
3317 if (mBackdropFilters != aNewData.mBackdropFilters) {
3318 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
3321 return hint;
3324 static bool TransformOperationHasPercent(const StyleTransformOperation& aOp) {
3325 switch (aOp.tag) {
3326 case StyleTransformOperation::Tag::TranslateX:
3327 return aOp.AsTranslateX().HasPercent();
3328 case StyleTransformOperation::Tag::TranslateY:
3329 return aOp.AsTranslateY().HasPercent();
3330 case StyleTransformOperation::Tag::TranslateZ:
3331 return false;
3332 case StyleTransformOperation::Tag::Translate3D: {
3333 const auto& translate = aOp.AsTranslate3D();
3334 // NOTE(emilio): z translation is a `<length>`, so can't have percentages.
3335 return translate._0.HasPercent() || translate._1.HasPercent();
3337 case StyleTransformOperation::Tag::Translate: {
3338 const auto& translate = aOp.AsTranslate();
3339 return translate._0.HasPercent() || translate._1.HasPercent();
3341 case StyleTransformOperation::Tag::AccumulateMatrix: {
3342 const auto& accum = aOp.AsAccumulateMatrix();
3343 return accum.from_list.HasPercent() || accum.to_list.HasPercent();
3345 case StyleTransformOperation::Tag::InterpolateMatrix: {
3346 const auto& interpolate = aOp.AsInterpolateMatrix();
3347 return interpolate.from_list.HasPercent() ||
3348 interpolate.to_list.HasPercent();
3350 case StyleTransformOperation::Tag::Perspective:
3351 case StyleTransformOperation::Tag::RotateX:
3352 case StyleTransformOperation::Tag::RotateY:
3353 case StyleTransformOperation::Tag::RotateZ:
3354 case StyleTransformOperation::Tag::Rotate:
3355 case StyleTransformOperation::Tag::Rotate3D:
3356 case StyleTransformOperation::Tag::SkewX:
3357 case StyleTransformOperation::Tag::SkewY:
3358 case StyleTransformOperation::Tag::Skew:
3359 case StyleTransformOperation::Tag::ScaleX:
3360 case StyleTransformOperation::Tag::ScaleY:
3361 case StyleTransformOperation::Tag::ScaleZ:
3362 case StyleTransformOperation::Tag::Scale:
3363 case StyleTransformOperation::Tag::Scale3D:
3364 case StyleTransformOperation::Tag::Matrix:
3365 case StyleTransformOperation::Tag::Matrix3D:
3366 return false;
3367 default:
3368 MOZ_ASSERT_UNREACHABLE("Unknown transform operation");
3369 return false;
3373 template <>
3374 bool StyleTransform::HasPercent() const {
3375 for (const auto& op : Operations()) {
3376 if (TransformOperationHasPercent(op)) {
3377 return true;
3380 return false;
3383 template <>
3384 void StyleCalcNode::ScaleLengthsBy(float aScale) {
3385 auto ScaleNode = [aScale](const StyleCalcNode& aNode) {
3386 // This const_cast could be removed by generating more mut-casts, if
3387 // needed.
3388 const_cast<StyleCalcNode&>(aNode).ScaleLengthsBy(aScale);
3391 switch (tag) {
3392 case Tag::Leaf: {
3393 const auto& leaf = AsLeaf();
3394 if (leaf.IsLength()) {
3395 // This const_cast could be removed by generating more mut-casts, if
3396 // needed.
3397 const_cast<Length&>(leaf.AsLength()).ScaleBy(aScale);
3399 break;
3401 case Tag::Clamp: {
3402 const auto& clamp = AsClamp();
3403 ScaleNode(*clamp.min);
3404 ScaleNode(*clamp.center);
3405 ScaleNode(*clamp.max);
3406 break;
3408 case Tag::Round: {
3409 const auto& round = AsRound();
3410 ScaleNode(*round.value);
3411 ScaleNode(*round.step);
3412 break;
3414 case Tag::ModRem: {
3415 const auto& modRem = AsModRem();
3416 ScaleNode(*modRem.dividend);
3417 ScaleNode(*modRem.divisor);
3418 break;
3420 case Tag::MinMax: {
3421 for (const auto& child : AsMinMax()._0.AsSpan()) {
3422 ScaleNode(child);
3424 break;
3426 case Tag::Sum: {
3427 for (const auto& child : AsSum().AsSpan()) {
3428 ScaleNode(child);
3430 break;
3432 case Tag::Product: {
3433 for (const auto& child : AsProduct().AsSpan()) {
3434 ScaleNode(child);
3436 break;
3438 case Tag::Negate: {
3439 const auto& negate = AsNegate();
3440 ScaleNode(*negate);
3441 break;
3443 case Tag::Invert: {
3444 const auto& invert = AsInvert();
3445 ScaleNode(*invert);
3446 break;
3448 case Tag::Hypot: {
3449 for (const auto& child : AsHypot().AsSpan()) {
3450 ScaleNode(child);
3452 break;
3454 case Tag::Abs: {
3455 const auto& abs = AsAbs();
3456 ScaleNode(*abs);
3457 break;
3459 case Tag::Sign: {
3460 const auto& sign = AsSign();
3461 ScaleNode(*sign);
3462 break;
3467 bool nsStyleDisplay::PrecludesSizeContainmentOrContentVisibilityWithFrame(
3468 const nsIFrame& aFrame) const {
3469 // The spec says that in the case of SVG, the contain property only applies
3470 // to <svg> elements that have an associated CSS layout box.
3471 // https://drafts.csswg.org/css-contain/#contain-property
3472 // Internal SVG elements do not use the standard CSS box model, and wouldn't
3473 // be affected by size containment. By disabling it we prevent them from
3474 // becoming query containers for size features.
3475 if (aFrame.HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
3476 return true;
3479 // Note: The spec for size containment says it should have no effect
3480 // - on non-atomic, inline-level boxes.
3481 // - on internal ruby boxes.
3482 // - if inner display type is table.
3483 // - on internal table boxes.
3484 // https://drafts.csswg.org/css-contain/#containment-size
3485 bool isNonReplacedInline = aFrame.IsLineParticipant() && !aFrame.IsReplaced();
3486 return isNonReplacedInline || IsInternalRubyDisplayType() ||
3487 DisplayInside() == mozilla::StyleDisplayInside::Table ||
3488 IsInnerTableStyle();
3491 ContainSizeAxes nsStyleDisplay::GetContainSizeAxes(
3492 const nsIFrame& aFrame) const {
3493 // Short circuit for no containment whatsoever
3494 if (MOZ_LIKELY(!mEffectiveContainment)) {
3495 return ContainSizeAxes(false, false);
3498 if (PrecludesSizeContainmentOrContentVisibilityWithFrame(aFrame)) {
3499 return ContainSizeAxes(false, false);
3502 // https://drafts.csswg.org/css-contain-2/#content-visibility
3503 // If this content skips its content via content-visibility, it always has
3504 // size containment.
3505 if (MOZ_LIKELY(!(mEffectiveContainment & StyleContain::SIZE)) &&
3506 MOZ_UNLIKELY(aFrame.HidesContent())) {
3507 return ContainSizeAxes(true, true);
3510 return ContainSizeAxes(
3511 static_cast<bool>(mEffectiveContainment & StyleContain::INLINE_SIZE),
3512 static_cast<bool>(mEffectiveContainment & StyleContain::BLOCK_SIZE));
3515 StyleContentVisibility nsStyleDisplay::ContentVisibility(
3516 const nsIFrame& aFrame) const {
3517 if (MOZ_LIKELY(mContentVisibility == StyleContentVisibility::Visible)) {
3518 return StyleContentVisibility::Visible;
3520 // content-visibility applies to elements for which size containment applies.
3521 // https://drafts.csswg.org/css-contain/#content-visibility
3522 if (PrecludesSizeContainmentOrContentVisibilityWithFrame(aFrame)) {
3523 return StyleContentVisibility::Visible;
3525 return mContentVisibility;
3528 static nscoord Resolve(const StyleContainIntrinsicSize& aSize,
3529 nscoord aNoneValue, const nsIFrame& aFrame,
3530 LogicalAxis aAxis) {
3531 if (aSize.IsNone()) {
3532 return aNoneValue;
3534 if (aSize.IsLength()) {
3535 return aSize.AsLength().ToAppUnits();
3537 MOZ_ASSERT(aSize.HasAuto());
3538 if (const auto* element = Element::FromNodeOrNull(aFrame.GetContent())) {
3539 Maybe<float> lastSize = aAxis == eLogicalAxisBlock
3540 ? element->GetLastRememberedBSize()
3541 : element->GetLastRememberedISize();
3542 if (lastSize && aFrame.HidesContent()) {
3543 return CSSPixel::ToAppUnits(*lastSize);
3546 if (aSize.IsAutoNone()) {
3547 return aNoneValue;
3549 return aSize.AsAutoLength().ToAppUnits();
3552 Maybe<nscoord> ContainSizeAxes::ContainIntrinsicBSize(
3553 const nsIFrame& aFrame, nscoord aNoneValue) const {
3554 if (!mBContained) {
3555 return Nothing();
3557 const StyleContainIntrinsicSize& bSize =
3558 aFrame.StylePosition()->ContainIntrinsicBSize(aFrame.GetWritingMode());
3559 return Some(Resolve(bSize, aNoneValue, aFrame, eLogicalAxisBlock));
3562 Maybe<nscoord> ContainSizeAxes::ContainIntrinsicISize(
3563 const nsIFrame& aFrame, nscoord aNoneValue) const {
3564 if (!mIContained) {
3565 return Nothing();
3567 const StyleContainIntrinsicSize& iSize =
3568 aFrame.StylePosition()->ContainIntrinsicISize(aFrame.GetWritingMode());
3569 return Some(Resolve(iSize, aNoneValue, aFrame, eLogicalAxisInline));
3572 nsSize ContainSizeAxes::ContainSize(const nsSize& aUncontainedSize,
3573 const nsIFrame& aFrame) const {
3574 if (!IsAny()) {
3575 return aUncontainedSize;
3577 if (aFrame.GetWritingMode().IsVertical()) {
3578 return nsSize(
3579 ContainIntrinsicBSize(aFrame).valueOr(aUncontainedSize.Width()),
3580 ContainIntrinsicISize(aFrame).valueOr(aUncontainedSize.Height()));
3582 return nsSize(
3583 ContainIntrinsicISize(aFrame).valueOr(aUncontainedSize.Width()),
3584 ContainIntrinsicBSize(aFrame).valueOr(aUncontainedSize.Height()));
3587 IntrinsicSize ContainSizeAxes::ContainIntrinsicSize(
3588 const IntrinsicSize& aUncontainedSize, const nsIFrame& aFrame) const {
3589 if (!IsAny()) {
3590 return aUncontainedSize;
3592 IntrinsicSize result(aUncontainedSize);
3593 const bool isVerticalWM = aFrame.GetWritingMode().IsVertical();
3594 if (Maybe<nscoord> containBSize = ContainIntrinsicBSize(aFrame)) {
3595 (isVerticalWM ? result.width : result.height) = containBSize;
3597 if (Maybe<nscoord> containISize = ContainIntrinsicISize(aFrame)) {
3598 (isVerticalWM ? result.height : result.width) = containISize;
3600 return result;