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/. */
6 
7 /*
8  * structs that contain the data provided by ComputedStyle, the
9  * internal API for computed style data for an element
10  */
11 
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"
23 
24 #include "nsCOMPtr.h"
25 
26 #include "nsBidiUtils.h"
27 #include "nsLayoutUtils.h"
28 
29 #include "imgIRequest.h"
30 #include "imgIContainer.h"
31 #include "CounterStyleManager.h"
32 
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"
49 
50 using namespace mozilla;
51 using namespace mozilla::dom;
52 
53 static const nscoord kMediumBorderWidth = nsPresContext::CSSPixelsToAppUnits(3);
54 
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;
59 
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;
66 };
67 
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
74 
operator ==(const StyleCssUrlData & aOther) const75 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;
85   }
86   return serialization == aOther.serialization;
87 }
88 
~StyleLoadData()89 StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); }
90 
ResolveLocalRef(nsIURI * aURI) const91 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(nsIURI* aURI) const {
92   nsCOMPtr<nsIURI> result = GetURI();
93   if (result && IsLocalRef()) {
94     nsCString ref;
95     result->GetRef(ref);
96 
97     nsresult rv = NS_MutateURI(aURI).SetRef(ref).Finalize(result);
98 
99     if (NS_FAILED(rv)) {
100       // If setting the ref failed, just return the original URI.
101       result = aURI;
102     }
103   }
104   return result.forget();
105 }
106 
ResolveLocalRef(const nsIContent * aContent) const107 already_AddRefed<nsIURI> StyleComputedUrl::ResolveLocalRef(
108     const nsIContent* aContent) const {
109   return ResolveLocalRef(aContent->GetBaseURI());
110 }
111 
ResolveImage(Document & aDocument,const StyleComputedUrl * aOldImage)112 void StyleComputedUrl::ResolveImage(Document& aDocument,
113                                     const StyleComputedUrl* aOldImage) {
114   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
115 
116   StyleLoadData& data = LoadData();
117 
118   MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE));
119 
120   data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE;
121 
122   nsIURI* docURI = aDocument.GetDocumentURI();
123   if (HasRef()) {
124     bool isEqualExceptRef = false;
125     nsIURI* imageURI = GetURI();
126     if (!imageURI) {
127       return;
128     }
129 
130     if (NS_SUCCEEDED(imageURI->EqualsExceptRef(docURI, &isEqualExceptRef)) &&
131         isEqualExceptRef) {
132       // Prevent loading an internal resource.
133       return;
134     }
135   }
136 
137   MOZ_ASSERT(NS_IsMainThread());
138 
139   // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
140   // lack of non-http image caching in imagelib (bug 1406134), which causes
141   // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
142   // 1440305, but that seems too risky, and a lot of work to do before 60.
143   //
144   // Once that's fixed, the "old style" argument to TriggerImageLoads can go
145   // away, and same for mSharedCount in the image loader and so on.
146   const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) &&
147                           aOldImage && aOldImage->IsImageResolved() &&
148                           *this == *aOldImage;
149 
150   RefPtr<imgRequestProxy> request;
151   if (reuseProxy) {
152     request = aOldImage->LoadData().resolved_image;
153     if (request) {
154       css::ImageLoader::NoteSharedLoad(request);
155     }
156   } else {
157     request = css::ImageLoader::LoadImage(*this, aDocument);
158   }
159 
160   if (!request) {
161     return;
162   }
163 
164   data.resolved_image = request.forget().take();
165 
166   // Boost priority now that we know the image is present in the ComputedStyle
167   // of some frame.
168   data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE);
169 }
170 
171 /**
172  * Runnable to release the image request's mRequestProxy
173  * and mImageTracker on the main thread, and to perform
174  * any necessary unlocking and untracking of the image.
175  */
176 class StyleImageRequestCleanupTask final : public mozilla::Runnable {
177  public:
StyleImageRequestCleanupTask(StyleLoadData & aData)178   explicit StyleImageRequestCleanupTask(StyleLoadData& aData)
179       : mozilla::Runnable("StyleImageRequestCleanupTask"),
180         mRequestProxy(dont_AddRef(aData.resolved_image)) {
181     MOZ_ASSERT(mRequestProxy);
182     aData.resolved_image = nullptr;
183   }
184 
Run()185   NS_IMETHOD Run() final {
186     MOZ_ASSERT(NS_IsMainThread());
187     css::ImageLoader::UnloadImage(mRequestProxy);
188     return NS_OK;
189   }
190 
191  protected:
~StyleImageRequestCleanupTask()192   virtual ~StyleImageRequestCleanupTask() {
193     MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
194                "mRequestProxy destructor need to run on the main thread!");
195   }
196 
197  private:
198   // Since we always dispatch this runnable to the main thread, these will be
199   // released on the main thread when the runnable itself is released.
200   RefPtr<imgRequestProxy> mRequestProxy;
201 };
202 
203 // This is defined here for parallelism with LoadURI.
Gecko_LoadData_Drop(StyleLoadData * aData)204 void Gecko_LoadData_Drop(StyleLoadData* aData) {
205   if (aData->resolved_image) {
206     auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData);
207     if (NS_IsMainThread()) {
208       task->Run();
209     } else {
210       // if Resolve was not called at some point, mDocGroup is not set.
211       SchedulerGroup::Dispatch(TaskCategory::Other, task.forget());
212     }
213   }
214 
215   // URIs are safe to refcount from any thread.
216   NS_IF_RELEASE(aData->resolved_uri);
217 }
218 
219 // --------------------
220 // nsStyleFont
221 //
nsStyleFont(const nsStyleFont & aSrc)222 nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
223     : mFont(aSrc.mFont),
224       mSize(aSrc.mSize),
225       mFontSizeFactor(aSrc.mFontSizeFactor),
226       mFontSizeOffset(aSrc.mFontSizeOffset),
227       mFontSizeKeyword(aSrc.mFontSizeKeyword),
228       mGenericID(aSrc.mGenericID),
229       mScriptLevel(aSrc.mScriptLevel),
230       mMathVariant(aSrc.mMathVariant),
231       mMathDisplay(aSrc.mMathDisplay),
232       mMinFontSizeRatio(aSrc.mMinFontSizeRatio),
233       mExplicitLanguage(aSrc.mExplicitLanguage),
234       mAllowZoomAndMinSize(aSrc.mAllowZoomAndMinSize),
235       mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
236       mScriptMinSize(aSrc.mScriptMinSize),
237       mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier),
238       mLanguage(aSrc.mLanguage) {
239   MOZ_COUNT_CTOR(nsStyleFont);
240 }
241 
nsStyleFont(const Document & aDocument)242 nsStyleFont::nsStyleFont(const Document& aDocument)
243     : mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont(
244           StyleGenericFontFamily::None)),
245       mSize(ZoomText(aDocument, mFont.size)),
246       mFontSizeFactor(1.0),
247       mFontSizeOffset(0),
248       mFontSizeKeyword(StyleFontSize::Medium),
249       mGenericID(StyleGenericFontFamily::None),
250       mScriptLevel(0),
251       mMathVariant(NS_MATHML_MATHVARIANT_NONE),
252       mMathDisplay(NS_MATHML_DISPLAYSTYLE_INLINE),
253       mMinFontSizeRatio(100),  // 100%
254       mExplicitLanguage(false),
255       mAllowZoomAndMinSize(true),
256       mScriptUnconstrainedSize(mSize),
257       mScriptMinSize(nsPresContext::CSSTwipsToAppUnits(
258           NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))),
259       mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER),
260       mLanguage(aDocument.GetLanguageForStyle()) {
261   MOZ_COUNT_CTOR(nsStyleFont);
262   MOZ_ASSERT(NS_IsMainThread());
263   mFont.size = mSize;
264   if (!nsContentUtils::IsChromeDoc(&aDocument)) {
265     nscoord minimumFontSize =
266         aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize;
267     mFont.size = std::max(mSize, minimumFontSize);
268   }
269 }
270 
CalcDifference(const nsStyleFont & aNewData) const271 nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const {
272   MOZ_ASSERT(
273       mAllowZoomAndMinSize == aNewData.mAllowZoomAndMinSize,
274       "expected mAllowZoomAndMinSize to be the same on both nsStyleFonts");
275   if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage ||
276       mExplicitLanguage != aNewData.mExplicitLanguage ||
277       mMathVariant != aNewData.mMathVariant ||
278       mMathDisplay != aNewData.mMathDisplay ||
279       mMinFontSizeRatio != aNewData.mMinFontSizeRatio) {
280     return NS_STYLE_HINT_REFLOW;
281   }
282 
283   switch (mFont.CalcDifference(aNewData.mFont)) {
284     case nsFont::MaxDifference::eLayoutAffecting:
285       return NS_STYLE_HINT_REFLOW;
286 
287     case nsFont::MaxDifference::eVisual:
288       return NS_STYLE_HINT_VISUAL;
289 
290     case nsFont::MaxDifference::eNone:
291       break;
292   }
293 
294   // XXX Should any of these cause a non-nsChangeHint_NeutralChange change?
295   if (mGenericID != aNewData.mGenericID ||
296       mScriptLevel != aNewData.mScriptLevel ||
297       mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize ||
298       mScriptMinSize != aNewData.mScriptMinSize ||
299       mScriptSizeMultiplier != aNewData.mScriptSizeMultiplier) {
300     return nsChangeHint_NeutralChange;
301   }
302 
303   return nsChangeHint(0);
304 }
305 
ZoomText(const Document & aDocument,nscoord aSize)306 nscoord nsStyleFont::ZoomText(const Document& aDocument, nscoord aSize) {
307   float textZoom = 1.0;
308   if (auto* pc = aDocument.GetPresContext()) {
309     textZoom = pc->EffectiveTextZoom();
310   }
311   // aSize can be negative (e.g.: calc(-1px)) so we can't assert that here.
312   // The caller is expected deal with that.
313   return NSToCoordTruncClamped(float(aSize) * textZoom);
314 }
315 
316 template <typename T>
StyleRectWithAllSides(const T & aSide)317 static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
318   return {aSide, aSide, aSide, aSide};
319 }
320 
nsStyleMargin(const Document & aDocument)321 nsStyleMargin::nsStyleMargin(const Document& aDocument)
322     : mMargin(StyleRectWithAllSides(
323           LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Zero()))),
324       mScrollMargin(StyleRectWithAllSides(StyleLength{0.})) {
325   MOZ_COUNT_CTOR(nsStyleMargin);
326 }
327 
nsStyleMargin(const nsStyleMargin & aSrc)328 nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
329     : mMargin(aSrc.mMargin), mScrollMargin(aSrc.mScrollMargin) {
330   MOZ_COUNT_CTOR(nsStyleMargin);
331 }
332 
CalcDifference(const nsStyleMargin & aNewData) const333 nsChangeHint nsStyleMargin::CalcDifference(
334     const nsStyleMargin& aNewData) const {
335   nsChangeHint hint = nsChangeHint(0);
336 
337   if (mMargin != aNewData.mMargin) {
338     // Margin differences can't affect descendant intrinsic sizes and
339     // don't need to force children to reflow.
340     hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition |
341             nsChangeHint_ClearAncestorIntrinsics;
342   }
343 
344   if (mScrollMargin != aNewData.mScrollMargin) {
345     // FIXME: Bug 1530253 Support re-snapping when scroll-margin changes.
346     hint |= nsChangeHint_NeutralChange;
347   }
348 
349   return hint;
350 }
351 
nsStylePadding(const Document & aDocument)352 nsStylePadding::nsStylePadding(const Document& aDocument)
353     : mPadding(StyleRectWithAllSides(LengthPercentage::Zero())),
354       mScrollPadding(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())) {
355   MOZ_COUNT_CTOR(nsStylePadding);
356 }
357 
nsStylePadding(const nsStylePadding & aSrc)358 nsStylePadding::nsStylePadding(const nsStylePadding& aSrc)
359     : mPadding(aSrc.mPadding), mScrollPadding(aSrc.mScrollPadding) {
360   MOZ_COUNT_CTOR(nsStylePadding);
361 }
362 
CalcDifference(const nsStylePadding & aNewData) const363 nsChangeHint nsStylePadding::CalcDifference(
364     const nsStylePadding& aNewData) const {
365   nsChangeHint hint = nsChangeHint(0);
366 
367   if (mPadding != aNewData.mPadding) {
368     // Padding differences can't affect descendant intrinsic sizes, but do need
369     // to force children to reflow so that we can reposition them, since their
370     // offsets are from our frame bounds but our content rect's position within
371     // those bounds is moving.
372     // FIXME: It would be good to return a weaker hint here that doesn't
373     // force reflow of all descendants, but the hint would need to force
374     // reflow of the frame's children (see how
375     // ReflowInput::InitResizeFlags initializes the inline-resize flag).
376     hint |= NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
377   }
378 
379   if (mScrollPadding != aNewData.mScrollPadding) {
380     // FIXME: Bug 1530253 Support re-snapping when scroll-padding changes.
381     hint |= nsChangeHint_NeutralChange;
382   }
383 
384   return hint;
385 }
386 
TwipsPerPixel(const Document & aDocument)387 static nscoord TwipsPerPixel(const Document& aDocument) {
388   auto* pc = aDocument.GetPresContext();
389   return pc ? pc->AppUnitsPerDevPixel() : mozilla::AppUnitsPerCSSPixel();
390 }
391 
ZeroBorderRadius()392 static inline BorderRadius ZeroBorderRadius() {
393   auto zero = LengthPercentage::Zero();
394   return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}};
395 }
396 
nsStyleBorder(const Document & aDocument)397 nsStyleBorder::nsStyleBorder(const Document& aDocument)
398     : mBorderRadius(ZeroBorderRadius()),
399       mBorderImageSource(StyleImage::None()),
400       mBorderImageWidth(
401           StyleRectWithAllSides(StyleBorderImageSideWidth::Number(1.))),
402       mBorderImageOutset(
403           StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))),
404       mBorderImageSlice(
405           {StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})),
406            false}),
407       mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
408       mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
409       mFloatEdge(StyleFloatEdge::ContentBox),
410       mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
411       mBorderTopColor(StyleColor::CurrentColor()),
412       mBorderRightColor(StyleColor::CurrentColor()),
413       mBorderBottomColor(StyleColor::CurrentColor()),
414       mBorderLeftColor(StyleColor::CurrentColor()),
415       mComputedBorder(0, 0, 0, 0),
416       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
417   MOZ_COUNT_CTOR(nsStyleBorder);
418 
419   nscoord medium = kMediumBorderWidth;
420   for (const auto side : mozilla::AllPhysicalSides()) {
421     mBorder.Side(side) = medium;
422     mBorderStyle[side] = StyleBorderStyle::None;
423   }
424 }
425 
nsStyleBorder(const nsStyleBorder & aSrc)426 nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
427     : mBorderRadius(aSrc.mBorderRadius),
428       mBorderImageSource(aSrc.mBorderImageSource),
429       mBorderImageWidth(aSrc.mBorderImageWidth),
430       mBorderImageOutset(aSrc.mBorderImageOutset),
431       mBorderImageSlice(aSrc.mBorderImageSlice),
432       mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
433       mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
434       mFloatEdge(aSrc.mFloatEdge),
435       mBoxDecorationBreak(aSrc.mBoxDecorationBreak),
436       mBorderTopColor(aSrc.mBorderTopColor),
437       mBorderRightColor(aSrc.mBorderRightColor),
438       mBorderBottomColor(aSrc.mBorderBottomColor),
439       mBorderLeftColor(aSrc.mBorderLeftColor),
440       mComputedBorder(aSrc.mComputedBorder),
441       mBorder(aSrc.mBorder),
442       mTwipsPerPixel(aSrc.mTwipsPerPixel) {
443   MOZ_COUNT_CTOR(nsStyleBorder);
444   for (const auto side : mozilla::AllPhysicalSides()) {
445     mBorderStyle[side] = aSrc.mBorderStyle[side];
446   }
447 }
448 
~nsStyleBorder()449 nsStyleBorder::~nsStyleBorder() { MOZ_COUNT_DTOR(nsStyleBorder); }
450 
TriggerImageLoads(Document & aDocument,const nsStyleBorder * aOldStyle)451 void nsStyleBorder::TriggerImageLoads(Document& aDocument,
452                                       const nsStyleBorder* aOldStyle) {
453   MOZ_ASSERT(NS_IsMainThread());
454 
455   mBorderImageSource.ResolveImage(
456       aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
457 }
458 
GetImageOutset() const459 nsMargin nsStyleBorder::GetImageOutset() const {
460   // We don't check whether there is a border-image (which is OK since
461   // the initial values yields 0 outset) so that we don't have to
462   // reflow to update overflow areas when an image loads.
463   nsMargin outset;
464   for (const auto s : mozilla::AllPhysicalSides()) {
465     const auto& coord = mBorderImageOutset.Get(s);
466     nscoord value;
467     if (coord.IsLength()) {
468       value = coord.AsLength().ToAppUnits();
469     } else {
470       MOZ_ASSERT(coord.IsNumber());
471       value = coord.AsNumber() * mComputedBorder.Side(s);
472     }
473     outset.Side(s) = value;
474   }
475   return outset;
476 }
477 
CalcDifference(const nsStyleBorder & aNewData) const478 nsChangeHint nsStyleBorder::CalcDifference(
479     const nsStyleBorder& aNewData) const {
480   // FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these
481   // differences should not need to clear descendant intrinsics.
482   // FIXME: It would be good to return a weaker hint for the
483   // GetComputedBorder() differences (and perhaps others) that doesn't
484   // force reflow of all descendants, but the hint would need to force
485   // reflow of the frame's children (see how
486   // ReflowInput::InitResizeFlags initializes the inline-resize flag).
487   if (mTwipsPerPixel != aNewData.mTwipsPerPixel ||
488       GetComputedBorder() != aNewData.GetComputedBorder() ||
489       mFloatEdge != aNewData.mFloatEdge ||
490       mBorderImageOutset != aNewData.mBorderImageOutset ||
491       mBoxDecorationBreak != aNewData.mBoxDecorationBreak) {
492     return NS_STYLE_HINT_REFLOW;
493   }
494 
495   for (const auto ix : mozilla::AllPhysicalSides()) {
496     // See the explanation in nsChangeHint.h of
497     // nsChangeHint_BorderStyleNoneChange .
498     // Furthermore, even though we know *this* side is 0 width, just
499     // assume a repaint hint for some other change rather than bother
500     // tracking this result through the rest of the function.
501     if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) {
502       return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange;
503     }
504   }
505 
506   // Note that mBorderStyle stores not only the border style but also
507   // color-related flags.  Given that we've already done an mComputedBorder
508   // comparison, border-style differences can only lead to a repaint hint.  So
509   // it's OK to just compare the values directly -- if either the actual
510   // style or the color flags differ we want to repaint.
511   for (const auto ix : mozilla::AllPhysicalSides()) {
512     if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] ||
513         BorderColorFor(ix) != aNewData.BorderColorFor(ix)) {
514       return nsChangeHint_RepaintFrame;
515     }
516   }
517 
518   if (mBorderRadius != aNewData.mBorderRadius) {
519     return nsChangeHint_RepaintFrame;
520   }
521 
522   // Loading status of the border image can be accessed in main thread only
523   // while CalcDifference might be executed on a background thread. As a
524   // result, we have to check mBorderImage* fields even before border image was
525   // actually loaded.
526   if (!mBorderImageSource.IsNone() || !aNewData.mBorderImageSource.IsNone()) {
527     if (mBorderImageSource != aNewData.mBorderImageSource ||
528         mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
529         mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
530         mBorderImageSlice != aNewData.mBorderImageSlice ||
531         mBorderImageWidth != aNewData.mBorderImageWidth) {
532       return nsChangeHint_RepaintFrame;
533     }
534   }
535 
536   // mBorder is the specified border value.  Changes to this don't
537   // need any change processing, since we operate on the computed
538   // border values instead.
539   if (mBorder != aNewData.mBorder) {
540     return nsChangeHint_NeutralChange;
541   }
542 
543   // mBorderImage* fields are checked only when border-image is not 'none'.
544   if (mBorderImageSource != aNewData.mBorderImageSource ||
545       mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
546       mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
547       mBorderImageSlice != aNewData.mBorderImageSlice ||
548       mBorderImageWidth != aNewData.mBorderImageWidth) {
549     return nsChangeHint_NeutralChange;
550   }
551 
552   return nsChangeHint(0);
553 }
554 
nsStyleOutline(const Document & aDocument)555 nsStyleOutline::nsStyleOutline(const Document& aDocument)
556     : mOutlineRadius(ZeroBorderRadius()),
557       mOutlineWidth(kMediumBorderWidth),
558       mOutlineOffset({0.0f}),
559       mOutlineColor(StyleColor::CurrentColor()),
560       mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
561       mActualOutlineWidth(0),
562       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
563   MOZ_COUNT_CTOR(nsStyleOutline);
564 }
565 
nsStyleOutline(const nsStyleOutline & aSrc)566 nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
567     : mOutlineRadius(aSrc.mOutlineRadius),
568       mOutlineWidth(aSrc.mOutlineWidth),
569       mOutlineOffset(aSrc.mOutlineOffset),
570       mOutlineColor(aSrc.mOutlineColor),
571       mOutlineStyle(aSrc.mOutlineStyle),
572       mActualOutlineWidth(aSrc.mActualOutlineWidth),
573       mTwipsPerPixel(aSrc.mTwipsPerPixel) {
574   MOZ_COUNT_CTOR(nsStyleOutline);
575 }
576 
CalcDifference(const nsStyleOutline & aNewData) const577 nsChangeHint nsStyleOutline::CalcDifference(
578     const nsStyleOutline& aNewData) const {
579   if (mActualOutlineWidth != aNewData.mActualOutlineWidth ||
580       (mActualOutlineWidth > 0 && mOutlineOffset != aNewData.mOutlineOffset)) {
581     return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
582            nsChangeHint_RepaintFrame;
583   }
584 
585   if (mOutlineStyle != aNewData.mOutlineStyle ||
586       mOutlineColor != aNewData.mOutlineColor ||
587       mOutlineRadius != aNewData.mOutlineRadius) {
588     if (mActualOutlineWidth > 0) {
589       return nsChangeHint_RepaintFrame;
590     }
591     return nsChangeHint_NeutralChange;
592   }
593 
594   if (mOutlineWidth != aNewData.mOutlineWidth ||
595       mOutlineOffset != aNewData.mOutlineOffset ||
596       mTwipsPerPixel != aNewData.mTwipsPerPixel) {
597     return nsChangeHint_NeutralChange;
598   }
599 
600   return nsChangeHint(0);
601 }
602 
603 // --------------------
604 // nsStyleList
605 //
nsStyleList(const Document & aDocument)606 nsStyleList::nsStyleList(const Document& aDocument)
607     : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE),
608       mQuotes(StyleQuotes::Auto()),
609       mListStyleImage(StyleImageUrlOrNone::None()),
610       mImageRegion(StyleClipRectOrAuto::Auto()),
611       mMozListReversed(StyleMozListReversed::False) {
612   MOZ_COUNT_CTOR(nsStyleList);
613   MOZ_ASSERT(NS_IsMainThread());
614 
615   mCounterStyle = nsGkAtoms::disc;
616 }
617 
~nsStyleList()618 nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
619 
nsStyleList(const nsStyleList & aSource)620 nsStyleList::nsStyleList(const nsStyleList& aSource)
621     : mListStylePosition(aSource.mListStylePosition),
622       mCounterStyle(aSource.mCounterStyle),
623       mQuotes(aSource.mQuotes),
624       mListStyleImage(aSource.mListStyleImage),
625       mImageRegion(aSource.mImageRegion),
626       mMozListReversed(aSource.mMozListReversed) {
627   MOZ_COUNT_CTOR(nsStyleList);
628 }
629 
TriggerImageLoads(Document & aDocument,const nsStyleList * aOldStyle)630 void nsStyleList::TriggerImageLoads(Document& aDocument,
631                                     const nsStyleList* aOldStyle) {
632   MOZ_ASSERT(NS_IsMainThread());
633 
634   if (mListStyleImage.IsUrl() && !mListStyleImage.AsUrl().IsImageResolved()) {
635     auto* oldUrl = aOldStyle && aOldStyle->mListStyleImage.IsUrl()
636                        ? &aOldStyle->mListStyleImage.AsUrl()
637                        : nullptr;
638     const_cast<StyleComputedImageUrl&>(mListStyleImage.AsUrl())
639         .ResolveImage(aDocument, oldUrl);
640   }
641 }
642 
CalcDifference(const nsStyleList & aNewData,const nsStyleDisplay & aOldDisplay) const643 nsChangeHint nsStyleList::CalcDifference(
644     const nsStyleList& aNewData, const nsStyleDisplay& aOldDisplay) const {
645   // If the quotes implementation is ever going to change we might not need
646   // a framechange here and a reflow should be sufficient.  See bug 35768.
647   if (mQuotes != aNewData.mQuotes) {
648     return nsChangeHint_ReconstructFrame;
649   }
650   nsChangeHint hint = nsChangeHint(0);
651   // Only elements whose display value is list-item can be affected by
652   // list-style-position and list-style-type. If the old display struct
653   // doesn't exist, assume it isn't affected by display value at all,
654   // and thus these properties should not affect it either. This also
655   // relies on that when the display value changes from something else
656   // to list-item, that change itself would cause ReconstructFrame.
657   if (aOldDisplay.IsListItem()) {
658     if (mListStylePosition != aNewData.mListStylePosition) {
659       return nsChangeHint_ReconstructFrame;
660     }
661     if (mCounterStyle != aNewData.mCounterStyle) {
662       return NS_STYLE_HINT_REFLOW;
663     }
664   } else if (mListStylePosition != aNewData.mListStylePosition ||
665              mCounterStyle != aNewData.mCounterStyle) {
666     hint = nsChangeHint_NeutralChange;
667   }
668   // This is an internal UA-sheet property that is true only for <ol reversed>
669   // so hopefully it changes rarely.
670   if (mMozListReversed != aNewData.mMozListReversed) {
671     return NS_STYLE_HINT_REFLOW;
672   }
673   // list-style-image and -moz-image-region may affect some XUL elements
674   // regardless of display value, so we still need to check them.
675   if (mListStyleImage != aNewData.mListStyleImage) {
676     return NS_STYLE_HINT_REFLOW;
677   }
678   if (mImageRegion != aNewData.mImageRegion) {
679     nsRect region = GetImageRegion();
680     nsRect newRegion = aNewData.GetImageRegion();
681     if (region.width != newRegion.width || region.height != newRegion.height) {
682       return NS_STYLE_HINT_REFLOW;
683     }
684     return NS_STYLE_HINT_VISUAL;
685   }
686   return hint;
687 }
688 
GetListStyleImageURI() const689 already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
690   if (!mListStyleImage.IsUrl()) {
691     return nullptr;
692   }
693 
694   nsCOMPtr<nsIURI> uri = mListStyleImage.AsUrl().GetURI();
695   return uri.forget();
696 }
697 
698 // --------------------
699 // nsStyleXUL
700 //
nsStyleXUL(const Document & aDocument)701 nsStyleXUL::nsStyleXUL(const Document& aDocument)
702     : mBoxFlex(0.0f),
703       mBoxOrdinal(1),
704       mBoxAlign(StyleBoxAlign::Stretch),
705       mBoxDirection(StyleBoxDirection::Normal),
706       mBoxOrient(StyleBoxOrient::Horizontal),
707       mBoxPack(StyleBoxPack::Start) {
708   MOZ_COUNT_CTOR(nsStyleXUL);
709 }
710 
~nsStyleXUL()711 nsStyleXUL::~nsStyleXUL() { MOZ_COUNT_DTOR(nsStyleXUL); }
712 
nsStyleXUL(const nsStyleXUL & aSource)713 nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource)
714     : mBoxFlex(aSource.mBoxFlex),
715       mBoxOrdinal(aSource.mBoxOrdinal),
716       mBoxAlign(aSource.mBoxAlign),
717       mBoxDirection(aSource.mBoxDirection),
718       mBoxOrient(aSource.mBoxOrient),
719       mBoxPack(aSource.mBoxPack) {
720   MOZ_COUNT_CTOR(nsStyleXUL);
721 }
722 
CalcDifference(const nsStyleXUL & aNewData) const723 nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const {
724   if (mBoxAlign == aNewData.mBoxAlign &&
725       mBoxDirection == aNewData.mBoxDirection &&
726       mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient &&
727       mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal) {
728     return nsChangeHint(0);
729   }
730   if (mBoxOrdinal != aNewData.mBoxOrdinal) {
731     return nsChangeHint_ReconstructFrame;
732   }
733   return NS_STYLE_HINT_REFLOW;
734 }
735 
736 // --------------------
737 // nsStyleColumn
738 //
739 /* static */ const uint32_t nsStyleColumn::kMaxColumnCount;
740 /* static */ const uint32_t nsStyleColumn::kColumnCountAuto;
741 
nsStyleColumn(const Document & aDocument)742 nsStyleColumn::nsStyleColumn(const Document& aDocument)
743     : mColumnWidth(LengthOrAuto::Auto()),
744       mColumnRuleColor(StyleColor::CurrentColor()),
745       mColumnRuleStyle(StyleBorderStyle::None),
746       mColumnRuleWidth(kMediumBorderWidth),
747       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
748   MOZ_COUNT_CTOR(nsStyleColumn);
749 }
750 
~nsStyleColumn()751 nsStyleColumn::~nsStyleColumn() { MOZ_COUNT_DTOR(nsStyleColumn); }
752 
nsStyleColumn(const nsStyleColumn & aSource)753 nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource)
754     : mColumnCount(aSource.mColumnCount),
755       mColumnWidth(aSource.mColumnWidth),
756       mColumnRuleColor(aSource.mColumnRuleColor),
757       mColumnRuleStyle(aSource.mColumnRuleStyle),
758       mColumnFill(aSource.mColumnFill),
759       mColumnSpan(aSource.mColumnSpan),
760       mColumnRuleWidth(aSource.mColumnRuleWidth),
761       mTwipsPerPixel(aSource.mTwipsPerPixel) {
762   MOZ_COUNT_CTOR(nsStyleColumn);
763 }
764 
CalcDifference(const nsStyleColumn & aNewData) const765 nsChangeHint nsStyleColumn::CalcDifference(
766     const nsStyleColumn& aNewData) const {
767   if (mColumnWidth.IsAuto() != aNewData.mColumnWidth.IsAuto() ||
768       mColumnCount != aNewData.mColumnCount ||
769       mColumnSpan != aNewData.mColumnSpan) {
770     // We force column count changes to do a reframe, because it's tricky to
771     // handle some edge cases where the column count gets smaller and content
772     // overflows.
773     // XXX not ideal
774     return nsChangeHint_ReconstructFrame;
775   }
776 
777   if (mColumnWidth != aNewData.mColumnWidth ||
778       mColumnFill != aNewData.mColumnFill) {
779     return NS_STYLE_HINT_REFLOW;
780   }
781 
782   if (GetComputedColumnRuleWidth() != aNewData.GetComputedColumnRuleWidth() ||
783       mColumnRuleStyle != aNewData.mColumnRuleStyle ||
784       mColumnRuleColor != aNewData.mColumnRuleColor) {
785     return NS_STYLE_HINT_VISUAL;
786   }
787 
788   // XXX Is it right that we never check mTwipsPerPixel to return a
789   // non-nsChangeHint_NeutralChange hint?
790   if (mColumnRuleWidth != aNewData.mColumnRuleWidth ||
791       mTwipsPerPixel != aNewData.mTwipsPerPixel) {
792     return nsChangeHint_NeutralChange;
793   }
794 
795   return nsChangeHint(0);
796 }
797 
798 using SVGPaintFallback = StyleGenericSVGPaintFallback<StyleColor>;
799 
800 // --------------------
801 // nsStyleSVG
802 //
nsStyleSVG(const Document & aDocument)803 nsStyleSVG::nsStyleSVG(const Document& aDocument)
804     : mFill{StyleSVGPaintKind::Color(StyleColor::Black()),
805             SVGPaintFallback::Unset()},
806       mStroke{StyleSVGPaintKind::None(), SVGPaintFallback::Unset()},
807       mMarkerEnd(StyleUrlOrNone::None()),
808       mMarkerMid(StyleUrlOrNone::None()),
809       mMarkerStart(StyleUrlOrNone::None()),
810       mMozContextProperties{{}, {0}},
811       mStrokeDasharray(StyleSVGStrokeDashArray::Values({})),
812       mStrokeDashoffset(
813           StyleSVGLength::LengthPercentage(LengthPercentage::Zero())),
814       mStrokeWidth(
815           StyleSVGWidth::LengthPercentage(LengthPercentage::FromPixels(1.0f))),
816       mFillOpacity(StyleSVGOpacity::Opacity(1.0f)),
817       mStrokeMiterlimit(4.0f),
818       mStrokeOpacity(StyleSVGOpacity::Opacity(1.0f)),
819       mClipRule(StyleFillRule::Nonzero),
820       mColorInterpolation(StyleColorInterpolation::Srgb),
821       mColorInterpolationFilters(StyleColorInterpolation::Linearrgb),
822       mFillRule(StyleFillRule::Nonzero),
823       mPaintOrder(0),
824       mShapeRendering(StyleShapeRendering::Auto),
825       mStrokeLinecap(StyleStrokeLinecap::Butt),
826       mStrokeLinejoin(StyleStrokeLinejoin::Miter),
827       mDominantBaseline(StyleDominantBaseline::Auto),
828       mTextAnchor(StyleTextAnchor::Start) {
829   MOZ_COUNT_CTOR(nsStyleSVG);
830 }
831 
~nsStyleSVG()832 nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); }
833 
nsStyleSVG(const nsStyleSVG & aSource)834 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
835     : mFill(aSource.mFill),
836       mStroke(aSource.mStroke),
837       mMarkerEnd(aSource.mMarkerEnd),
838       mMarkerMid(aSource.mMarkerMid),
839       mMarkerStart(aSource.mMarkerStart),
840       mMozContextProperties(aSource.mMozContextProperties),
841       mStrokeDasharray(aSource.mStrokeDasharray),
842       mStrokeDashoffset(aSource.mStrokeDashoffset),
843       mStrokeWidth(aSource.mStrokeWidth),
844       mFillOpacity(aSource.mFillOpacity),
845       mStrokeMiterlimit(aSource.mStrokeMiterlimit),
846       mStrokeOpacity(aSource.mStrokeOpacity),
847       mClipRule(aSource.mClipRule),
848       mColorInterpolation(aSource.mColorInterpolation),
849       mColorInterpolationFilters(aSource.mColorInterpolationFilters),
850       mFillRule(aSource.mFillRule),
851       mPaintOrder(aSource.mPaintOrder),
852       mShapeRendering(aSource.mShapeRendering),
853       mStrokeLinecap(aSource.mStrokeLinecap),
854       mStrokeLinejoin(aSource.mStrokeLinejoin),
855       mDominantBaseline(aSource.mDominantBaseline),
856       mTextAnchor(aSource.mTextAnchor) {
857   MOZ_COUNT_CTOR(nsStyleSVG);
858 }
859 
PaintURIChanged(const StyleSVGPaint & aPaint1,const StyleSVGPaint & aPaint2)860 static bool PaintURIChanged(const StyleSVGPaint& aPaint1,
861                             const StyleSVGPaint& aPaint2) {
862   if (aPaint1.kind.IsPaintServer() != aPaint2.kind.IsPaintServer()) {
863     return true;
864   }
865   return aPaint1.kind.IsPaintServer() &&
866          aPaint1.kind.AsPaintServer() != aPaint2.kind.AsPaintServer();
867 }
868 
CalcDifference(const nsStyleSVG & aNewData) const869 nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const {
870   nsChangeHint hint = nsChangeHint(0);
871 
872   if (mMarkerEnd != aNewData.mMarkerEnd || mMarkerMid != aNewData.mMarkerMid ||
873       mMarkerStart != aNewData.mMarkerStart) {
874     // Markers currently contribute to SVGGeometryFrame::mRect,
875     // so we need a reflow as well as a repaint. No intrinsic sizes need
876     // to change, so nsChangeHint_NeedReflow is sufficient.
877     return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow |
878            nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
879            nsChangeHint_RepaintFrame;
880   }
881 
882   if (mFill != aNewData.mFill || mStroke != aNewData.mStroke ||
883       mFillOpacity != aNewData.mFillOpacity ||
884       mStrokeOpacity != aNewData.mStrokeOpacity) {
885     hint |= nsChangeHint_RepaintFrame;
886     if (HasStroke() != aNewData.HasStroke() ||
887         (!HasStroke() && HasFill() != aNewData.HasFill())) {
888       // Frame bounds and overflow rects depend on whether we "have" fill or
889       // stroke. Whether we have stroke or not just changed, or else we have no
890       // stroke (in which case whether we have fill or not is significant to
891       // frame bounds) and whether we have fill or not just changed. In either
892       // case we need to reflow so the frame rect is updated.
893       // XXXperf this is a waste on non SVGGeometryFrames.
894       hint |= nsChangeHint_NeedReflow |
895               nsChangeHint_NeedDirtyReflow;  // XXX remove me: bug 876085
896     }
897     if (PaintURIChanged(mFill, aNewData.mFill) ||
898         PaintURIChanged(mStroke, aNewData.mStroke)) {
899       hint |= nsChangeHint_UpdateEffects;
900     }
901   }
902 
903   // Stroke currently contributes to SVGGeometryFrame::mRect, so
904   // we need a reflow here. No intrinsic sizes need to change, so
905   // nsChangeHint_NeedReflow is sufficient.
906   // Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect.
907   // text-anchor and dominant-baseline changes also require a reflow since
908   // they change frames' rects.
909   if (mStrokeWidth != aNewData.mStrokeWidth ||
910       mStrokeMiterlimit != aNewData.mStrokeMiterlimit ||
911       mStrokeLinecap != aNewData.mStrokeLinecap ||
912       mStrokeLinejoin != aNewData.mStrokeLinejoin ||
913       mDominantBaseline != aNewData.mDominantBaseline ||
914       mTextAnchor != aNewData.mTextAnchor) {
915     return hint | nsChangeHint_NeedReflow |
916            nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
917            nsChangeHint_RepaintFrame;
918   }
919 
920   if (hint & nsChangeHint_RepaintFrame) {
921     return hint;  // we don't add anything else below
922   }
923 
924   if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
925       mClipRule != aNewData.mClipRule ||
926       mColorInterpolation != aNewData.mColorInterpolation ||
927       mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
928       mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
929       mShapeRendering != aNewData.mShapeRendering ||
930       mStrokeDasharray != aNewData.mStrokeDasharray ||
931       mMozContextProperties.bits != aNewData.mMozContextProperties.bits) {
932     return hint | nsChangeHint_RepaintFrame;
933   }
934 
935   if (!hint) {
936     if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) {
937       hint = nsChangeHint_NeutralChange;
938     }
939   }
940 
941   return hint;
942 }
943 
944 // --------------------
945 // nsStyleSVGReset
946 //
nsStyleSVGReset(const Document & aDocument)947 nsStyleSVGReset::nsStyleSVGReset(const Document& aDocument)
948     : mX(LengthPercentage::Zero()),
949       mY(LengthPercentage::Zero()),
950       mCx(LengthPercentage::Zero()),
951       mCy(LengthPercentage::Zero()),
952       mRx(NonNegativeLengthPercentageOrAuto::Auto()),
953       mRy(NonNegativeLengthPercentageOrAuto::Auto()),
954       mR(NonNegativeLengthPercentage::Zero()),
955       mMask(nsStyleImageLayers::LayerType::Mask),
956       mClipPath(StyleClipPath::None()),
957       mStopColor(StyleColor::Black()),
958       mFloodColor(StyleColor::Black()),
959       mLightingColor(StyleColor::White()),
960       mStopOpacity(1.0f),
961       mFloodOpacity(1.0f),
962       mVectorEffect(StyleVectorEffect::None),
963       mMaskType(StyleMaskType::Luminance) {
964   MOZ_COUNT_CTOR(nsStyleSVGReset);
965 }
966 
~nsStyleSVGReset()967 nsStyleSVGReset::~nsStyleSVGReset() { MOZ_COUNT_DTOR(nsStyleSVGReset); }
968 
nsStyleSVGReset(const nsStyleSVGReset & aSource)969 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
970     : mX(aSource.mX),
971       mY(aSource.mY),
972       mCx(aSource.mCx),
973       mCy(aSource.mCy),
974       mRx(aSource.mRx),
975       mRy(aSource.mRy),
976       mR(aSource.mR),
977       mMask(aSource.mMask),
978       mClipPath(aSource.mClipPath),
979       mStopColor(aSource.mStopColor),
980       mFloodColor(aSource.mFloodColor),
981       mLightingColor(aSource.mLightingColor),
982       mStopOpacity(aSource.mStopOpacity),
983       mFloodOpacity(aSource.mFloodOpacity),
984       mVectorEffect(aSource.mVectorEffect),
985       mMaskType(aSource.mMaskType) {
986   MOZ_COUNT_CTOR(nsStyleSVGReset);
987 }
988 
TriggerImageLoads(Document & aDocument,const nsStyleSVGReset * aOldStyle)989 void nsStyleSVGReset::TriggerImageLoads(Document& aDocument,
990                                         const nsStyleSVGReset* aOldStyle) {
991   MOZ_ASSERT(NS_IsMainThread());
992   // NOTE(emilio): we intentionally don't call TriggerImageLoads for clip-path.
993 
994   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
995     auto& image = mMask.mLayers[i].mImage;
996     if (!image.IsImageRequestType()) {
997       continue;
998     }
999     const auto* url = image.GetImageRequestURLValue();
1000     // If the url is a local ref, it must be a <mask-resource>, so we don't
1001     // need to resolve the style image.
1002     if (url->IsLocalRef()) {
1003       continue;
1004     }
1005 #if 0
1006     // XXX The old style system also checks whether this is a reference to
1007     // the current document with reference, but it doesn't seem to be a
1008     // behavior mentioned anywhere, so we comment out the code for now.
1009     nsIURI* docURI = aPresContext->Document()->GetDocumentURI();
1010     if (url->EqualsExceptRef(docURI)) {
1011       continue;
1012     }
1013 #endif
1014     // Otherwise, we may need the image even if it has a reference, in case
1015     // the referenced element isn't a valid SVG <mask> element.
1016     const auto* oldImage = (aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
1017                                ? &aOldStyle->mMask.mLayers[i].mImage
1018                                : nullptr;
1019 
1020     image.ResolveImage(aDocument, oldImage);
1021   }
1022 }
1023 
CalcDifference(const nsStyleSVGReset & aNewData) const1024 nsChangeHint nsStyleSVGReset::CalcDifference(
1025     const nsStyleSVGReset& aNewData) const {
1026   nsChangeHint hint = nsChangeHint(0);
1027 
1028   if (mX != aNewData.mX || mY != aNewData.mY || mCx != aNewData.mCx ||
1029       mCy != aNewData.mCy || mR != aNewData.mR || mRx != aNewData.mRx ||
1030       mRy != aNewData.mRy) {
1031     hint |= nsChangeHint_InvalidateRenderingObservers | nsChangeHint_NeedReflow;
1032   }
1033 
1034   if (mClipPath != aNewData.mClipPath) {
1035     hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
1036   }
1037 
1038   if (mVectorEffect != aNewData.mVectorEffect) {
1039     // Stroke currently affects SVGGeometryFrame::mRect, and
1040     // vector-effect affect stroke. As a result we need to reflow if
1041     // vector-effect changes in order to have SVGGeometryFrame::
1042     // ReflowSVG called to update its mRect. No intrinsic sizes need
1043     // to change so nsChangeHint_NeedReflow is sufficient.
1044     hint |= nsChangeHint_NeedReflow |
1045             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
1046             nsChangeHint_RepaintFrame;
1047   } else if (mStopColor != aNewData.mStopColor ||
1048              mFloodColor != aNewData.mFloodColor ||
1049              mLightingColor != aNewData.mLightingColor ||
1050              mStopOpacity != aNewData.mStopOpacity ||
1051              mFloodOpacity != aNewData.mFloodOpacity ||
1052              mMaskType != aNewData.mMaskType) {
1053     hint |= nsChangeHint_RepaintFrame;
1054   }
1055 
1056   hint |=
1057       mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask);
1058 
1059   return hint;
1060 }
1061 
HasMask() const1062 bool nsStyleSVGReset::HasMask() const {
1063   for (uint32_t i = 0; i < mMask.mImageCount; i++) {
1064     if (!mMask.mLayers[i].mImage.IsNone()) {
1065       return true;
1066     }
1067   }
1068 
1069   return false;
1070 }
1071 
1072 // --------------------
1073 // nsStylePosition
1074 //
nsStylePosition(const Document & aDocument)1075 nsStylePosition::nsStylePosition(const Document& aDocument)
1076     : mObjectPosition(Position::FromPercentage(0.5f)),
1077       mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
1078       mWidth(StyleSize::Auto()),
1079       mMinWidth(StyleSize::Auto()),
1080       mMaxWidth(StyleMaxSize::None()),
1081       mHeight(StyleSize::Auto()),
1082       mMinHeight(StyleSize::Auto()),
1083       mMaxHeight(StyleMaxSize::None()),
1084       mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
1085       mAspectRatio(StyleAspectRatio::Auto()),
1086       mGridAutoFlow(StyleGridAutoFlow::ROW),
1087       mMasonryAutoFlow(NS_STYLE_MASONRY_AUTO_FLOW_INITIAL_VALUE),
1088       mAlignContent({StyleAlignFlags::NORMAL}),
1089       mAlignItems({StyleAlignFlags::NORMAL}),
1090       mAlignSelf({StyleAlignFlags::AUTO}),
1091       mJustifyContent({StyleAlignFlags::NORMAL}),
1092       mJustifyItems({{StyleAlignFlags::LEGACY}, {StyleAlignFlags::NORMAL}}),
1093       mJustifySelf({StyleAlignFlags::AUTO}),
1094       mFlexDirection(StyleFlexDirection::Row),
1095       mFlexWrap(StyleFlexWrap::Nowrap),
1096       mObjectFit(StyleObjectFit::Fill),
1097       mBoxSizing(StyleBoxSizing::Content),
1098       mOrder(NS_STYLE_ORDER_INITIAL),
1099       mFlexGrow(0.0f),
1100       mFlexShrink(1.0f),
1101       mZIndex(StyleZIndex::Auto()),
1102       mGridTemplateColumns(StyleGridTemplateComponent::None()),
1103       mGridTemplateRows(StyleGridTemplateComponent::None()),
1104       mGridTemplateAreas(StyleGridTemplateAreas::None()),
1105       mColumnGap(NonNegativeLengthPercentageOrNormal::Normal()),
1106       mRowGap(NonNegativeLengthPercentageOrNormal::Normal()) {
1107   MOZ_COUNT_CTOR(nsStylePosition);
1108 
1109   // The initial value of grid-auto-columns and grid-auto-rows is 'auto',
1110   // which computes to 'minmax(auto, auto)'.
1111 
1112   // Other members get their default constructors
1113   // which initialize them to representations of their respective initial value.
1114   // mGridTemplate{Rows,Columns}: false and empty arrays for 'none'
1115   // mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto'
1116 }
1117 
~nsStylePosition()1118 nsStylePosition::~nsStylePosition() { MOZ_COUNT_DTOR(nsStylePosition); }
1119 
nsStylePosition(const nsStylePosition & aSource)1120 nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
1121     : mAlignTracks(aSource.mAlignTracks),
1122       mJustifyTracks(aSource.mJustifyTracks),
1123       mObjectPosition(aSource.mObjectPosition),
1124       mOffset(aSource.mOffset),
1125       mWidth(aSource.mWidth),
1126       mMinWidth(aSource.mMinWidth),
1127       mMaxWidth(aSource.mMaxWidth),
1128       mHeight(aSource.mHeight),
1129       mMinHeight(aSource.mMinHeight),
1130       mMaxHeight(aSource.mMaxHeight),
1131       mFlexBasis(aSource.mFlexBasis),
1132       mGridAutoColumns(aSource.mGridAutoColumns),
1133       mGridAutoRows(aSource.mGridAutoRows),
1134       mAspectRatio(aSource.mAspectRatio),
1135       mGridAutoFlow(aSource.mGridAutoFlow),
1136       mMasonryAutoFlow(aSource.mMasonryAutoFlow),
1137       mAlignContent(aSource.mAlignContent),
1138       mAlignItems(aSource.mAlignItems),
1139       mAlignSelf(aSource.mAlignSelf),
1140       mJustifyContent(aSource.mJustifyContent),
1141       mJustifyItems(aSource.mJustifyItems),
1142       mJustifySelf(aSource.mJustifySelf),
1143       mFlexDirection(aSource.mFlexDirection),
1144       mFlexWrap(aSource.mFlexWrap),
1145       mObjectFit(aSource.mObjectFit),
1146       mBoxSizing(aSource.mBoxSizing),
1147       mOrder(aSource.mOrder),
1148       mFlexGrow(aSource.mFlexGrow),
1149       mFlexShrink(aSource.mFlexShrink),
1150       mZIndex(aSource.mZIndex),
1151       mGridTemplateColumns(aSource.mGridTemplateColumns),
1152       mGridTemplateRows(aSource.mGridTemplateRows),
1153       mGridTemplateAreas(aSource.mGridTemplateAreas),
1154       mGridColumnStart(aSource.mGridColumnStart),
1155       mGridColumnEnd(aSource.mGridColumnEnd),
1156       mGridRowStart(aSource.mGridRowStart),
1157       mGridRowEnd(aSource.mGridRowEnd),
1158       mColumnGap(aSource.mColumnGap),
1159       mRowGap(aSource.mRowGap) {
1160   MOZ_COUNT_CTOR(nsStylePosition);
1161 }
1162 
IsAutonessEqual(const StyleRect<LengthPercentageOrAuto> & aSides1,const StyleRect<LengthPercentageOrAuto> & aSides2)1163 static bool IsAutonessEqual(const StyleRect<LengthPercentageOrAuto>& aSides1,
1164                             const StyleRect<LengthPercentageOrAuto>& aSides2) {
1165   for (const auto side : mozilla::AllPhysicalSides()) {
1166     if (aSides1.Get(side).IsAuto() != aSides2.Get(side).IsAuto()) {
1167       return false;
1168     }
1169   }
1170   return true;
1171 }
1172 
CalcDifference(const nsStylePosition & aNewData,const nsStyleVisibility & aOldStyleVisibility) const1173 nsChangeHint nsStylePosition::CalcDifference(
1174     const nsStylePosition& aNewData,
1175     const nsStyleVisibility& aOldStyleVisibility) const {
1176   if (mGridTemplateColumns.IsMasonry() !=
1177           aNewData.mGridTemplateColumns.IsMasonry() ||
1178       mGridTemplateRows.IsMasonry() != aNewData.mGridTemplateRows.IsMasonry()) {
1179     // XXXmats this could be optimized to AllReflowHints with a bit of work,
1180     // but I'll assume this is a very rare use case in practice. (bug 1623886)
1181     return nsChangeHint_ReconstructFrame;
1182   }
1183 
1184   nsChangeHint hint = nsChangeHint(0);
1185 
1186   // Changes to "z-index" require a repaint.
1187   if (mZIndex != aNewData.mZIndex) {
1188     hint |= nsChangeHint_RepaintFrame;
1189   }
1190 
1191   // Changes to "object-fit" & "object-position" require a repaint.  They
1192   // may also require a reflow, if we have a nsSubDocumentFrame, so that we
1193   // can adjust the size & position of the subdocument.
1194   if (mObjectFit != aNewData.mObjectFit ||
1195       mObjectPosition != aNewData.mObjectPosition) {
1196     hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow;
1197   }
1198 
1199   if (mOrder != aNewData.mOrder) {
1200     // "order" impacts both layout order and stacking order, so we need both a
1201     // reflow and a repaint when it changes.  (Technically, we only need a
1202     // reflow if we're in a multi-line flexbox (which we can't be sure about,
1203     // since that's determined by styling on our parent) -- there, "order" can
1204     // affect which flex line we end up on, & hence can affect our sizing by
1205     // changing the group of flex items we're competing with for space.)
1206     return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints;
1207   }
1208 
1209   if (mBoxSizing != aNewData.mBoxSizing) {
1210     // Can affect both widths and heights; just a bad scene.
1211     return hint | nsChangeHint_AllReflowHints;
1212   }
1213 
1214   if (mAlignItems != aNewData.mAlignItems ||
1215       mAlignSelf != aNewData.mAlignSelf ||
1216       mJustifyTracks != aNewData.mJustifyTracks ||
1217       mAlignTracks != aNewData.mAlignTracks) {
1218     return hint | nsChangeHint_AllReflowHints;
1219   }
1220 
1221   // Properties that apply to flex items:
1222   // XXXdholbert These should probably be more targeted (bug 819536)
1223   if (mFlexBasis != aNewData.mFlexBasis || mFlexGrow != aNewData.mFlexGrow ||
1224       mFlexShrink != aNewData.mFlexShrink) {
1225     return hint | nsChangeHint_AllReflowHints;
1226   }
1227 
1228   // Properties that apply to flex containers:
1229   // - flex-direction can swap a flex container between vertical & horizontal.
1230   // - flex-wrap changes whether a flex container's children are wrapped, which
1231   //   impacts their sizing/positioning and hence impacts the container's size.
1232   if (mFlexDirection != aNewData.mFlexDirection ||
1233       mFlexWrap != aNewData.mFlexWrap) {
1234     return hint | nsChangeHint_AllReflowHints;
1235   }
1236 
1237   // Properties that apply to grid containers:
1238   // FIXME: only for grid containers
1239   // (ie. 'display: grid' or 'display: inline-grid')
1240   if (mGridTemplateColumns != aNewData.mGridTemplateColumns ||
1241       mGridTemplateRows != aNewData.mGridTemplateRows ||
1242       mGridTemplateAreas != aNewData.mGridTemplateAreas ||
1243       mGridAutoColumns != aNewData.mGridAutoColumns ||
1244       mGridAutoRows != aNewData.mGridAutoRows ||
1245       mGridAutoFlow != aNewData.mGridAutoFlow ||
1246       mMasonryAutoFlow != aNewData.mMasonryAutoFlow) {
1247     return hint | nsChangeHint_AllReflowHints;
1248   }
1249 
1250   // Properties that apply to grid items:
1251   // FIXME: only for grid items
1252   // (ie. parent frame is 'display: grid' or 'display: inline-grid')
1253   if (mGridColumnStart != aNewData.mGridColumnStart ||
1254       mGridColumnEnd != aNewData.mGridColumnEnd ||
1255       mGridRowStart != aNewData.mGridRowStart ||
1256       mGridRowEnd != aNewData.mGridRowEnd ||
1257       mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) {
1258     return hint | nsChangeHint_AllReflowHints;
1259   }
1260 
1261   // Changing 'justify-content/items/self' might affect the positioning,
1262   // but it won't affect any sizing.
1263   if (mJustifyContent != aNewData.mJustifyContent ||
1264       mJustifyItems.computed != aNewData.mJustifyItems.computed ||
1265       mJustifySelf != aNewData.mJustifySelf) {
1266     hint |= nsChangeHint_NeedReflow;
1267   }
1268 
1269   // No need to do anything if specified justify-items changes, as long as the
1270   // computed one (tested above) is unchanged.
1271   if (mJustifyItems.specified != aNewData.mJustifyItems.specified) {
1272     hint |= nsChangeHint_NeutralChange;
1273   }
1274 
1275   // 'align-content' doesn't apply to a single-line flexbox but we don't know
1276   // if we're a flex container at this point so we can't optimize for that.
1277   if (mAlignContent != aNewData.mAlignContent) {
1278     hint |= nsChangeHint_NeedReflow;
1279   }
1280 
1281   bool widthChanged = mWidth != aNewData.mWidth ||
1282                       mMinWidth != aNewData.mMinWidth ||
1283                       mMaxWidth != aNewData.mMaxWidth;
1284   bool heightChanged = mHeight != aNewData.mHeight ||
1285                        mMinHeight != aNewData.mMinHeight ||
1286                        mMaxHeight != aNewData.mMaxHeight;
1287 
1288   // It doesn't matter whether we're looking at the old or new visibility
1289   // struct, since a change between vertical and horizontal writing-mode will
1290   // cause a reframe.
1291   bool isVertical =
1292       aOldStyleVisibility.mWritingMode != NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
1293   if (isVertical ? widthChanged : heightChanged) {
1294     hint |= nsChangeHint_ReflowHintsForBSizeChange;
1295   }
1296 
1297   if (isVertical ? heightChanged : widthChanged) {
1298     hint |= nsChangeHint_ReflowHintsForISizeChange;
1299   }
1300 
1301   if (mAspectRatio != aNewData.mAspectRatio) {
1302     hint |= nsChangeHint_ReflowHintsForISizeChange |
1303             nsChangeHint_ReflowHintsForBSizeChange;
1304   }
1305 
1306   // If any of the offsets have changed, then return the respective hints
1307   // so that we would hopefully be able to avoid reflowing.
1308   // Note that it is possible that we'll need to reflow when processing
1309   // restyles, but we don't have enough information to make a good decision
1310   // right now.
1311   // Don't try to handle changes between "auto" and non-auto efficiently;
1312   // that's tricky to do and will hardly ever be able to avoid a reflow.
1313   if (mOffset != aNewData.mOffset) {
1314     if (IsAutonessEqual(mOffset, aNewData.mOffset)) {
1315       hint |=
1316           nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow;
1317     } else {
1318       hint |=
1319           nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
1320     }
1321   }
1322   return hint;
1323 }
1324 
UsedAlignSelf(const ComputedStyle * aParent) const1325 StyleAlignSelf nsStylePosition::UsedAlignSelf(
1326     const ComputedStyle* aParent) const {
1327   if (mAlignSelf._0 != StyleAlignFlags::AUTO) {
1328     return mAlignSelf;
1329   }
1330   if (MOZ_LIKELY(aParent)) {
1331     auto parentAlignItems = aParent->StylePosition()->mAlignItems;
1332     MOZ_ASSERT(!(parentAlignItems._0 & StyleAlignFlags::LEGACY),
1333                "align-items can't have 'legacy'");
1334     return {parentAlignItems._0};
1335   }
1336   return {StyleAlignFlags::NORMAL};
1337 }
1338 
UsedJustifySelf(const ComputedStyle * aParent) const1339 StyleJustifySelf nsStylePosition::UsedJustifySelf(
1340     const ComputedStyle* aParent) const {
1341   if (mJustifySelf._0 != StyleAlignFlags::AUTO) {
1342     return mJustifySelf;
1343   }
1344   if (MOZ_LIKELY(aParent)) {
1345     const auto& inheritedJustifyItems =
1346         aParent->StylePosition()->mJustifyItems.computed;
1347     return {inheritedJustifyItems._0 & ~StyleAlignFlags::LEGACY};
1348   }
1349   return {StyleAlignFlags::NORMAL};
1350 }
1351 
1352 // --------------------
1353 // nsStyleTable
1354 //
1355 
nsStyleTable(const Document & aDocument)1356 nsStyleTable::nsStyleTable(const Document& aDocument)
1357     : mLayoutStrategy(StyleTableLayout::Auto), mXSpan(1) {
1358   MOZ_COUNT_CTOR(nsStyleTable);
1359 }
1360 
~nsStyleTable()1361 nsStyleTable::~nsStyleTable() { MOZ_COUNT_DTOR(nsStyleTable); }
1362 
nsStyleTable(const nsStyleTable & aSource)1363 nsStyleTable::nsStyleTable(const nsStyleTable& aSource)
1364     : mLayoutStrategy(aSource.mLayoutStrategy), mXSpan(aSource.mXSpan) {
1365   MOZ_COUNT_CTOR(nsStyleTable);
1366 }
1367 
CalcDifference(const nsStyleTable & aNewData) const1368 nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const {
1369   if (mXSpan != aNewData.mXSpan ||
1370       mLayoutStrategy != aNewData.mLayoutStrategy) {
1371     return nsChangeHint_ReconstructFrame;
1372   }
1373   return nsChangeHint(0);
1374 }
1375 
1376 // -----------------------
1377 // nsStyleTableBorder
1378 
nsStyleTableBorder(const Document & aDocument)1379 nsStyleTableBorder::nsStyleTableBorder(const Document& aDocument)
1380     : mBorderSpacingCol(0),
1381       mBorderSpacingRow(0),
1382       mBorderCollapse(StyleBorderCollapse::Separate),
1383       mCaptionSide(NS_STYLE_CAPTION_SIDE_TOP),
1384       mEmptyCells(StyleEmptyCells::Show) {
1385   MOZ_COUNT_CTOR(nsStyleTableBorder);
1386 }
1387 
~nsStyleTableBorder()1388 nsStyleTableBorder::~nsStyleTableBorder() {
1389   MOZ_COUNT_DTOR(nsStyleTableBorder);
1390 }
1391 
nsStyleTableBorder(const nsStyleTableBorder & aSource)1392 nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource)
1393     : mBorderSpacingCol(aSource.mBorderSpacingCol),
1394       mBorderSpacingRow(aSource.mBorderSpacingRow),
1395       mBorderCollapse(aSource.mBorderCollapse),
1396       mCaptionSide(aSource.mCaptionSide),
1397       mEmptyCells(aSource.mEmptyCells) {
1398   MOZ_COUNT_CTOR(nsStyleTableBorder);
1399 }
1400 
CalcDifference(const nsStyleTableBorder & aNewData) const1401 nsChangeHint nsStyleTableBorder::CalcDifference(
1402     const nsStyleTableBorder& aNewData) const {
1403   // Border-collapse changes need a reframe, because we use a different frame
1404   // class for table cells in the collapsed border model.  This is used to
1405   // conserve memory when using the separated border model (collapsed borders
1406   // require extra state to be stored).
1407   if (mBorderCollapse != aNewData.mBorderCollapse) {
1408     return nsChangeHint_ReconstructFrame;
1409   }
1410 
1411   if ((mCaptionSide == aNewData.mCaptionSide) &&
1412       (mBorderSpacingCol == aNewData.mBorderSpacingCol) &&
1413       (mBorderSpacingRow == aNewData.mBorderSpacingRow)) {
1414     if (mEmptyCells == aNewData.mEmptyCells) {
1415       return nsChangeHint(0);
1416     }
1417     return NS_STYLE_HINT_VISUAL;
1418   } else {
1419     return NS_STYLE_HINT_REFLOW;
1420   }
1421 }
1422 
1423 template <typename T>
GradientItemsAreOpaque(Span<const StyleGenericGradientItem<StyleColor,T>> aItems)1424 static bool GradientItemsAreOpaque(
1425     Span<const StyleGenericGradientItem<StyleColor, T>> aItems) {
1426   for (auto& stop : aItems) {
1427     if (stop.IsInterpolationHint()) {
1428       continue;
1429     }
1430 
1431     auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop()
1432                                            : stop.AsComplexColorStop().color;
1433     if (color.MaybeTransparent()) {
1434       // We don't know the foreground color here, so if it's being used
1435       // we must assume it might be transparent.
1436       return false;
1437     }
1438   }
1439 
1440   return true;
1441 }
1442 
1443 template <>
IsOpaque() const1444 bool StyleGradient::IsOpaque() const {
1445   if (IsLinear()) {
1446     return GradientItemsAreOpaque(AsLinear().items.AsSpan());
1447   }
1448   if (IsRadial()) {
1449     return GradientItemsAreOpaque(AsRadial().items.AsSpan());
1450   }
1451   return GradientItemsAreOpaque(AsConic().items.AsSpan());
1452 }
1453 
1454 // --------------------
1455 // CachedBorderImageData
1456 
PurgeCachedImages()1457 void CachedBorderImageData::PurgeCachedImages() {
1458   MOZ_ASSERT(!ServoStyleSet::IsInServoTraversal());
1459   MOZ_ASSERT(NS_IsMainThread());
1460   mSubImages.Clear();
1461 }
1462 
PurgeCacheForViewportChange(const Maybe<nsSize> & aSize,const bool aHasIntrinsicRatio)1463 void CachedBorderImageData::PurgeCacheForViewportChange(
1464     const Maybe<nsSize>& aSize, const bool aHasIntrinsicRatio) {
1465   // If we're redrawing with a different viewport-size than we used for our
1466   // cached subimages, then we can't trust that our subimages are valid;
1467   // any percent sizes/positions in our SVG doc may be different now. Purge!
1468   // (We don't have to purge if the SVG document has an intrinsic ratio,
1469   // though, because the actual size of elements in SVG documant's coordinate
1470   // axis are fixed in this case.)
1471   if (aSize != mCachedSVGViewportSize && !aHasIntrinsicRatio) {
1472     PurgeCachedImages();
1473     SetCachedSVGViewportSize(aSize);
1474   }
1475 }
1476 
ConvertToPixelCoord(const StyleNumberOrPercentage & aCoord,int32_t aPercentScale)1477 static int32_t ConvertToPixelCoord(const StyleNumberOrPercentage& aCoord,
1478                                    int32_t aPercentScale) {
1479   double pixelValue;
1480   if (aCoord.IsNumber()) {
1481     pixelValue = aCoord.AsNumber();
1482   } else {
1483     MOZ_ASSERT(aCoord.IsPercentage());
1484     pixelValue = aCoord.AsPercentage()._0 * aPercentScale;
1485   }
1486   MOZ_ASSERT(pixelValue >= 0, "we ensured non-negative while parsing");
1487   pixelValue = std::min(pixelValue, double(INT32_MAX));  // avoid overflow
1488   return NS_lround(pixelValue);
1489 }
1490 
1491 template <>
ComputeActualCropRect() const1492 Maybe<StyleImage::ActualCropRect> StyleImage::ComputeActualCropRect() const {
1493   MOZ_ASSERT(IsRect(),
1494              "This function is designed to be used only image-rect images");
1495 
1496   imgRequestProxy* req = GetImageRequest();
1497   if (!req) {
1498     return Nothing();
1499   }
1500 
1501   nsCOMPtr<imgIContainer> imageContainer;
1502   req->GetImage(getter_AddRefs(imageContainer));
1503   if (!imageContainer) {
1504     return Nothing();
1505   }
1506 
1507   nsIntSize imageSize;
1508   imageContainer->GetWidth(&imageSize.width);
1509   imageContainer->GetHeight(&imageSize.height);
1510   if (imageSize.width <= 0 || imageSize.height <= 0) {
1511     return Nothing();
1512   }
1513 
1514   auto& rect = AsRect();
1515 
1516   int32_t left = ConvertToPixelCoord(rect->left, imageSize.width);
1517   int32_t top = ConvertToPixelCoord(rect->top, imageSize.height);
1518   int32_t right = ConvertToPixelCoord(rect->right, imageSize.width);
1519   int32_t bottom = ConvertToPixelCoord(rect->bottom, imageSize.height);
1520 
1521   // IntersectRect() returns an empty rect if we get negative width or height
1522   nsIntRect cropRect(left, top, right - left, bottom - top);
1523   nsIntRect imageRect(nsIntPoint(0, 0), imageSize);
1524   auto finalRect = imageRect.Intersect(cropRect);
1525   bool isEntireImage = finalRect.IsEqualInterior(imageRect);
1526   return Some(ActualCropRect{finalRect, isEntireImage});
1527 }
1528 
1529 template <>
StartDecoding() const1530 bool StyleImage::StartDecoding() const {
1531   if (IsImageRequestType()) {
1532     imgRequestProxy* req = GetImageRequest();
1533     return req &&
1534            req->StartDecodingWithResult(imgIContainer::FLAG_ASYNC_NOTIFY);
1535   }
1536   // None always returns false from IsComplete, so we do the same here.
1537   return !IsNone();
1538 }
1539 
1540 template <>
IsOpaque() const1541 bool StyleImage::IsOpaque() const {
1542   if (!IsComplete()) {
1543     return false;
1544   }
1545 
1546   if (IsGradient()) {
1547     return AsGradient()->IsOpaque();
1548   }
1549 
1550   if (IsElement()) {
1551     return false;
1552   }
1553 
1554   MOZ_ASSERT(IsImageRequestType(), "unexpected image type");
1555   MOZ_ASSERT(GetImageRequest(), "should've returned earlier above");
1556 
1557   nsCOMPtr<imgIContainer> imageContainer;
1558   GetImageRequest()->GetImage(getter_AddRefs(imageContainer));
1559   MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
1560 
1561   // Check if the crop region of the image is opaque.
1562   if (imageContainer->WillDrawOpaqueNow()) {
1563     if (!IsRect()) {
1564       return true;
1565     }
1566 
1567     // Must make sure if the crop rect contains at least a pixel.
1568     // XXX Is this optimization worth it? Maybe I should just return false.
1569     auto croprect = ComputeActualCropRect();
1570     return croprect && !croprect->mRect.IsEmpty();
1571   }
1572 
1573   return false;
1574 }
1575 
1576 template <>
IsComplete() const1577 bool StyleImage::IsComplete() const {
1578   switch (tag) {
1579     case Tag::None:
1580       return false;
1581     case Tag::Gradient:
1582     case Tag::Element:
1583       return true;
1584     case Tag::Url:
1585     case Tag::Rect: {
1586       if (!IsResolved()) {
1587         return false;
1588       }
1589       imgRequestProxy* req = GetImageRequest();
1590       if (!req) {
1591         return false;
1592       }
1593       uint32_t status = imgIRequest::STATUS_ERROR;
1594       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1595              (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
1596              (status & imgIRequest::STATUS_FRAME_COMPLETE);
1597     }
1598     default:
1599       MOZ_ASSERT_UNREACHABLE("unexpected image type");
1600       return false;
1601   }
1602 }
1603 
1604 template <>
IsSizeAvailable() const1605 bool StyleImage::IsSizeAvailable() const {
1606   switch (tag) {
1607     case Tag::None:
1608       return false;
1609     case Tag::Gradient:
1610     case Tag::Element:
1611       return true;
1612     case Tag::Url:
1613     case Tag::Rect: {
1614       imgRequestProxy* req = GetImageRequest();
1615       if (!req) {
1616         return false;
1617       }
1618       uint32_t status = imgIRequest::STATUS_ERROR;
1619       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
1620              !(status & imgIRequest::STATUS_ERROR) &&
1621              (status & imgIRequest::STATUS_SIZE_AVAILABLE);
1622     }
1623     default:
1624       MOZ_ASSERT_UNREACHABLE("unexpected image type");
1625       return false;
1626   }
1627 }
1628 
1629 template <>
ResolveImage(Document & aDoc,const StyleImage * aOld)1630 void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) {
1631   if (IsResolved()) {
1632     return;
1633   }
1634   auto* old = aOld ? aOld->GetImageRequestURLValue() : nullptr;
1635   auto* url = GetImageRequestURLValue();
1636   // We could avoid this const_cast generating more code but it's not really
1637   // worth it.
1638   const_cast<StyleComputedImageUrl*>(url)->ResolveImage(aDoc, old);
1639 }
1640 
1641 // --------------------
1642 // nsStyleImageLayers
1643 //
1644 
1645 const nsCSSPropertyID nsStyleImageLayers::kBackgroundLayerTable[] = {
1646     eCSSProperty_background,             // shorthand
1647     eCSSProperty_background_color,       // color
1648     eCSSProperty_background_image,       // image
1649     eCSSProperty_background_repeat,      // repeat
1650     eCSSProperty_background_position_x,  // positionX
1651     eCSSProperty_background_position_y,  // positionY
1652     eCSSProperty_background_clip,        // clip
1653     eCSSProperty_background_origin,      // origin
1654     eCSSProperty_background_size,        // size
1655     eCSSProperty_background_attachment,  // attachment
1656     eCSSProperty_UNKNOWN,                // maskMode
1657     eCSSProperty_UNKNOWN                 // composite
1658 };
1659 
1660 const nsCSSPropertyID nsStyleImageLayers::kMaskLayerTable[] = {
1661     eCSSProperty_mask,             // shorthand
1662     eCSSProperty_UNKNOWN,          // color
1663     eCSSProperty_mask_image,       // image
1664     eCSSProperty_mask_repeat,      // repeat
1665     eCSSProperty_mask_position_x,  // positionX
1666     eCSSProperty_mask_position_y,  // positionY
1667     eCSSProperty_mask_clip,        // clip
1668     eCSSProperty_mask_origin,      // origin
1669     eCSSProperty_mask_size,        // size
1670     eCSSProperty_UNKNOWN,          // attachment
1671     eCSSProperty_mask_mode,        // maskMode
1672     eCSSProperty_mask_composite    // composite
1673 };
1674 
nsStyleImageLayers(nsStyleImageLayers::LayerType aType)1675 nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType)
1676     : mAttachmentCount(1),
1677       mClipCount(1),
1678       mOriginCount(1),
1679       mRepeatCount(1),
1680       mPositionXCount(1),
1681       mPositionYCount(1),
1682       mImageCount(1),
1683       mSizeCount(1),
1684       mMaskModeCount(1),
1685       mBlendModeCount(1),
1686       mCompositeCount(1),
1687       mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT) {
1688   MOZ_COUNT_CTOR(nsStyleImageLayers);
1689 
1690   // Ensure first layer is initialized as specified layer type
1691   mLayers[0].Initialize(aType);
1692 }
1693 
nsStyleImageLayers(const nsStyleImageLayers & aSource)1694 nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers& aSource)
1695     : mAttachmentCount(aSource.mAttachmentCount),
1696       mClipCount(aSource.mClipCount),
1697       mOriginCount(aSource.mOriginCount),
1698       mRepeatCount(aSource.mRepeatCount),
1699       mPositionXCount(aSource.mPositionXCount),
1700       mPositionYCount(aSource.mPositionYCount),
1701       mImageCount(aSource.mImageCount),
1702       mSizeCount(aSource.mSizeCount),
1703       mMaskModeCount(aSource.mMaskModeCount),
1704       mBlendModeCount(aSource.mBlendModeCount),
1705       mCompositeCount(aSource.mCompositeCount),
1706       mLayers(aSource.mLayers)  // deep copy
1707 {
1708   MOZ_COUNT_CTOR(nsStyleImageLayers);
1709   // If the deep copy of mLayers failed, truncate the counts.
1710   uint32_t count = mLayers.Length();
1711   if (count != aSource.mLayers.Length()) {
1712     NS_WARNING("truncating counts due to out-of-memory");
1713     mAttachmentCount = std::max(mAttachmentCount, count);
1714     mClipCount = std::max(mClipCount, count);
1715     mOriginCount = std::max(mOriginCount, count);
1716     mRepeatCount = std::max(mRepeatCount, count);
1717     mPositionXCount = std::max(mPositionXCount, count);
1718     mPositionYCount = std::max(mPositionYCount, count);
1719     mImageCount = std::max(mImageCount, count);
1720     mSizeCount = std::max(mSizeCount, count);
1721     mMaskModeCount = std::max(mMaskModeCount, count);
1722     mBlendModeCount = std::max(mBlendModeCount, count);
1723     mCompositeCount = std::max(mCompositeCount, count);
1724   }
1725 }
1726 
AnyLayerIsElementImage(const nsStyleImageLayers & aLayers)1727 static bool AnyLayerIsElementImage(const nsStyleImageLayers& aLayers) {
1728   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aLayers) {
1729     if (aLayers.mLayers[i].mImage.IsElement()) {
1730       return true;
1731     }
1732   }
1733   return false;
1734 }
1735 
CalcDifference(const nsStyleImageLayers & aNewLayers,LayerType aType) const1736 nsChangeHint nsStyleImageLayers::CalcDifference(
1737     const nsStyleImageLayers& aNewLayers, LayerType aType) const {
1738   nsChangeHint hint = nsChangeHint(0);
1739 
1740   // If the number of visible images changes, then it's easy-peasy.
1741   if (mImageCount != aNewLayers.mImageCount) {
1742     hint |= nsChangeHint_RepaintFrame;
1743     if (aType == nsStyleImageLayers::LayerType::Mask ||
1744         AnyLayerIsElementImage(*this) || AnyLayerIsElementImage(aNewLayers)) {
1745       hint |= nsChangeHint_UpdateEffects;
1746     }
1747     return hint;
1748   }
1749 
1750   const nsStyleImageLayers& moreLayers =
1751       mLayers.Length() > aNewLayers.mLayers.Length() ? *this : aNewLayers;
1752   const nsStyleImageLayers& lessLayers =
1753       mLayers.Length() > aNewLayers.mLayers.Length() ? aNewLayers : *this;
1754 
1755   for (size_t i = 0; i < moreLayers.mLayers.Length(); ++i) {
1756     const Layer& moreLayersLayer = moreLayers.mLayers[i];
1757     if (i < moreLayers.mImageCount) {
1758       // This is a visible image we're diffing, we may need to repaint.
1759       const Layer& lessLayersLayer = lessLayers.mLayers[i];
1760       nsChangeHint layerDifference =
1761           moreLayersLayer.CalcDifference(lessLayersLayer);
1762       if (layerDifference && (moreLayersLayer.mImage.IsElement() ||
1763                               lessLayersLayer.mImage.IsElement())) {
1764         layerDifference |=
1765             nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
1766       }
1767       hint |= layerDifference;
1768       continue;
1769     }
1770     if (hint) {
1771       // If they're different by now, we're done.
1772       return hint;
1773     }
1774     if (i >= lessLayers.mLayers.Length()) {
1775       // The layer count differs, we know some property has changed, but if we
1776       // got here we know it won't affect rendering.
1777       return nsChangeHint_NeutralChange;
1778     }
1779 
1780     const Layer& lessLayersLayer = lessLayers.mLayers[i];
1781     MOZ_ASSERT(moreLayersLayer.mImage.IsNone());
1782     MOZ_ASSERT(lessLayersLayer.mImage.IsNone());
1783     if (moreLayersLayer.CalcDifference(lessLayersLayer)) {
1784       // We don't care about the difference returned, we know it's not visible,
1785       // but if something changed, then we need to return the neutral change.
1786       return nsChangeHint_NeutralChange;
1787     }
1788   }
1789 
1790   if (hint) {
1791     // If they're different by now, we're done.
1792     return hint;
1793   }
1794 
1795   // We could have same layers and values, but different count still.
1796   if (mAttachmentCount != aNewLayers.mAttachmentCount ||
1797       mBlendModeCount != aNewLayers.mBlendModeCount ||
1798       mClipCount != aNewLayers.mClipCount ||
1799       mCompositeCount != aNewLayers.mCompositeCount ||
1800       mMaskModeCount != aNewLayers.mMaskModeCount ||
1801       mOriginCount != aNewLayers.mOriginCount ||
1802       mRepeatCount != aNewLayers.mRepeatCount ||
1803       mPositionXCount != aNewLayers.mPositionXCount ||
1804       mPositionYCount != aNewLayers.mPositionYCount ||
1805       mSizeCount != aNewLayers.mSizeCount) {
1806     hint |= nsChangeHint_NeutralChange;
1807   }
1808 
1809   return hint;
1810 }
1811 
operator =(const nsStyleImageLayers & aOther)1812 nsStyleImageLayers& nsStyleImageLayers::operator=(
1813     const nsStyleImageLayers& aOther) {
1814   mAttachmentCount = aOther.mAttachmentCount;
1815   mClipCount = aOther.mClipCount;
1816   mOriginCount = aOther.mOriginCount;
1817   mRepeatCount = aOther.mRepeatCount;
1818   mPositionXCount = aOther.mPositionXCount;
1819   mPositionYCount = aOther.mPositionYCount;
1820   mImageCount = aOther.mImageCount;
1821   mSizeCount = aOther.mSizeCount;
1822   mMaskModeCount = aOther.mMaskModeCount;
1823   mBlendModeCount = aOther.mBlendModeCount;
1824   mCompositeCount = aOther.mCompositeCount;
1825   mLayers = aOther.mLayers;
1826 
1827   uint32_t count = mLayers.Length();
1828   if (count != aOther.mLayers.Length()) {
1829     NS_WARNING("truncating counts due to out-of-memory");
1830     mAttachmentCount = std::max(mAttachmentCount, count);
1831     mClipCount = std::max(mClipCount, count);
1832     mOriginCount = std::max(mOriginCount, count);
1833     mRepeatCount = std::max(mRepeatCount, count);
1834     mPositionXCount = std::max(mPositionXCount, count);
1835     mPositionYCount = std::max(mPositionYCount, count);
1836     mImageCount = std::max(mImageCount, count);
1837     mSizeCount = std::max(mSizeCount, count);
1838     mMaskModeCount = std::max(mMaskModeCount, count);
1839     mBlendModeCount = std::max(mBlendModeCount, count);
1840     mCompositeCount = std::max(mCompositeCount, count);
1841   }
1842 
1843   return *this;
1844 }
1845 
operator =(nsStyleImageLayers && aOther)1846 nsStyleImageLayers& nsStyleImageLayers::operator=(nsStyleImageLayers&& aOther) {
1847   mAttachmentCount = aOther.mAttachmentCount;
1848   mClipCount = aOther.mClipCount;
1849   mOriginCount = aOther.mOriginCount;
1850   mRepeatCount = aOther.mRepeatCount;
1851   mPositionXCount = aOther.mPositionXCount;
1852   mPositionYCount = aOther.mPositionYCount;
1853   mImageCount = aOther.mImageCount;
1854   mSizeCount = aOther.mSizeCount;
1855   mMaskModeCount = aOther.mMaskModeCount;
1856   mBlendModeCount = aOther.mBlendModeCount;
1857   mCompositeCount = aOther.mCompositeCount;
1858   mLayers = std::move(aOther.mLayers);
1859 
1860   uint32_t count = mLayers.Length();
1861   if (count != aOther.mLayers.Length()) {
1862     NS_WARNING("truncating counts due to out-of-memory");
1863     mAttachmentCount = std::max(mAttachmentCount, count);
1864     mClipCount = std::max(mClipCount, count);
1865     mOriginCount = std::max(mOriginCount, count);
1866     mRepeatCount = std::max(mRepeatCount, count);
1867     mPositionXCount = std::max(mPositionXCount, count);
1868     mPositionYCount = std::max(mPositionYCount, count);
1869     mImageCount = std::max(mImageCount, count);
1870     mSizeCount = std::max(mSizeCount, count);
1871     mMaskModeCount = std::max(mMaskModeCount, count);
1872     mBlendModeCount = std::max(mBlendModeCount, count);
1873     mCompositeCount = std::max(mCompositeCount, count);
1874   }
1875 
1876   return *this;
1877 }
1878 
operator ==(const nsStyleImageLayers & aOther) const1879 bool nsStyleImageLayers::operator==(const nsStyleImageLayers& aOther) const {
1880   if (mAttachmentCount != aOther.mAttachmentCount ||
1881       mClipCount != aOther.mClipCount || mOriginCount != aOther.mOriginCount ||
1882       mRepeatCount != aOther.mRepeatCount ||
1883       mPositionXCount != aOther.mPositionXCount ||
1884       mPositionYCount != aOther.mPositionYCount ||
1885       mImageCount != aOther.mImageCount || mSizeCount != aOther.mSizeCount ||
1886       mMaskModeCount != aOther.mMaskModeCount ||
1887       mBlendModeCount != aOther.mBlendModeCount) {
1888     return false;
1889   }
1890 
1891   if (mLayers.Length() != aOther.mLayers.Length()) {
1892     return false;
1893   }
1894 
1895   for (uint32_t i = 0; i < mLayers.Length(); i++) {
1896     if (mLayers[i].mPosition != aOther.mLayers[i].mPosition ||
1897         mLayers[i].mImage != aOther.mLayers[i].mImage ||
1898         mLayers[i].mSize != aOther.mLayers[i].mSize ||
1899         mLayers[i].mClip != aOther.mLayers[i].mClip ||
1900         mLayers[i].mOrigin != aOther.mLayers[i].mOrigin ||
1901         mLayers[i].mAttachment != aOther.mLayers[i].mAttachment ||
1902         mLayers[i].mBlendMode != aOther.mLayers[i].mBlendMode ||
1903         mLayers[i].mComposite != aOther.mLayers[i].mComposite ||
1904         mLayers[i].mMaskMode != aOther.mLayers[i].mMaskMode ||
1905         mLayers[i].mRepeat != aOther.mLayers[i].mRepeat) {
1906       return false;
1907     }
1908   }
1909 
1910   return true;
1911 }
1912 
SizeDependsOnPositioningAreaSize(const StyleBackgroundSize & aSize,const StyleImage & aImage)1913 static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
1914                                              const StyleImage& aImage) {
1915   MOZ_ASSERT(!aImage.IsNone(), "caller should have handled this");
1916 
1917   // Contain and cover straightforwardly depend on frame size.
1918   if (aSize.IsCover() || aSize.IsContain()) {
1919     return true;
1920   }
1921 
1922   MOZ_ASSERT(aSize.IsExplicitSize());
1923   auto& size = aSize.explicit_size;
1924 
1925   // If either dimension contains a non-zero percentage, rendering for that
1926   // dimension straightforwardly depends on frame size.
1927   if (size.width.HasPercent() || size.height.HasPercent()) {
1928     return true;
1929   }
1930 
1931   // If both dimensions are fixed lengths, there's no dependency.
1932   if (!size.width.IsAuto() && !size.height.IsAuto()) {
1933     return false;
1934   }
1935 
1936   // Gradient rendering depends on frame size when auto is involved because
1937   // gradients have no intrinsic ratio or dimensions, and therefore the relevant
1938   // dimension is "treat[ed] as 100%".
1939   if (aImage.IsGradient()) {
1940     return true;
1941   }
1942 
1943   // XXX Element rendering for auto or fixed length doesn't depend on frame size
1944   //     according to the spec.  However, we don't implement the spec yet, so
1945   //     for now we bail and say element() plus auto affects ultimate size.
1946   if (aImage.IsElement()) {
1947     return true;
1948   }
1949 
1950   MOZ_ASSERT(aImage.IsImageRequestType(), "Missed some image");
1951   if (auto* request = aImage.GetImageRequest()) {
1952     nsCOMPtr<imgIContainer> imgContainer;
1953     request->GetImage(getter_AddRefs(imgContainer));
1954     if (imgContainer) {
1955       CSSIntSize imageSize;
1956       AspectRatio imageRatio;
1957       bool hasWidth, hasHeight;
1958       nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio,
1959                                            hasWidth, hasHeight);
1960 
1961       // If the image has a fixed width and height, rendering never depends on
1962       // the frame size.
1963       if (hasWidth && hasHeight) {
1964         return false;
1965       }
1966 
1967       // If the image has an intrinsic ratio, rendering will depend on frame
1968       // size when background-size is all auto.
1969       if (imageRatio) {
1970         return size.width.IsAuto() == size.height.IsAuto();
1971       }
1972 
1973       // Otherwise, rendering depends on frame size when the image dimensions
1974       // and background-size don't complement each other.
1975       return !(hasWidth && size.width.IsLengthPercentage()) &&
1976              !(hasHeight && size.height.IsLengthPercentage());
1977     }
1978   }
1979 
1980   // Passed the gauntlet: no dependency.
1981   return false;
1982 }
1983 
Layer()1984 nsStyleImageLayers::Layer::Layer()
1985     : mImage(StyleImage::None()),
1986       mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(),
1987                                               LengthPercentageOrAuto::Auto())),
1988 
1989       mClip(StyleGeometryBox::BorderBox),
1990       mAttachment(StyleImageLayerAttachment::Scroll),
1991       mBlendMode(StyleBlend::Normal),
1992       mComposite(StyleMaskComposite::Add),
1993       mMaskMode(StyleMaskMode::MatchSource) {}
1994 
1995 nsStyleImageLayers::Layer::~Layer() = default;
1996 
Initialize(nsStyleImageLayers::LayerType aType)1997 void nsStyleImageLayers::Layer::Initialize(
1998     nsStyleImageLayers::LayerType aType) {
1999   mRepeat.SetInitialValues();
2000 
2001   mPosition = Position::FromPercentage(0.);
2002 
2003   if (aType == LayerType::Background) {
2004     mOrigin = StyleGeometryBox::PaddingBox;
2005   } else {
2006     MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type.");
2007     mOrigin = StyleGeometryBox::BorderBox;
2008   }
2009 }
2010 
2011 bool nsStyleImageLayers::Layer::
RenderingMightDependOnPositioningAreaSizeChange() const2012     RenderingMightDependOnPositioningAreaSizeChange() const {
2013   // Do we even have an image?
2014   if (mImage.IsNone()) {
2015     return false;
2016   }
2017 
2018   return mPosition.DependsOnPositioningAreaSize() ||
2019          SizeDependsOnPositioningAreaSize(mSize, mImage) ||
2020          mRepeat.DependsOnPositioningAreaSize();
2021 }
2022 
operator ==(const Layer & aOther) const2023 bool nsStyleImageLayers::Layer::operator==(const Layer& aOther) const {
2024   return mAttachment == aOther.mAttachment && mClip == aOther.mClip &&
2025          mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat &&
2026          mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition &&
2027          mSize == aOther.mSize && mImage == aOther.mImage &&
2028          mMaskMode == aOther.mMaskMode && mComposite == aOther.mComposite;
2029 }
2030 
2031 template <class ComputedValueItem>
FillImageLayerList(nsStyleAutoArray<nsStyleImageLayers::Layer> & aLayers,ComputedValueItem nsStyleImageLayers::Layer::* aResultLocation,uint32_t aItemCount,uint32_t aFillCount)2032 static void FillImageLayerList(
2033     nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
2034     ComputedValueItem nsStyleImageLayers::Layer::*aResultLocation,
2035     uint32_t aItemCount, uint32_t aFillCount) {
2036   MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
2037   for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
2038        ++sourceLayer, ++destLayer) {
2039     aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation;
2040   }
2041 }
2042 
2043 // The same as FillImageLayerList, but for values stored in
2044 // layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
FillImageLayerPositionCoordList(nsStyleAutoArray<nsStyleImageLayers::Layer> & aLayers,LengthPercentage Position::* aResultLocation,uint32_t aItemCount,uint32_t aFillCount)2045 static void FillImageLayerPositionCoordList(
2046     nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
2047     LengthPercentage Position::*aResultLocation, uint32_t aItemCount,
2048     uint32_t aFillCount) {
2049   MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
2050   for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
2051        ++sourceLayer, ++destLayer) {
2052     aLayers[destLayer].mPosition.*aResultLocation =
2053         aLayers[sourceLayer].mPosition.*aResultLocation;
2054   }
2055 }
2056 
FillAllLayers(uint32_t aMaxItemCount)2057 void nsStyleImageLayers::FillAllLayers(uint32_t aMaxItemCount) {
2058   // Delete any extra items.  We need to keep layers in which any
2059   // property was specified.
2060   mLayers.TruncateLengthNonZero(aMaxItemCount);
2061 
2062   uint32_t fillCount = mImageCount;
2063   FillImageLayerList(mLayers, &Layer::mImage, mImageCount, fillCount);
2064   FillImageLayerList(mLayers, &Layer::mRepeat, mRepeatCount, fillCount);
2065   FillImageLayerList(mLayers, &Layer::mAttachment, mAttachmentCount, fillCount);
2066   FillImageLayerList(mLayers, &Layer::mClip, mClipCount, fillCount);
2067   FillImageLayerList(mLayers, &Layer::mBlendMode, mBlendModeCount, fillCount);
2068   FillImageLayerList(mLayers, &Layer::mOrigin, mOriginCount, fillCount);
2069   FillImageLayerPositionCoordList(mLayers, &Position::horizontal,
2070                                   mPositionXCount, fillCount);
2071   FillImageLayerPositionCoordList(mLayers, &Position::vertical, mPositionYCount,
2072                                   fillCount);
2073   FillImageLayerList(mLayers, &Layer::mSize, mSizeCount, fillCount);
2074   FillImageLayerList(mLayers, &Layer::mMaskMode, mMaskModeCount, fillCount);
2075   FillImageLayerList(mLayers, &Layer::mComposite, mCompositeCount, fillCount);
2076 }
2077 
UrlValuesEqual(const StyleImage & aImage,const StyleImage & aOtherImage)2078 static bool UrlValuesEqual(const StyleImage& aImage,
2079                            const StyleImage& aOtherImage) {
2080   auto* url = aImage.GetImageRequestURLValue();
2081   auto* other = aOtherImage.GetImageRequestURLValue();
2082   return url == other || (url && other && *url == *other);
2083 }
2084 
CalcDifference(const nsStyleImageLayers::Layer & aNewLayer) const2085 nsChangeHint nsStyleImageLayers::Layer::CalcDifference(
2086     const nsStyleImageLayers::Layer& aNewLayer) const {
2087   nsChangeHint hint = nsChangeHint(0);
2088   if (!UrlValuesEqual(mImage, aNewLayer.mImage)) {
2089     hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects;
2090   } else if (mAttachment != aNewLayer.mAttachment || mClip != aNewLayer.mClip ||
2091              mOrigin != aNewLayer.mOrigin || mRepeat != aNewLayer.mRepeat ||
2092              mBlendMode != aNewLayer.mBlendMode || mSize != aNewLayer.mSize ||
2093              mImage != aNewLayer.mImage || mMaskMode != aNewLayer.mMaskMode ||
2094              mComposite != aNewLayer.mComposite) {
2095     hint |= nsChangeHint_RepaintFrame;
2096   }
2097 
2098   if (mPosition != aNewLayer.mPosition) {
2099     hint |= nsChangeHint_UpdateBackgroundPosition;
2100   }
2101 
2102   return hint;
2103 }
2104 
2105 // --------------------
2106 // nsStyleBackground
2107 //
2108 
nsStyleBackground(const Document & aDocument)2109 nsStyleBackground::nsStyleBackground(const Document& aDocument)
2110     : mImage(nsStyleImageLayers::LayerType::Background),
2111       mBackgroundColor(StyleColor::Transparent()) {
2112   MOZ_COUNT_CTOR(nsStyleBackground);
2113 }
2114 
nsStyleBackground(const nsStyleBackground & aSource)2115 nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource)
2116     : mImage(aSource.mImage), mBackgroundColor(aSource.mBackgroundColor) {
2117   MOZ_COUNT_CTOR(nsStyleBackground);
2118 }
2119 
~nsStyleBackground()2120 nsStyleBackground::~nsStyleBackground() { MOZ_COUNT_DTOR(nsStyleBackground); }
2121 
TriggerImageLoads(Document & aDocument,const nsStyleBackground * aOldStyle)2122 void nsStyleBackground::TriggerImageLoads(Document& aDocument,
2123                                           const nsStyleBackground* aOldStyle) {
2124   MOZ_ASSERT(NS_IsMainThread());
2125   mImage.ResolveImages(aDocument, aOldStyle ? &aOldStyle->mImage : nullptr);
2126 }
2127 
CalcDifference(const nsStyleBackground & aNewData) const2128 nsChangeHint nsStyleBackground::CalcDifference(
2129     const nsStyleBackground& aNewData) const {
2130   nsChangeHint hint = nsChangeHint(0);
2131   if (mBackgroundColor != aNewData.mBackgroundColor) {
2132     hint |= nsChangeHint_RepaintFrame;
2133   }
2134 
2135   hint |= mImage.CalcDifference(aNewData.mImage,
2136                                 nsStyleImageLayers::LayerType::Background);
2137 
2138   return hint;
2139 }
2140 
HasFixedBackground(nsIFrame * aFrame) const2141 bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const {
2142   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
2143     const nsStyleImageLayers::Layer& layer = mImage.mLayers[i];
2144     if (layer.mAttachment == StyleImageLayerAttachment::Fixed &&
2145         !layer.mImage.IsNone() && !nsLayoutUtils::IsTransformed(aFrame)) {
2146       return true;
2147     }
2148   }
2149   return false;
2150 }
2151 
BackgroundColor(const nsIFrame * aFrame) const2152 nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const {
2153   return mBackgroundColor.CalcColor(aFrame);
2154 }
2155 
BackgroundColor(ComputedStyle * aStyle) const2156 nscolor nsStyleBackground::BackgroundColor(ComputedStyle* aStyle) const {
2157   return mBackgroundColor.CalcColor(*aStyle);
2158 }
2159 
IsTransparent(const nsIFrame * aFrame) const2160 bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
2161   return IsTransparent(aFrame->Style());
2162 }
2163 
IsTransparent(mozilla::ComputedStyle * aStyle) const2164 bool nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const {
2165   return BottomLayer().mImage.IsNone() && mImage.mImageCount == 1 &&
2166          NS_GET_A(BackgroundColor(aStyle)) == 0;
2167 }
2168 
2169 StyleTransition::StyleTransition(const StyleTransition& aCopy) = default;
2170 
SetInitialValues()2171 void StyleTransition::SetInitialValues() {
2172   mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
2173   mDuration = 0.0;
2174   mDelay = 0.0;
2175   mProperty = eCSSPropertyExtra_all_properties;
2176 }
2177 
operator ==(const StyleTransition & aOther) const2178 bool StyleTransition::operator==(const StyleTransition& aOther) const {
2179   return mTimingFunction == aOther.mTimingFunction &&
2180          mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2181          mProperty == aOther.mProperty &&
2182          (mProperty != eCSSProperty_UNKNOWN ||
2183           mUnknownProperty == aOther.mUnknownProperty);
2184 }
2185 
2186 StyleAnimation::StyleAnimation(const StyleAnimation& aCopy) = default;
2187 
SetInitialValues()2188 void StyleAnimation::SetInitialValues() {
2189   mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
2190   mDuration = 0.0;
2191   mDelay = 0.0;
2192   mName = nsGkAtoms::_empty;
2193   mDirection = dom::PlaybackDirection::Normal;
2194   mFillMode = dom::FillMode::None;
2195   mPlayState = StyleAnimationPlayState::Running;
2196   mIterationCount = 1.0f;
2197 }
2198 
operator ==(const StyleAnimation & aOther) const2199 bool StyleAnimation::operator==(const StyleAnimation& aOther) const {
2200   return mTimingFunction == aOther.mTimingFunction &&
2201          mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
2202          mName == aOther.mName && mDirection == aOther.mDirection &&
2203          mFillMode == aOther.mFillMode && mPlayState == aOther.mPlayState &&
2204          mIterationCount == aOther.mIterationCount;
2205 }
2206 
2207 // --------------------
2208 // nsStyleDisplay
2209 //
nsStyleDisplay(const Document & aDocument)2210 nsStyleDisplay::nsStyleDisplay(const Document& aDocument)
2211     : mTransitions(
2212           nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
2213       mTransitionTimingFunctionCount(1),
2214       mTransitionDurationCount(1),
2215       mTransitionDelayCount(1),
2216       mTransitionPropertyCount(1),
2217       mAnimations(
2218           nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
2219       mAnimationTimingFunctionCount(1),
2220       mAnimationDurationCount(1),
2221       mAnimationDelayCount(1),
2222       mAnimationNameCount(1),
2223       mAnimationDirectionCount(1),
2224       mAnimationFillModeCount(1),
2225       mAnimationPlayStateCount(1),
2226       mAnimationIterationCountCount(1),
2227       mWillChange{{}, {0}},
2228       mDisplay(StyleDisplay::Inline),
2229       mOriginalDisplay(StyleDisplay::Inline),
2230       mContain(StyleContain::NONE),
2231       mAppearance(StyleAppearance::None),
2232       mPosition(StylePositionProperty::Static),
2233       mFloat(StyleFloat::None),
2234       mBreakType(StyleClear::None),
2235       mBreakInside(StyleBreakWithin::Auto),
2236       mBreakBefore(StyleBreakBetween::Auto),
2237       mBreakAfter(StyleBreakBetween::Auto),
2238       mOverflowX(StyleOverflow::Visible),
2239       mOverflowY(StyleOverflow::Visible),
2240       mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
2241       mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
2242       mResize(StyleResize::None),
2243       mOrient(StyleOrient::Inline),
2244       mIsolation(StyleIsolation::Auto),
2245       mTopLayer(StyleTopLayer::None),
2246       mTouchAction(StyleTouchAction::AUTO),
2247       mScrollBehavior(StyleScrollBehavior::Auto),
2248       mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
2249       mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
2250       mOverflowAnchor(StyleOverflowAnchor::Auto),
2251       mScrollSnapAlign{StyleScrollSnapAlignKeyword::None,
2252                        StyleScrollSnapAlignKeyword::None},
2253       mScrollSnapType{StyleScrollSnapAxis::Both,
2254                       StyleScrollSnapStrictness::None},
2255       mLineClamp(0),
2256       mRotate(StyleRotate::None()),
2257       mTranslate(StyleTranslate::None()),
2258       mScale(StyleScale::None()),
2259       mBackfaceVisibility(StyleBackfaceVisibility::Visible),
2260       mTransformStyle(StyleTransformStyle::Flat),
2261       mTransformBox(StyleGeometryBox::BorderBox),
2262       mOffsetPath(StyleOffsetPath::None()),
2263       mOffsetDistance(LengthPercentage::Zero()),
2264       mOffsetRotate{true, StyleAngle{0.0}},
2265       mOffsetAnchor(StylePositionOrAuto::Auto()),
2266       mTransformOrigin{LengthPercentage::FromPercentage(0.5),
2267                        LengthPercentage::FromPercentage(0.5),
2268                        {0.}},
2269       mChildPerspective(StylePerspective::None()),
2270       mPerspectiveOrigin(Position::FromPercentage(0.5f)),
2271       mVerticalAlign(
2272           StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)),
2273       mShapeMargin(LengthPercentage::Zero()),
2274       mShapeOutside(StyleShapeOutside::None()) {
2275   MOZ_COUNT_CTOR(nsStyleDisplay);
2276 
2277   mTransitions[0].SetInitialValues();
2278   mAnimations[0].SetInitialValues();
2279 }
2280 
nsStyleDisplay(const nsStyleDisplay & aSource)2281 nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
2282     : mTransitions(aSource.mTransitions),
2283       mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount),
2284       mTransitionDurationCount(aSource.mTransitionDurationCount),
2285       mTransitionDelayCount(aSource.mTransitionDelayCount),
2286       mTransitionPropertyCount(aSource.mTransitionPropertyCount),
2287       mAnimations(aSource.mAnimations),
2288       mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount),
2289       mAnimationDurationCount(aSource.mAnimationDurationCount),
2290       mAnimationDelayCount(aSource.mAnimationDelayCount),
2291       mAnimationNameCount(aSource.mAnimationNameCount),
2292       mAnimationDirectionCount(aSource.mAnimationDirectionCount),
2293       mAnimationFillModeCount(aSource.mAnimationFillModeCount),
2294       mAnimationPlayStateCount(aSource.mAnimationPlayStateCount),
2295       mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
2296       mWillChange(aSource.mWillChange),
2297       mDisplay(aSource.mDisplay),
2298       mOriginalDisplay(aSource.mOriginalDisplay),
2299       mContain(aSource.mContain),
2300       mAppearance(aSource.mAppearance),
2301       mPosition(aSource.mPosition),
2302       mFloat(aSource.mFloat),
2303       mBreakType(aSource.mBreakType),
2304       mBreakInside(aSource.mBreakInside),
2305       mBreakBefore(aSource.mBreakBefore),
2306       mBreakAfter(aSource.mBreakAfter),
2307       mOverflowX(aSource.mOverflowX),
2308       mOverflowY(aSource.mOverflowY),
2309       mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
2310       mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
2311       mResize(aSource.mResize),
2312       mOrient(aSource.mOrient),
2313       mIsolation(aSource.mIsolation),
2314       mTopLayer(aSource.mTopLayer),
2315       mTouchAction(aSource.mTouchAction),
2316       mScrollBehavior(aSource.mScrollBehavior),
2317       mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
2318       mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
2319       mOverflowAnchor(aSource.mOverflowAnchor),
2320       mScrollSnapAlign(aSource.mScrollSnapAlign),
2321       mScrollSnapType(aSource.mScrollSnapType),
2322       mLineClamp(aSource.mLineClamp),
2323       mTransform(aSource.mTransform),
2324       mRotate(aSource.mRotate),
2325       mTranslate(aSource.mTranslate),
2326       mScale(aSource.mScale),
2327       mBackfaceVisibility(aSource.mBackfaceVisibility),
2328       mTransformStyle(aSource.mTransformStyle),
2329       mTransformBox(aSource.mTransformBox),
2330       mOffsetPath(aSource.mOffsetPath),
2331       mOffsetDistance(aSource.mOffsetDistance),
2332       mOffsetRotate(aSource.mOffsetRotate),
2333       mOffsetAnchor(aSource.mOffsetAnchor),
2334       mTransformOrigin(aSource.mTransformOrigin),
2335       mChildPerspective(aSource.mChildPerspective),
2336       mPerspectiveOrigin(aSource.mPerspectiveOrigin),
2337       mVerticalAlign(aSource.mVerticalAlign),
2338       mShapeImageThreshold(aSource.mShapeImageThreshold),
2339       mShapeMargin(aSource.mShapeMargin),
2340       mShapeOutside(aSource.mShapeOutside) {
2341   MOZ_COUNT_CTOR(nsStyleDisplay);
2342 }
2343 
~nsStyleDisplay()2344 nsStyleDisplay::~nsStyleDisplay() { MOZ_COUNT_DTOR(nsStyleDisplay); }
2345 
TriggerImageLoads(Document & aDocument,const nsStyleDisplay * aOldStyle)2346 void nsStyleDisplay::TriggerImageLoads(Document& aDocument,
2347                                        const nsStyleDisplay* aOldStyle) {
2348   MOZ_ASSERT(NS_IsMainThread());
2349 
2350   if (mShapeOutside.IsImage()) {
2351     auto* old = aOldStyle && aOldStyle->mShapeOutside.IsImage()
2352                     ? &aOldStyle->mShapeOutside.AsImage()
2353                     : nullptr;
2354     // Const-cast is ugly but legit, we could avoid it by generating mut-casts
2355     // with cbindgen.
2356     const_cast<StyleImage&>(mShapeOutside.AsImage())
2357         .ResolveImage(aDocument, old);
2358   }
2359 }
2360 
2361 template <typename TransformLike>
CompareTransformValues(const TransformLike & aOldTransform,const TransformLike & aNewTransform)2362 static inline nsChangeHint CompareTransformValues(
2363     const TransformLike& aOldTransform, const TransformLike& aNewTransform) {
2364   nsChangeHint result = nsChangeHint(0);
2365 
2366   // Note: If we add a new change hint for transform changes here, we have to
2367   // modify KeyframeEffect::CalculateCumulativeChangeHint too!
2368   if (aOldTransform != aNewTransform) {
2369     result |= nsChangeHint_UpdateTransformLayer;
2370     if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) {
2371       result |= nsChangeHint_UpdatePostTransformOverflow;
2372     } else {
2373       result |= nsChangeHint_UpdateOverflow;
2374     }
2375   }
2376 
2377   return result;
2378 }
2379 
CompareMotionValues(const nsStyleDisplay & aDisplay,const nsStyleDisplay & aNewDisplay)2380 static inline nsChangeHint CompareMotionValues(
2381     const nsStyleDisplay& aDisplay, const nsStyleDisplay& aNewDisplay) {
2382   if (aDisplay.mOffsetPath == aNewDisplay.mOffsetPath) {
2383     if (aDisplay.mOffsetDistance == aNewDisplay.mOffsetDistance &&
2384         aDisplay.mOffsetRotate == aNewDisplay.mOffsetRotate &&
2385         aDisplay.mOffsetAnchor == aNewDisplay.mOffsetAnchor) {
2386       return nsChangeHint(0);
2387     }
2388 
2389     if (aDisplay.mOffsetPath.IsNone()) {
2390       return nsChangeHint_NeutralChange;
2391     }
2392   }
2393 
2394   // TODO: Bug 1482737: This probably doesn't need to UpdateOverflow
2395   // (or UpdateTransformLayer) if there's already a transform.
2396   // Set the same hints as what we use for transform because motion path is
2397   // a kind of transform and will be combined with other transforms.
2398   nsChangeHint result = nsChangeHint_UpdateTransformLayer;
2399   if (!aDisplay.mOffsetPath.IsNone() && !aNewDisplay.mOffsetPath.IsNone()) {
2400     result |= nsChangeHint_UpdatePostTransformOverflow;
2401   } else {
2402     result |= nsChangeHint_UpdateOverflow;
2403   }
2404   return result;
2405 }
2406 
ScrollbarGenerationChanged(const nsStyleDisplay & aOld,const nsStyleDisplay & aNew)2407 static bool ScrollbarGenerationChanged(const nsStyleDisplay& aOld,
2408                                        const nsStyleDisplay& aNew) {
2409   auto changed = [](StyleOverflow aOld, StyleOverflow aNew) {
2410     return aOld != aNew &&
2411            (aOld == StyleOverflow::Hidden || aNew == StyleOverflow::Hidden);
2412   };
2413   return changed(aOld.mOverflowX, aNew.mOverflowX) ||
2414          changed(aOld.mOverflowY, aNew.mOverflowY);
2415 }
2416 
CalcDifference(const nsStyleDisplay & aNewData,const nsStylePosition & aOldPosition) const2417 nsChangeHint nsStyleDisplay::CalcDifference(
2418     const nsStyleDisplay& aNewData, const nsStylePosition& aOldPosition) const {
2419   if (mDisplay != aNewData.mDisplay || mContain != aNewData.mContain ||
2420       (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None) ||
2421       mScrollBehavior != aNewData.mScrollBehavior ||
2422       mScrollSnapType != aNewData.mScrollSnapType ||
2423       mTopLayer != aNewData.mTopLayer || mResize != aNewData.mResize) {
2424     return nsChangeHint_ReconstructFrame;
2425   }
2426 
2427   if ((mAppearance == StyleAppearance::Textfield &&
2428        aNewData.mAppearance != StyleAppearance::Textfield) ||
2429       (mAppearance != StyleAppearance::Textfield &&
2430        aNewData.mAppearance == StyleAppearance::Textfield)) {
2431     // This is for <input type=number> where we allow authors to specify a
2432     // |-moz-appearance:textfield| to get a control without a spinner. (The
2433     // spinner is present for |-moz-appearance:number-input| but also other
2434     // values such as 'none'.) We need to reframe since we want to use
2435     // nsTextControlFrame instead of nsNumberControlFrame if the author
2436     // specifies 'textfield'.
2437     return nsChangeHint_ReconstructFrame;
2438   }
2439 
2440   auto hint = nsChangeHint(0);
2441   if (mPosition != aNewData.mPosition) {
2442     if (IsAbsolutelyPositionedStyle() ||
2443         aNewData.IsAbsolutelyPositionedStyle()) {
2444       // This changes our parent relationship on the frame tree and / or needs
2445       // to create a placeholder, so gotta reframe. There are some cases (when
2446       // switching from fixed to absolute or viceversa, if our containing block
2447       // happens to remain the same, i.e., if it has a transform or such) where
2448       // this wouldn't really be needed (though we'd still need to move the
2449       // frame from one child list to another). In any case we don't have a hand
2450       // to that information from here, and it doesn't seem like a case worth
2451       // optimizing for.
2452       return nsChangeHint_ReconstructFrame;
2453     }
2454     // We start or stop being a containing block for abspos descendants. This
2455     // also causes painting to change, as we'd become a pseudo-stacking context.
2456     if (IsRelativelyPositionedStyle() !=
2457         aNewData.IsRelativelyPositionedStyle()) {
2458       hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_RepaintFrame;
2459     }
2460     if (IsPositionForcingStackingContext() !=
2461         aNewData.IsPositionForcingStackingContext()) {
2462       hint |= nsChangeHint_RepaintFrame;
2463     }
2464     // On top of that: if the above ends up not reframing, we need a reflow to
2465     // compute our relative, static or sticky position.
2466     hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2467   }
2468 
2469   if (mScrollSnapAlign != aNewData.mScrollSnapAlign) {
2470     // FIXME: Bug 1530253 Support re-snapping when scroll-snap-align changes.
2471     hint |= nsChangeHint_NeutralChange;
2472   }
2473 
2474   if (mOverflowX != aNewData.mOverflowX || mOverflowY != aNewData.mOverflowY) {
2475     const bool isScrollable = IsScrollableOverflow();
2476     if (isScrollable != aNewData.IsScrollableOverflow()) {
2477       // We may need to construct or destroy a scroll frame as a result of this
2478       // change.
2479       hint |= nsChangeHint_ScrollbarChange;
2480     } else if (isScrollable) {
2481       if (ScrollbarGenerationChanged(*this, aNewData)) {
2482         // We need to reframe in the case of hidden -> non-hidden case though,
2483         // since ScrollFrameHelper::CreateAnonymousContent avoids creating
2484         // scrollbars altogether for overflow: hidden. That seems it could
2485         // create some interesting perf cliffs...
2486         //
2487         // We reframe when non-hidden -> hidden too, for now.
2488         //
2489         // FIXME(bug 1590247): Seems we could avoid reframing once we've created
2490         // scrollbars, which should get us the optimization for elements that
2491         // have toggled scrollbars, but would prevent the cliff of toggling
2492         // overflow causing jank.
2493         hint |= nsChangeHint_ScrollbarChange;
2494       } else {
2495         // Otherwise, for changes where both overflow values are scrollable,
2496         // means that scrollbars may appear or disappear. We need to reflow,
2497         // since reflow is what determines which scrollbars if any are visible.
2498         hint |= nsChangeHint_ReflowHintsForScrollbarChange;
2499       }
2500     } else {
2501       // Otherwise this is a change between visible and
2502       // -moz-hidden-unscrollable. Here only whether we have a clip changes, so
2503       // just repaint and update our overflow areas in that case.
2504       hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2505     }
2506   }
2507 
2508   /* Note: When mScrollBehavior or mScrollSnapType are changed,
2509    * nsChangeHint_NeutralChange is not sufficient to enter
2510    * nsCSSFrameConstructor::PropagateScrollToViewport. By using the same hint as
2511    * used when the overflow css property changes, nsChangeHint_ReconstructFrame,
2512    * PropagateScrollToViewport will be called.
2513    *
2514    * The scroll-behavior css property is not expected to change often (the
2515    * CSSOM-View DOM methods are likely to be used in those cases); however,
2516    * if this does become common perhaps a faster-path might be worth while.
2517    *
2518    * FIXME(emilio): Can we do what we do for overflow changes?
2519    *
2520    * FIXME(emilio): These properties no longer propagate from the body to the
2521    * viewport.
2522    */
2523 
2524   if (mFloat != aNewData.mFloat) {
2525     // Changing which side we're floating on (float:none was handled above).
2526     hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2527   }
2528 
2529   if (mShapeOutside != aNewData.mShapeOutside ||
2530       mShapeMargin != aNewData.mShapeMargin ||
2531       mShapeImageThreshold != aNewData.mShapeImageThreshold) {
2532     if (aNewData.mFloat != StyleFloat::None) {
2533       // If we are floating, and our shape-outside, shape-margin, or
2534       // shape-image-threshold are changed, our descendants are not impacted,
2535       // but our ancestor and siblings are.
2536       hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
2537     } else {
2538       // shape-outside or shape-margin or shape-image-threshold changed,
2539       // but we don't need to reflow because we're not floating.
2540       hint |= nsChangeHint_NeutralChange;
2541     }
2542   }
2543 
2544   if (mLineClamp != aNewData.mLineClamp) {
2545     hint |= NS_STYLE_HINT_REFLOW;
2546   }
2547 
2548   if (mVerticalAlign != aNewData.mVerticalAlign) {
2549     // XXX Can this just be AllReflowHints + RepaintFrame, and be included in
2550     // the block below?
2551     hint |= NS_STYLE_HINT_REFLOW;
2552   }
2553 
2554   // XXX the following is conservative, for now: changing float breaking
2555   // shouldn't necessarily require a repaint, reflow should suffice.
2556   //
2557   // FIXME(emilio): We definitely change the frame tree in nsCSSFrameConstructor
2558   // based on break-before / break-after... Shouldn't that reframe?
2559   if (mBreakType != aNewData.mBreakType ||
2560       mBreakInside != aNewData.mBreakInside ||
2561       mBreakBefore != aNewData.mBreakBefore ||
2562       mBreakAfter != aNewData.mBreakAfter ||
2563       mAppearance != aNewData.mAppearance || mOrient != aNewData.mOrient ||
2564       mOverflowClipBoxBlock != aNewData.mOverflowClipBoxBlock ||
2565       mOverflowClipBoxInline != aNewData.mOverflowClipBoxInline) {
2566     hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2567   }
2568 
2569   if (mIsolation != aNewData.mIsolation) {
2570     hint |= nsChangeHint_RepaintFrame;
2571   }
2572 
2573   /* If we've added or removed the transform property, we need to reconstruct
2574    * the frame to add or remove the view object, and also to handle abs-pos and
2575    * fixed-pos containers.
2576    */
2577   if (HasTransformStyle() != aNewData.HasTransformStyle()) {
2578     hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
2579   } else {
2580     /* Otherwise, if we've kept the property lying around and we already had a
2581      * transform, we need to see whether or not we've changed the transform.
2582      * If so, we need to recompute its overflow rect (which probably changed
2583      * if the transform changed) and to redraw within the bounds of that new
2584      * overflow rect.
2585      *
2586      * If the property isn't present in either style struct, we still do the
2587      * comparisons but turn all the resulting change hints into
2588      * nsChangeHint_NeutralChange.
2589      */
2590     nsChangeHint transformHint = nsChangeHint(0);
2591 
2592     transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
2593     transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
2594     transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
2595     transformHint |= CompareTransformValues(mScale, aNewData.mScale);
2596     transformHint |= CompareMotionValues(*this, aNewData);
2597 
2598     if (mTransformOrigin != aNewData.mTransformOrigin) {
2599       transformHint |= nsChangeHint_UpdateTransformLayer |
2600                        nsChangeHint_UpdatePostTransformOverflow;
2601     }
2602 
2603     if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
2604         mTransformStyle != aNewData.mTransformStyle ||
2605         mTransformBox != aNewData.mTransformBox) {
2606       transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2607     }
2608 
2609     if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
2610       transformHint |= nsChangeHint_RepaintFrame;
2611     }
2612 
2613     if (transformHint) {
2614       if (HasTransformStyle()) {
2615         hint |= transformHint;
2616       } else {
2617         hint |= nsChangeHint_NeutralChange;
2618       }
2619     }
2620   }
2621 
2622   if (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) {
2623     // A change from/to being a containing block for position:fixed.
2624     hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_UpdateOverflow |
2625             nsChangeHint_RepaintFrame;
2626   } else if (mChildPerspective != aNewData.mChildPerspective) {
2627     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
2628   }
2629 
2630   // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
2631   // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM
2632   // bit, which in turn handles frame reconstruction for changes in the
2633   // containing block of fixed-positioned elements.
2634   auto willChangeBitsChanged = mWillChange.bits ^ aNewData.mWillChange.bits;
2635 
2636   if (willChangeBitsChanged &
2637       (StyleWillChangeBits::STACKING_CONTEXT | StyleWillChangeBits::SCROLL |
2638        StyleWillChangeBits::OPACITY)) {
2639     hint |= nsChangeHint_RepaintFrame;
2640   }
2641 
2642   if (willChangeBitsChanged &
2643       (StyleWillChangeBits::FIXPOS_CB | StyleWillChangeBits::ABSPOS_CB)) {
2644     hint |= nsChangeHint_UpdateContainingBlock;
2645   }
2646 
2647   // If touch-action is changed, we need to regenerate the event regions on
2648   // the layers and send it over to the compositor for APZ to handle.
2649   if (mTouchAction != aNewData.mTouchAction) {
2650     hint |= nsChangeHint_RepaintFrame;
2651   }
2652 
2653   // If overscroll-behavior has changed, the changes are picked up
2654   // during a repaint.
2655   if (mOverscrollBehaviorX != aNewData.mOverscrollBehaviorX ||
2656       mOverscrollBehaviorY != aNewData.mOverscrollBehaviorY) {
2657     hint |= nsChangeHint_SchedulePaint;
2658   }
2659 
2660   if (mOriginalDisplay != aNewData.mOriginalDisplay) {
2661     // Our hypothetical box position may have changed.
2662     //
2663     // Note that it doesn't matter if we look at the old or the new struct,
2664     // since a change on whether we need a hypothetical position would trigger
2665     // reflow anyway.
2666     if (IsAbsolutelyPositionedStyle() &&
2667         aOldPosition.NeedsHypotheticalPositionIfAbsPos()) {
2668       hint |=
2669           nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition;
2670     } else {
2671       hint |= nsChangeHint_NeutralChange;
2672     }
2673   }
2674 
2675   // Note:  Our current behavior for handling changes to the
2676   // transition-duration, transition-delay, and transition-timing-function
2677   // properties is to do nothing.  In other words, the transition
2678   // property that matters is what it is when the transition begins, and
2679   // we don't stop a transition later because the transition property
2680   // changed.
2681   // We do handle changes to transition-property, but we don't need to
2682   // bother with anything here, since the transition manager is notified
2683   // of any ComputedStyle change anyway.
2684 
2685   // Note: Likewise, for animation-*, the animation manager gets
2686   // notified about every new ComputedStyle constructed, and it uses
2687   // that opportunity to handle dynamic changes appropriately.
2688 
2689   // But we still need to return nsChangeHint_NeutralChange for these
2690   // properties, since some data did change in the style struct.
2691 
2692   if (!hint && (mTransitions != aNewData.mTransitions ||
2693                 mTransitionTimingFunctionCount !=
2694                     aNewData.mTransitionTimingFunctionCount ||
2695                 mTransitionDurationCount != aNewData.mTransitionDurationCount ||
2696                 mTransitionDelayCount != aNewData.mTransitionDelayCount ||
2697                 mTransitionPropertyCount != aNewData.mTransitionPropertyCount ||
2698                 mAnimations != aNewData.mAnimations ||
2699                 mAnimationTimingFunctionCount !=
2700                     aNewData.mAnimationTimingFunctionCount ||
2701                 mAnimationDurationCount != aNewData.mAnimationDurationCount ||
2702                 mAnimationDelayCount != aNewData.mAnimationDelayCount ||
2703                 mAnimationNameCount != aNewData.mAnimationNameCount ||
2704                 mAnimationDirectionCount != aNewData.mAnimationDirectionCount ||
2705                 mAnimationFillModeCount != aNewData.mAnimationFillModeCount ||
2706                 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount ||
2707                 mAnimationIterationCountCount !=
2708                     aNewData.mAnimationIterationCountCount ||
2709                 mWillChange != aNewData.mWillChange ||
2710                 mOverflowAnchor != aNewData.mOverflowAnchor)) {
2711     hint |= nsChangeHint_NeutralChange;
2712   }
2713 
2714   return hint;
2715 }
2716 
2717 // --------------------
2718 // nsStyleVisibility
2719 //
2720 
nsStyleVisibility(const Document & aDocument)2721 nsStyleVisibility::nsStyleVisibility(const Document& aDocument)
2722     : mImageOrientation(
2723           StaticPrefs::layout_css_image_orientation_initial_from_image()
2724               ? StyleImageOrientation::FromImage
2725               : StyleImageOrientation::None),
2726       mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL
2727                      ? StyleDirection::Rtl
2728                      : StyleDirection::Ltr),
2729       mVisible(StyleVisibility::Visible),
2730       mImageRendering(StyleImageRendering::Auto),
2731       mWritingMode(NS_STYLE_WRITING_MODE_HORIZONTAL_TB),
2732       mTextOrientation(StyleTextOrientation::Mixed),
2733       mColorAdjust(StyleColorAdjust::Economy) {
2734   MOZ_COUNT_CTOR(nsStyleVisibility);
2735 }
2736 
nsStyleVisibility(const nsStyleVisibility & aSource)2737 nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource)
2738     : mImageOrientation(aSource.mImageOrientation),
2739       mDirection(aSource.mDirection),
2740       mVisible(aSource.mVisible),
2741       mImageRendering(aSource.mImageRendering),
2742       mWritingMode(aSource.mWritingMode),
2743       mTextOrientation(aSource.mTextOrientation),
2744       mColorAdjust(aSource.mColorAdjust) {
2745   MOZ_COUNT_CTOR(nsStyleVisibility);
2746 }
2747 
CalcDifference(const nsStyleVisibility & aNewData) const2748 nsChangeHint nsStyleVisibility::CalcDifference(
2749     const nsStyleVisibility& aNewData) const {
2750   nsChangeHint hint = nsChangeHint(0);
2751 
2752   if (mDirection != aNewData.mDirection ||
2753       mWritingMode != aNewData.mWritingMode) {
2754     // It's important that a change in mWritingMode results in frame
2755     // reconstruction, because it may affect intrinsic size (see
2756     // nsSubDocumentFrame::GetIntrinsicISize/BSize).
2757     // Also, the used writing-mode value is now a field on nsIFrame and some
2758     // classes (e.g. table rows/cells) copy their value from an ancestor.
2759     hint |= nsChangeHint_ReconstructFrame;
2760   } else {
2761     if ((mImageOrientation != aNewData.mImageOrientation)) {
2762       hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
2763     }
2764     if (mVisible != aNewData.mVisible) {
2765       if (mVisible == StyleVisibility::Visible ||
2766           aNewData.mVisible == StyleVisibility::Visible) {
2767         hint |= nsChangeHint_VisibilityChange;
2768       }
2769       if (StyleVisibility::Collapse == mVisible ||
2770           StyleVisibility::Collapse == aNewData.mVisible) {
2771         hint |= NS_STYLE_HINT_REFLOW;
2772       } else {
2773         hint |= NS_STYLE_HINT_VISUAL;
2774       }
2775     }
2776     if (mTextOrientation != aNewData.mTextOrientation) {
2777       hint |= NS_STYLE_HINT_REFLOW;
2778     }
2779     if (mImageRendering != aNewData.mImageRendering) {
2780       hint |= nsChangeHint_RepaintFrame;
2781     }
2782     if (mColorAdjust != aNewData.mColorAdjust) {
2783       // color-adjust only affects media where dynamic changes can't happen.
2784       hint |= nsChangeHint_NeutralChange;
2785     }
2786   }
2787   return hint;
2788 }
2789 
2790 //-----------------------
2791 // nsStyleContent
2792 //
2793 
nsStyleContent(const Document & aDocument)2794 nsStyleContent::nsStyleContent(const Document& aDocument)
2795     : mContent(StyleContent::Normal()) {
2796   MOZ_COUNT_CTOR(nsStyleContent);
2797 }
2798 
~nsStyleContent()2799 nsStyleContent::~nsStyleContent() { MOZ_COUNT_DTOR(nsStyleContent); }
2800 
nsStyleContent(const nsStyleContent & aSource)2801 nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
2802     : mContent(aSource.mContent),
2803       mCounterIncrement(aSource.mCounterIncrement),
2804       mCounterReset(aSource.mCounterReset),
2805       mCounterSet(aSource.mCounterSet) {
2806   MOZ_COUNT_CTOR(nsStyleContent);
2807 }
2808 
CalcDifference(const nsStyleContent & aNewData) const2809 nsChangeHint nsStyleContent::CalcDifference(
2810     const nsStyleContent& aNewData) const {
2811   // Unfortunately we need to reframe even if the content lengths are the same;
2812   // a simple reflow will not pick up different text or different image URLs,
2813   // since we set all that up in the CSSFrameConstructor
2814   if (mContent != aNewData.mContent ||
2815       mCounterIncrement != aNewData.mCounterIncrement ||
2816       mCounterReset != aNewData.mCounterReset ||
2817       mCounterSet != aNewData.mCounterSet) {
2818     return nsChangeHint_ReconstructFrame;
2819   }
2820 
2821   return nsChangeHint(0);
2822 }
2823 
TriggerImageLoads(Document & aDoc,const nsStyleContent * aOld)2824 void nsStyleContent::TriggerImageLoads(Document& aDoc,
2825                                        const nsStyleContent* aOld) {
2826   if (!mContent.IsItems()) {
2827     return;
2828   }
2829 
2830   Span<const StyleContentItem> oldItems;
2831   if (aOld && aOld->mContent.IsItems()) {
2832     oldItems = aOld->mContent.AsItems().AsSpan();
2833   }
2834 
2835   auto items = mContent.AsItems().AsSpan();
2836 
2837   for (size_t i = 0; i < items.Length(); ++i) {
2838     auto& item = items[i];
2839     if (!item.IsUrl()) {
2840       continue;
2841     }
2842     auto& url = item.AsUrl();
2843     if (url.IsImageResolved()) {
2844       continue;
2845     }
2846     auto* oldUrl = i < oldItems.Length() && oldItems[i].IsUrl()
2847                        ? &oldItems[i].AsUrl()
2848                        : nullptr;
2849     const_cast<StyleComputedImageUrl&>(url).ResolveImage(aDoc, oldUrl);
2850   }
2851 }
2852 
2853 // --------------------
2854 // nsStyleTextReset
2855 //
2856 
nsStyleTextReset(const Document & aDocument)2857 nsStyleTextReset::nsStyleTextReset(const Document& aDocument)
2858     : mTextOverflow(),
2859       mTextDecorationLine(StyleTextDecorationLine::NONE),
2860       mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
2861       mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL),
2862       mInitialLetterSink(0),
2863       mInitialLetterSize(0.0f),
2864       mTextDecorationColor(StyleColor::CurrentColor()),
2865       mTextDecorationThickness(StyleTextDecorationLength::Auto()) {
2866   MOZ_COUNT_CTOR(nsStyleTextReset);
2867 }
2868 
nsStyleTextReset(const nsStyleTextReset & aSource)2869 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
2870     : mTextOverflow(aSource.mTextOverflow),
2871       mTextDecorationLine(aSource.mTextDecorationLine),
2872       mTextDecorationStyle(aSource.mTextDecorationStyle),
2873       mUnicodeBidi(aSource.mUnicodeBidi),
2874       mInitialLetterSink(aSource.mInitialLetterSink),
2875       mInitialLetterSize(aSource.mInitialLetterSize),
2876       mTextDecorationColor(aSource.mTextDecorationColor),
2877       mTextDecorationThickness(aSource.mTextDecorationThickness) {
2878   MOZ_COUNT_CTOR(nsStyleTextReset);
2879 }
2880 
~nsStyleTextReset()2881 nsStyleTextReset::~nsStyleTextReset() { MOZ_COUNT_DTOR(nsStyleTextReset); }
2882 
CalcDifference(const nsStyleTextReset & aNewData) const2883 nsChangeHint nsStyleTextReset::CalcDifference(
2884     const nsStyleTextReset& aNewData) const {
2885   if (mUnicodeBidi != aNewData.mUnicodeBidi ||
2886       mInitialLetterSink != aNewData.mInitialLetterSink ||
2887       mInitialLetterSize != aNewData.mInitialLetterSize) {
2888     return NS_STYLE_HINT_REFLOW;
2889   }
2890 
2891   if (mTextDecorationLine != aNewData.mTextDecorationLine ||
2892       mTextDecorationStyle != aNewData.mTextDecorationStyle ||
2893       mTextDecorationThickness != aNewData.mTextDecorationThickness) {
2894     // Changes to our text-decoration line can impact our overflow area &
2895     // also our descendants' overflow areas (particularly for text-frame
2896     // descendants).  So, we update those areas & trigger a repaint.
2897     return nsChangeHint_RepaintFrame | nsChangeHint_UpdateSubtreeOverflow |
2898            nsChangeHint_SchedulePaint;
2899   }
2900 
2901   // Repaint for decoration color changes
2902   if (mTextDecorationColor != aNewData.mTextDecorationColor) {
2903     return nsChangeHint_RepaintFrame;
2904   }
2905 
2906   if (mTextOverflow != aNewData.mTextOverflow) {
2907     return nsChangeHint_RepaintFrame;
2908   }
2909 
2910   return nsChangeHint(0);
2911 }
2912 
2913 // --------------------
2914 // nsStyleText
2915 //
2916 
DefaultColor(const Document & aDocument)2917 static StyleRGBA DefaultColor(const Document& aDocument) {
2918   return StyleRGBA::FromColor(
2919       PreferenceSheet::PrefsFor(aDocument).mDefaultColor);
2920 }
2921 
nsStyleText(const Document & aDocument)2922 nsStyleText::nsStyleText(const Document& aDocument)
2923     : mColor(DefaultColor(aDocument)),
2924       mTextTransform(StyleTextTransform::None()),
2925       mTextAlign(StyleTextAlign::Start),
2926       mTextAlignLast(StyleTextAlignLast::Auto),
2927       mTextJustify(StyleTextJustify::Auto),
2928       mWhiteSpace(StyleWhiteSpace::Normal),
2929       mHyphens(StyleHyphens::Manual),
2930       mRubyAlign(StyleRubyAlign::SpaceAround),
2931       mRubyPosition(StyleRubyPosition::Over),
2932       mTextSizeAdjust(StyleTextSizeAdjust::Auto),
2933       mTextCombineUpright(NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE),
2934       mControlCharacterVisibility(
2935           nsLayoutUtils::ControlCharVisibilityDefault()),
2936       mTextRendering(StyleTextRendering::Auto),
2937       mTextEmphasisColor(StyleColor::CurrentColor()),
2938       mWebkitTextFillColor(StyleColor::CurrentColor()),
2939       mWebkitTextStrokeColor(StyleColor::CurrentColor()),
2940       mMozTabSize(
2941           StyleNonNegativeLengthOrNumber::Number(NS_STYLE_TABSIZE_INITIAL)),
2942       mWordSpacing(LengthPercentage::Zero()),
2943       mLetterSpacing({0.}),
2944       mLineHeight(StyleLineHeight::Normal()),
2945       mTextIndent(LengthPercentage::Zero()),
2946       mTextUnderlineOffset(LengthPercentageOrAuto::Auto()),
2947       mTextDecorationSkipInk(StyleTextDecorationSkipInk::Auto),
2948       mTextUnderlinePosition(StyleTextUnderlinePosition::AUTO),
2949       mWebkitTextStrokeWidth(0),
2950       mTextEmphasisStyle(StyleTextEmphasisStyle::None()) {
2951   MOZ_COUNT_CTOR(nsStyleText);
2952   RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
2953   mTextEmphasisPosition =
2954       language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
2955           ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
2956           : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
2957 }
2958 
nsStyleText(const nsStyleText & aSource)2959 nsStyleText::nsStyleText(const nsStyleText& aSource)
2960     : mColor(aSource.mColor),
2961       mTextTransform(aSource.mTextTransform),
2962       mTextAlign(aSource.mTextAlign),
2963       mTextAlignLast(aSource.mTextAlignLast),
2964       mTextJustify(aSource.mTextJustify),
2965       mWhiteSpace(aSource.mWhiteSpace),
2966       mLineBreak(aSource.mLineBreak),
2967       mWordBreak(aSource.mWordBreak),
2968       mOverflowWrap(aSource.mOverflowWrap),
2969       mHyphens(aSource.mHyphens),
2970       mRubyAlign(aSource.mRubyAlign),
2971       mRubyPosition(aSource.mRubyPosition),
2972       mTextSizeAdjust(aSource.mTextSizeAdjust),
2973       mTextCombineUpright(aSource.mTextCombineUpright),
2974       mControlCharacterVisibility(aSource.mControlCharacterVisibility),
2975       mTextEmphasisPosition(aSource.mTextEmphasisPosition),
2976       mTextRendering(aSource.mTextRendering),
2977       mTextEmphasisColor(aSource.mTextEmphasisColor),
2978       mWebkitTextFillColor(aSource.mWebkitTextFillColor),
2979       mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor),
2980       mMozTabSize(aSource.mMozTabSize),
2981       mWordSpacing(aSource.mWordSpacing),
2982       mLetterSpacing(aSource.mLetterSpacing),
2983       mLineHeight(aSource.mLineHeight),
2984       mTextIndent(aSource.mTextIndent),
2985       mTextUnderlineOffset(aSource.mTextUnderlineOffset),
2986       mTextDecorationSkipInk(aSource.mTextDecorationSkipInk),
2987       mTextUnderlinePosition(aSource.mTextUnderlinePosition),
2988       mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth),
2989       mTextShadow(aSource.mTextShadow),
2990       mTextEmphasisStyle(aSource.mTextEmphasisStyle) {
2991   MOZ_COUNT_CTOR(nsStyleText);
2992 }
2993 
~nsStyleText()2994 nsStyleText::~nsStyleText() { MOZ_COUNT_DTOR(nsStyleText); }
2995 
CalcDifference(const nsStyleText & aNewData) const2996 nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
2997   if (WhiteSpaceOrNewlineIsSignificant() !=
2998       aNewData.WhiteSpaceOrNewlineIsSignificant()) {
2999     // This may require construction of suppressed text frames
3000     return nsChangeHint_ReconstructFrame;
3001   }
3002 
3003   if (mTextCombineUpright != aNewData.mTextCombineUpright ||
3004       mControlCharacterVisibility != aNewData.mControlCharacterVisibility) {
3005     return nsChangeHint_ReconstructFrame;
3006   }
3007 
3008   if ((mTextAlign != aNewData.mTextAlign) ||
3009       (mTextAlignLast != aNewData.mTextAlignLast) ||
3010       (mTextTransform != aNewData.mTextTransform) ||
3011       (mWhiteSpace != aNewData.mWhiteSpace) ||
3012       (mLineBreak != aNewData.mLineBreak) ||
3013       (mWordBreak != aNewData.mWordBreak) ||
3014       (mOverflowWrap != aNewData.mOverflowWrap) ||
3015       (mHyphens != aNewData.mHyphens) || (mRubyAlign != aNewData.mRubyAlign) ||
3016       (mRubyPosition != aNewData.mRubyPosition) ||
3017       (mTextSizeAdjust != aNewData.mTextSizeAdjust) ||
3018       (mLetterSpacing != aNewData.mLetterSpacing) ||
3019       (mLineHeight != aNewData.mLineHeight) ||
3020       (mTextIndent != aNewData.mTextIndent) ||
3021       (mTextJustify != aNewData.mTextJustify) ||
3022       (mWordSpacing != aNewData.mWordSpacing) ||
3023       (mMozTabSize != aNewData.mMozTabSize)) {
3024     return NS_STYLE_HINT_REFLOW;
3025   }
3026 
3027   if (HasEffectiveTextEmphasis() != aNewData.HasEffectiveTextEmphasis() ||
3028       (HasEffectiveTextEmphasis() &&
3029        mTextEmphasisPosition != aNewData.mTextEmphasisPosition)) {
3030     // Text emphasis position change could affect line height calculation.
3031     return nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
3032   }
3033 
3034   nsChangeHint hint = nsChangeHint(0);
3035 
3036   // text-rendering changes require a reflow since they change SVG
3037   // frames' rects.
3038   if (mTextRendering != aNewData.mTextRendering) {
3039     hint |= nsChangeHint_NeedReflow |
3040             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
3041             nsChangeHint_RepaintFrame;
3042   }
3043 
3044   if (mTextShadow != aNewData.mTextShadow ||
3045       mTextEmphasisStyle != aNewData.mTextEmphasisStyle ||
3046       mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth ||
3047       mTextUnderlineOffset != aNewData.mTextUnderlineOffset ||
3048       mTextDecorationSkipInk != aNewData.mTextDecorationSkipInk ||
3049       mTextUnderlinePosition != aNewData.mTextUnderlinePosition) {
3050     hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint |
3051             nsChangeHint_RepaintFrame;
3052 
3053     // We don't add any other hints below.
3054     return hint;
3055   }
3056 
3057   if (mColor != aNewData.mColor) {
3058     hint |= nsChangeHint_RepaintFrame;
3059   }
3060 
3061   if (mTextEmphasisColor != aNewData.mTextEmphasisColor ||
3062       mWebkitTextFillColor != aNewData.mWebkitTextFillColor ||
3063       mWebkitTextStrokeColor != aNewData.mWebkitTextStrokeColor) {
3064     hint |= nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame;
3065   }
3066 
3067   if (hint) {
3068     return hint;
3069   }
3070 
3071   if (mTextEmphasisPosition != aNewData.mTextEmphasisPosition) {
3072     return nsChangeHint_NeutralChange;
3073   }
3074 
3075   return nsChangeHint(0);
3076 }
3077 
TextEmphasisSide(WritingMode aWM) const3078 LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
3079   MOZ_ASSERT(
3080       (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) !=
3081        !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT)) &&
3082       (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) !=
3083        !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)));
3084   mozilla::Side side =
3085       aWM.IsVertical()
3086           ? (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT
3087                  ? eSideLeft
3088                  : eSideRight)
3089           : (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER
3090                  ? eSideTop
3091                  : eSideBottom);
3092   LogicalSide result = aWM.LogicalSideForPhysicalSide(side);
3093   MOZ_ASSERT(IsBlock(result));
3094   return result;
3095 }
3096 
3097 //-----------------------
3098 // nsStyleUI
3099 //
3100 
nsStyleUI(const Document & aDocument)3101 nsStyleUI::nsStyleUI(const Document& aDocument)
3102     : mUserInput(StyleUserInput::Auto),
3103       mUserModify(StyleUserModify::ReadOnly),
3104       mUserFocus(StyleUserFocus::None),
3105       mPointerEvents(StylePointerEvents::Auto),
3106       mCursor{{}, StyleCursorKind::Auto},
3107       mCaretColor(StyleColorOrAuto::Auto()),
3108       mScrollbarColor(StyleScrollbarColor::Auto()) {
3109   MOZ_COUNT_CTOR(nsStyleUI);
3110 }
3111 
nsStyleUI(const nsStyleUI & aSource)3112 nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
3113     : mUserInput(aSource.mUserInput),
3114       mUserModify(aSource.mUserModify),
3115       mUserFocus(aSource.mUserFocus),
3116       mPointerEvents(aSource.mPointerEvents),
3117       mCursor(aSource.mCursor),
3118       mCaretColor(aSource.mCaretColor),
3119       mScrollbarColor(aSource.mScrollbarColor) {
3120   MOZ_COUNT_CTOR(nsStyleUI);
3121 }
3122 
~nsStyleUI()3123 nsStyleUI::~nsStyleUI() { MOZ_COUNT_DTOR(nsStyleUI); }
3124 
TriggerImageLoads(Document & aDocument,const nsStyleUI * aOldStyle)3125 void nsStyleUI::TriggerImageLoads(Document& aDocument,
3126                                   const nsStyleUI* aOldStyle) {
3127   MOZ_ASSERT(NS_IsMainThread());
3128 
3129   auto cursorImages = mCursor.images.AsSpan();
3130   auto oldCursorImages = aOldStyle ? aOldStyle->mCursor.images.AsSpan()
3131                                    : Span<const StyleCursorImage>();
3132   for (size_t i = 0; i < cursorImages.Length(); ++i) {
3133     auto& cursor = cursorImages[i];
3134 
3135     if (!cursor.url.IsImageResolved()) {
3136       const auto* oldCursor =
3137           oldCursorImages.Length() > i ? &oldCursorImages[i] : nullptr;
3138       const_cast<StyleComputedImageUrl&>(cursor.url)
3139           .ResolveImage(aDocument, oldCursor ? &oldCursor->url : nullptr);
3140     }
3141   }
3142 }
3143 
CalcDifference(const nsStyleUI & aNewData) const3144 nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const {
3145   nsChangeHint hint = nsChangeHint(0);
3146   if (mCursor != aNewData.mCursor) {
3147     hint |= nsChangeHint_UpdateCursor;
3148   }
3149 
3150   if (mPointerEvents != aNewData.mPointerEvents) {
3151     // SVGGeometryFrame's mRect depends on stroke _and_ on the value
3152     // of pointer-events. See SVGGeometryFrame::ReflowSVG's use of
3153     // GetHitTestFlags. (Only a reflow, no visual change.)
3154     hint |= nsChangeHint_NeedReflow |
3155             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
3156             nsChangeHint_SchedulePaint;     // pointer-events changes can change
3157                                             // event regions overrides on layers
3158                                             // and so needs a repaint.
3159   }
3160 
3161   if (mUserModify != aNewData.mUserModify) {
3162     hint |= NS_STYLE_HINT_VISUAL;
3163   }
3164 
3165   if (mUserInput != aNewData.mUserInput) {
3166     if (StyleUserInput::None == mUserInput ||
3167         StyleUserInput::None == aNewData.mUserInput) {
3168       hint |= nsChangeHint_ReconstructFrame;
3169     } else {
3170       hint |= nsChangeHint_NeutralChange;
3171     }
3172   }
3173 
3174   if (mUserFocus != aNewData.mUserFocus) {
3175     hint |= nsChangeHint_NeutralChange;
3176   }
3177 
3178   if (mCaretColor != aNewData.mCaretColor ||
3179       mScrollbarColor != aNewData.mScrollbarColor) {
3180     hint |= nsChangeHint_RepaintFrame;
3181   }
3182 
3183   return hint;
3184 }
3185 
3186 //-----------------------
3187 // nsStyleUIReset
3188 //
3189 
nsStyleUIReset(const Document & aDocument)3190 nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
3191     : mUserSelect(StyleUserSelect::Auto),
3192       mScrollbarWidth(StyleScrollbarWidth::Auto),
3193       mMozForceBrokenImageIcon(0),
3194       mIMEMode(StyleImeMode::Auto),
3195       mWindowDragging(StyleWindowDragging::Default),
3196       mWindowShadow(StyleWindowShadow::Default),
3197       mWindowOpacity(1.0),
3198       mWindowTransformOrigin{LengthPercentage::FromPercentage(0.5),
3199                              LengthPercentage::FromPercentage(0.5),
3200                              {0.}} {
3201   MOZ_COUNT_CTOR(nsStyleUIReset);
3202 }
3203 
nsStyleUIReset(const nsStyleUIReset & aSource)3204 nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
3205     : mUserSelect(aSource.mUserSelect),
3206       mScrollbarWidth(aSource.mScrollbarWidth),
3207       mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon),
3208       mIMEMode(aSource.mIMEMode),
3209       mWindowDragging(aSource.mWindowDragging),
3210       mWindowShadow(aSource.mWindowShadow),
3211       mWindowOpacity(aSource.mWindowOpacity),
3212       mMozWindowTransform(aSource.mMozWindowTransform),
3213       mWindowTransformOrigin(aSource.mWindowTransformOrigin) {
3214   MOZ_COUNT_CTOR(nsStyleUIReset);
3215 }
3216 
~nsStyleUIReset()3217 nsStyleUIReset::~nsStyleUIReset() { MOZ_COUNT_DTOR(nsStyleUIReset); }
3218 
CalcDifference(const nsStyleUIReset & aNewData) const3219 nsChangeHint nsStyleUIReset::CalcDifference(
3220     const nsStyleUIReset& aNewData) const {
3221   nsChangeHint hint = nsChangeHint(0);
3222 
3223   if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) {
3224     hint |= nsChangeHint_ReconstructFrame;
3225   }
3226   if (mScrollbarWidth != aNewData.mScrollbarWidth) {
3227     // For scrollbar-width change, we need some special handling similar
3228     // to overflow properties. Specifically, we may need to reconstruct
3229     // the scrollbar or force reflow of the viewport scrollbar.
3230     hint |= nsChangeHint_ScrollbarChange;
3231   }
3232   if (mWindowShadow != aNewData.mWindowShadow) {
3233     // We really need just an nsChangeHint_SyncFrameView, except
3234     // on an ancestor of the frame, so we get that by doing a
3235     // reflow.
3236     hint |= NS_STYLE_HINT_REFLOW;
3237   }
3238   if (mUserSelect != aNewData.mUserSelect) {
3239     hint |= NS_STYLE_HINT_VISUAL;
3240   }
3241 
3242   if (mWindowDragging != aNewData.mWindowDragging) {
3243     hint |= nsChangeHint_SchedulePaint;
3244   }
3245 
3246   if (!hint && (mIMEMode != aNewData.mIMEMode ||
3247                 mWindowOpacity != aNewData.mWindowOpacity ||
3248                 mMozWindowTransform != aNewData.mMozWindowTransform)) {
3249     hint |= nsChangeHint_NeutralChange;
3250   }
3251 
3252   return hint;
3253 }
3254 
3255 //-----------------------
3256 // nsStyleEffects
3257 //
3258 
nsStyleEffects(const Document &)3259 nsStyleEffects::nsStyleEffects(const Document&)
3260     : mClip(StyleClipRectOrAuto::Auto()),
3261       mOpacity(1.0f),
3262       mMixBlendMode(StyleBlend::Normal) {
3263   MOZ_COUNT_CTOR(nsStyleEffects);
3264 }
3265 
nsStyleEffects(const nsStyleEffects & aSource)3266 nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource)
3267     : mFilters(aSource.mFilters),
3268       mBoxShadow(aSource.mBoxShadow),
3269       mBackdropFilters(aSource.mBackdropFilters),
3270       mClip(aSource.mClip),
3271       mOpacity(aSource.mOpacity),
3272       mMixBlendMode(aSource.mMixBlendMode) {
3273   MOZ_COUNT_CTOR(nsStyleEffects);
3274 }
3275 
~nsStyleEffects()3276 nsStyleEffects::~nsStyleEffects() { MOZ_COUNT_DTOR(nsStyleEffects); }
3277 
AnyAutonessChanged(const StyleClipRectOrAuto & aOld,const StyleClipRectOrAuto & aNew)3278 static bool AnyAutonessChanged(const StyleClipRectOrAuto& aOld,
3279                                const StyleClipRectOrAuto& aNew) {
3280   if (aOld.IsAuto() != aNew.IsAuto()) {
3281     return true;
3282   }
3283   if (aOld.IsAuto()) {
3284     return false;
3285   }
3286   auto& oldRect = aOld.AsRect();
3287   auto& newRect = aNew.AsRect();
3288   return oldRect.top.IsAuto() != newRect.top.IsAuto() ||
3289          oldRect.right.IsAuto() != newRect.right.IsAuto() ||
3290          oldRect.bottom.IsAuto() != newRect.bottom.IsAuto() ||
3291          oldRect.left.IsAuto() != newRect.left.IsAuto();
3292 }
3293 
CalcDifference(const nsStyleEffects & aNewData) const3294 nsChangeHint nsStyleEffects::CalcDifference(
3295     const nsStyleEffects& aNewData) const {
3296   nsChangeHint hint = nsChangeHint(0);
3297 
3298   if (mBoxShadow != aNewData.mBoxShadow) {
3299     // Update overflow regions & trigger DLBI to be sure it's noticed.
3300     // Also request a repaint, since it's possible that only the color
3301     // of the shadow is changing (and UpdateOverflow/SchedulePaint won't
3302     // repaint for that, since they won't know what needs invalidating.)
3303     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
3304             nsChangeHint_RepaintFrame;
3305   }
3306 
3307   if (AnyAutonessChanged(mClip, aNewData.mClip)) {
3308     hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
3309   } else if (mClip != aNewData.mClip) {
3310     // If the clip has changed, we just need to update overflow areas. DLBI
3311     // will handle the invalidation.
3312     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint;
3313   }
3314 
3315   if (mOpacity != aNewData.mOpacity) {
3316     hint |= nsChangeHint_UpdateOpacityLayer;
3317 
3318     // If we're going from the optimized >=0.99 opacity value to 1.0 or back,
3319     // then repaint the frame because DLBI will not catch the invalidation.
3320     // Otherwise, just update the opacity layer.
3321     if ((mOpacity >= 0.99f && mOpacity < 1.0f && aNewData.mOpacity == 1.0f) ||
3322         (aNewData.mOpacity >= 0.99f && aNewData.mOpacity < 1.0f &&
3323          mOpacity == 1.0f)) {
3324       hint |= nsChangeHint_RepaintFrame;
3325     } else {
3326       if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) {
3327         hint |= nsChangeHint_UpdateUsesOpacity;
3328       }
3329     }
3330   }
3331 
3332   if (HasFilters() != aNewData.HasFilters()) {
3333     // A change from/to being a containing block for position:fixed.
3334     hint |= nsChangeHint_UpdateContainingBlock;
3335   }
3336 
3337   if (mFilters != aNewData.mFilters) {
3338     hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame |
3339             nsChangeHint_UpdateOverflow;
3340   }
3341 
3342   if (mMixBlendMode != aNewData.mMixBlendMode) {
3343     hint |= nsChangeHint_RepaintFrame;
3344   }
3345 
3346   if (HasBackdropFilters() != aNewData.HasBackdropFilters()) {
3347     // A change from/to being a containing block for position:fixed.
3348     hint |= nsChangeHint_UpdateContainingBlock;
3349   }
3350 
3351   if (mBackdropFilters != aNewData.mBackdropFilters) {
3352     hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
3353   }
3354 
3355   return hint;
3356 }
3357 
TransformOperationHasPercent(const StyleTransformOperation & aOp)3358 static bool TransformOperationHasPercent(const StyleTransformOperation& aOp) {
3359   switch (aOp.tag) {
3360     case StyleTransformOperation::Tag::TranslateX:
3361       return aOp.AsTranslateX().HasPercent();
3362     case StyleTransformOperation::Tag::TranslateY:
3363       return aOp.AsTranslateY().HasPercent();
3364     case StyleTransformOperation::Tag::TranslateZ:
3365       return false;
3366     case StyleTransformOperation::Tag::Translate3D: {
3367       auto& translate = aOp.AsTranslate3D();
3368       // NOTE(emilio): z translation is a `<length>`, so can't have percentages.
3369       return translate._0.HasPercent() || translate._1.HasPercent();
3370     }
3371     case StyleTransformOperation::Tag::Translate: {
3372       auto& translate = aOp.AsTranslate();
3373       return translate._0.HasPercent() || translate._1.HasPercent();
3374     }
3375     case StyleTransformOperation::Tag::AccumulateMatrix: {
3376       auto& accum = aOp.AsAccumulateMatrix();
3377       return accum.from_list.HasPercent() || accum.to_list.HasPercent();
3378     }
3379     case StyleTransformOperation::Tag::InterpolateMatrix: {
3380       auto& interpolate = aOp.AsInterpolateMatrix();
3381       return interpolate.from_list.HasPercent() ||
3382              interpolate.to_list.HasPercent();
3383     }
3384     case StyleTransformOperation::Tag::Perspective:
3385     case StyleTransformOperation::Tag::RotateX:
3386     case StyleTransformOperation::Tag::RotateY:
3387     case StyleTransformOperation::Tag::RotateZ:
3388     case StyleTransformOperation::Tag::Rotate:
3389     case StyleTransformOperation::Tag::Rotate3D:
3390     case StyleTransformOperation::Tag::SkewX:
3391     case StyleTransformOperation::Tag::SkewY:
3392     case StyleTransformOperation::Tag::Skew:
3393     case StyleTransformOperation::Tag::ScaleX:
3394     case StyleTransformOperation::Tag::ScaleY:
3395     case StyleTransformOperation::Tag::ScaleZ:
3396     case StyleTransformOperation::Tag::Scale:
3397     case StyleTransformOperation::Tag::Scale3D:
3398     case StyleTransformOperation::Tag::Matrix:
3399     case StyleTransformOperation::Tag::Matrix3D:
3400       return false;
3401     default:
3402       MOZ_ASSERT_UNREACHABLE("Unknown transform operation");
3403       return false;
3404   }
3405 }
3406 
3407 template <>
HasPercent() const3408 bool StyleTransform::HasPercent() const {
3409   for (const auto& op : Operations()) {
3410     if (TransformOperationHasPercent(op)) {
3411       return true;
3412     }
3413   }
3414   return false;
3415 }
3416 
3417 template <>
ScaleLengthsBy(float aScale)3418 void StyleCalcNode::ScaleLengthsBy(float aScale) {
3419   auto ScaleNode = [aScale](const StyleCalcNode& aNode) {
3420     // This const_cast could be removed by generating more mut-casts, if
3421     // needed.
3422     const_cast<StyleCalcNode&>(aNode).ScaleLengthsBy(aScale);
3423   };
3424 
3425   switch (tag) {
3426     case Tag::Leaf: {
3427       auto& leaf = AsLeaf();
3428       if (leaf.IsLength()) {
3429         // This const_cast could be removed by generating more mut-casts, if
3430         // needed.
3431         const_cast<Length&>(leaf.AsLength()).ScaleBy(aScale);
3432       }
3433       break;
3434     }
3435     case Tag::Clamp: {
3436       auto& clamp = AsClamp();
3437       ScaleNode(*clamp.min);
3438       ScaleNode(*clamp.center);
3439       ScaleNode(*clamp.max);
3440       break;
3441     }
3442     case Tag::MinMax: {
3443       for (auto& child : AsMinMax()._0.AsSpan()) {
3444         ScaleNode(child);
3445       }
3446       break;
3447     }
3448     case Tag::Sum: {
3449       for (auto& child : AsSum().AsSpan()) {
3450         ScaleNode(child);
3451       }
3452       break;
3453     }
3454   }
3455 }
3456 
3457 template <>
3458 template <typename ResultT, typename PercentageConverter>
3459 ResultT StyleCalcNode::ResolveInternal(ResultT aPercentageBasis,
3460                                        PercentageConverter aConverter) const {
3461   static_assert(std::is_same_v<decltype(aConverter(1.0f)), ResultT>);
3462   static_assert(std::is_same_v<ResultT, nscoord> ||
3463                 std::is_same_v<ResultT, CSSCoord>);
3464 
3465   switch (tag) {
3466     case Tag::Leaf: {
3467       auto& leaf = AsLeaf();
3468       if (leaf.IsPercentage()) {
3469         return aConverter(leaf.AsPercentage()._0 * aPercentageBasis);
3470       }
3471       if constexpr (std::is_same_v<ResultT, nscoord>) {
3472         return leaf.AsLength().ToAppUnits();
3473       } else {
3474         return leaf.AsLength().ToCSSPixels();
3475       }
3476     }
3477     case Tag::Clamp: {
3478       auto& clamp = AsClamp();
3479       auto min = clamp.min->ResolveInternal(aPercentageBasis, aConverter);
3480       auto center = clamp.center->ResolveInternal(aPercentageBasis, aConverter);
3481       auto max = clamp.max->ResolveInternal(aPercentageBasis, aConverter);
3482       return std::max(min, std::min(center, max));
3483     }
3484     case Tag::MinMax: {
3485       auto children = AsMinMax()._0.AsSpan();
3486       StyleMinMaxOp op = AsMinMax()._1;
3487 
3488       ResultT result =
3489           children[0].ResolveInternal(aPercentageBasis, aConverter);
3490       for (auto& child : children.From(1)) {
3491         ResultT candidate = child.ResolveInternal(aPercentageBasis, aConverter);
3492         if (op == StyleMinMaxOp::Max) {
3493           result = std::max(result, candidate);
3494         } else {
3495           result = std::min(result, candidate);
3496         }
3497       }
3498       return result;
3499     }
3500     case Tag::Sum: {
3501       ResultT result = 0;
3502       for (auto& child : AsSum().AsSpan()) {
3503         result += child.ResolveInternal(aPercentageBasis, aConverter);
3504       }
3505       return result;
3506     }
3507   }
3508 
3509   MOZ_ASSERT_UNREACHABLE("Unknown calc node");
3510   return 0;
3511 }
3512 
3513 template <>
ResolveToCSSPixels(CSSCoord aBasis) const3514 CSSCoord StyleCalcNode::ResolveToCSSPixels(CSSCoord aBasis) const {
3515   return ResolveInternal(aBasis, [](CSSCoord aPercent) { return aPercent; });
3516 }
3517 
3518 template <>
Resolve(nscoord aBasis,CoordPercentageRounder aRounder) const3519 nscoord StyleCalcNode::Resolve(nscoord aBasis,
3520                                CoordPercentageRounder aRounder) const {
3521   return ResolveInternal(aBasis, aRounder);
3522 }
3523