Bug 1730256 [wpt PR 30555] - Move getWindowSegments to visualViewport.segments, a...
[gecko.git] / layout / style / nsStyleStruct.cpp
blobda8140bcaa0b6929723aa065865e2f780d98cd06
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/DocGroup.h"
35 #include "mozilla/dom/ImageTracker.h"
36 #include "mozilla/CORSMode.h"
37 #include "mozilla/ClearOnShutdown.h"
38 #include "mozilla/GeckoBindings.h"
39 #include "mozilla/PreferenceSheet.h"
40 #include "mozilla/SchedulerGroup.h"
41 #include "mozilla/StaticPresData.h"
42 #include "mozilla/Likely.h"
43 #include "nsIURI.h"
44 #include "mozilla/dom/Document.h"
45 #include "mozilla/dom/DocumentInlines.h"
46 #include <algorithm>
47 #include "ImageLoader.h"
48 #include "mozilla/StaticPrefs_layout.h"
50 using namespace mozilla;
51 using namespace mozilla::dom;
53 static const nscoord kMediumBorderWidth = nsPresContext::CSSPixelsToAppUnits(3);
55 // We set the size limit of style structs to 504 bytes so that when they
56 // are allocated by Servo side with Arc, the total size doesn't exceed
57 // 512 bytes, which minimizes allocator slop.
58 static constexpr size_t kStyleStructSizeLimit = 504;
60 template <typename Struct, size_t Actual, size_t Limit>
61 struct AssertSizeIsLessThan {
62 static_assert(Actual == sizeof(Struct), "Bogus invocation");
63 static_assert(Actual <= Limit,
64 "Style struct became larger than the size limit");
65 static constexpr bool instantiate = true;
68 #define STYLE_STRUCT(name_) \
69 static_assert(AssertSizeIsLessThan<nsStyle##name_, sizeof(nsStyle##name_), \
70 kStyleStructSizeLimit>::instantiate, \
71 "");
72 #include "nsStyleStructList.h"
73 #undef STYLE_STRUCT
75 bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const {
76 // This very intentionally avoids comparing LoadData and such.
77 const auto& extra = extra_data.get();
78 const auto& otherExtra = aOther.extra_data.get();
79 if (extra.BaseURI() != otherExtra.BaseURI() ||
80 extra.Principal() != otherExtra.Principal() ||
81 cors_mode != aOther.cors_mode) {
82 // NOTE(emilio): This does pointer comparison, but it's what URLValue used
83 // to do. That's ok though since this is only used for style struct diffing.
84 return false;
86 return serialization == aOther.serialization;
89 StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); }
91 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(nsIURI* aURI) const {
92 nsCOMPtr<nsIURI> result = GetURI();
93 if (result && IsLocalRef()) {
94 nsCString ref;
95 result->GetRef(ref);
97 nsresult rv = NS_MutateURI(aURI).SetRef(ref).Finalize(result);
99 if (NS_FAILED(rv)) {
100 // If setting the ref failed, just return the original URI.
101 result = aURI;
104 return result.forget();
107 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
108 const nsIContent* aContent) const {
109 return ResolveLocalRef(aContent->GetBaseURI());
112 void StyleComputedUrl::ResolveImage(Document& aDocument,
113 const StyleComputedUrl* aOldImage) {
114 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
116 StyleLoadData& data = LoadData();
118 MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE));
120 data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE;
122 MOZ_ASSERT(NS_IsMainThread());
124 // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
125 // lack of non-http image caching in imagelib (bug 1406134), which causes
126 // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
127 // 1440305, but that seems too risky, and a lot of work to do before 60.
129 // Once that's fixed, the "old style" argument to TriggerImageLoads can go
130 // away, and same for mSharedCount in the image loader and so on.
131 const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) &&
132 aOldImage && aOldImage->IsImageResolved() &&
133 *this == *aOldImage;
135 RefPtr<imgRequestProxy> request;
136 if (reuseProxy) {
137 request = aOldImage->LoadData().resolved_image;
138 if (request) {
139 css::ImageLoader::NoteSharedLoad(request);
141 } else {
142 request = css::ImageLoader::LoadImage(*this, aDocument);
145 if (!request) {
146 return;
149 data.resolved_image = request.forget().take();
151 // Boost priority now that we know the image is present in the ComputedStyle
152 // of some frame.
153 data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
157 * Runnable to release the image request's mRequestProxy
158 * and mImageTracker on the main thread, and to perform
159 * any necessary unlocking and untracking of the image.
161 class StyleImageRequestCleanupTask final : public mozilla::Runnable {
162 public:
163 explicit StyleImageRequestCleanupTask(StyleLoadData& aData)
164 : mozilla::Runnable("StyleImageRequestCleanupTask"),
165 mRequestProxy(dont_AddRef(aData.resolved_image)) {
166 MOZ_ASSERT(mRequestProxy);
167 aData.resolved_image = nullptr;
170 NS_IMETHOD Run() final {
171 MOZ_ASSERT(NS_IsMainThread());
172 css::ImageLoader::UnloadImage(mRequestProxy);
173 return NS_OK;
176 protected:
177 virtual ~StyleImageRequestCleanupTask() {
178 MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
179 "mRequestProxy destructor need to run on the main thread!");
182 private:
183 // Since we always dispatch this runnable to the main thread, these will be
184 // released on the main thread when the runnable itself is released.
185 RefPtr<imgRequestProxy> mRequestProxy;
188 // This is defined here for parallelism with LoadURI.
189 void Gecko_LoadData_Drop(StyleLoadData* aData) {
190 if (aData->resolved_image) {
191 // We want to dispatch this async to prevent reentrancy issues, as
192 // imgRequestProxy going away can destroy documents, etc, see bug 1677555.
193 auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData);
194 SchedulerGroup::Dispatch(TaskCategory::Other, task.forget());
197 // URIs are safe to refcount from any thread.
198 NS_IF_RELEASE(aData->resolved_uri);
201 // --------------------
202 // nsStyleFont
204 nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
205 : mFont(aSrc.mFont),
206 mSize(aSrc.mSize),
207 mFontSizeFactor(aSrc.mFontSizeFactor),
208 mFontSizeOffset(aSrc.mFontSizeOffset),
209 mFontSizeKeyword(aSrc.mFontSizeKeyword),
210 mMathDepth(aSrc.mMathDepth),
211 mMathVariant(aSrc.mMathVariant),
212 mMathStyle(aSrc.mMathStyle),
213 mMinFontSizeRatio(aSrc.mMinFontSizeRatio),
214 mExplicitLanguage(aSrc.mExplicitLanguage),
215 mAllowZoomAndMinSize(aSrc.mAllowZoomAndMinSize),
216 mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
217 mScriptMinSize(aSrc.mScriptMinSize),
218 mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier),
219 mLanguage(aSrc.mLanguage) {
220 MOZ_COUNT_CTOR(nsStyleFont);
223 nsStyleFont::nsStyleFont(const Document& aDocument)
224 : mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont(
225 StyleGenericFontFamily::None)),
226 mSize(ZoomText(aDocument, mFont.size)),
227 mFontSizeFactor(1.0),
228 mFontSizeOffset{0},
229 mFontSizeKeyword(StyleFontSizeKeyword::Medium),
230 mMathDepth(0),
231 mMathVariant(NS_MATHML_MATHVARIANT_NONE),
232 mMathStyle(NS_STYLE_MATH_STYLE_NORMAL),
233 mMinFontSizeRatio(100), // 100%
234 mExplicitLanguage(false),
235 mAllowZoomAndMinSize(true),
236 mScriptUnconstrainedSize(mSize),
237 mScriptMinSize(Length::FromPixels(
238 CSSPixel::FromPoints(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))),
239 mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER),
240 mLanguage(aDocument.GetLanguageForStyle()) {
241 MOZ_COUNT_CTOR(nsStyleFont);
242 MOZ_ASSERT(NS_IsMainThread());
243 mFont.size = mSize;
244 if (!nsContentUtils::IsChromeDoc(&aDocument)) {
245 Length minimumFontSize =
246 aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize;
247 mFont.size = Length::FromPixels(
248 std::max(mSize.ToCSSPixels(), minimumFontSize.ToCSSPixels()));
252 nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const {
253 MOZ_ASSERT(
254 mAllowZoomAndMinSize == aNewData.mAllowZoomAndMinSize,
255 "expected mAllowZoomAndMinSize to be the same on both nsStyleFonts");
256 if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage ||
257 mExplicitLanguage != aNewData.mExplicitLanguage ||
258 mMathVariant != aNewData.mMathVariant ||
259 mMathStyle != aNewData.mMathStyle ||
260 mMinFontSizeRatio != aNewData.mMinFontSizeRatio) {
261 return NS_STYLE_HINT_REFLOW;
264 switch (mFont.CalcDifference(aNewData.mFont)) {
265 case nsFont::MaxDifference::eLayoutAffecting:
266 return NS_STYLE_HINT_REFLOW;
268 case nsFont::MaxDifference::eVisual:
269 return NS_STYLE_HINT_VISUAL;
271 case nsFont::MaxDifference::eNone:
272 break;
275 // XXX Should any of these cause a non-nsChangeHint_NeutralChange change?
276 if (mMathDepth != aNewData.mMathDepth ||
277 mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize ||
278 mScriptMinSize != aNewData.mScriptMinSize ||
279 mScriptSizeMultiplier != aNewData.mScriptSizeMultiplier) {
280 return nsChangeHint_NeutralChange;
283 return nsChangeHint(0);
286 Length nsStyleFont::ZoomText(const Document& aDocument, Length aSize) {
287 if (auto* pc = aDocument.GetPresContext()) {
288 aSize.ScaleBy(pc->EffectiveTextZoom());
290 return aSize;
293 template <typename T>
294 static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
295 return {aSide, aSide, aSide, aSide};
298 nsStyleMargin::nsStyleMargin(const Document& aDocument)
299 : mMargin(StyleRectWithAllSides(
300 LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Zero()))),
301 mScrollMargin(StyleRectWithAllSides(StyleLength{0.})) {
302 MOZ_COUNT_CTOR(nsStyleMargin);
305 nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
306 : mMargin(aSrc.mMargin), mScrollMargin(aSrc.mScrollMargin) {
307 MOZ_COUNT_CTOR(nsStyleMargin);
310 nsChangeHint nsStyleMargin::CalcDifference(
311 const nsStyleMargin& aNewData) const {
312 nsChangeHint hint = nsChangeHint(0);
314 if (mMargin != aNewData.mMargin) {
315 // Margin differences can't affect descendant intrinsic sizes and
316 // don't need to force children to reflow.
317 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition |
318 nsChangeHint_ClearAncestorIntrinsics;
321 if (mScrollMargin != aNewData.mScrollMargin) {
322 // FIXME: Bug 1530253 Support re-snapping when scroll-margin changes.
323 hint |= nsChangeHint_NeutralChange;
326 return hint;
329 nsStylePadding::nsStylePadding(const Document& aDocument)
330 : mPadding(StyleRectWithAllSides(LengthPercentage::Zero())),
331 mScrollPadding(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())) {
332 MOZ_COUNT_CTOR(nsStylePadding);
335 nsStylePadding::nsStylePadding(const nsStylePadding& aSrc)
336 : mPadding(aSrc.mPadding), mScrollPadding(aSrc.mScrollPadding) {
337 MOZ_COUNT_CTOR(nsStylePadding);
340 nsChangeHint nsStylePadding::CalcDifference(
341 const nsStylePadding& aNewData) const {
342 nsChangeHint hint = nsChangeHint(0);
344 if (mPadding != aNewData.mPadding) {
345 // Padding differences can't affect descendant intrinsic sizes, but do need
346 // to force children to reflow so that we can reposition them, since their
347 // offsets are from our frame bounds but our content rect's position within
348 // those bounds is moving.
349 // FIXME: It would be good to return a weaker hint here that doesn't
350 // force reflow of all descendants, but the hint would need to force
351 // reflow of the frame's children (see how
352 // ReflowInput::InitResizeFlags initializes the inline-resize flag).
353 hint |= NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
356 if (mScrollPadding != aNewData.mScrollPadding) {
357 // FIXME: Bug 1530253 Support re-snapping when scroll-padding changes.
358 hint |= nsChangeHint_NeutralChange;
361 return hint;
364 static nscoord TwipsPerPixel(const Document& aDocument) {
365 auto* pc = aDocument.GetPresContext();
366 return pc ? pc->AppUnitsPerDevPixel() : mozilla::AppUnitsPerCSSPixel();
369 static inline BorderRadius ZeroBorderRadius() {
370 auto zero = LengthPercentage::Zero();
371 return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}};
374 nsStyleBorder::nsStyleBorder(const Document& aDocument)
375 : mBorderRadius(ZeroBorderRadius()),
376 mBorderImageSource(StyleImage::None()),
377 mBorderImageWidth(
378 StyleRectWithAllSides(StyleBorderImageSideWidth::Number(1.))),
379 mBorderImageOutset(
380 StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))),
381 mBorderImageSlice(
382 {StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})),
383 false}),
384 mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
385 mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
386 mFloatEdge(StyleFloatEdge::ContentBox),
387 mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
388 mBorderTopColor(StyleColor::CurrentColor()),
389 mBorderRightColor(StyleColor::CurrentColor()),
390 mBorderBottomColor(StyleColor::CurrentColor()),
391 mBorderLeftColor(StyleColor::CurrentColor()),
392 mComputedBorder(0, 0, 0, 0),
393 mTwipsPerPixel(TwipsPerPixel(aDocument)) {
394 MOZ_COUNT_CTOR(nsStyleBorder);
396 nscoord medium = kMediumBorderWidth;
397 for (const auto side : mozilla::AllPhysicalSides()) {
398 mBorder.Side(side) = medium;
399 mBorderStyle[side] = StyleBorderStyle::None;
403 nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
404 : mBorderRadius(aSrc.mBorderRadius),
405 mBorderImageSource(aSrc.mBorderImageSource),
406 mBorderImageWidth(aSrc.mBorderImageWidth),
407 mBorderImageOutset(aSrc.mBorderImageOutset),
408 mBorderImageSlice(aSrc.mBorderImageSlice),
409 mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
410 mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
411 mFloatEdge(aSrc.mFloatEdge),
412 mBoxDecorationBreak(aSrc.mBoxDecorationBreak),
413 mBorderTopColor(aSrc.mBorderTopColor),
414 mBorderRightColor(aSrc.mBorderRightColor),
415 mBorderBottomColor(aSrc.mBorderBottomColor),
416 mBorderLeftColor(aSrc.mBorderLeftColor),
417 mComputedBorder(aSrc.mComputedBorder),
418 mBorder(aSrc.mBorder),
419 mTwipsPerPixel(aSrc.mTwipsPerPixel) {
420 MOZ_COUNT_CTOR(nsStyleBorder);
421 for (const auto side : mozilla::AllPhysicalSides()) {
422 mBorderStyle[side] = aSrc.mBorderStyle[side];
426 nsStyleBorder::~nsStyleBorder() { MOZ_COUNT_DTOR(nsStyleBorder); }
428 void nsStyleBorder::TriggerImageLoads(Document& aDocument,
429 const nsStyleBorder* aOldStyle) {
430 MOZ_ASSERT(NS_IsMainThread());
432 mBorderImageSource.ResolveImage(
433 aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
436 nsMargin nsStyleBorder::GetImageOutset() const {
437 // We don't check whether there is a border-image (which is OK since
438 // the initial values yields 0 outset) so that we don't have to
439 // reflow to update overflow areas when an image loads.
440 nsMargin outset;
441 for (const auto s : mozilla::AllPhysicalSides()) {
442 const auto& coord = mBorderImageOutset.Get(s);
443 nscoord value;
444 if (coord.IsLength()) {
445 value = coord.AsLength().ToAppUnits();
446 } else {
447 MOZ_ASSERT(coord.IsNumber());
448 value = coord.AsNumber() * mComputedBorder.Side(s);
450 outset.Side(s) = value;
452 return outset;
455 nsChangeHint nsStyleBorder::CalcDifference(
456 const nsStyleBorder& aNewData) const {
457 // FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these
458 // differences should not need to clear descendant intrinsics.
459 // FIXME: It would be good to return a weaker hint for the
460 // GetComputedBorder() differences (and perhaps others) that doesn't
461 // force reflow of all descendants, but the hint would need to force
462 // reflow of the frame's children (see how
463 // ReflowInput::InitResizeFlags initializes the inline-resize flag).
464 if (mTwipsPerPixel != aNewData.mTwipsPerPixel ||
465 GetComputedBorder() != aNewData.GetComputedBorder() ||
466 mFloatEdge != aNewData.mFloatEdge ||
467 mBorderImageOutset != aNewData.mBorderImageOutset ||
468 mBoxDecorationBreak != aNewData.mBoxDecorationBreak) {
469 return NS_STYLE_HINT_REFLOW;
472 for (const auto ix : mozilla::AllPhysicalSides()) {
473 // See the explanation in nsChangeHint.h of
474 // nsChangeHint_BorderStyleNoneChange .
475 // Furthermore, even though we know *this* side is 0 width, just
476 // assume a repaint hint for some other change rather than bother
477 // tracking this result through the rest of the function.
478 if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) {
479 return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange;
483 // Note that mBorderStyle stores not only the border style but also
484 // color-related flags. Given that we've already done an mComputedBorder
485 // comparison, border-style differences can only lead to a repaint hint. So
486 // it's OK to just compare the values directly -- if either the actual
487 // style or the color flags differ we want to repaint.
488 for (const auto ix : mozilla::AllPhysicalSides()) {
489 if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] ||
490 BorderColorFor(ix) != aNewData.BorderColorFor(ix)) {
491 return nsChangeHint_RepaintFrame;
495 // Note that border radius also controls the outline radius if the
496 // layout.css.outline-follows-border-radius.enabled pref is set. Any
497 // optimizations here should apply to both.
498 if (mBorderRadius != aNewData.mBorderRadius) {
499 return nsChangeHint_RepaintFrame;
502 // Loading status of the border image can be accessed in main thread only
503 // while CalcDifference might be executed on a background thread. As a
504 // result, we have to check mBorderImage* fields even before border image was
505 // actually loaded.
506 if (!mBorderImageSource.IsNone() || !aNewData.mBorderImageSource.IsNone()) {
507 if (mBorderImageSource != aNewData.mBorderImageSource ||
508 mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
509 mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
510 mBorderImageSlice != aNewData.mBorderImageSlice ||
511 mBorderImageWidth != aNewData.mBorderImageWidth) {
512 return nsChangeHint_RepaintFrame;
516 // mBorder is the specified border value. Changes to this don't
517 // need any change processing, since we operate on the computed
518 // border values instead.
519 if (mBorder != aNewData.mBorder) {
520 return nsChangeHint_NeutralChange;
523 // mBorderImage* fields are checked only when border-image is not 'none'.
524 if (mBorderImageSource != aNewData.mBorderImageSource ||
525 mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
526 mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
527 mBorderImageSlice != aNewData.mBorderImageSlice ||
528 mBorderImageWidth != aNewData.mBorderImageWidth) {
529 return nsChangeHint_NeutralChange;
532 return nsChangeHint(0);
535 nsStyleOutline::nsStyleOutline(const Document& aDocument)
536 : mOutlineWidth(kMediumBorderWidth),
537 mOutlineOffset({0.0f}),
538 mOutlineColor(StyleColor::CurrentColor()),
539 mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
540 mActualOutlineWidth(0),
541 mTwipsPerPixel(TwipsPerPixel(aDocument)) {
542 MOZ_COUNT_CTOR(nsStyleOutline);
545 nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
546 : mOutlineWidth(aSrc.mOutlineWidth),
547 mOutlineOffset(aSrc.mOutlineOffset),
548 mOutlineColor(aSrc.mOutlineColor),
549 mOutlineStyle(aSrc.mOutlineStyle),
550 mActualOutlineWidth(aSrc.mActualOutlineWidth),
551 mTwipsPerPixel(aSrc.mTwipsPerPixel) {
552 MOZ_COUNT_CTOR(nsStyleOutline);
555 nsChangeHint nsStyleOutline::CalcDifference(
556 const nsStyleOutline& aNewData) const {
557 if (mActualOutlineWidth != aNewData.mActualOutlineWidth ||
558 (mActualOutlineWidth > 0 && mOutlineOffset != aNewData.mOutlineOffset)) {
559 return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
560 nsChangeHint_RepaintFrame;
563 if (mOutlineStyle != aNewData.mOutlineStyle ||
564 mOutlineColor != aNewData.mOutlineColor) {
565 if (mActualOutlineWidth > 0) {
566 return nsChangeHint_RepaintFrame;
568 return nsChangeHint_NeutralChange;
571 if (mOutlineWidth != aNewData.mOutlineWidth ||
572 mOutlineOffset != aNewData.mOutlineOffset ||
573 mTwipsPerPixel != aNewData.mTwipsPerPixel) {
574 return nsChangeHint_NeutralChange;
577 return nsChangeHint(0);
580 // --------------------
581 // nsStyleList
583 nsStyleList::nsStyleList(const Document& aDocument)
584 : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
585 mQuotes(StyleQuotes::Auto()),
586 mListStyleImage(StyleImage::None()),
587 mImageRegion(StyleClipRectOrAuto::Auto()),
588 mMozListReversed(StyleMozListReversed::False) {
589 MOZ_COUNT_CTOR(nsStyleList);
590 MOZ_ASSERT(NS_IsMainThread());
592 mCounterStyle = nsGkAtoms::disc;
595 nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
597 nsStyleList::nsStyleList(const nsStyleList& aSource)
598 : mListStylePosition(aSource.mListStylePosition),
599 mCounterStyle(aSource.mCounterStyle),
600 mQuotes(aSource.mQuotes),
601 mListStyleImage(aSource.mListStyleImage),
602 mImageRegion(aSource.mImageRegion),
603 mMozListReversed(aSource.mMozListReversed) {
604 MOZ_COUNT_CTOR(nsStyleList);
607 void nsStyleList::TriggerImageLoads(Document& aDocument,
608 const nsStyleList* aOldStyle) {
609 MOZ_ASSERT(NS_IsMainThread());
610 mListStyleImage.ResolveImage(
611 aDocument, aOldStyle ? &aOldStyle->mListStyleImage : nullptr);
614 nsChangeHint nsStyleList::CalcDifference(
615 const nsStyleList& aNewData, const nsStyleDisplay& aOldDisplay) const {
616 // If the quotes implementation is ever going to change we might not need
617 // a framechange here and a reflow should be sufficient. See bug 35768.
618 if (mQuotes != aNewData.mQuotes) {
619 return nsChangeHint_ReconstructFrame;
621 nsChangeHint hint = nsChangeHint(0);
622 // Only elements whose display value is list-item can be affected by
623 // list-style-position and list-style-type. If the old display struct
624 // doesn't exist, assume it isn't affected by display value at all,
625 // and thus these properties should not affect it either. This also
626 // relies on that when the display value changes from something else
627 // to list-item, that change itself would cause ReconstructFrame.
628 if (aOldDisplay.IsListItem()) {
629 if (mListStylePosition != aNewData.mListStylePosition ||
630 mCounterStyle != aNewData.mCounterStyle ||
631 mListStyleImage != aNewData.mListStyleImage) {
632 return nsChangeHint_ReconstructFrame;
634 } else if (mListStylePosition != aNewData.mListStylePosition ||
635 mCounterStyle != aNewData.mCounterStyle) {
636 hint = nsChangeHint_NeutralChange;
638 // This is an internal UA-sheet property that is true only for <ol reversed>
639 // so hopefully it changes rarely.
640 if (mMozListReversed != aNewData.mMozListReversed) {
641 return NS_STYLE_HINT_REFLOW;
643 // list-style-image and -moz-image-region may affect some XUL elements
644 // regardless of display value, so we still need to check them.
645 if (mListStyleImage != aNewData.mListStyleImage) {
646 return NS_STYLE_HINT_REFLOW;
648 if (mImageRegion != aNewData.mImageRegion) {
649 nsRect region = GetImageRegion();
650 nsRect newRegion = aNewData.GetImageRegion();
651 if (region.width != newRegion.width || region.height != newRegion.height) {
652 return NS_STYLE_HINT_REFLOW;
654 return NS_STYLE_HINT_VISUAL;
656 return hint;
659 already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
660 if (!mListStyleImage.IsUrl()) {
661 return nullptr;
664 return do_AddRef(mListStyleImage.AsUrl().GetURI());
667 // --------------------
668 // nsStyleXUL
670 nsStyleXUL::nsStyleXUL(const Document& aDocument)
671 : mBoxFlex(0.0f),
672 mBoxOrdinal(1),
673 mBoxAlign(StyleBoxAlign::Stretch),
674 mBoxDirection(StyleBoxDirection::Normal),
675 mBoxOrient(StyleBoxOrient::Horizontal),
676 mBoxPack(StyleBoxPack::Start) {
677 MOZ_COUNT_CTOR(nsStyleXUL);
680 nsStyleXUL::~nsStyleXUL() { MOZ_COUNT_DTOR(nsStyleXUL); }
682 nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource)
683 : mBoxFlex(aSource.mBoxFlex),
684 mBoxOrdinal(aSource.mBoxOrdinal),
685 mBoxAlign(aSource.mBoxAlign),
686 mBoxDirection(aSource.mBoxDirection),
687 mBoxOrient(aSource.mBoxOrient),
688 mBoxPack(aSource.mBoxPack) {
689 MOZ_COUNT_CTOR(nsStyleXUL);
692 nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const {
693 if (mBoxAlign == aNewData.mBoxAlign &&
694 mBoxDirection == aNewData.mBoxDirection &&
695 mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient &&
696 mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal) {
697 return nsChangeHint(0);
699 if (mBoxOrdinal != aNewData.mBoxOrdinal) {
700 return nsChangeHint_ReconstructFrame;
702 return NS_STYLE_HINT_REFLOW;
705 // --------------------
706 // nsStyleColumn
708 /* static */ const uint32_t nsStyleColumn::kMaxColumnCount;
709 /* static */ const uint32_t nsStyleColumn::kColumnCountAuto;
711 nsStyleColumn::nsStyleColumn(const Document& aDocument)
712 : mColumnWidth(LengthOrAuto::Auto()),
713 mColumnRuleColor(StyleColor::CurrentColor()),
714 mColumnRuleStyle(StyleBorderStyle::None),
715 mColumnRuleWidth(kMediumBorderWidth),
716 mTwipsPerPixel(TwipsPerPixel(aDocument)) {
717 MOZ_COUNT_CTOR(nsStyleColumn);
720 nsStyleColumn::~nsStyleColumn() { MOZ_COUNT_DTOR(nsStyleColumn); }
722 nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource)
723 : mColumnCount(aSource.mColumnCount),
724 mColumnWidth(aSource.mColumnWidth),
725 mColumnRuleColor(aSource.mColumnRuleColor),
726 mColumnRuleStyle(aSource.mColumnRuleStyle),
727 mColumnFill(aSource.mColumnFill),
728 mColumnSpan(aSource.mColumnSpan),
729 mColumnRuleWidth(aSource.mColumnRuleWidth),
730 mTwipsPerPixel(aSource.mTwipsPerPixel) {
731 MOZ_COUNT_CTOR(nsStyleColumn);
734 nsChangeHint nsStyleColumn::CalcDifference(
735 const nsStyleColumn& aNewData) const {
736 if (mColumnWidth.IsAuto() != aNewData.mColumnWidth.IsAuto() ||
737 mColumnCount != aNewData.mColumnCount ||
738 mColumnSpan != aNewData.mColumnSpan) {
739 // We force column count changes to do a reframe, because it's tricky to
740 // handle some edge cases where the column count gets smaller and content
741 // overflows.
742 // XXX not ideal
743 return nsChangeHint_ReconstructFrame;
746 if (mColumnWidth != aNewData.mColumnWidth ||
747 mColumnFill != aNewData.mColumnFill) {
748 return NS_STYLE_HINT_REFLOW;
751 if (GetComputedColumnRuleWidth() != aNewData.GetComputedColumnRuleWidth() ||
752 mColumnRuleStyle != aNewData.mColumnRuleStyle ||
753 mColumnRuleColor != aNewData.mColumnRuleColor) {
754 return NS_STYLE_HINT_VISUAL;
757 // XXX Is it right that we never check mTwipsPerPixel to return a
758 // non-nsChangeHint_NeutralChange hint?
759 if (mColumnRuleWidth != aNewData.mColumnRuleWidth ||
760 mTwipsPerPixel != aNewData.mTwipsPerPixel) {
761 return nsChangeHint_NeutralChange;
764 return nsChangeHint(0);
767 using SVGPaintFallback = StyleGenericSVGPaintFallback<StyleColor>;
769 // --------------------
770 // nsStyleSVG
772 nsStyleSVG::nsStyleSVG(const Document& aDocument)
773 : mFill{StyleSVGPaintKind::Color(StyleColor::Black()),
774 SVGPaintFallback::Unset()},
775 mStroke{StyleSVGPaintKind::None(), SVGPaintFallback::Unset()},
776 mMarkerEnd(StyleUrlOrNone::None()),
777 mMarkerMid(StyleUrlOrNone::None()),
778 mMarkerStart(StyleUrlOrNone::None()),
779 mMozContextProperties{{}, {0}},
780 mStrokeDasharray(StyleSVGStrokeDashArray::Values({})),
781 mStrokeDashoffset(
782 StyleSVGLength::LengthPercentage(LengthPercentage::Zero())),
783 mStrokeWidth(
784 StyleSVGWidth::LengthPercentage(LengthPercentage::FromPixels(1.0f))),
785 mFillOpacity(StyleSVGOpacity::Opacity(1.0f)),
786 mStrokeMiterlimit(4.0f),
787 mStrokeOpacity(StyleSVGOpacity::Opacity(1.0f)),
788 mClipRule(StyleFillRule::Nonzero),
789 mColorInterpolation(StyleColorInterpolation::Srgb),
790 mColorInterpolationFilters(StyleColorInterpolation::Linearrgb),
791 mFillRule(StyleFillRule::Nonzero),
792 mPaintOrder(0),
793 mShapeRendering(StyleShapeRendering::Auto),
794 mStrokeLinecap(StyleStrokeLinecap::Butt),
795 mStrokeLinejoin(StyleStrokeLinejoin::Miter),
796 mDominantBaseline(StyleDominantBaseline::Auto),
797 mTextAnchor(StyleTextAnchor::Start) {
798 MOZ_COUNT_CTOR(nsStyleSVG);
801 nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); }
803 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
804 : mFill(aSource.mFill),
805 mStroke(aSource.mStroke),
806 mMarkerEnd(aSource.mMarkerEnd),
807 mMarkerMid(aSource.mMarkerMid),
808 mMarkerStart(aSource.mMarkerStart),
809 mMozContextProperties(aSource.mMozContextProperties),
810 mStrokeDasharray(aSource.mStrokeDasharray),
811 mStrokeDashoffset(aSource.mStrokeDashoffset),
812 mStrokeWidth(aSource.mStrokeWidth),
813 mFillOpacity(aSource.mFillOpacity),
814 mStrokeMiterlimit(aSource.mStrokeMiterlimit),
815 mStrokeOpacity(aSource.mStrokeOpacity),
816 mClipRule(aSource.mClipRule),
817 mColorInterpolation(aSource.mColorInterpolation),
818 mColorInterpolationFilters(aSource.mColorInterpolationFilters),
819 mFillRule(aSource.mFillRule),
820 mPaintOrder(aSource.mPaintOrder),
821 mShapeRendering(aSource.mShapeRendering),
822 mStrokeLinecap(aSource.mStrokeLinecap),
823 mStrokeLinejoin(aSource.mStrokeLinejoin),
824 mDominantBaseline(aSource.mDominantBaseline),
825 mTextAnchor(aSource.mTextAnchor) {
826 MOZ_COUNT_CTOR(nsStyleSVG);
829 static bool PaintURIChanged(const StyleSVGPaint& aPaint1,
830 const StyleSVGPaint& aPaint2) {
831 if (aPaint1.kind.IsPaintServer() != aPaint2.kind.IsPaintServer()) {
832 return true;
834 return aPaint1.kind.IsPaintServer() &&
835 aPaint1.kind.AsPaintServer() != aPaint2.kind.AsPaintServer();
838 nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const {
839 nsChangeHint hint = nsChangeHint(0);
841 if (mMarkerEnd != aNewData.mMarkerEnd || mMarkerMid != aNewData.mMarkerMid ||
842 mMarkerStart != aNewData.mMarkerStart) {
843 // Markers currently contribute to SVGGeometryFrame::mRect,
844 // so we need a reflow as well as a repaint. No intrinsic sizes need
845 // to change, so nsChangeHint_NeedReflow is sufficient.
846 return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow |
847 nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085
848 nsChangeHint_RepaintFrame;
851 if (mFill != aNewData.mFill || mStroke != aNewData.mStroke ||
852 mFillOpacity != aNewData.mFillOpacity ||
853 mStrokeOpacity != aNewData.mStrokeOpacity) {
854 hint |= nsChangeHint_RepaintFrame;
855 if (HasStroke() != aNewData.HasStroke() ||
856 (!HasStroke() && HasFill() != aNewData.HasFill())) {
857 // Frame bounds and overflow rects depend on whether we "have" fill or
858 // stroke. Whether we have stroke or not just changed, or else we have no
859 // stroke (in which case whether we have fill or not is significant to
860 // frame bounds) and whether we have fill or not just changed. In either
861 // case we need to reflow so the frame rect is updated.
862 // XXXperf this is a waste on non SVGGeometryFrames.
863 hint |= nsChangeHint_NeedReflow |
864 nsChangeHint_NeedDirtyReflow; // XXX remove me: bug 876085
866 if (PaintURIChanged(mFill, aNewData.mFill) ||
867 PaintURIChanged(mStroke, aNewData.mStroke)) {
868 hint |= nsChangeHint_UpdateEffects;
872 // Stroke currently contributes to SVGGeometryFrame::mRect, so
873 // we need a reflow here. No intrinsic sizes need to change, so
874 // nsChangeHint_NeedReflow is sufficient.
875 // Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect.
876 // text-anchor and dominant-baseline changes also require a reflow since
877 // they change frames' rects.
878 if (mStrokeWidth != aNewData.mStrokeWidth ||
879 mStrokeMiterlimit != aNewData.mStrokeMiterlimit ||
880 mStrokeLinecap != aNewData.mStrokeLinecap ||
881 mStrokeLinejoin != aNewData.mStrokeLinejoin ||
882 mDominantBaseline != aNewData.mDominantBaseline ||
883 mTextAnchor != aNewData.mTextAnchor) {
884 return hint | nsChangeHint_NeedReflow |
885 nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085
886 nsChangeHint_RepaintFrame;
889 if (hint & nsChangeHint_RepaintFrame) {
890 return hint; // we don't add anything else below
893 if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
894 mClipRule != aNewData.mClipRule ||
895 mColorInterpolation != aNewData.mColorInterpolation ||
896 mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
897 mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
898 mShapeRendering != aNewData.mShapeRendering ||
899 mStrokeDasharray != aNewData.mStrokeDasharray ||
900 mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
901 return hint | nsChangeHint_RepaintFrame;
904 if (!hint) {
905 if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
906 hint = nsChangeHint_NeutralChange;
910 return hint;
913 // --------------------
914 // nsStyleSVGReset
916 nsStyleSVGReset::nsStyleSVGReset(const Document& aDocument)
917 : mX(LengthPercentage::Zero()),
918 mY(LengthPercentage::Zero()),
919 mCx(LengthPercentage::Zero()),
920 mCy(LengthPercentage::Zero()),
921 mRx(NonNegativeLengthPercentageOrAuto::Auto()),
922 mRy(NonNegativeLengthPercentageOrAuto::Auto()),
923 mR(NonNegativeLengthPercentage::Zero()),
924 mMask(nsStyleImageLayers::LayerType::Mask),
925 mClipPath(StyleClipPath::None()),
926 mStopColor(StyleColor::Black()),
927 mFloodColor(StyleColor::Black()),
928 mLightingColor(StyleColor::White()),
929 mStopOpacity(1.0f),
930 mFloodOpacity(1.0f),
931 mVectorEffect(StyleVectorEffect::None),
932 mMaskType(StyleMaskType::Luminance),
933 mD(StyleDProperty::None()) {
934 MOZ_COUNT_CTOR(nsStyleSVGReset);
937 nsStyleSVGReset::~nsStyleSVGReset() { MOZ_COUNT_DTOR(nsStyleSVGReset); }
939 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
940 : mX(aSource.mX),
941 mY(aSource.mY),
942 mCx(aSource.mCx),
943 mCy(aSource.mCy),
944 mRx(aSource.mRx),
945 mRy(aSource.mRy),
946 mR(aSource.mR),
947 mMask(aSource.mMask),
948 mClipPath(aSource.mClipPath),
949 mStopColor(aSource.mStopColor),
950 mFloodColor(aSource.mFloodColor),
951 mLightingColor(aSource.mLightingColor),
952 mStopOpacity(aSource.mStopOpacity),
953 mFloodOpacity(aSource.mFloodOpacity),
954 mVectorEffect(aSource.mVectorEffect),
955 mMaskType(aSource.mMaskType),
956 mD(aSource.mD) {
957 MOZ_COUNT_CTOR(nsStyleSVGReset);
960 void nsStyleSVGReset::TriggerImageLoads(Document& aDocument,
961 const nsStyleSVGReset* aOldStyle) {
962 MOZ_ASSERT(NS_IsMainThread());
963 // NOTE(emilio): we intentionally don't call TriggerImageLoads for clip-path.
965 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
966 auto& image = mMask.mLayers[i].mImage;
967 if (!image.IsImageRequestType()) {
968 continue;
970 const auto* url = image.GetImageRequestURLValue();
971 // If the url is a local ref, it must be a <mask-resource>, so we don't
972 // need to resolve the style image.
973 if (url->IsLocalRef()) {
974 continue;
976 #if 0
977 // XXX The old style system also checks whether this is a reference to
978 // the current document with reference, but it doesn't seem to be a
979 // behavior mentioned anywhere, so we comment out the code for now.
980 nsIURI* docURI = aPresContext->Document()->GetDocumentURI();
981 if (url->EqualsExceptRef(docURI)) {
982 continue;
984 #endif
985 // Otherwise, we may need the image even if it has a reference, in case
986 // the referenced element isn't a valid SVG <mask> element.
987 const auto* oldImage = (aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
988 ? &aOldStyle->mMask.mLayers[i].mImage
989 : nullptr;
991 image.ResolveImage(aDocument, oldImage);
995 nsChangeHint nsStyleSVGReset::CalcDifference(
996 const nsStyleSVGReset& aNewData) const {
997 nsChangeHint hint = nsChangeHint(0);
999 if (mX != aNewData.mX || mY != aNewData.mY || mCx != aNewData.mCx ||
1000 mCy != aNewData.mCy || mR != aNewData.mR || mRx != aNewData.mRx ||
1001 mRy != aNewData.mRy || mD != aNewData.mD) {
1002 hint |= nsChangeHint_InvalidateRenderingObservers | nsChangeHint_NeedReflow;
1005 if (mClipPath != aNewData.mClipPath) {
1006 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
1009 if (mVectorEffect != aNewData.mVectorEffect) {
1010 // Stroke currently affects SVGGeometryFrame::mRect, and
1011 // vector-effect affect stroke. As a result we need to reflow if
1012 // vector-effect changes in order to have SVGGeometryFrame::
1013 // ReflowSVG called to update its mRect. No intrinsic sizes need
1014 // to change so nsChangeHint_NeedReflow is sufficient.
1015 hint |= nsChangeHint_NeedReflow |
1016 nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085
1017 nsChangeHint_RepaintFrame;
1018 } else if (mStopColor != aNewData.mStopColor ||
1019 mFloodColor != aNewData.mFloodColor ||
1020 mLightingColor != aNewData.mLightingColor ||
1021 mStopOpacity != aNewData.mStopOpacity ||
1022 mFloodOpacity != aNewData.mFloodOpacity ||
1023 mMaskType != aNewData.mMaskType || mD != aNewData.mD) {
1024 hint |= nsChangeHint_RepaintFrame;
1027 hint |=
1028 mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask);
1030 return hint;
1033 bool nsStyleSVGReset::HasMask() const {
1034 for (uint32_t i = 0; i < mMask.mImageCount; i++) {
1035 if (!mMask.mLayers[i].mImage.IsNone()) {
1036 return true;
1040 return false;
1043 // --------------------
1044 // nsStylePage
1047 nsChangeHint nsStylePage::CalcDifference(const nsStylePage& aNewData) const {
1048 // Page rule styling only matters when printing or using print preview.
1049 if (aNewData.mSize != mSize) {
1050 return nsChangeHint_NeutralChange;
1052 return nsChangeHint_Empty;
1055 // --------------------
1056 // nsStylePosition
1058 nsStylePosition::nsStylePosition(const Document& aDocument)
1059 : mObjectPosition(Position::FromPercentage(0.5f)),
1060 mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
1061 mWidth(StyleSize::Auto()),
1062 mMinWidth(StyleSize::Auto()),
1063 mMaxWidth(StyleMaxSize::None()),
1064 mHeight(StyleSize::Auto()),
1065 mMinHeight(StyleSize::Auto()),
1066 mMaxHeight(StyleMaxSize::None()),
1067 mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
1068 mAspectRatio(StyleAspectRatio::Auto()),
1069 mGridAutoFlow(StyleGridAutoFlow::ROW),
1070 mMasonryAutoFlow(NS_STYLE_MASONRY_AUTO_FLOW_INITIAL_VALUE),
1071 mAlignContent({StyleAlignFlags::NORMAL}),
1072 mAlignItems({StyleAlignFlags::NORMAL}),
1073 mAlignSelf({StyleAlignFlags::AUTO}),
1074 mJustifyContent({StyleAlignFlags::NORMAL}),
1075 mJustifyItems({{StyleAlignFlags::LEGACY}, {StyleAlignFlags::NORMAL}}),
1076 mJustifySelf({StyleAlignFlags::AUTO}),
1077 mFlexDirection(StyleFlexDirection::Row),
1078 mFlexWrap(StyleFlexWrap::Nowrap),
1079 mObjectFit(StyleObjectFit::Fill),
1080 mBoxSizing(StyleBoxSizing::Content),
1081 mOrder(NS_STYLE_ORDER_INITIAL),
1082 mFlexGrow(0.0f),
1083 mFlexShrink(1.0f),
1084 mZIndex(StyleZIndex::Auto()),
1085 mGridTemplateColumns(StyleGridTemplateComponent::None()),
1086 mGridTemplateRows(StyleGridTemplateComponent::None()),
1087 mGridTemplateAreas(StyleGridTemplateAreas::None()),
1088 mColumnGap(NonNegativeLengthPercentageOrNormal::Normal()),
1089 mRowGap(NonNegativeLengthPercentageOrNormal::Normal()) {
1090 MOZ_COUNT_CTOR(nsStylePosition);
1092 // The initial value of grid-auto-columns and grid-auto-rows is 'auto',
1093 // which computes to 'minmax(auto, auto)'.
1095 // Other members get their default constructors
1096 // which initialize them to representations of their respective initial value.
1097 // mGridTemplate{Rows,Columns}: false and empty arrays for 'none'
1098 // mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto'
1101 nsStylePosition::~nsStylePosition() { MOZ_COUNT_DTOR(nsStylePosition); }
1103 nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
1104 : mAlignTracks(aSource.mAlignTracks),
1105 mJustifyTracks(aSource.mJustifyTracks),
1106 mObjectPosition(aSource.mObjectPosition),
1107 mOffset(aSource.mOffset),
1108 mWidth(aSource.mWidth),
1109 mMinWidth(aSource.mMinWidth),
1110 mMaxWidth(aSource.mMaxWidth),
1111 mHeight(aSource.mHeight),
1112 mMinHeight(aSource.mMinHeight),
1113 mMaxHeight(aSource.mMaxHeight),
1114 mFlexBasis(aSource.mFlexBasis),
1115 mGridAutoColumns(aSource.mGridAutoColumns),
1116 mGridAutoRows(aSource.mGridAutoRows),
1117 mAspectRatio(aSource.mAspectRatio),
1118 mGridAutoFlow(aSource.mGridAutoFlow),
1119 mMasonryAutoFlow(aSource.mMasonryAutoFlow),
1120 mAlignContent(aSource.mAlignContent),
1121 mAlignItems(aSource.mAlignItems),
1122 mAlignSelf(aSource.mAlignSelf),
1123 mJustifyContent(aSource.mJustifyContent),
1124 mJustifyItems(aSource.mJustifyItems),
1125 mJustifySelf(aSource.mJustifySelf),
1126 mFlexDirection(aSource.mFlexDirection),
1127 mFlexWrap(aSource.mFlexWrap),
1128 mObjectFit(aSource.mObjectFit),
1129 mBoxSizing(aSource.mBoxSizing),
1130 mOrder(aSource.mOrder),
1131 mFlexGrow(aSource.mFlexGrow),
1132 mFlexShrink(aSource.mFlexShrink),
1133 mZIndex(aSource.mZIndex),
1134 mGridTemplateColumns(aSource.mGridTemplateColumns),
1135 mGridTemplateRows(aSource.mGridTemplateRows),
1136 mGridTemplateAreas(aSource.mGridTemplateAreas),
1137 mGridColumnStart(aSource.mGridColumnStart),
1138 mGridColumnEnd(aSource.mGridColumnEnd),
1139 mGridRowStart(aSource.mGridRowStart),
1140 mGridRowEnd(aSource.mGridRowEnd),
1141 mColumnGap(aSource.mColumnGap),
1142 mRowGap(aSource.mRowGap) {
1143 MOZ_COUNT_CTOR(nsStylePosition);
1146 static bool IsAutonessEqual(const StyleRect<LengthPercentageOrAuto>& aSides1,
1147 const StyleRect<LengthPercentageOrAuto>& aSides2) {
1148 for (const auto side : mozilla::AllPhysicalSides()) {
1149 if (aSides1.Get(side).IsAuto() != aSides2.Get(side).IsAuto()) {
1150 return false;
1153 return true;
1156 nsChangeHint nsStylePosition::CalcDifference(
1157 const nsStylePosition& aNewData,
1158 const nsStyleVisibility& aOldStyleVisibility) const {
1159 if (mGridTemplateColumns.IsMasonry() !=
1160 aNewData.mGridTemplateColumns.IsMasonry() ||
1161 mGridTemplateRows.IsMasonry() != aNewData.mGridTemplateRows.IsMasonry()) {
1162 // XXXmats this could be optimized to AllReflowHints with a bit of work,
1163 // but I'll assume this is a very rare use case in practice. (bug 1623886)
1164 return nsChangeHint_ReconstructFrame;
1167 nsChangeHint hint = nsChangeHint(0);
1169 // Changes to "z-index" require a repaint.
1170 if (mZIndex != aNewData.mZIndex) {
1171 hint |= nsChangeHint_RepaintFrame;
1174 // Changes to "object-fit" & "object-position" require a repaint. They
1175 // may also require a reflow, if we have a nsSubDocumentFrame, so that we
1176 // can adjust the size & position of the subdocument.
1177 if (mObjectFit != aNewData.mObjectFit ||
1178 mObjectPosition != aNewData.mObjectPosition) {
1179 hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow;
1182 if (mOrder != aNewData.mOrder) {
1183 // "order" impacts both layout order and stacking order, so we need both a
1184 // reflow and a repaint when it changes. (Technically, we only need a
1185 // reflow if we're in a multi-line flexbox (which we can't be sure about,
1186 // since that's determined by styling on our parent) -- there, "order" can
1187 // affect which flex line we end up on, & hence can affect our sizing by
1188 // changing the group of flex items we're competing with for space.)
1189 return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints;
1192 if (mBoxSizing != aNewData.mBoxSizing) {
1193 // Can affect both widths and heights; just a bad scene.
1194 return hint | nsChangeHint_AllReflowHints;
1197 if (mAlignItems != aNewData.mAlignItems ||
1198 mAlignSelf != aNewData.mAlignSelf ||
1199 mJustifyTracks != aNewData.mJustifyTracks ||
1200 mAlignTracks != aNewData.mAlignTracks) {
1201 return hint | nsChangeHint_AllReflowHints;
1204 // Properties that apply to flex items:
1205 // XXXdholbert These should probably be more targeted (bug 819536)
1206 if (mFlexBasis != aNewData.mFlexBasis || mFlexGrow != aNewData.mFlexGrow ||
1207 mFlexShrink != aNewData.mFlexShrink) {
1208 return hint | nsChangeHint_AllReflowHints;
1211 // Properties that apply to flex containers:
1212 // - flex-direction can swap a flex container between vertical & horizontal.
1213 // - flex-wrap changes whether a flex container's children are wrapped, which
1214 // impacts their sizing/positioning and hence impacts the container's size.
1215 if (mFlexDirection != aNewData.mFlexDirection ||
1216 mFlexWrap != aNewData.mFlexWrap) {
1217 return hint | nsChangeHint_AllReflowHints;
1220 // Properties that apply to grid containers:
1221 // FIXME: only for grid containers
1222 // (ie. 'display: grid' or 'display: inline-grid')
1223 if (mGridTemplateColumns != aNewData.mGridTemplateColumns ||
1224 mGridTemplateRows != aNewData.mGridTemplateRows ||
1225 mGridTemplateAreas != aNewData.mGridTemplateAreas ||
1226 mGridAutoColumns != aNewData.mGridAutoColumns ||
1227 mGridAutoRows != aNewData.mGridAutoRows ||
1228 mGridAutoFlow != aNewData.mGridAutoFlow ||
1229 mMasonryAutoFlow != aNewData.mMasonryAutoFlow) {
1230 return hint | nsChangeHint_AllReflowHints;
1233 // Properties that apply to grid items:
1234 // FIXME: only for grid items
1235 // (ie. parent frame is 'display: grid' or 'display: inline-grid')
1236 if (mGridColumnStart != aNewData.mGridColumnStart ||
1237 mGridColumnEnd != aNewData.mGridColumnEnd ||
1238 mGridRowStart != aNewData.mGridRowStart ||
1239 mGridRowEnd != aNewData.mGridRowEnd ||
1240 mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) {
1241 return hint | nsChangeHint_AllReflowHints;
1244 // Changing 'justify-content/items/self' might affect the positioning,
1245 // but it won't affect any sizing.
1246 if (mJustifyContent != aNewData.mJustifyContent ||
1247 mJustifyItems.computed != aNewData.mJustifyItems.computed ||
1248 mJustifySelf != aNewData.mJustifySelf) {
1249 hint |= nsChangeHint_NeedReflow;
1252 // No need to do anything if specified justify-items changes, as long as the
1253 // computed one (tested above) is unchanged.
1254 if (mJustifyItems.specified != aNewData.mJustifyItems.specified) {
1255 hint |= nsChangeHint_NeutralChange;
1258 // 'align-content' doesn't apply to a single-line flexbox but we don't know
1259 // if we're a flex container at this point so we can't optimize for that.
1260 if (mAlignContent != aNewData.mAlignContent) {
1261 hint |= nsChangeHint_NeedReflow;
1264 bool widthChanged = mWidth != aNewData.mWidth ||
1265 mMinWidth != aNewData.mMinWidth ||
1266 mMaxWidth != aNewData.mMaxWidth;
1267 bool heightChanged = mHeight != aNewData.mHeight ||
1268 mMinHeight != aNewData.mMinHeight ||
1269 mMaxHeight != aNewData.mMaxHeight;
1271 // It doesn't matter whether we're looking at the old or new visibility
1272 // struct, since a change between vertical and horizontal writing-mode will
1273 // cause a reframe.
1274 bool isVertical = aOldStyleVisibility.mWritingMode !=
1275 StyleWritingModeProperty::HorizontalTb;
1276 if (isVertical ? widthChanged : heightChanged) {
1277 hint |= nsChangeHint_ReflowHintsForBSizeChange;
1280 if (isVertical ? heightChanged : widthChanged) {
1281 hint |= nsChangeHint_ReflowHintsForISizeChange;
1284 if (mAspectRatio != aNewData.mAspectRatio) {
1285 hint |= nsChangeHint_ReflowHintsForISizeChange |
1286 nsChangeHint_ReflowHintsForBSizeChange;
1289 // If any of the offsets have changed, then return the respective hints
1290 // so that we would hopefully be able to avoid reflowing.
1291 // Note that it is possible that we'll need to reflow when processing
1292 // restyles, but we don't have enough information to make a good decision
1293 // right now.
1294 // Don't try to handle changes between "auto" and non-auto efficiently;
1295 // that's tricky to do and will hardly ever be able to avoid a reflow.
1296 if (mOffset != aNewData.mOffset) {
1297 if (IsAutonessEqual(mOffset, aNewData.mOffset)) {
1298 hint |=
1299 nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow;
1300 } else {
1301 hint |=
1302 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
1305 return hint;
1308 StyleAlignSelf nsStylePosition::UsedAlignSelf(
1309 const ComputedStyle* aParent) const {
1310 if (mAlignSelf._0 != StyleAlignFlags::AUTO) {
1311 return mAlignSelf;
1313 if (MOZ_LIKELY(aParent)) {
1314 auto parentAlignItems = aParent->StylePosition()->mAlignItems;
1315 MOZ_ASSERT(!(parentAlignItems._0 & StyleAlignFlags::LEGACY),
1316 "align-items can't have 'legacy'");
1317 return {parentAlignItems._0};
1319 return {StyleAlignFlags::NORMAL};
1322 StyleJustifySelf nsStylePosition::UsedJustifySelf(
1323 const ComputedStyle* aParent) const {
1324 if (mJustifySelf._0 != StyleAlignFlags::AUTO) {
1325 return mJustifySelf;
1327 if (MOZ_LIKELY(aParent)) {
1328 const auto& inheritedJustifyItems =
1329 aParent->StylePosition()->mJustifyItems.computed;
1330 return {inheritedJustifyItems._0 & ~StyleAlignFlags::LEGACY};
1332 return {StyleAlignFlags::NORMAL};
1335 // --------------------
1336 // nsStyleTable
1339 nsStyleTable::nsStyleTable(const Document& aDocument)
1340 : mLayoutStrategy(StyleTableLayout::Auto), mXSpan(1) {
1341 MOZ_COUNT_CTOR(nsStyleTable);
1344 nsStyleTable::~nsStyleTable() { MOZ_COUNT_DTOR(nsStyleTable); }
1346 nsStyleTable::nsStyleTable(const nsStyleTable& aSource)
1347 : mLayoutStrategy(aSource.mLayoutStrategy), mXSpan(aSource.mXSpan) {
1348 MOZ_COUNT_CTOR(nsStyleTable);
1351 nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const {
1352 if (mXSpan != aNewData.mXSpan ||
1353 mLayoutStrategy != aNewData.mLayoutStrategy) {
1354 return nsChangeHint_ReconstructFrame;
1356 return nsChangeHint(0);
1359 // -----------------------
1360 // nsStyleTableBorder
1362 nsStyleTableBorder::nsStyleTableBorder(const Document& aDocument)
1363 : mBorderSpacingCol(0),
1364 mBorderSpacingRow(0),
1365 mBorderCollapse(StyleBorderCollapse::Separate),
1366 mCaptionSide(StyleCaptionSide::Top),
1367 mEmptyCells(StyleEmptyCells::Show) {
1368 MOZ_COUNT_CTOR(nsStyleTableBorder);
1371 nsStyleTableBorder::~nsStyleTableBorder() {
1372 MOZ_COUNT_DTOR(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;
1401 } else {
1402 return NS_STYLE_HINT_REFLOW;
1406 template <typename T>
1407 static bool GradientItemsAreOpaque(
1408 Span<const StyleGenericGradientItem<StyleColor, T>> aItems) {
1409 for (auto& stop : aItems) {
1410 if (stop.IsInterpolationHint()) {
1411 continue;
1414 auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop()
1415 : stop.AsComplexColorStop().color;
1416 if (color.MaybeTransparent()) {
1417 // We don't know the foreground color here, so if it's being used
1418 // we must assume it might be transparent.
1419 return false;
1423 return true;
1426 template <>
1427 bool StyleGradient::IsOpaque() const {
1428 if (IsLinear()) {
1429 return GradientItemsAreOpaque(AsLinear().items.AsSpan());
1431 if (IsRadial()) {
1432 return GradientItemsAreOpaque(AsRadial().items.AsSpan());
1434 return GradientItemsAreOpaque(AsConic().items.AsSpan());
1437 // --------------------
1438 // CachedBorderImageData
1440 void CachedBorderImageData::PurgeCachedImages() {
1441 MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
1442 MOZ_ASSERT(NS_IsMainThread());
1443 mSubImages.Clear();
1446 void CachedBorderImageData::PurgeCacheForViewportChange(
1447 const Maybe<nsSize>& aSize, const bool aHasIntrinsicRatio) {
1448 // If we're redrawing with a different viewport-size than we used for our
1449 // cached subimages, then we can't trust that our subimages are valid;
1450 // any percent sizes/positions in our SVG doc may be different now. Purge!
1451 // (We don't have to purge if the SVG document has an intrinsic ratio,
1452 // though, because the actual size of elements in SVG documant's coordinate
1453 // axis are fixed in this case.)
1454 if (aSize != mCachedSVGViewportSize && !aHasIntrinsicRatio) {
1455 PurgeCachedImages();
1456 SetCachedSVGViewportSize(aSize);
1460 static int32_t ConvertToPixelCoord(const StyleNumberOrPercentage& aCoord,
1461 int32_t aPercentScale) {
1462 double pixelValue;
1463 if (aCoord.IsNumber()) {
1464 pixelValue = aCoord.AsNumber();
1465 } else {
1466 MOZ_ASSERT(aCoord.IsPercentage());
1467 pixelValue = aCoord.AsPercentage()._0 * aPercentScale;
1469 MOZ_ASSERT(pixelValue >= 0, "we ensured non-negative while parsing");
1470 pixelValue = std::min(pixelValue, double(INT32_MAX)); // avoid overflow
1471 return NS_lround(pixelValue);
1474 template <>
1475 Maybe<StyleImage::ActualCropRect> StyleImage::ComputeActualCropRect() const {
1476 MOZ_ASSERT(IsRect(),
1477 "This function is designed to be used only image-rect images");
1479 imgRequestProxy* req = GetImageRequest();
1480 if (!req) {
1481 return Nothing();
1484 nsCOMPtr<imgIContainer> imageContainer;
1485 req->GetImage(getter_AddRefs(imageContainer));
1486 if (!imageContainer) {
1487 return Nothing();
1490 nsIntSize imageSize;
1491 imageContainer->GetWidth(&imageSize.width);
1492 imageContainer->GetHeight(&imageSize.height);
1493 if (imageSize.width <= 0 || imageSize.height <= 0) {
1494 return Nothing();
1497 auto& rect = AsRect();
1499 int32_t left = ConvertToPixelCoord(rect->left, imageSize.width);
1500 int32_t top = ConvertToPixelCoord(rect->top, imageSize.height);
1501 int32_t right = ConvertToPixelCoord(rect->right, imageSize.width);
1502 int32_t bottom = ConvertToPixelCoord(rect->bottom, imageSize.height);
1504 // IntersectRect() returns an empty rect if we get negative width or height
1505 nsIntRect cropRect(left, top, right - left, bottom - top);
1506 nsIntRect imageRect(nsIntPoint(0, 0), imageSize);
1507 auto finalRect = imageRect.Intersect(cropRect);
1508 bool isEntireImage = finalRect.IsEqualInterior(imageRect);
1509 return Some(ActualCropRect{finalRect, isEntireImage});
1512 template <>
1513 bool StyleImage::IsOpaque() const {
1514 if (IsImageSet()) {
1515 return FinalImage().IsOpaque();
1518 if (!IsComplete()) {
1519 return false;
1522 if (IsGradient()) {
1523 return AsGradient()->IsOpaque();
1526 if (IsElement()) {
1527 return false;
1530 MOZ_ASSERT(IsImageRequestType(), "unexpected image type");
1531 MOZ_ASSERT(GetImageRequest(), "should've returned earlier above");
1533 nsCOMPtr<imgIContainer> imageContainer;
1534 GetImageRequest()->GetImage(getter_AddRefs(imageContainer));
1535 MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
1537 // Check if the crop region of the image is opaque.
1538 if (imageContainer->WillDrawOpaqueNow()) {
1539 if (!IsRect()) {
1540 return true;
1543 // Must make sure if the crop rect contains at least a pixel.
1544 // XXX Is this optimization worth it? Maybe I should just return false.
1545 auto croprect = ComputeActualCropRect();
1546 return croprect && !croprect->mRect.IsEmpty();
1549 return false;
1552 template <>
1553 bool StyleImage::IsComplete() const {
1554 switch (tag) {
1555 case Tag::None:
1556 return false;
1557 case Tag::Gradient:
1558 case Tag::Element:
1559 return true;
1560 case Tag::Url:
1561 case Tag::Rect: {
1562 if (!IsResolved()) {
1563 return false;
1565 imgRequestProxy* req = GetImageRequest();
1566 if (!req) {
1567 return false;
1569 uint32_t status = imgIRequest::STATUS_ERROR;
1570 return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1571 (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
1572 (status & imgIRequest::STATUS_FRAME_COMPLETE);
1574 case Tag::ImageSet:
1575 return FinalImage().IsComplete();
1576 // Bug 546052 cross-fade not yet implemented.
1577 case Tag::CrossFade:
1578 return true;
1580 MOZ_ASSERT_UNREACHABLE("unexpected image type");
1581 return false;
1584 template <>
1585 bool StyleImage::IsSizeAvailable() const {
1586 switch (tag) {
1587 case Tag::None:
1588 return false;
1589 case Tag::Gradient:
1590 case Tag::Element:
1591 return true;
1592 case Tag::Url:
1593 case Tag::Rect: {
1594 imgRequestProxy* req = GetImageRequest();
1595 if (!req) {
1596 return false;
1598 uint32_t status = imgIRequest::STATUS_ERROR;
1599 return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1600 !(status & imgIRequest::STATUS_ERROR) &&
1601 (status & imgIRequest::STATUS_SIZE_AVAILABLE);
1603 case Tag::ImageSet:
1604 return FinalImage().IsSizeAvailable();
1605 case Tag::CrossFade:
1606 // TODO: Bug 546052 cross-fade not yet implemented.
1607 return true;
1609 MOZ_ASSERT_UNREACHABLE("unexpected image type");
1610 return false;
1613 template <>
1614 void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
1615 if (IsResolved()) {
1616 return;
1618 auto* old = aOld ? aOld->GetImageRequestURLValue() : nullptr;
1619 auto* url = GetImageRequestURLValue();
1620 // We could avoid this const_cast generating more code but it's not really
1621 // worth it.
1622 const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
1625 template <>
1626 ImageResolution StyleImage::GetResolution() const {
1627 ImageResolution resolution;
1628 if (imgRequestProxy* request = GetImageRequest()) {
1629 RefPtr<imgIContainer> image;
1630 request->GetImage(getter_AddRefs(image));
1631 if (image) {
1632 resolution = image->GetResolution();
1635 if (IsImageSet()) {
1636 auto& set = AsImageSet();
1637 float r = set->items.AsSpan()[set->selected_index].resolution._0;
1638 resolution.ScaleBy(r);
1640 return resolution;
1643 template <>
1644 Maybe<CSSIntSize> StyleImage::GetIntrinsicSize() const {
1645 imgRequestProxy* request = GetImageRequest();
1646 if (!request) {
1647 return Nothing();
1649 RefPtr<imgIContainer> image;
1650 request->GetImage(getter_AddRefs(image));
1651 if (!image) {
1652 return Nothing();
1654 // FIXME(emilio): Seems like this should be smarter about unspecified width /
1655 // height, aspect ratio, etc, but this preserves the current behavior of our
1656 // only caller for now...
1657 int32_t w = 0, h = 0;
1658 image->GetWidth(&w);
1659 image->GetHeight(&h);
1660 GetResolution().ApplyTo(w, h);
1661 return Some(CSSIntSize{w, h});
1664 // --------------------
1665 // nsStyleImageLayers
1668 const nsCSSPropertyID nsStyleImageLayers::kBackgroundLayerTable[] = {
1669 eCSSProperty_background, // shorthand
1670 eCSSProperty_background_color, // color
1671 eCSSProperty_background_image, // image
1672 eCSSProperty_background_repeat, // repeat
1673 eCSSProperty_background_position_x, // positionX
1674 eCSSProperty_background_position_y, // positionY
1675 eCSSProperty_background_clip, // clip
1676 eCSSProperty_background_origin, // origin
1677 eCSSProperty_background_size, // size
1678 eCSSProperty_background_attachment, // attachment
1679 eCSSProperty_UNKNOWN, // maskMode
1680 eCSSProperty_UNKNOWN // composite
1683 const nsCSSPropertyID nsStyleImageLayers::kMaskLayerTable[] = {
1684 eCSSProperty_mask, // shorthand
1685 eCSSProperty_UNKNOWN, // color
1686 eCSSProperty_mask_image, // image
1687 eCSSProperty_mask_repeat, // repeat
1688 eCSSProperty_mask_position_x, // positionX
1689 eCSSProperty_mask_position_y, // positionY
1690 eCSSProperty_mask_clip, // clip
1691 eCSSProperty_mask_origin, // origin
1692 eCSSProperty_mask_size, // size
1693 eCSSProperty_UNKNOWN, // attachment
1694 eCSSProperty_mask_mode, // maskMode
1695 eCSSProperty_mask_composite // composite
1698 nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType)
1699 : mAttachmentCount(1),
1700 mClipCount(1),
1701 mOriginCount(1),
1702 mRepeatCount(1),
1703 mPositionXCount(1),
1704 mPositionYCount(1),
1705 mImageCount(1),
1706 mSizeCount(1),
1707 mMaskModeCount(1),
1708 mBlendModeCount(1),
1709 mCompositeCount(1),
1710 mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT) {
1711 MOZ_COUNT_CTOR(nsStyleImageLayers);
1713 // Ensure first layer is initialized as specified layer type
1714 mLayers[0].Initialize(aType);
1717 nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers& aSource)
1718 : mAttachmentCount(aSource.mAttachmentCount),
1719 mClipCount(aSource.mClipCount),
1720 mOriginCount(aSource.mOriginCount),
1721 mRepeatCount(aSource.mRepeatCount),
1722 mPositionXCount(aSource.mPositionXCount),
1723 mPositionYCount(aSource.mPositionYCount),
1724 mImageCount(aSource.mImageCount),
1725 mSizeCount(aSource.mSizeCount),
1726 mMaskModeCount(aSource.mMaskModeCount),
1727 mBlendModeCount(aSource.mBlendModeCount),
1728 mCompositeCount(aSource.mCompositeCount),
1729 mLayers(aSource.mLayers.Clone()) {
1730 MOZ_COUNT_CTOR(nsStyleImageLayers);
1733 static bool AnyLayerIsElementImage(const nsStyleImageLayers& aLayers) {
1734 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aLayers) {
1735 if (aLayers.mLayers[i].mImage.FinalImage().IsElement()) {
1736 return true;
1739 return false;
1742 nsChangeHint nsStyleImageLayers::CalcDifference(
1743 const nsStyleImageLayers& aNewLayers, LayerType aType) const {
1744 nsChangeHint hint = nsChangeHint(0);
1746 // If the number of visible images changes, then it's easy-peasy.
1747 if (mImageCount != aNewLayers.mImageCount) {
1748 hint |= nsChangeHint_RepaintFrame;
1749 if (aType == nsStyleImageLayers::LayerType::Mask ||
1750 AnyLayerIsElementImage(*this) || AnyLayerIsElementImage(aNewLayers)) {
1751 hint |= nsChangeHint_UpdateEffects;
1753 return hint;
1756 const nsStyleImageLayers& moreLayers =
1757 mLayers.Length() > aNewLayers.mLayers.Length() ? *this : aNewLayers;
1758 const nsStyleImageLayers& lessLayers =
1759 mLayers.Length() > aNewLayers.mLayers.Length() ? aNewLayers : *this;
1761 for (size_t i = 0; i < moreLayers.mLayers.Length(); ++i) {
1762 const Layer& moreLayersLayer = moreLayers.mLayers[i];
1763 if (i < moreLayers.mImageCount) {
1764 // This is a visible image we're diffing, we may need to repaint.
1765 const Layer& lessLayersLayer = lessLayers.mLayers[i];
1766 nsChangeHint layerDifference =
1767 moreLayersLayer.CalcDifference(lessLayersLayer);
1768 if (layerDifference &&
1769 (moreLayersLayer.mImage.FinalImage().IsElement() ||
1770 lessLayersLayer.mImage.FinalImage().IsElement())) {
1771 layerDifference |=
1772 nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
1774 hint |= layerDifference;
1775 continue;
1777 if (hint) {
1778 // If they're different by now, we're done.
1779 return hint;
1781 if (i >= lessLayers.mLayers.Length()) {
1782 // The layer count differs, we know some property has changed, but if we
1783 // got here we know it won't affect rendering.
1784 return nsChangeHint_NeutralChange;
1787 const Layer& lessLayersLayer = lessLayers.mLayers[i];
1788 MOZ_ASSERT(moreLayersLayer.mImage.IsNone());
1789 MOZ_ASSERT(lessLayersLayer.mImage.IsNone());
1790 if (moreLayersLayer.CalcDifference(lessLayersLayer)) {
1791 // We don't care about the difference returned, we know it's not visible,
1792 // but if something changed, then we need to return the neutral change.
1793 return nsChangeHint_NeutralChange;
1797 if (hint) {
1798 // If they're different by now, we're done.
1799 return hint;
1802 // We could have same layers and values, but different count still.
1803 if (mAttachmentCount != aNewLayers.mAttachmentCount ||
1804 mBlendModeCount != aNewLayers.mBlendModeCount ||
1805 mClipCount != aNewLayers.mClipCount ||
1806 mCompositeCount != aNewLayers.mCompositeCount ||
1807 mMaskModeCount != aNewLayers.mMaskModeCount ||
1808 mOriginCount != aNewLayers.mOriginCount ||
1809 mRepeatCount != aNewLayers.mRepeatCount ||
1810 mPositionXCount != aNewLayers.mPositionXCount ||
1811 mPositionYCount != aNewLayers.mPositionYCount ||
1812 mSizeCount != aNewLayers.mSizeCount) {
1813 hint |= nsChangeHint_NeutralChange;
1816 return hint;
1819 nsStyleImageLayers& nsStyleImageLayers::operator=(
1820 const nsStyleImageLayers& aOther) {
1821 mAttachmentCount = aOther.mAttachmentCount;
1822 mClipCount = aOther.mClipCount;
1823 mOriginCount = aOther.mOriginCount;
1824 mRepeatCount = aOther.mRepeatCount;
1825 mPositionXCount = aOther.mPositionXCount;
1826 mPositionYCount = aOther.mPositionYCount;
1827 mImageCount = aOther.mImageCount;
1828 mSizeCount = aOther.mSizeCount;
1829 mMaskModeCount = aOther.mMaskModeCount;
1830 mBlendModeCount = aOther.mBlendModeCount;
1831 mCompositeCount = aOther.mCompositeCount;
1832 mLayers = aOther.mLayers.Clone();
1834 return *this;
1837 bool nsStyleImageLayers::operator==(const nsStyleImageLayers& aOther) const {
1838 if (mAttachmentCount != aOther.mAttachmentCount ||
1839 mClipCount != aOther.mClipCount || mOriginCount != aOther.mOriginCount ||
1840 mRepeatCount != aOther.mRepeatCount ||
1841 mPositionXCount != aOther.mPositionXCount ||
1842 mPositionYCount != aOther.mPositionYCount ||
1843 mImageCount != aOther.mImageCount || mSizeCount != aOther.mSizeCount ||
1844 mMaskModeCount != aOther.mMaskModeCount ||
1845 mBlendModeCount != aOther.mBlendModeCount) {
1846 return false;
1849 if (mLayers.Length() != aOther.mLayers.Length()) {
1850 return false;
1853 for (uint32_t i = 0; i < mLayers.Length(); i++) {
1854 if (mLayers[i].mPosition != aOther.mLayers[i].mPosition ||
1855 mLayers[i].mImage != aOther.mLayers[i].mImage ||
1856 mLayers[i].mSize != aOther.mLayers[i].mSize ||
1857 mLayers[i].mClip != aOther.mLayers[i].mClip ||
1858 mLayers[i].mOrigin != aOther.mLayers[i].mOrigin ||
1859 mLayers[i].mAttachment != aOther.mLayers[i].mAttachment ||
1860 mLayers[i].mBlendMode != aOther.mLayers[i].mBlendMode ||
1861 mLayers[i].mComposite != aOther.mLayers[i].mComposite ||
1862 mLayers[i].mMaskMode != aOther.mLayers[i].mMaskMode ||
1863 mLayers[i].mRepeat != aOther.mLayers[i].mRepeat) {
1864 return false;
1868 return true;
1871 static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
1872 const StyleImage& aImage) {
1873 MOZ_ASSERT(!aImage.IsNone(), "caller should have handled this");
1874 MOZ_ASSERT(!aImage.IsImageSet(), "caller should have handled this");
1876 // Contain and cover straightforwardly depend on frame size.
1877 if (aSize.IsCover() || aSize.IsContain()) {
1878 return true;
1881 MOZ_ASSERT(aSize.IsExplicitSize());
1882 auto& size = aSize.explicit_size;
1884 // If either dimension contains a non-zero percentage, rendering for that
1885 // dimension straightforwardly depends on frame size.
1886 if (size.width.HasPercent() || size.height.HasPercent()) {
1887 return true;
1890 // If both dimensions are fixed lengths, there's no dependency.
1891 if (!size.width.IsAuto() && !size.height.IsAuto()) {
1892 return false;
1895 // Gradient rendering depends on frame size when auto is involved because
1896 // gradients have no intrinsic ratio or dimensions, and therefore the relevant
1897 // dimension is "treat[ed] as 100%".
1898 if (aImage.IsGradient()) {
1899 return true;
1902 // XXX Element rendering for auto or fixed length doesn't depend on frame size
1903 // according to the spec. However, we don't implement the spec yet, so
1904 // for now we bail and say element() plus auto affects ultimate size.
1905 if (aImage.IsElement()) {
1906 return true;
1909 MOZ_ASSERT(aImage.IsImageRequestType(), "Missed some image");
1910 if (auto* request = aImage.GetImageRequest()) {
1911 nsCOMPtr<imgIContainer> imgContainer;
1912 request->GetImage(getter_AddRefs(imgContainer));
1913 if (imgContainer) {
1914 CSSIntSize imageSize;
1915 AspectRatio imageRatio;
1916 bool hasWidth, hasHeight;
1917 // We could bother getting the right resolution here but it doesn't matter
1918 // since we ignore `imageSize`.
1919 nsLayoutUtils::ComputeSizeForDrawing(imgContainer, ImageResolution(),
1920 imageSize, imageRatio, hasWidth,
1921 hasHeight);
1923 // If the image has a fixed width and height, rendering never depends on
1924 // the frame size.
1925 if (hasWidth && hasHeight) {
1926 return false;
1929 // If the image has an intrinsic ratio, rendering will depend on frame
1930 // size when background-size is all auto.
1931 if (imageRatio) {
1932 return size.width.IsAuto() == size.height.IsAuto();
1935 // Otherwise, rendering depends on frame size when the image dimensions
1936 // and background-size don't complement each other.
1937 return !(hasWidth && size.width.IsLengthPercentage()) &&
1938 !(hasHeight && size.height.IsLengthPercentage());
1942 // Passed the gauntlet: no dependency.
1943 return false;
1946 nsStyleImageLayers::Layer::Layer()
1947 : mImage(StyleImage::None()),
1948 mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(),
1949 LengthPercentageOrAuto::Auto())),
1951 mClip(StyleGeometryBox::BorderBox),
1952 mAttachment(StyleImageLayerAttachment::Scroll),
1953 mBlendMode(StyleBlend::Normal),
1954 mComposite(StyleMaskComposite::Add),
1955 mMaskMode(StyleMaskMode::MatchSource) {}
1957 nsStyleImageLayers::Layer::~Layer() = default;
1959 void nsStyleImageLayers::Layer::Initialize(
1960 nsStyleImageLayers::LayerType aType) {
1961 mRepeat.SetInitialValues();
1963 mPosition = Position::FromPercentage(0.);
1965 if (aType == LayerType::Background) {
1966 mOrigin = StyleGeometryBox::PaddingBox;
1967 } else {
1968 MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type.");
1969 mOrigin = StyleGeometryBox::BorderBox;
1973 bool nsStyleImageLayers::Layer::
1974 RenderingMightDependOnPositioningAreaSizeChange() const {
1975 // Do we even have an image?
1976 if (mImage.IsNone()) {
1977 return false;
1980 return mPosition.DependsOnPositioningAreaSize() ||
1981 SizeDependsOnPositioningAreaSize(mSize, mImage.FinalImage()) ||
1982 mRepeat.DependsOnPositioningAreaSize();
1985 bool nsStyleImageLayers::Layer::operator==(const Layer& aOther) const {
1986 return mAttachment == aOther.mAttachment && mClip == aOther.mClip &&
1987 mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat &&
1988 mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition &&
1989 mSize == aOther.mSize && mImage == aOther.mImage &&
1990 mMaskMode == aOther.mMaskMode && mComposite == aOther.mComposite;
1993 template <class ComputedValueItem>
1994 static void FillImageLayerList(
1995 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
1996 ComputedValueItem nsStyleImageLayers::Layer::*aResultLocation,
1997 uint32_t aItemCount, uint32_t aFillCount) {
1998 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
1999 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
2000 ++sourceLayer, ++destLayer) {
2001 aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation;
2005 // The same as FillImageLayerList, but for values stored in
2006 // layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
2007 static void FillImageLayerPositionCoordList(
2008 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
2009 LengthPercentage Position::*aResultLocation, uint32_t aItemCount,
2010 uint32_t aFillCount) {
2011 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
2012 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
2013 ++sourceLayer, ++destLayer) {
2014 aLayers[destLayer].mPosition.*aResultLocation =
2015 aLayers[sourceLayer].mPosition.*aResultLocation;
2019 void nsStyleImageLayers::FillAllLayers(uint32_t aMaxItemCount) {
2020 // Delete any extra items. We need to keep layers in which any
2021 // property was specified.
2022 mLayers.TruncateLengthNonZero(aMaxItemCount);
2024 uint32_t fillCount = mImageCount;
2025 FillImageLayerList(mLayers, &Layer::mImage, mImageCount, fillCount);
2026 FillImageLayerList(mLayers, &Layer::mRepeat, mRepeatCount, fillCount);
2027 FillImageLayerList(mLayers, &Layer::mAttachment, mAttachmentCount, fillCount);
2028 FillImageLayerList(mLayers, &Layer::mClip, mClipCount, fillCount);
2029 FillImageLayerList(mLayers, &Layer::mBlendMode, mBlendModeCount, fillCount);
2030 FillImageLayerList(mLayers, &Layer::mOrigin, mOriginCount, fillCount);
2031 FillImageLayerPositionCoordList(mLayers, &Position::horizontal,
2032 mPositionXCount, fillCount);
2033 FillImageLayerPositionCoordList(mLayers, &Position::vertical, mPositionYCount,
2034 fillCount);
2035 FillImageLayerList(mLayers, &Layer::mSize, mSizeCount, fillCount);
2036 FillImageLayerList(mLayers, &Layer::mMaskMode, mMaskModeCount, fillCount);
2037 FillImageLayerList(mLayers, &Layer::mComposite, mCompositeCount, fillCount);
2040 static bool UrlValuesEqual(const StyleImage& aImage,
2041 const StyleImage& aOtherImage) {
2042 auto* url = aImage.GetImageRequestURLValue();
2043 auto* other = aOtherImage.GetImageRequestURLValue();
2044 return url == other || (url && other && *url == *other);
2047 nsChangeHint nsStyleImageLayers::Layer::CalcDifference(
2048 const nsStyleImageLayers::Layer& aNewLayer) const {
2049 nsChangeHint hint = nsChangeHint(0);
2050 if (!UrlValuesEqual(mImage, aNewLayer.mImage)) {
2051 hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects;
2052 } else if (mAttachment != aNewLayer.mAttachment || mClip != aNewLayer.mClip ||
2053 mOrigin != aNewLayer.mOrigin || mRepeat != aNewLayer.mRepeat ||
2054 mBlendMode != aNewLayer.mBlendMode || mSize != aNewLayer.mSize ||
2055 mImage != aNewLayer.mImage || mMaskMode != aNewLayer.mMaskMode ||
2056 mComposite != aNewLayer.mComposite) {
2057 hint |= nsChangeHint_RepaintFrame;
2060 if (mPosition != aNewLayer.mPosition) {
2061 hint |= nsChangeHint_UpdateBackgroundPosition;
2064 return hint;
2067 // --------------------
2068 // nsStyleBackground
2071 nsStyleBackground::nsStyleBackground(const Document& aDocument)
2072 : mImage(nsStyleImageLayers::LayerType::Background),
2073 mBackgroundColor(StyleColor::Transparent()) {
2074 MOZ_COUNT_CTOR(nsStyleBackground);
2077 nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource)
2078 : mImage(aSource.mImage), mBackgroundColor(aSource.mBackgroundColor) {
2079 MOZ_COUNT_CTOR(nsStyleBackground);
2082 nsStyleBackground::~nsStyleBackground() { MOZ_COUNT_DTOR(nsStyleBackground); }
2084 void nsStyleBackground::TriggerImageLoads(Document& aDocument,
2085 const nsStyleBackground* aOldStyle) {
2086 MOZ_ASSERT(NS_IsMainThread());
2087 mImage.ResolveImages(aDocument, aOldStyle ? &aOldStyle->mImage : nullptr);
2090 nsChangeHint nsStyleBackground::CalcDifference(
2091 const nsStyleBackground& aNewData) const {
2092 nsChangeHint hint = nsChangeHint(0);
2093 if (mBackgroundColor != aNewData.mBackgroundColor) {
2094 hint |= nsChangeHint_RepaintFrame;
2097 hint |= mImage.CalcDifference(aNewData.mImage,
2098 nsStyleImageLayers::LayerType::Background);
2100 return hint;
2103 bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const {
2104 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
2105 const nsStyleImageLayers::Layer& layer = mImage.mLayers[i];
2106 if (layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2107 !layer.mImage.IsNone() && !nsLayoutUtils::IsTransformed(aFrame)) {
2108 return true;
2111 return false;
2114 nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const {
2115 return mBackgroundColor.CalcColor(aFrame);
2118 nscolor nsStyleBackground::BackgroundColor(const ComputedStyle* aStyle) const {
2119 return mBackgroundColor.CalcColor(*aStyle);
2122 bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
2123 return IsTransparent(aFrame->Style());
2126 bool nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const {
2127 return BottomLayer().mImage.IsNone() && mImage.mImageCount == 1 &&
2128 NS_GET_A(BackgroundColor(aStyle)) == 0;
2131 StyleTransition::StyleTransition(const StyleTransition& aCopy) = default;
2133 void StyleTransition::SetInitialValues() {
2134 mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
2135 mDuration = 0.0;
2136 mDelay = 0.0;
2137 mProperty = eCSSPropertyExtra_all_properties;
2140 bool StyleTransition::operator==(const StyleTransition& aOther) const {
2141 return mTimingFunction == aOther.mTimingFunction &&
2142 mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2143 mProperty == aOther.mProperty &&
2144 (mProperty != eCSSProperty_UNKNOWN ||
2145 mUnknownProperty == aOther.mUnknownProperty);
2148 StyleAnimation::StyleAnimation(const StyleAnimation& aCopy) = default;
2150 void StyleAnimation::SetInitialValues() {
2151 mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
2152 mDuration = 0.0;
2153 mDelay = 0.0;
2154 mName = nsGkAtoms::_empty;
2155 mDirection = dom::PlaybackDirection::Normal;
2156 mFillMode = dom::FillMode::None;
2157 mPlayState = StyleAnimationPlayState::Running;
2158 mIterationCount = 1.0f;
2159 mTimeline = StyleAnimationTimeline::Auto();
2162 bool StyleAnimation::operator==(const StyleAnimation& aOther) const {
2163 return mTimingFunction == aOther.mTimingFunction &&
2164 mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2165 mName == aOther.mName && mDirection == aOther.mDirection &&
2166 mFillMode == aOther.mFillMode && mPlayState == aOther.mPlayState &&
2167 mIterationCount == aOther.mIterationCount &&
2168 mTimeline == aOther.mTimeline;
2171 // --------------------
2172 // nsStyleDisplay
2174 nsStyleDisplay::nsStyleDisplay(const Document& aDocument)
2175 : mTransitions(
2176 nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
2177 mTransitionTimingFunctionCount(1),
2178 mTransitionDurationCount(1),
2179 mTransitionDelayCount(1),
2180 mTransitionPropertyCount(1),
2181 mAnimations(
2182 nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
2183 mAnimationTimingFunctionCount(1),
2184 mAnimationDurationCount(1),
2185 mAnimationDelayCount(1),
2186 mAnimationNameCount(1),
2187 mAnimationDirectionCount(1),
2188 mAnimationFillModeCount(1),
2189 mAnimationPlayStateCount(1),
2190 mAnimationIterationCountCount(1),
2191 mAnimationTimelineCount(1),
2192 mWillChange{{}, {0}},
2193 mDisplay(StyleDisplay::Inline),
2194 mOriginalDisplay(StyleDisplay::Inline),
2195 mContain(StyleContain::NONE),
2196 mAppearance(StyleAppearance::None),
2197 mDefaultAppearance(StyleAppearance::None),
2198 mPosition(StylePositionProperty::Static),
2199 mFloat(StyleFloat::None),
2200 mBreakType(StyleClear::None),
2201 mBreakInside(StyleBreakWithin::Auto),
2202 mBreakBefore(StyleBreakBetween::Auto),
2203 mBreakAfter(StyleBreakBetween::Auto),
2204 mOverflowX(StyleOverflow::Visible),
2205 mOverflowY(StyleOverflow::Visible),
2206 mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
2207 mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
2208 mResize(StyleResize::None),
2209 mOrient(StyleOrient::Inline),
2210 mIsolation(StyleIsolation::Auto),
2211 mTopLayer(StyleTopLayer::None),
2212 mTouchAction(StyleTouchAction::AUTO),
2213 mScrollBehavior(StyleScrollBehavior::Auto),
2214 mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
2215 mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
2216 mOverflowAnchor(StyleOverflowAnchor::Auto),
2217 mScrollSnapAlign{StyleScrollSnapAlignKeyword::None,
2218 StyleScrollSnapAlignKeyword::None},
2219 mScrollSnapType{StyleScrollSnapAxis::Both,
2220 StyleScrollSnapStrictness::None},
2221 mLineClamp(0),
2222 mRotate(StyleRotate::None()),
2223 mTranslate(StyleTranslate::None()),
2224 mScale(StyleScale::None()),
2225 mBackfaceVisibility(StyleBackfaceVisibility::Visible),
2226 mTransformStyle(StyleTransformStyle::Flat),
2227 mTransformBox(StyleGeometryBox::BorderBox),
2228 mOffsetPath(StyleOffsetPath::None()),
2229 mOffsetDistance(LengthPercentage::Zero()),
2230 mOffsetRotate{true, StyleAngle{0.0}},
2231 mOffsetAnchor(StylePositionOrAuto::Auto()),
2232 mTransformOrigin{LengthPercentage::FromPercentage(0.5),
2233 LengthPercentage::FromPercentage(0.5),
2234 {0.}},
2235 mChildPerspective(StylePerspective::None()),
2236 mPerspectiveOrigin(Position::FromPercentage(0.5f)),
2237 mVerticalAlign(
2238 StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)),
2239 mShapeMargin(LengthPercentage::Zero()),
2240 mShapeOutside(StyleShapeOutside::None()) {
2241 MOZ_COUNT_CTOR(nsStyleDisplay);
2243 mTransitions[0].SetInitialValues();
2244 mAnimations[0].SetInitialValues();
2247 nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
2248 : mTransitions(aSource.mTransitions.Clone()),
2249 mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount),
2250 mTransitionDurationCount(aSource.mTransitionDurationCount),
2251 mTransitionDelayCount(aSource.mTransitionDelayCount),
2252 mTransitionPropertyCount(aSource.mTransitionPropertyCount),
2253 mAnimations(aSource.mAnimations.Clone()),
2254 mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount),
2255 mAnimationDurationCount(aSource.mAnimationDurationCount),
2256 mAnimationDelayCount(aSource.mAnimationDelayCount),
2257 mAnimationNameCount(aSource.mAnimationNameCount),
2258 mAnimationDirectionCount(aSource.mAnimationDirectionCount),
2259 mAnimationFillModeCount(aSource.mAnimationFillModeCount),
2260 mAnimationPlayStateCount(aSource.mAnimationPlayStateCount),
2261 mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
2262 mAnimationTimelineCount(aSource.mAnimationTimelineCount),
2263 mWillChange(aSource.mWillChange),
2264 mDisplay(aSource.mDisplay),
2265 mOriginalDisplay(aSource.mOriginalDisplay),
2266 mContain(aSource.mContain),
2267 mAppearance(aSource.mAppearance),
2268 mDefaultAppearance(aSource.mDefaultAppearance),
2269 mPosition(aSource.mPosition),
2270 mFloat(aSource.mFloat),
2271 mBreakType(aSource.mBreakType),
2272 mBreakInside(aSource.mBreakInside),
2273 mBreakBefore(aSource.mBreakBefore),
2274 mBreakAfter(aSource.mBreakAfter),
2275 mOverflowX(aSource.mOverflowX),
2276 mOverflowY(aSource.mOverflowY),
2277 mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
2278 mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
2279 mResize(aSource.mResize),
2280 mOrient(aSource.mOrient),
2281 mIsolation(aSource.mIsolation),
2282 mTopLayer(aSource.mTopLayer),
2283 mTouchAction(aSource.mTouchAction),
2284 mScrollBehavior(aSource.mScrollBehavior),
2285 mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
2286 mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
2287 mOverflowAnchor(aSource.mOverflowAnchor),
2288 mScrollSnapAlign(aSource.mScrollSnapAlign),
2289 mScrollSnapType(aSource.mScrollSnapType),
2290 mLineClamp(aSource.mLineClamp),
2291 mTransform(aSource.mTransform),
2292 mRotate(aSource.mRotate),
2293 mTranslate(aSource.mTranslate),
2294 mScale(aSource.mScale),
2295 mBackfaceVisibility(aSource.mBackfaceVisibility),
2296 mTransformStyle(aSource.mTransformStyle),
2297 mTransformBox(aSource.mTransformBox),
2298 mOffsetPath(aSource.mOffsetPath),
2299 mOffsetDistance(aSource.mOffsetDistance),
2300 mOffsetRotate(aSource.mOffsetRotate),
2301 mOffsetAnchor(aSource.mOffsetAnchor),
2302 mTransformOrigin(aSource.mTransformOrigin),
2303 mChildPerspective(aSource.mChildPerspective),
2304 mPerspectiveOrigin(aSource.mPerspectiveOrigin),
2305 mVerticalAlign(aSource.mVerticalAlign),
2306 mShapeImageThreshold(aSource.mShapeImageThreshold),
2307 mShapeMargin(aSource.mShapeMargin),
2308 mShapeOutside(aSource.mShapeOutside) {
2309 MOZ_COUNT_CTOR(nsStyleDisplay);
2312 nsStyleDisplay::~nsStyleDisplay() { MOZ_COUNT_DTOR(nsStyleDisplay); }
2314 void nsStyleDisplay::TriggerImageLoads(Document& aDocument,
2315 const nsStyleDisplay* aOldStyle) {
2316 MOZ_ASSERT(NS_IsMainThread());
2318 if (mShapeOutside.IsImage()) {
2319 auto* old = aOldStyle && aOldStyle->mShapeOutside.IsImage()
2320 ? &aOldStyle->mShapeOutside.AsImage()
2321 : nullptr;
2322 // Const-cast is ugly but legit, we could avoid it by generating mut-casts
2323 // with cbindgen.
2324 const_cast<StyleImage&>(mShapeOutside.AsImage())
2325 .ResolveImage(aDocument, old);
2329 template <typename TransformLike>
2330 static inline nsChangeHint CompareTransformValues(
2331 const TransformLike& aOldTransform, const TransformLike& aNewTransform) {
2332 nsChangeHint result = nsChangeHint(0);
2334 // Note: If we add a new change hint for transform changes here, we have to
2335 // modify KeyframeEffect::CalculateCumulativeChangeHint too!
2336 if (aOldTransform != aNewTransform) {
2337 result |= nsChangeHint_UpdateTransformLayer;
2338 if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) {
2339 result |= nsChangeHint_UpdatePostTransformOverflow;
2340 } else {
2341 result |= nsChangeHint_UpdateOverflow;
2345 return result;
2348 static inline nsChangeHint CompareMotionValues(
2349 const nsStyleDisplay& aDisplay, const nsStyleDisplay& aNewDisplay) {
2350 if (aDisplay.mOffsetPath == aNewDisplay.mOffsetPath) {
2351 if (aDisplay.mOffsetDistance == aNewDisplay.mOffsetDistance &&
2352 aDisplay.mOffsetRotate == aNewDisplay.mOffsetRotate &&
2353 aDisplay.mOffsetAnchor == aNewDisplay.mOffsetAnchor) {
2354 return nsChangeHint(0);
2357 if (aDisplay.mOffsetPath.IsNone()) {
2358 return nsChangeHint_NeutralChange;
2362 // TODO: Bug 1482737: This probably doesn't need to UpdateOverflow
2363 // (or UpdateTransformLayer) if there's already a transform.
2364 // Set the same hints as what we use for transform because motion path is
2365 // a kind of transform and will be combined with other transforms.
2366 nsChangeHint result = nsChangeHint_UpdateTransformLayer;
2367 if (!aDisplay.mOffsetPath.IsNone() && !aNewDisplay.mOffsetPath.IsNone()) {
2368 result |= nsChangeHint_UpdatePostTransformOverflow;
2369 } else {
2370 result |= nsChangeHint_UpdateOverflow;
2372 return result;
2375 static bool ScrollbarGenerationChanged(const nsStyleDisplay& aOld,
2376 const nsStyleDisplay& aNew) {
2377 auto changed = [](StyleOverflow aOld, StyleOverflow aNew) {
2378 return aOld != aNew &&
2379 (aOld == StyleOverflow::Hidden || aNew == StyleOverflow::Hidden);
2381 return changed(aOld.mOverflowX, aNew.mOverflowX) ||
2382 changed(aOld.mOverflowY, aNew.mOverflowY);
2385 nsChangeHint nsStyleDisplay::CalcDifference(
2386 const nsStyleDisplay& aNewData, const nsStylePosition& aOldPosition) const {
2387 if (mDisplay != aNewData.mDisplay || mContain != aNewData.mContain ||
2388 (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None) ||
2389 mScrollBehavior != aNewData.mScrollBehavior ||
2390 mScrollSnapType != aNewData.mScrollSnapType ||
2391 mTopLayer != aNewData.mTopLayer || mResize != aNewData.mResize) {
2392 return nsChangeHint_ReconstructFrame;
2395 auto oldAppearance = EffectiveAppearance();
2396 auto newAppearance = aNewData.EffectiveAppearance();
2398 if ((oldAppearance == StyleAppearance::Textfield &&
2399 newAppearance != StyleAppearance::Textfield) ||
2400 (oldAppearance != StyleAppearance::Textfield &&
2401 newAppearance == StyleAppearance::Textfield)) {
2402 // This is for <input type=number> where we allow authors to specify a
2403 // |-moz-appearance:textfield| to get a control without a spinner. (The
2404 // spinner is present for |-moz-appearance:number-input| but also other
2405 // values such as 'none'.) We need to reframe since we want to use
2406 // nsTextControlFrame instead of nsNumberControlFrame if the author
2407 // specifies 'textfield'.
2408 return nsChangeHint_ReconstructFrame;
2411 auto hint = nsChangeHint(0);
2412 if (mPosition != aNewData.mPosition) {
2413 if (IsAbsolutelyPositionedStyle() ||
2414 aNewData.IsAbsolutelyPositionedStyle()) {
2415 // This changes our parent relationship on the frame tree and / or needs
2416 // to create a placeholder, so gotta reframe. There are some cases (when
2417 // switching from fixed to absolute or viceversa, if our containing block
2418 // happens to remain the same, i.e., if it has a transform or such) where
2419 // this wouldn't really be needed (though we'd still need to move the
2420 // frame from one child list to another). In any case we don't have a hand
2421 // to that information from here, and it doesn't seem like a case worth
2422 // optimizing for.
2423 return nsChangeHint_ReconstructFrame;
2425 // We start or stop being a containing block for abspos descendants. This
2426 // also causes painting to change, as we'd become a pseudo-stacking context.
2427 if (IsRelativelyPositionedStyle() !=
2428 aNewData.IsRelativelyPositionedStyle()) {
2429 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_RepaintFrame;
2431 if (IsPositionForcingStackingContext() !=
2432 aNewData.IsPositionForcingStackingContext()) {
2433 hint |= nsChangeHint_RepaintFrame;
2435 // On top of that: if the above ends up not reframing, we need a reflow to
2436 // compute our relative, static or sticky position.
2437 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2440 if (mScrollSnapAlign != aNewData.mScrollSnapAlign) {
2441 // FIXME: Bug 1530253 Support re-snapping when scroll-snap-align changes.
2442 hint |= nsChangeHint_NeutralChange;
2445 if (mOverflowX != aNewData.mOverflowX || mOverflowY != aNewData.mOverflowY) {
2446 const bool isScrollable = IsScrollableOverflow();
2447 if (isScrollable != aNewData.IsScrollableOverflow()) {
2448 // We may need to construct or destroy a scroll frame as a result of this
2449 // change.
2450 hint |= nsChangeHint_ScrollbarChange;
2451 } else if (isScrollable) {
2452 if (ScrollbarGenerationChanged(*this, aNewData)) {
2453 // We need to reframe in the case of hidden -> non-hidden case though,
2454 // since ScrollFrameHelper::CreateAnonymousContent avoids creating
2455 // scrollbars altogether for overflow: hidden. That seems it could
2456 // create some interesting perf cliffs...
2458 // We reframe when non-hidden -> hidden too, for now.
2460 // FIXME(bug 1590247): Seems we could avoid reframing once we've created
2461 // scrollbars, which should get us the optimization for elements that
2462 // have toggled scrollbars, but would prevent the cliff of toggling
2463 // overflow causing jank.
2464 hint |= nsChangeHint_ScrollbarChange;
2465 } else {
2466 // Otherwise, for changes where both overflow values are scrollable,
2467 // means that scrollbars may appear or disappear. We need to reflow,
2468 // since reflow is what determines which scrollbars if any are visible.
2469 hint |= nsChangeHint_ReflowHintsForScrollbarChange;
2471 } else {
2472 // Otherwise this is a change between 'visible' and 'clip'.
2473 // Here only whether we have a 'clip' changes, so just repaint and
2474 // update our overflow areas in that case.
2475 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2479 /* Note: When mScrollBehavior or mScrollSnapType are changed,
2480 * nsChangeHint_NeutralChange is not sufficient to enter
2481 * nsCSSFrameConstructor::PropagateScrollToViewport. By using the same hint as
2482 * used when the overflow css property changes, nsChangeHint_ReconstructFrame,
2483 * PropagateScrollToViewport will be called.
2485 * The scroll-behavior css property is not expected to change often (the
2486 * CSSOM-View DOM methods are likely to be used in those cases); however,
2487 * if this does become common perhaps a faster-path might be worth while.
2489 * FIXME(emilio): Can we do what we do for overflow changes?
2491 * FIXME(emilio): These properties no longer propagate from the body to the
2492 * viewport.
2495 if (mFloat != aNewData.mFloat) {
2496 // Changing which side we're floating on (float:none was handled above).
2497 hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2500 if (mShapeOutside != aNewData.mShapeOutside ||
2501 mShapeMargin != aNewData.mShapeMargin ||
2502 mShapeImageThreshold != aNewData.mShapeImageThreshold) {
2503 if (aNewData.mFloat != StyleFloat::None) {
2504 // If we are floating, and our shape-outside, shape-margin, or
2505 // shape-image-threshold are changed, our descendants are not impacted,
2506 // but our ancestor and siblings are.
2507 hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2508 } else {
2509 // shape-outside or shape-margin or shape-image-threshold changed,
2510 // but we don't need to reflow because we're not floating.
2511 hint |= nsChangeHint_NeutralChange;
2515 if (mLineClamp != aNewData.mLineClamp) {
2516 hint |= NS_STYLE_HINT_REFLOW;
2519 if (mVerticalAlign != aNewData.mVerticalAlign) {
2520 // XXX Can this just be AllReflowHints + RepaintFrame, and be included in
2521 // the block below?
2522 hint |= NS_STYLE_HINT_REFLOW;
2525 // XXX the following is conservative, for now: changing float breaking
2526 // shouldn't necessarily require a repaint, reflow should suffice.
2528 // FIXME(emilio): We definitely change the frame tree in nsCSSFrameConstructor
2529 // based on break-before / break-after... Shouldn't that reframe?
2530 if (mBreakType != aNewData.mBreakType ||
2531 mBreakInside != aNewData.mBreakInside ||
2532 mBreakBefore != aNewData.mBreakBefore ||
2533 mBreakAfter != aNewData.mBreakAfter ||
2534 mAppearance != aNewData.mAppearance ||
2535 mDefaultAppearance != aNewData.mDefaultAppearance ||
2536 mOrient != aNewData.mOrient ||
2537 mOverflowClipBoxBlock != aNewData.mOverflowClipBoxBlock ||
2538 mOverflowClipBoxInline != aNewData.mOverflowClipBoxInline) {
2539 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2542 if (mIsolation != aNewData.mIsolation) {
2543 hint |= nsChangeHint_RepaintFrame;
2546 /* If we've added or removed the transform property, we need to reconstruct
2547 * the frame to add or remove the view object, and also to handle abs-pos and
2548 * fixed-pos containers.
2550 if (HasTransformStyle() != aNewData.HasTransformStyle()) {
2551 hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
2552 } else {
2553 /* Otherwise, if we've kept the property lying around and we already had a
2554 * transform, we need to see whether or not we've changed the transform.
2555 * If so, we need to recompute its overflow rect (which probably changed
2556 * if the transform changed) and to redraw within the bounds of that new
2557 * overflow rect.
2559 * If the property isn't present in either style struct, we still do the
2560 * comparisons but turn all the resulting change hints into
2561 * nsChangeHint_NeutralChange.
2563 nsChangeHint transformHint = nsChangeHint(0);
2565 transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
2566 transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
2567 transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
2568 transformHint |= CompareTransformValues(mScale, aNewData.mScale);
2569 transformHint |= CompareMotionValues(*this, aNewData);
2571 if (mTransformOrigin != aNewData.mTransformOrigin) {
2572 transformHint |= nsChangeHint_UpdateTransformLayer |
2573 nsChangeHint_UpdatePostTransformOverflow;
2576 if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
2577 mTransformStyle != aNewData.mTransformStyle ||
2578 mTransformBox != aNewData.mTransformBox) {
2579 transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2582 if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
2583 transformHint |= nsChangeHint_RepaintFrame;
2586 if (transformHint) {
2587 if (HasTransformStyle()) {
2588 hint |= transformHint;
2589 } else {
2590 hint |= nsChangeHint_NeutralChange;
2595 if (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) {
2596 // A change from/to being a containing block for position:fixed.
2597 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_UpdateOverflow |
2598 nsChangeHint_RepaintFrame;
2599 } else if (mChildPerspective != aNewData.mChildPerspective) {
2600 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2603 // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
2604 // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM
2605 // bit, which in turn handles frame reconstruction for changes in the
2606 // containing block of fixed-positioned elements.
2607 auto willChangeBitsChanged = mWillChange.bits ^ aNewData.mWillChange.bits;
2609 if (willChangeBitsChanged &
2610 (StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL |
2611 StyleWillChangeBits::SCROLL | StyleWillChangeBits::OPACITY |
2612 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::TRANSFORM |
2613 StyleWillChangeBits::Z_INDEX)) {
2614 hint |= nsChangeHint_RepaintFrame;
2617 if (willChangeBitsChanged &
2618 (StyleWillChangeBits::FIXPOS_CB_NON_SVG | StyleWillChangeBits::TRANSFORM |
2619 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::POSITION |
2620 StyleWillChangeBits::CONTAIN)) {
2621 hint |= nsChangeHint_UpdateContainingBlock;
2624 // If touch-action is changed, we need to regenerate the event regions on
2625 // the layers and send it over to the compositor for APZ to handle.
2626 if (mTouchAction != aNewData.mTouchAction) {
2627 hint |= nsChangeHint_RepaintFrame;
2630 // If overscroll-behavior has changed, the changes are picked up
2631 // during a repaint.
2632 if (mOverscrollBehaviorX != aNewData.mOverscrollBehaviorX ||
2633 mOverscrollBehaviorY != aNewData.mOverscrollBehaviorY) {
2634 hint |= nsChangeHint_SchedulePaint;
2637 if (mOriginalDisplay != aNewData.mOriginalDisplay) {
2638 // Our hypothetical box position may have changed.
2640 // Note that it doesn't matter if we look at the old or the new struct,
2641 // since a change on whether we need a hypothetical position would trigger
2642 // reflow anyway.
2643 if (IsAbsolutelyPositionedStyle() &&
2644 aOldPosition.NeedsHypotheticalPositionIfAbsPos()) {
2645 hint |=
2646 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2647 } else {
2648 hint |= nsChangeHint_NeutralChange;
2652 // Note: Our current behavior for handling changes to the
2653 // transition-duration, transition-delay, and transition-timing-function
2654 // properties is to do nothing. In other words, the transition
2655 // property that matters is what it is when the transition begins, and
2656 // we don't stop a transition later because the transition property
2657 // changed.
2658 // We do handle changes to transition-property, but we don't need to
2659 // bother with anything here, since the transition manager is notified
2660 // of any ComputedStyle change anyway.
2662 // Note: Likewise, for animation-*, the animation manager gets
2663 // notified about every new ComputedStyle constructed, and it uses
2664 // that opportunity to handle dynamic changes appropriately.
2666 // But we still need to return nsChangeHint_NeutralChange for these
2667 // properties, since some data did change in the style struct.
2669 if (!hint && (mTransitions != aNewData.mTransitions ||
2670 mTransitionTimingFunctionCount !=
2671 aNewData.mTransitionTimingFunctionCount ||
2672 mTransitionDurationCount != aNewData.mTransitionDurationCount ||
2673 mTransitionDelayCount != aNewData.mTransitionDelayCount ||
2674 mTransitionPropertyCount != aNewData.mTransitionPropertyCount ||
2675 mAnimations != aNewData.mAnimations ||
2676 mAnimationTimingFunctionCount !=
2677 aNewData.mAnimationTimingFunctionCount ||
2678 mAnimationDurationCount != aNewData.mAnimationDurationCount ||
2679 mAnimationDelayCount != aNewData.mAnimationDelayCount ||
2680 mAnimationNameCount != aNewData.mAnimationNameCount ||
2681 mAnimationDirectionCount != aNewData.mAnimationDirectionCount ||
2682 mAnimationFillModeCount != aNewData.mAnimationFillModeCount ||
2683 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount ||
2684 mAnimationIterationCountCount !=
2685 aNewData.mAnimationIterationCountCount ||
2686 mAnimationTimelineCount != aNewData.mAnimationTimelineCount ||
2687 mWillChange != aNewData.mWillChange ||
2688 mOverflowAnchor != aNewData.mOverflowAnchor)) {
2689 hint |= nsChangeHint_NeutralChange;
2692 return hint;
2695 // --------------------
2696 // nsStyleVisibility
2699 nsStyleVisibility::nsStyleVisibility(const Document& aDocument)
2700 : mImageOrientation(StyleImageOrientation::FromImage),
2701 mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL
2702 ? StyleDirection::Rtl
2703 : StyleDirection::Ltr),
2704 mVisible(StyleVisibility::Visible),
2705 mImageRendering(StyleImageRendering::Auto),
2706 mWritingMode(StyleWritingModeProperty::HorizontalTb),
2707 mTextOrientation(StyleTextOrientation::Mixed),
2708 mColorAdjust(StyleColorAdjust::Economy) {
2709 MOZ_COUNT_CTOR(nsStyleVisibility);
2712 nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource)
2713 : mImageOrientation(aSource.mImageOrientation),
2714 mDirection(aSource.mDirection),
2715 mVisible(aSource.mVisible),
2716 mImageRendering(aSource.mImageRendering),
2717 mWritingMode(aSource.mWritingMode),
2718 mTextOrientation(aSource.mTextOrientation),
2719 mColorAdjust(aSource.mColorAdjust) {
2720 MOZ_COUNT_CTOR(nsStyleVisibility);
2723 nsChangeHint nsStyleVisibility::CalcDifference(
2724 const nsStyleVisibility& aNewData) const {
2725 nsChangeHint hint = nsChangeHint(0);
2727 if (mDirection != aNewData.mDirection ||
2728 mWritingMode != aNewData.mWritingMode) {
2729 // It's important that a change in mWritingMode results in frame
2730 // reconstruction, because it may affect intrinsic size (see
2731 // nsSubDocumentFrame::GetIntrinsicISize/BSize).
2732 // Also, the used writing-mode value is now a field on nsIFrame and some
2733 // classes (e.g. table rows/cells) copy their value from an ancestor.
2734 hint |= nsChangeHint_ReconstructFrame;
2735 } else {
2736 if ((mImageOrientation != aNewData.mImageOrientation)) {
2737 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2739 if (mVisible != aNewData.mVisible) {
2740 if (mVisible == StyleVisibility::Visible ||
2741 aNewData.mVisible == StyleVisibility::Visible) {
2742 hint |= nsChangeHint_VisibilityChange;
2744 if (StyleVisibility::Collapse == mVisible ||
2745 StyleVisibility::Collapse == aNewData.mVisible) {
2746 hint |= NS_STYLE_HINT_REFLOW;
2747 } else {
2748 hint |= NS_STYLE_HINT_VISUAL;
2751 if (mTextOrientation != aNewData.mTextOrientation) {
2752 hint |= NS_STYLE_HINT_REFLOW;
2754 if (mImageRendering != aNewData.mImageRendering) {
2755 hint |= nsChangeHint_RepaintFrame;
2757 if (mColorAdjust != aNewData.mColorAdjust) {
2758 // color-adjust only affects media where dynamic changes can't happen.
2759 hint |= nsChangeHint_NeutralChange;
2762 return hint;
2765 //-----------------------
2766 // nsStyleContent
2769 nsStyleContent::nsStyleContent(const Document& aDocument)
2770 : mContent(StyleContent::Normal()) {
2771 MOZ_COUNT_CTOR(nsStyleContent);
2774 nsStyleContent::~nsStyleContent() { MOZ_COUNT_DTOR(nsStyleContent); }
2776 nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
2777 : mContent(aSource.mContent),
2778 mCounterIncrement(aSource.mCounterIncrement),
2779 mCounterReset(aSource.mCounterReset),
2780 mCounterSet(aSource.mCounterSet) {
2781 MOZ_COUNT_CTOR(nsStyleContent);
2784 nsChangeHint nsStyleContent::CalcDifference(
2785 const nsStyleContent& aNewData) const {
2786 // Unfortunately we need to reframe even if the content lengths are the same;
2787 // a simple reflow will not pick up different text or different image URLs,
2788 // since we set all that up in the CSSFrameConstructor
2789 if (mContent != aNewData.mContent ||
2790 mCounterIncrement != aNewData.mCounterIncrement ||
2791 mCounterReset != aNewData.mCounterReset ||
2792 mCounterSet != aNewData.mCounterSet) {
2793 return nsChangeHint_ReconstructFrame;
2796 return nsChangeHint(0);
2799 void nsStyleContent::TriggerImageLoads(Document& aDoc,
2800 const nsStyleContent* aOld) {
2801 if (!mContent.IsItems()) {
2802 return;
2805 Span<const StyleContentItem> oldItems;
2806 if (aOld && aOld->mContent.IsItems()) {
2807 oldItems = aOld->mContent.AsItems().AsSpan();
2810 auto items = mContent.AsItems().AsSpan();
2812 for (size_t i = 0; i < items.Length(); ++i) {
2813 auto& item = items[i];
2814 if (!item.IsImage()) {
2815 continue;
2817 auto& image = item.AsImage();
2818 auto* oldImage = i < oldItems.Length() && oldItems[i].IsImage()
2819 ? &oldItems[i].AsImage()
2820 : nullptr;
2821 const_cast<StyleImage&>(image).ResolveImage(aDoc, oldImage);
2825 // --------------------
2826 // nsStyleTextReset
2829 nsStyleTextReset::nsStyleTextReset(const Document& aDocument)
2830 : mTextOverflow(),
2831 mTextDecorationLine(StyleTextDecorationLine::NONE),
2832 mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
2833 mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL),
2834 mInitialLetterSink(0),
2835 mInitialLetterSize(0.0f),
2836 mTextDecorationColor(StyleColor::CurrentColor()),
2837 mTextDecorationThickness(StyleTextDecorationLength::Auto()) {
2838 MOZ_COUNT_CTOR(nsStyleTextReset);
2841 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
2842 : mTextOverflow(aSource.mTextOverflow),
2843 mTextDecorationLine(aSource.mTextDecorationLine),
2844 mTextDecorationStyle(aSource.mTextDecorationStyle),
2845 mUnicodeBidi(aSource.mUnicodeBidi),
2846 mInitialLetterSink(aSource.mInitialLetterSink),
2847 mInitialLetterSize(aSource.mInitialLetterSize),
2848 mTextDecorationColor(aSource.mTextDecorationColor),
2849 mTextDecorationThickness(aSource.mTextDecorationThickness) {
2850 MOZ_COUNT_CTOR(nsStyleTextReset);
2853 nsStyleTextReset::~nsStyleTextReset() { MOZ_COUNT_DTOR(nsStyleTextReset); }
2855 nsChangeHint nsStyleTextReset::CalcDifference(
2856 const nsStyleTextReset& aNewData) const {
2857 if (mUnicodeBidi != aNewData.mUnicodeBidi ||
2858 mInitialLetterSink != aNewData.mInitialLetterSink ||
2859 mInitialLetterSize != aNewData.mInitialLetterSize) {
2860 return NS_STYLE_HINT_REFLOW;
2863 if (mTextDecorationLine != aNewData.mTextDecorationLine ||
2864 mTextDecorationStyle != aNewData.mTextDecorationStyle ||
2865 mTextDecorationThickness != aNewData.mTextDecorationThickness) {
2866 // Changes to our text-decoration line can impact our overflow area &
2867 // also our descendants' overflow areas (particularly for text-frame
2868 // descendants). So, we update those areas & trigger a repaint.
2869 return nsChangeHint_RepaintFrame | nsChangeHint_UpdateSubtreeOverflow |
2870 nsChangeHint_SchedulePaint;
2873 // Repaint for decoration color changes
2874 if (mTextDecorationColor != aNewData.mTextDecorationColor) {
2875 return nsChangeHint_RepaintFrame;
2878 if (mTextOverflow != aNewData.mTextOverflow) {
2879 return nsChangeHint_RepaintFrame;
2882 return nsChangeHint(0);
2885 // --------------------
2886 // nsStyleText
2889 static StyleRGBA DefaultColor(const Document& aDocument) {
2890 return StyleRGBA::FromColor(
2891 PreferenceSheet::PrefsFor(aDocument).mColors.mDefault);
2894 nsStyleText::nsStyleText(const Document& aDocument)
2895 : mColor(DefaultColor(aDocument)),
2896 mTextTransform(StyleTextTransform::None()),
2897 mTextAlign(StyleTextAlign::Start),
2898 mTextAlignLast(StyleTextAlignLast::Auto),
2899 mTextJustify(StyleTextJustify::Auto),
2900 mWhiteSpace(StyleWhiteSpace::Normal),
2901 mHyphens(StyleHyphens::Manual),
2902 mRubyAlign(StyleRubyAlign::SpaceAround),
2903 mRubyPosition(StyleRubyPosition::AlternateOver),
2904 mTextSizeAdjust(StyleTextSizeAdjust::Auto),
2905 mTextCombineUpright(NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE),
2906 mMozControlCharacterVisibility(
2907 StaticPrefs::layout_css_control_characters_visible()
2908 ? StyleMozControlCharacterVisibility::Visible
2909 : StyleMozControlCharacterVisibility::Hidden),
2910 mTextRendering(StyleTextRendering::Auto),
2911 mTextEmphasisColor(StyleColor::CurrentColor()),
2912 mWebkitTextFillColor(StyleColor::CurrentColor()),
2913 mWebkitTextStrokeColor(StyleColor::CurrentColor()),
2914 mTabSize(
2915 StyleNonNegativeLengthOrNumber::Number(NS_STYLE_TABSIZE_INITIAL)),
2916 mWordSpacing(LengthPercentage::Zero()),
2917 mLetterSpacing({0.}),
2918 mLineHeight(StyleLineHeight::Normal()),
2919 mTextIndent(LengthPercentage::Zero()),
2920 mTextUnderlineOffset(LengthPercentageOrAuto::Auto()),
2921 mTextDecorationSkipInk(StyleTextDecorationSkipInk::Auto),
2922 mTextUnderlinePosition(StyleTextUnderlinePosition::AUTO),
2923 mWebkitTextStrokeWidth(0),
2924 mTextEmphasisStyle(StyleTextEmphasisStyle::None()) {
2925 MOZ_COUNT_CTOR(nsStyleText);
2926 RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
2927 mTextEmphasisPosition =
2928 language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
2929 ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
2930 : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
2933 nsStyleText::nsStyleText(const nsStyleText& aSource)
2934 : mColor(aSource.mColor),
2935 mTextTransform(aSource.mTextTransform),
2936 mTextAlign(aSource.mTextAlign),
2937 mTextAlignLast(aSource.mTextAlignLast),
2938 mTextJustify(aSource.mTextJustify),
2939 mWhiteSpace(aSource.mWhiteSpace),
2940 mLineBreak(aSource.mLineBreak),
2941 mWordBreak(aSource.mWordBreak),
2942 mOverflowWrap(aSource.mOverflowWrap),
2943 mHyphens(aSource.mHyphens),
2944 mRubyAlign(aSource.mRubyAlign),
2945 mRubyPosition(aSource.mRubyPosition),
2946 mTextSizeAdjust(aSource.mTextSizeAdjust),
2947 mTextCombineUpright(aSource.mTextCombineUpright),
2948 mMozControlCharacterVisibility(aSource.mMozControlCharacterVisibility),
2949 mTextEmphasisPosition(aSource.mTextEmphasisPosition),
2950 mTextRendering(aSource.mTextRendering),
2951 mTextEmphasisColor(aSource.mTextEmphasisColor),
2952 mWebkitTextFillColor(aSource.mWebkitTextFillColor),
2953 mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor),
2954 mTabSize(aSource.mTabSize),
2955 mWordSpacing(aSource.mWordSpacing),
2956 mLetterSpacing(aSource.mLetterSpacing),
2957 mLineHeight(aSource.mLineHeight),
2958 mTextIndent(aSource.mTextIndent),
2959 mTextUnderlineOffset(aSource.mTextUnderlineOffset),
2960 mTextDecorationSkipInk(aSource.mTextDecorationSkipInk),
2961 mTextUnderlinePosition(aSource.mTextUnderlinePosition),
2962 mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth),
2963 mTextShadow(aSource.mTextShadow),
2964 mTextEmphasisStyle(aSource.mTextEmphasisStyle) {
2965 MOZ_COUNT_CTOR(nsStyleText);
2968 nsStyleText::~nsStyleText() { MOZ_COUNT_DTOR(nsStyleText); }
2970 nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
2971 if (WhiteSpaceOrNewlineIsSignificant() !=
2972 aNewData.WhiteSpaceOrNewlineIsSignificant()) {
2973 // This may require construction of suppressed text frames
2974 return nsChangeHint_ReconstructFrame;
2977 if (mTextCombineUpright != aNewData.mTextCombineUpright ||
2978 mMozControlCharacterVisibility !=
2979 aNewData.mMozControlCharacterVisibility) {
2980 return nsChangeHint_ReconstructFrame;
2983 if ((mTextAlign != aNewData.mTextAlign) ||
2984 (mTextAlignLast != aNewData.mTextAlignLast) ||
2985 (mTextTransform != aNewData.mTextTransform) ||
2986 (mWhiteSpace != aNewData.mWhiteSpace) ||
2987 (mLineBreak != aNewData.mLineBreak) ||
2988 (mWordBreak != aNewData.mWordBreak) ||
2989 (mOverflowWrap != aNewData.mOverflowWrap) ||
2990 (mHyphens != aNewData.mHyphens) || (mRubyAlign != aNewData.mRubyAlign) ||
2991 (mRubyPosition != aNewData.mRubyPosition) ||
2992 (mTextSizeAdjust != aNewData.mTextSizeAdjust) ||
2993 (mLetterSpacing != aNewData.mLetterSpacing) ||
2994 (mLineHeight != aNewData.mLineHeight) ||
2995 (mTextIndent != aNewData.mTextIndent) ||
2996 (mTextJustify != aNewData.mTextJustify) ||
2997 (mWordSpacing != aNewData.mWordSpacing) ||
2998 (mTabSize != aNewData.mTabSize)) {
2999 return NS_STYLE_HINT_REFLOW;
3002 if (HasEffectiveTextEmphasis() != aNewData.HasEffectiveTextEmphasis() ||
3003 (HasEffectiveTextEmphasis() &&
3004 mTextEmphasisPosition != aNewData.mTextEmphasisPosition)) {
3005 // Text emphasis position change could affect line height calculation.
3006 return nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
3009 nsChangeHint hint = nsChangeHint(0);
3011 // text-rendering changes require a reflow since they change SVG
3012 // frames' rects.
3013 if (mTextRendering != aNewData.mTextRendering) {
3014 hint |= nsChangeHint_NeedReflow |
3015 nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085
3016 nsChangeHint_RepaintFrame;
3019 if (mTextShadow != aNewData.mTextShadow ||
3020 mTextEmphasisStyle != aNewData.mTextEmphasisStyle ||
3021 mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth ||
3022 mTextUnderlineOffset != aNewData.mTextUnderlineOffset ||
3023 mTextDecorationSkipInk != aNewData.mTextDecorationSkipInk ||
3024 mTextUnderlinePosition != aNewData.mTextUnderlinePosition) {
3025 hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint |
3026 nsChangeHint_RepaintFrame;
3028 // We don't add any other hints below.
3029 return hint;
3032 if (mColor != aNewData.mColor) {
3033 hint |= nsChangeHint_RepaintFrame;
3036 if (mTextEmphasisColor != aNewData.mTextEmphasisColor ||
3037 mWebkitTextFillColor != aNewData.mWebkitTextFillColor ||
3038 mWebkitTextStrokeColor != aNewData.mWebkitTextStrokeColor) {
3039 hint |= nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame;
3042 if (hint) {
3043 return hint;
3046 if (mTextEmphasisPosition != aNewData.mTextEmphasisPosition) {
3047 return nsChangeHint_NeutralChange;
3050 return nsChangeHint(0);
3053 LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
3054 MOZ_ASSERT(
3055 (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) !=
3056 !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT)) &&
3057 (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) !=
3058 !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)));
3059 mozilla::Side side =
3060 aWM.IsVertical()
3061 ? (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT
3062 ? eSideLeft
3063 : eSideRight)
3064 : (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER
3065 ? eSideTop
3066 : eSideBottom);
3067 LogicalSide result = aWM.LogicalSideForPhysicalSide(side);
3068 MOZ_ASSERT(IsBlock(result));
3069 return result;
3072 //-----------------------
3073 // nsStyleUI
3076 nsStyleUI::nsStyleUI(const Document& aDocument)
3077 : mInert(StyleInert::None),
3078 mUserInput(StyleUserInput::Auto),
3079 mUserModify(StyleUserModify::ReadOnly),
3080 mUserFocus(StyleUserFocus::None),
3081 mPointerEvents(StylePointerEvents::Auto),
3082 mCursor{{}, StyleCursorKind::Auto},
3083 mAccentColor(StyleColorOrAuto::Auto()),
3084 mCaretColor(StyleColorOrAuto::Auto()),
3085 mScrollbarColor(StyleScrollbarColor::Auto()),
3086 mColorScheme(StyleColorScheme{{}, {}}) {
3087 MOZ_COUNT_CTOR(nsStyleUI);
3090 nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
3091 : mInert(aSource.mInert),
3092 mUserInput(aSource.mUserInput),
3093 mUserModify(aSource.mUserModify),
3094 mUserFocus(aSource.mUserFocus),
3095 mPointerEvents(aSource.mPointerEvents),
3096 mCursor(aSource.mCursor),
3097 mAccentColor(aSource.mAccentColor),
3098 mCaretColor(aSource.mCaretColor),
3099 mScrollbarColor(aSource.mScrollbarColor),
3100 mColorScheme(aSource.mColorScheme) {
3101 MOZ_COUNT_CTOR(nsStyleUI);
3104 nsStyleUI::~nsStyleUI() { MOZ_COUNT_DTOR(nsStyleUI); }
3106 void nsStyleUI::TriggerImageLoads(Document& aDocument,
3107 const nsStyleUI* aOldStyle) {
3108 MOZ_ASSERT(NS_IsMainThread());
3110 auto cursorImages = mCursor.images.AsSpan();
3111 auto oldCursorImages = aOldStyle ? aOldStyle->mCursor.images.AsSpan()
3112 : Span<const StyleCursorImage>();
3113 for (size_t i = 0; i < cursorImages.Length(); ++i) {
3114 auto& cursor = cursorImages[i];
3115 const auto* oldCursorImage =
3116 oldCursorImages.Length() > i ? &oldCursorImages[i].image : nullptr;
3117 const_cast<StyleCursorImage&>(cursor).image.ResolveImage(aDocument,
3118 oldCursorImage);
3122 nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const {
3123 nsChangeHint hint = nsChangeHint(0);
3124 if (mCursor != aNewData.mCursor) {
3125 hint |= nsChangeHint_UpdateCursor;
3128 if (mPointerEvents != aNewData.mPointerEvents) {
3129 // SVGGeometryFrame's mRect depends on stroke _and_ on the value
3130 // of pointer-events. See SVGGeometryFrame::ReflowSVG's use of
3131 // GetHitTestFlags. (Only a reflow, no visual change.)
3132 hint |= nsChangeHint_NeedReflow |
3133 nsChangeHint_NeedDirtyReflow | // XXX remove me: bug 876085
3134 nsChangeHint_SchedulePaint; // pointer-events changes can change
3135 // event regions overrides on layers
3136 // and so needs a repaint.
3139 if (mUserModify != aNewData.mUserModify) {
3140 hint |= NS_STYLE_HINT_VISUAL;
3143 if (mUserFocus != aNewData.mUserFocus || mInert != aNewData.mInert ||
3144 mUserInput != aNewData.mUserInput) {
3145 hint |= nsChangeHint_NeutralChange;
3148 if (mCaretColor != aNewData.mCaretColor ||
3149 mAccentColor != aNewData.mAccentColor ||
3150 mScrollbarColor != aNewData.mScrollbarColor ||
3151 mColorScheme != aNewData.mColorScheme) {
3152 hint |= nsChangeHint_RepaintFrame;
3155 return hint;
3158 //-----------------------
3159 // nsStyleUIReset
3162 nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
3163 : mUserSelect(StyleUserSelect::Auto),
3164 mScrollbarWidth(StyleScrollbarWidth::Auto),
3165 mMozForceBrokenImageIcon(0),
3166 mIMEMode(StyleImeMode::Auto),
3167 mWindowDragging(StyleWindowDragging::Default),
3168 mWindowShadow(StyleWindowShadow::Default),
3169 mWindowOpacity(1.0),
3170 mWindowTransformOrigin{LengthPercentage::FromPercentage(0.5),
3171 LengthPercentage::FromPercentage(0.5),
3172 {0.}} {
3173 MOZ_COUNT_CTOR(nsStyleUIReset);
3176 nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
3177 : mUserSelect(aSource.mUserSelect),
3178 mScrollbarWidth(aSource.mScrollbarWidth),
3179 mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon),
3180 mIMEMode(aSource.mIMEMode),
3181 mWindowDragging(aSource.mWindowDragging),
3182 mWindowShadow(aSource.mWindowShadow),
3183 mWindowOpacity(aSource.mWindowOpacity),
3184 mMozWindowTransform(aSource.mMozWindowTransform),
3185 mWindowTransformOrigin(aSource.mWindowTransformOrigin) {
3186 MOZ_COUNT_CTOR(nsStyleUIReset);
3189 nsStyleUIReset::~nsStyleUIReset() { MOZ_COUNT_DTOR(nsStyleUIReset); }
3191 nsChangeHint nsStyleUIReset::CalcDifference(
3192 const nsStyleUIReset& aNewData) const {
3193 nsChangeHint hint = nsChangeHint(0);
3195 if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) {
3196 hint |= nsChangeHint_ReconstructFrame;
3198 if (mScrollbarWidth != aNewData.mScrollbarWidth) {
3199 // For scrollbar-width change, we need some special handling similar
3200 // to overflow properties. Specifically, we may need to reconstruct
3201 // the scrollbar or force reflow of the viewport scrollbar.
3202 hint |= nsChangeHint_ScrollbarChange;
3204 if (mWindowShadow != aNewData.mWindowShadow) {
3205 // We really need just an nsChangeHint_SyncFrameView, except
3206 // on an ancestor of the frame, so we get that by doing a
3207 // reflow.
3208 hint |= NS_STYLE_HINT_REFLOW;
3210 if (mUserSelect != aNewData.mUserSelect) {
3211 hint |= NS_STYLE_HINT_VISUAL;
3214 if (mWindowDragging != aNewData.mWindowDragging) {
3215 hint |= nsChangeHint_SchedulePaint;
3218 if (!hint && (mIMEMode != aNewData.mIMEMode ||
3219 mWindowOpacity != aNewData.mWindowOpacity ||
3220 mMozWindowTransform != aNewData.mMozWindowTransform)) {
3221 hint |= nsChangeHint_NeutralChange;
3224 return hint;
3227 //-----------------------
3228 // nsStyleEffects
3231 nsStyleEffects::nsStyleEffects(const Document&)
3232 : mClip(StyleClipRectOrAuto::Auto()),
3233 mOpacity(1.0f),
3234 mMixBlendMode(StyleBlend::Normal) {
3235 MOZ_COUNT_CTOR(nsStyleEffects);
3238 nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource)
3239 : mFilters(aSource.mFilters),
3240 mBoxShadow(aSource.mBoxShadow),
3241 mBackdropFilters(aSource.mBackdropFilters),
3242 mClip(aSource.mClip),
3243 mOpacity(aSource.mOpacity),
3244 mMixBlendMode(aSource.mMixBlendMode) {
3245 MOZ_COUNT_CTOR(nsStyleEffects);
3248 nsStyleEffects::~nsStyleEffects() { MOZ_COUNT_DTOR(nsStyleEffects); }
3250 static bool AnyAutonessChanged(const StyleClipRectOrAuto& aOld,
3251 const StyleClipRectOrAuto& aNew) {
3252 if (aOld.IsAuto() != aNew.IsAuto()) {
3253 return true;
3255 if (aOld.IsAuto()) {
3256 return false;
3258 auto& oldRect = aOld.AsRect();
3259 auto& newRect = aNew.AsRect();
3260 return oldRect.top.IsAuto() != newRect.top.IsAuto() ||
3261 oldRect.right.IsAuto() != newRect.right.IsAuto() ||
3262 oldRect.bottom.IsAuto() != newRect.bottom.IsAuto() ||
3263 oldRect.left.IsAuto() != newRect.left.IsAuto();
3266 nsChangeHint nsStyleEffects::CalcDifference(
3267 const nsStyleEffects& aNewData) const {
3268 nsChangeHint hint = nsChangeHint(0);
3270 if (mBoxShadow != aNewData.mBoxShadow) {
3271 // Update overflow regions & trigger DLBI to be sure it's noticed.
3272 // Also request a repaint, since it's possible that only the color
3273 // of the shadow is changing (and UpdateOverflow/SchedulePaint won't
3274 // repaint for that, since they won't know what needs invalidating.)
3275 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
3276 nsChangeHint_RepaintFrame;
3279 if (AnyAutonessChanged(mClip, aNewData.mClip)) {
3280 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
3281 } else if (mClip != aNewData.mClip) {
3282 // If the clip has changed, we just need to update overflow areas. DLBI
3283 // will handle the invalidation.
3284 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint;
3287 if (mOpacity != aNewData.mOpacity) {
3288 hint |= nsChangeHint_UpdateOpacityLayer;
3290 // If we're going from the optimized >=0.99 opacity value to 1.0 or back,
3291 // then repaint the frame because DLBI will not catch the invalidation.
3292 // Otherwise, just update the opacity layer.
3293 if ((mOpacity >= 0.99f && mOpacity < 1.0f && aNewData.mOpacity == 1.0f) ||
3294 (aNewData.mOpacity >= 0.99f && aNewData.mOpacity < 1.0f &&
3295 mOpacity == 1.0f)) {
3296 hint |= nsChangeHint_RepaintFrame;
3297 } else {
3298 if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) {
3299 hint |= nsChangeHint_UpdateUsesOpacity;
3304 if (HasFilters() != aNewData.HasFilters()) {
3305 // A change from/to being a containing block for position:fixed.
3306 hint |= nsChangeHint_UpdateContainingBlock;
3309 if (mFilters != aNewData.mFilters) {
3310 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame |
3311 nsChangeHint_UpdateOverflow;
3314 if (mMixBlendMode != aNewData.mMixBlendMode) {
3315 hint |= nsChangeHint_RepaintFrame;
3318 if (HasBackdropFilters() != aNewData.HasBackdropFilters()) {
3319 // A change from/to being a containing block for position:fixed.
3320 hint |= nsChangeHint_UpdateContainingBlock;
3323 if (mBackdropFilters != aNewData.mBackdropFilters) {
3324 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
3327 return hint;
3330 static bool TransformOperationHasPercent(const StyleTransformOperation& aOp) {
3331 switch (aOp.tag) {
3332 case StyleTransformOperation::Tag::TranslateX:
3333 return aOp.AsTranslateX().HasPercent();
3334 case StyleTransformOperation::Tag::TranslateY:
3335 return aOp.AsTranslateY().HasPercent();
3336 case StyleTransformOperation::Tag::TranslateZ:
3337 return false;
3338 case StyleTransformOperation::Tag::Translate3D: {
3339 auto& translate = aOp.AsTranslate3D();
3340 // NOTE(emilio): z translation is a `<length>`, so can't have percentages.
3341 return translate._0.HasPercent() || translate._1.HasPercent();
3343 case StyleTransformOperation::Tag::Translate: {
3344 auto& translate = aOp.AsTranslate();
3345 return translate._0.HasPercent() || translate._1.HasPercent();
3347 case StyleTransformOperation::Tag::AccumulateMatrix: {
3348 auto& accum = aOp.AsAccumulateMatrix();
3349 return accum.from_list.HasPercent() || accum.to_list.HasPercent();
3351 case StyleTransformOperation::Tag::InterpolateMatrix: {
3352 auto& interpolate = aOp.AsInterpolateMatrix();
3353 return interpolate.from_list.HasPercent() ||
3354 interpolate.to_list.HasPercent();
3356 case StyleTransformOperation::Tag::Perspective:
3357 case StyleTransformOperation::Tag::RotateX:
3358 case StyleTransformOperation::Tag::RotateY:
3359 case StyleTransformOperation::Tag::RotateZ:
3360 case StyleTransformOperation::Tag::Rotate:
3361 case StyleTransformOperation::Tag::Rotate3D:
3362 case StyleTransformOperation::Tag::SkewX:
3363 case StyleTransformOperation::Tag::SkewY:
3364 case StyleTransformOperation::Tag::Skew:
3365 case StyleTransformOperation::Tag::ScaleX:
3366 case StyleTransformOperation::Tag::ScaleY:
3367 case StyleTransformOperation::Tag::ScaleZ:
3368 case StyleTransformOperation::Tag::Scale:
3369 case StyleTransformOperation::Tag::Scale3D:
3370 case StyleTransformOperation::Tag::Matrix:
3371 case StyleTransformOperation::Tag::Matrix3D:
3372 return false;
3373 default:
3374 MOZ_ASSERT_UNREACHABLE("Unknown transform operation");
3375 return false;
3379 template <>
3380 bool StyleTransform::HasPercent() const {
3381 for (const auto& op : Operations()) {
3382 if (TransformOperationHasPercent(op)) {
3383 return true;
3386 return false;
3389 template <>
3390 void StyleCalcNode::ScaleLengthsBy(float aScale) {
3391 auto ScaleNode = [aScale](const StyleCalcNode& aNode) {
3392 // This const_cast could be removed by generating more mut-casts, if
3393 // needed.
3394 const_cast<StyleCalcNode&>(aNode).ScaleLengthsBy(aScale);
3397 switch (tag) {
3398 case Tag::Leaf: {
3399 auto& leaf = AsLeaf();
3400 if (leaf.IsLength()) {
3401 // This const_cast could be removed by generating more mut-casts, if
3402 // needed.
3403 const_cast<Length&>(leaf.AsLength()).ScaleBy(aScale);
3405 break;
3407 case Tag::Clamp: {
3408 auto& clamp = AsClamp();
3409 ScaleNode(*clamp.min);
3410 ScaleNode(*clamp.center);
3411 ScaleNode(*clamp.max);
3412 break;
3414 case Tag::MinMax: {
3415 for (auto& child : AsMinMax()._0.AsSpan()) {
3416 ScaleNode(child);
3418 break;
3420 case Tag::Sum: {
3421 for (auto& child : AsSum().AsSpan()) {
3422 ScaleNode(child);
3424 break;
3429 template <>
3430 template <typename ResultT, typename PercentageConverter>
3431 ResultT StyleCalcNode::ResolveInternal(ResultT aPercentageBasis,
3432 PercentageConverter aConverter) const {
3433 static_assert(std::is_same_v<decltype(aConverter(1.0f)), ResultT>);
3434 static_assert(std::is_same_v<ResultT, nscoord> ||
3435 std::is_same_v<ResultT, CSSCoord>);
3437 switch (tag) {
3438 case Tag::Leaf: {
3439 auto& leaf = AsLeaf();
3440 if (leaf.IsPercentage()) {
3441 return aConverter(leaf.AsPercentage()._0 * aPercentageBasis);
3443 if constexpr (std::is_same_v<ResultT, nscoord>) {
3444 return leaf.AsLength().ToAppUnits();
3445 } else {
3446 return leaf.AsLength().ToCSSPixels();
3449 case Tag::Clamp: {
3450 auto& clamp = AsClamp();
3451 auto min = clamp.min->ResolveInternal(aPercentageBasis, aConverter);
3452 auto center = clamp.center->ResolveInternal(aPercentageBasis, aConverter);
3453 auto max = clamp.max->ResolveInternal(aPercentageBasis, aConverter);
3454 return std::max(min, std::min(center, max));
3456 case Tag::MinMax: {
3457 auto children = AsMinMax()._0.AsSpan();
3458 StyleMinMaxOp op = AsMinMax()._1;
3460 ResultT result =
3461 children[0].ResolveInternal(aPercentageBasis, aConverter);
3462 for (auto& child : children.From(1)) {
3463 ResultT candidate = child.ResolveInternal(aPercentageBasis, aConverter);
3464 if (op == StyleMinMaxOp::Max) {
3465 result = std::max(result, candidate);
3466 } else {
3467 result = std::min(result, candidate);
3470 return result;
3472 case Tag::Sum: {
3473 ResultT result = 0;
3474 for (auto& child : AsSum().AsSpan()) {
3475 result += child.ResolveInternal(aPercentageBasis, aConverter);
3477 return result;
3481 MOZ_ASSERT_UNREACHABLE("Unknown calc node");
3482 return 0;
3485 template <>
3486 CSSCoord StyleCalcNode::ResolveToCSSPixels(CSSCoord aBasis) const {
3487 CSSCoord result =
3488 ResolveInternal(aBasis, [](CSSCoord aPercent) { return aPercent; });
3489 if (IsNaN(float(result))) {
3490 return 0.0f; // This matches style::values::normalize
3492 return result;
3495 template <>
3496 nscoord StyleCalcNode::Resolve(nscoord aBasis,
3497 CoordPercentageRounder aRounder) const {
3498 return ResolveInternal(aBasis, aRounder);