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