nsStyleStruct.cpp (146413B)
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 14 #include <algorithm> 15 16 #include "AnchorPositioningUtils.h" 17 #include "CounterStyleManager.h" 18 #include "ImageLoader.h" 19 #include "imgIContainer.h" 20 #include "imgIRequest.h" 21 #include "mozilla/CORSMode.h" 22 #include "mozilla/ClearOnShutdown.h" 23 #include "mozilla/GeckoBindings.h" 24 #include "mozilla/Likely.h" 25 #include "mozilla/PreferenceSheet.h" 26 #include "mozilla/SchedulerGroup.h" 27 #include "mozilla/StaticPrefs_layout.h" 28 #include "mozilla/StaticPresData.h" 29 #include "mozilla/dom/AnimationEffectBinding.h" // for PlaybackDirection 30 #include "mozilla/dom/BaseKeyframeTypesBinding.h" // for CompositeOperation 31 #include "mozilla/dom/DocGroup.h" 32 #include "mozilla/dom/Document.h" 33 #include "mozilla/dom/DocumentInlines.h" 34 #include "nsBidiUtils.h" 35 #include "nsCOMPtr.h" 36 #include "nsCRTGlue.h" 37 #include "nsCSSProps.h" 38 #include "nsContainerFrame.h" 39 #include "nsDeviceContext.h" 40 #include "nsIURI.h" 41 #include "nsIURIMutator.h" 42 #include "nsIWidget.h" 43 #include "nsLayoutUtils.h" 44 #include "nsPresContext.h" 45 #include "nsString.h" 46 #include "nsStyleConsts.h" 47 #include "nsStyleStructInlines.h" 48 #include "nsStyleStructList.h" 49 #include "nsStyleUtil.h" 50 51 using namespace mozilla; 52 using namespace mozilla::dom; 53 54 MOZ_RUNINIT static const nscoord kMediumBorderWidth = 55 nsPresContext::CSSPixelsToAppUnits(3); 56 57 // We set the size limit of style structs to 504 bytes so that when they 58 // are allocated by Servo side with Arc, the total size doesn't exceed 59 // 512 bytes, which minimizes allocator slop. 60 static constexpr size_t kStyleStructSizeLimit = 504; 61 62 template <typename Struct, size_t Actual, size_t Limit> 63 struct AssertSizeIsLessThan { 64 static_assert(Actual == sizeof(Struct), "Bogus invocation"); 65 static_assert(Actual <= Limit, 66 "Style struct became larger than the size limit"); 67 static constexpr bool instantiate = true; 68 }; 69 70 #define ASSERT_SIZE(name_) \ 71 static_assert(AssertSizeIsLessThan<nsStyle##name_, sizeof(nsStyle##name_), \ 72 kStyleStructSizeLimit>::instantiate, \ 73 ""); 74 FOR_EACH_STYLE_STRUCT(ASSERT_SIZE, ASSERT_SIZE) 75 #undef ASSERT_SIZE 76 77 bool StyleCssUrlData::operator==(const StyleCssUrlData& aOther) const { 78 // This very intentionally avoids comparing LoadData and such. 79 const auto& extra = extra_data.get(); 80 const auto& otherExtra = aOther.extra_data.get(); 81 if (extra.BaseURI() != otherExtra.BaseURI() || 82 extra.Principal() != otherExtra.Principal() || 83 cors_mode != aOther.cors_mode) { 84 // NOTE(emilio): This does pointer comparison, but it's what URLValue used 85 // to do. That's ok though since this is only used for style struct diffing. 86 return false; 87 } 88 return serialization == aOther.serialization; 89 } 90 91 StyleLoadData::~StyleLoadData() { Gecko_LoadData_Drop(this); } 92 93 void StyleComputedUrl::ResolveImage(Document& aDocument, 94 const StyleComputedUrl* aOldImage) { 95 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); 96 97 StyleLoadData& data = MutLoadData(); 98 99 MOZ_ASSERT(!(data.flags & StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE)); 100 101 data.flags |= StyleLoadDataFlags::TRIED_TO_RESOLVE_IMAGE; 102 103 // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the 104 // lack of non-http image caching in imagelib (bug 1406134), which causes 105 // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug 106 // 1440305, but that seems too risky, and a lot of work to do before 60. 107 // 108 // Once that's fixed, the "old style" argument to TriggerImageLoads can go 109 // away, and same for mSharedCount in the image loader and so on. 110 const bool reuseProxy = nsContentUtils::IsChromeDoc(&aDocument) && 111 aOldImage && aOldImage->IsImageResolved() && 112 *this == *aOldImage; 113 114 RefPtr<imgRequestProxy> request; 115 if (reuseProxy) { 116 request = aOldImage->LoadData().resolved_image; 117 if (request) { 118 css::ImageLoader::NoteSharedLoad(request); 119 } 120 } else { 121 request = css::ImageLoader::LoadImage(*this, aDocument); 122 } 123 124 if (!request) { 125 return; 126 } 127 128 data.resolved_image = request.forget().take(); 129 130 // Boost priority now that we know the image is present in the ComputedStyle 131 // of some frame. 132 data.resolved_image->BoostPriority(imgIRequest::CATEGORY_FRAME_STYLE); 133 } 134 135 /** 136 * Runnable to release the image request's mRequestProxy on the main thread, and 137 * to perform any necessary unlocking and untracking of the image. 138 */ 139 class StyleImageRequestCleanupTask final : public mozilla::Runnable { 140 public: 141 explicit StyleImageRequestCleanupTask(StyleLoadData& aData) 142 : mozilla::Runnable("StyleImageRequestCleanupTask"), 143 mRequestProxy(dont_AddRef(aData.resolved_image)) { 144 MOZ_ASSERT(mRequestProxy); 145 aData.resolved_image = nullptr; 146 } 147 148 NS_IMETHOD Run() final { 149 MOZ_ASSERT(NS_IsMainThread()); 150 css::ImageLoader::UnloadImage(mRequestProxy); 151 return NS_OK; 152 } 153 154 protected: 155 virtual ~StyleImageRequestCleanupTask() { 156 MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(), 157 "mRequestProxy destructor need to run on the main thread!"); 158 } 159 160 private: 161 // Since we always dispatch this runnable to the main thread, these will be 162 // released on the main thread when the runnable itself is released. 163 RefPtr<imgRequestProxy> mRequestProxy; 164 }; 165 166 // This is defined here for parallelism with LoadURI. 167 void Gecko_LoadData_Drop(StyleLoadData* aData) { 168 if (aData->resolved_image) { 169 // We want to dispatch this async to prevent reentrancy issues, as 170 // imgRequestProxy going away can destroy documents, etc, see bug 1677555. 171 auto task = MakeRefPtr<StyleImageRequestCleanupTask>(*aData); 172 SchedulerGroup::Dispatch(task.forget()); 173 } 174 175 // URIs are safe to refcount from any thread. 176 NS_IF_RELEASE(aData->resolved_uri); 177 } 178 179 // -------------------- 180 // nsStyleFont 181 // 182 nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) 183 : mFont(aSrc.mFont), 184 mSize(aSrc.mSize), 185 mFontSizeFactor(aSrc.mFontSizeFactor), 186 mFontSizeOffset(aSrc.mFontSizeOffset), 187 mFontSizeKeyword(aSrc.mFontSizeKeyword), 188 mFontPalette(aSrc.mFontPalette), 189 mMathDepth(aSrc.mMathDepth), 190 mLineHeight(aSrc.mLineHeight), 191 mMinFontSizeRatio(aSrc.mMinFontSizeRatio), 192 mMathVariant(aSrc.mMathVariant), 193 mMathStyle(aSrc.mMathStyle), 194 mMathShift(aSrc.mMathShift), 195 mExplicitLanguage(aSrc.mExplicitLanguage), 196 mXTextScale(aSrc.mXTextScale), 197 mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize), 198 mScriptMinSize(aSrc.mScriptMinSize), 199 mLanguage(aSrc.mLanguage) { 200 MOZ_COUNT_CTOR(nsStyleFont); 201 } 202 203 static StyleXTextScale InitialTextScale(const Document& aDoc) { 204 if (nsContentUtils::IsChromeDoc(&aDoc) || 205 nsContentUtils::IsPDFJS(aDoc.NodePrincipal())) { 206 return StyleXTextScale::ZoomOnly; 207 } 208 return StyleXTextScale::All; 209 } 210 211 nsStyleFont::nsStyleFont(const Document& aDocument) 212 : mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont( 213 StyleGenericFontFamily::None)), 214 mSize(ZoomText(aDocument, mFont.size)), 215 mFontSizeFactor(1.0), 216 mFontSizeOffset{0}, 217 mFontSizeKeyword(StyleFontSizeKeyword::Medium), 218 mFontPalette(StyleFontPalette::Normal()), 219 mMathDepth(0), 220 mLineHeight(StyleLineHeight::Normal()), 221 mMathVariant(StyleMathVariant::None), 222 mMathStyle(StyleMathStyle::Normal), 223 mMathShift(StyleMathShift::Normal), 224 mXTextScale(InitialTextScale(aDocument)), 225 mScriptUnconstrainedSize(mSize), 226 mScriptMinSize(Length::FromPixels( 227 CSSPixel::FromPoints(kMathMLDefaultScriptMinSizePt))), 228 mLanguage(aDocument.GetLanguageForStyle()) { 229 MOZ_COUNT_CTOR(nsStyleFont); 230 MOZ_ASSERT(NS_IsMainThread()); 231 mFont.family.is_initial = true; 232 mFont.size = mSize; 233 if (MinFontSizeEnabled()) { 234 const Length minimumFontSize = 235 aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize; 236 mFont.size = Length::FromPixels( 237 std::max(mSize.ToCSSPixels(), minimumFontSize.ToCSSPixels())); 238 } 239 } 240 241 nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const { 242 MOZ_ASSERT(mXTextScale == aNewData.mXTextScale, 243 "expected -x-text-scale to be the same on both nsStyleFonts"); 244 if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage || 245 mExplicitLanguage != aNewData.mExplicitLanguage || 246 mMathVariant != aNewData.mMathVariant || 247 mMathStyle != aNewData.mMathStyle || mMathShift != aNewData.mMathShift || 248 mMinFontSizeRatio != aNewData.mMinFontSizeRatio || 249 mLineHeight != aNewData.mLineHeight) { 250 return NS_STYLE_HINT_REFLOW; 251 } 252 253 switch (mFont.CalcDifference(aNewData.mFont)) { 254 case nsFont::MaxDifference::eLayoutAffecting: 255 return NS_STYLE_HINT_REFLOW; 256 257 case nsFont::MaxDifference::eVisual: 258 return NS_STYLE_HINT_VISUAL; 259 260 case nsFont::MaxDifference::eNone: 261 break; 262 } 263 264 if (mFontPalette != aNewData.mFontPalette) { 265 return NS_STYLE_HINT_VISUAL; 266 } 267 268 // XXX Should any of these cause a non-nsChangeHint_NeutralChange change? 269 if (mMathDepth != aNewData.mMathDepth || 270 mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize || 271 mScriptMinSize != aNewData.mScriptMinSize) { 272 return nsChangeHint_NeutralChange; 273 } 274 275 return nsChangeHint(0); 276 } 277 278 Length nsStyleFont::ZoomText(const Document& aDocument, Length aSize) { 279 if (auto* pc = aDocument.GetPresContext()) { 280 aSize.ScaleBy(pc->TextZoom()); 281 } 282 return aSize; 283 } 284 285 template <typename T> 286 static StyleRect<T> StyleRectWithAllSides(const T& aSide) { 287 return {aSide, aSide, aSide, aSide}; 288 } 289 290 bool AnchorPosResolutionParams::AutoResolutionOverrideParams::OverriddenToZero( 291 StylePhysicalAxis aAxis) const { 292 if (mPositionAreaInUse) { 293 // If `position-area` is used "Any auto inset properties resolve to 0": 294 // https://drafts.csswg.org/css-anchor-position-1/#valdef-position-area-position-area 295 return true; 296 } 297 298 // If `anchor-center` is used with a valid anchor, "auto inset 299 // properties resolve to 0" on that axis: 300 // https://drafts.csswg.org/css-anchor-position-1/#anchor-center 301 if (aAxis == StylePhysicalAxis::Vertical) { 302 return mVAnchorCenter; 303 } 304 MOZ_ASSERT(aAxis == StylePhysicalAxis::Horizontal); 305 return mHAnchorCenter; 306 } 307 308 static AnchorPosResolutionParams::AutoResolutionOverrideParams 309 GetAutoResolutionOverrideParams(const nsIFrame* aFrame, 310 bool aDefaultAnchorValid) { 311 if (!aFrame) { 312 return {}; 313 } 314 nsIFrame* parent = aFrame->GetParent(); 315 if (!parent || !aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) || 316 !aDefaultAnchorValid) { 317 return {}; 318 } 319 320 const auto* stylePos = aFrame->StylePosition(); 321 const auto cbwm = parent->GetWritingMode(); 322 323 auto checkAxis = [&](LogicalAxis aAxis) { 324 StyleAlignFlags alignment = 325 stylePos->UsedSelfAlignment(aAxis, parent->Style()); 326 return (alignment & ~StyleAlignFlags::FLAG_BITS) == 327 StyleAlignFlags::ANCHOR_CENTER; 328 }; 329 330 const auto horizontalLogicalAxis = 331 cbwm.IsVertical() ? LogicalAxis::Block : LogicalAxis::Inline; 332 AnchorPosResolutionParams::AutoResolutionOverrideParams result; 333 result.mHAnchorCenter = checkAxis(horizontalLogicalAxis); 334 result.mVAnchorCenter = checkAxis(GetOrthogonalAxis(horizontalLogicalAxis)); 335 result.mPositionAreaInUse = !stylePos->mPositionArea.IsNone(); 336 return result; 337 } 338 339 AnchorPosResolutionParams::AutoResolutionOverrideParams:: 340 AutoResolutionOverrideParams( 341 const nsIFrame* aFrame, const mozilla::AnchorPosResolutionCache* aCache) 342 : AutoResolutionOverrideParams{GetAutoResolutionOverrideParams( 343 aFrame, aCache && aCache->mDefaultAnchorCache.mAnchor)} {} 344 345 AnchorPosResolutionParams::AutoResolutionOverrideParams:: 346 AutoResolutionOverrideParams(const nsIFrame* aFrame) 347 : AutoResolutionOverrideParams{ 348 GetAutoResolutionOverrideParams(aFrame, [&]() { 349 if (!aFrame) { 350 return false; 351 } 352 const auto* references = 353 aFrame->GetProperty(nsIFrame::AnchorPosReferences()); 354 if (!references || !references->mDefaultAnchorName) { 355 // It is presumed that this is called on a reflowed frame. 356 return false; 357 } 358 return references->Lookup(references->mDefaultAnchorName)->isSome(); 359 }())} {} 360 361 AnchorResolvedMargin AnchorResolvedMarginHelper::ResolveAnchor( 362 const StyleMargin& aValue, StylePhysicalAxis aAxis, 363 const AnchorPosResolutionParams& aParams) { 364 MOZ_ASSERT(aValue.HasAnchorPositioningFunction(), 365 "Calling anchor resolution without using it?"); 366 if (aValue.IsAnchorSizeFunction()) { 367 auto resolved = StyleAnchorPositioningFunctionResolution::Invalid(); 368 Servo_ResolveAnchorSizeFunctionForMargin(&*aValue.AsAnchorSizeFunction(), 369 &aParams, aAxis, &resolved); 370 if (resolved.IsInvalid()) { 371 return Zero(); 372 } 373 if (resolved.IsResolvedReference()) { 374 return MakeUniqueOfUniqueOrNonOwning<const StyleMargin>( 375 *resolved.AsResolvedReference()); 376 } 377 return MakeUniqueOfUniqueOrNonOwning<const StyleMargin>( 378 resolved.AsResolved()); 379 } 380 381 const auto& lp = aValue.AsAnchorContainingCalcFunction(); 382 const auto& c = lp.AsCalc(); 383 auto result = StyleCalcAnchorPositioningFunctionResolution::Invalid(); 384 AnchorPosOffsetResolutionParams params = 385 AnchorPosOffsetResolutionParams::UseCBFrameSize(aParams); 386 const auto allowed = 387 StyleAllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(aAxis); 388 Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &allowed, ¶ms, &result); 389 if (result.IsInvalid()) { 390 return Zero(); 391 } 392 return MakeUniqueOfUniqueOrNonOwning<const StyleMargin>(result.AsValid()); 393 } 394 395 nsStyleMargin::nsStyleMargin() 396 : mMargin(StyleRectWithAllSides( 397 StyleMargin::LengthPercentage(LengthPercentage::Zero()))), 398 mScrollMargin(StyleRectWithAllSides(StyleLength{0.})), 399 mOverflowClipMargin( 400 {StyleLength::Zero(), StyleOverflowClipMarginBox::PaddingBox}) { 401 MOZ_COUNT_CTOR(nsStyleMargin); 402 } 403 404 nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc) 405 : mMargin(aSrc.mMargin), 406 mScrollMargin(aSrc.mScrollMargin), 407 mOverflowClipMargin(aSrc.mOverflowClipMargin) { 408 MOZ_COUNT_CTOR(nsStyleMargin); 409 } 410 411 nsChangeHint nsStyleMargin::CalcDifference( 412 const nsStyleMargin& aNewData) const { 413 nsChangeHint hint = nsChangeHint(0); 414 415 if (!MarginEquals(aNewData)) { 416 // Margin differences can't affect descendant intrinsic sizes and 417 // don't need to force children to reflow. 418 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition | 419 nsChangeHint_ClearAncestorIntrinsics; 420 } 421 422 if (mScrollMargin != aNewData.mScrollMargin) { 423 // FIXME: Bug 1530253 Support re-snapping when scroll-margin changes. 424 hint |= nsChangeHint_NeutralChange; 425 } 426 427 if (mOverflowClipMargin != aNewData.mOverflowClipMargin) { 428 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame; 429 } 430 431 return hint; 432 } 433 434 nsStylePadding::nsStylePadding() 435 : mPadding(StyleRectWithAllSides(LengthPercentage::Zero())), 436 mScrollPadding(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())) { 437 MOZ_COUNT_CTOR(nsStylePadding); 438 } 439 440 nsStylePadding::nsStylePadding(const nsStylePadding& aSrc) 441 : mPadding(aSrc.mPadding), mScrollPadding(aSrc.mScrollPadding) { 442 MOZ_COUNT_CTOR(nsStylePadding); 443 } 444 445 nsChangeHint nsStylePadding::CalcDifference( 446 const nsStylePadding& aNewData) const { 447 nsChangeHint hint = nsChangeHint(0); 448 449 if (mPadding != aNewData.mPadding) { 450 // Padding differences can't affect descendant intrinsic sizes, but do need 451 // to force children to reflow so that we can reposition them, since their 452 // offsets are from our frame bounds but our content rect's position within 453 // those bounds is moving. 454 // FIXME: It would be good to return a weaker hint here that doesn't 455 // force reflow of all descendants, but the hint would need to force 456 // reflow of the frame's children (see how 457 // ReflowInput::InitResizeFlags initializes the inline-resize flag). 458 hint |= NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics; 459 } 460 461 if (mScrollPadding != aNewData.mScrollPadding) { 462 // FIXME: Bug 1530253 Support re-snapping when scroll-padding changes. 463 hint |= nsChangeHint_NeutralChange; 464 } 465 466 return hint; 467 } 468 469 static inline BorderRadius ZeroBorderRadius() { 470 auto zero = LengthPercentage::Zero(); 471 return {{{zero, zero}}, {{zero, zero}}, {{zero, zero}}, {{zero, zero}}}; 472 } 473 474 nsStyleBorder::nsStyleBorder() 475 : mBorderRadius(ZeroBorderRadius()), 476 mBorderImageSource(StyleImage::None()), 477 mBorderImageWidth( 478 StyleRectWithAllSides(StyleBorderImageSideWidth::Number(1.))), 479 mBorderImageOutset( 480 StyleRectWithAllSides(StyleNonNegativeLengthOrNumber::Number(0.))), 481 mBorderImageSlice( 482 {StyleRectWithAllSides(StyleNumberOrPercentage::Percentage({1.})), 483 false}), 484 mBorderImageRepeat{StyleBorderImageRepeatKeyword::Stretch, 485 StyleBorderImageRepeatKeyword::Stretch}, 486 mFloatEdge(StyleFloatEdge::ContentBox), 487 mBoxDecorationBreak(StyleBoxDecorationBreak::Slice), 488 mBorderStyle(StyleRectWithAllSides(StyleBorderStyle::None)), 489 mBorder(StyleRectWithAllSides(kMediumBorderWidth)), 490 mBorderTopColor(StyleColor::CurrentColor()), 491 mBorderRightColor(StyleColor::CurrentColor()), 492 mBorderBottomColor(StyleColor::CurrentColor()), 493 mBorderLeftColor(StyleColor::CurrentColor()) { 494 MOZ_COUNT_CTOR(nsStyleBorder); 495 } 496 497 nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) 498 : mBorderRadius(aSrc.mBorderRadius), 499 mBorderImageSource(aSrc.mBorderImageSource), 500 mBorderImageWidth(aSrc.mBorderImageWidth), 501 mBorderImageOutset(aSrc.mBorderImageOutset), 502 mBorderImageSlice(aSrc.mBorderImageSlice), 503 mBorderImageRepeat(aSrc.mBorderImageRepeat), 504 mFloatEdge(aSrc.mFloatEdge), 505 mBoxDecorationBreak(aSrc.mBoxDecorationBreak), 506 mBorderStyle(aSrc.mBorderStyle), 507 mBorder(aSrc.mBorder), 508 mBorderTopColor(aSrc.mBorderTopColor), 509 mBorderRightColor(aSrc.mBorderRightColor), 510 mBorderBottomColor(aSrc.mBorderBottomColor), 511 mBorderLeftColor(aSrc.mBorderLeftColor) { 512 MOZ_COUNT_CTOR(nsStyleBorder); 513 } 514 515 void nsStyleBorder::TriggerImageLoads(Document& aDocument, 516 const nsStyleBorder* aOldStyle) { 517 MOZ_ASSERT(NS_IsMainThread()); 518 519 mBorderImageSource.ResolveImage( 520 aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr); 521 } 522 523 nsMargin nsStyleBorder::GetImageOutset() const { 524 // We don't check whether there is a border-image (which is OK since 525 // the initial values yields 0 outset) so that we don't have to 526 // reflow to update overflow areas when an image loads. 527 nsMargin outset; 528 auto computedBorder = GetComputedBorder(); 529 for (const auto s : mozilla::AllPhysicalSides()) { 530 const auto& coord = mBorderImageOutset.Get(s); 531 nscoord value; 532 if (coord.IsLength()) { 533 value = coord.AsLength().ToAppUnits(); 534 } else { 535 MOZ_ASSERT(coord.IsNumber()); 536 value = coord.AsNumber() * computedBorder.Side(s); 537 } 538 outset.Side(s) = value; 539 } 540 return outset; 541 } 542 543 nsChangeHint nsStyleBorder::CalcDifference( 544 const nsStyleBorder& aNewData) const { 545 // FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these 546 // differences should not need to clear descendant intrinsics. 547 // FIXME: It would be good to return a weaker hint for the 548 // GetComputedBorder() differences (and perhaps others) that doesn't 549 // force reflow of all descendants, but the hint would need to force 550 // reflow of the frame's children (see how 551 // ReflowInput::InitResizeFlags initializes the inline-resize flag). 552 if (GetComputedBorder() != aNewData.GetComputedBorder() || 553 mFloatEdge != aNewData.mFloatEdge || 554 mBorderImageOutset != aNewData.mBorderImageOutset || 555 mBoxDecorationBreak != aNewData.mBoxDecorationBreak) { 556 return NS_STYLE_HINT_REFLOW; 557 } 558 559 for (const auto ix : mozilla::AllPhysicalSides()) { 560 if (mBorderStyle.Get(ix) != aNewData.mBorderStyle.Get(ix) || 561 BorderColorFor(ix) != aNewData.BorderColorFor(ix)) { 562 return nsChangeHint_RepaintFrame; 563 } 564 } 565 566 // Note that border radius also controls the outline radius if the 567 // layout.css.outline-follows-border-radius.enabled pref is set. Any 568 // optimizations here should apply to both. 569 if (mBorderRadius != aNewData.mBorderRadius) { 570 return nsChangeHint_RepaintFrame; 571 } 572 573 // Loading status of the border image can be accessed in main thread only 574 // while CalcDifference might be executed on a background thread. As a 575 // result, we have to check mBorderImage* fields even before border image was 576 // actually loaded. 577 if (!mBorderImageSource.IsNone() || !aNewData.mBorderImageSource.IsNone()) { 578 if (mBorderImageSource != aNewData.mBorderImageSource || 579 mBorderImageRepeat != aNewData.mBorderImageRepeat || 580 mBorderImageSlice != aNewData.mBorderImageSlice || 581 mBorderImageWidth != aNewData.mBorderImageWidth) { 582 return nsChangeHint_RepaintFrame; 583 } 584 } 585 586 // mBorder is the specified border value. Changes to this don't 587 // need any change processing, since we operate on the computed 588 // border values instead. 589 // mBorderImage* fields are checked only when border-image is not 'none'. 590 if (mBorder != aNewData.mBorder || 591 mBorderImageSource != aNewData.mBorderImageSource || 592 mBorderImageRepeat != aNewData.mBorderImageRepeat || 593 mBorderImageSlice != aNewData.mBorderImageSlice || 594 mBorderImageWidth != aNewData.mBorderImageWidth) { 595 return nsChangeHint_NeutralChange; 596 } 597 598 return nsChangeHint(0); 599 } 600 601 nsStyleOutline::nsStyleOutline() 602 : mOutlineWidth(kMediumBorderWidth), 603 mOutlineOffset(0), 604 mOutlineColor(StyleColor::CurrentColor()), 605 mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)) { 606 MOZ_COUNT_CTOR(nsStyleOutline); 607 } 608 609 nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc) 610 : mOutlineWidth(aSrc.mOutlineWidth), 611 mOutlineOffset(aSrc.mOutlineOffset), 612 mOutlineColor(aSrc.mOutlineColor), 613 mOutlineStyle(aSrc.mOutlineStyle) { 614 MOZ_COUNT_CTOR(nsStyleOutline); 615 } 616 617 nsChangeHint nsStyleOutline::CalcDifference( 618 const nsStyleOutline& aNewData) const { 619 const bool shouldPaintOutline = ShouldPaintOutline(); 620 // We need the explicit 'outline-style: auto' check because 621 // 'outline-style: auto' effectively also changes 'outline-width'. 622 if (shouldPaintOutline != aNewData.ShouldPaintOutline() || 623 mOutlineWidth != aNewData.mOutlineWidth || 624 mOutlineStyle.IsAuto() != aNewData.mOutlineStyle.IsAuto() || 625 (shouldPaintOutline && mOutlineOffset != aNewData.mOutlineOffset)) { 626 return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint | 627 nsChangeHint_RepaintFrame; 628 } 629 630 if (mOutlineStyle != aNewData.mOutlineStyle || 631 mOutlineColor != aNewData.mOutlineColor) { 632 return shouldPaintOutline ? nsChangeHint_RepaintFrame 633 : nsChangeHint_NeutralChange; 634 } 635 636 if (mOutlineWidth != aNewData.mOutlineWidth || 637 mOutlineOffset != aNewData.mOutlineOffset) { 638 return nsChangeHint_NeutralChange; 639 } 640 641 return nsChangeHint(0); 642 } 643 644 nsSize nsStyleOutline::EffectiveOffsetFor(const nsRect& aRect) const { 645 const nscoord offset = mOutlineOffset; 646 if (offset >= 0) { 647 // Fast path for non-negative offset values 648 return nsSize(offset, offset); 649 } 650 651 return nsSize(std::max(offset, -(aRect.Width() / 2)), 652 std::max(offset, -(aRect.Height() / 2))); 653 } 654 655 // -------------------- 656 // nsStyleList 657 // 658 nsStyleList::nsStyleList() 659 : mListStylePosition(StyleListStylePosition::Outside), 660 mListStyleType(StyleCounterStyle::Name({StyleAtom(nsGkAtoms::disc)})), 661 mQuotes(StyleQuotes::Auto()), 662 mListStyleImage(StyleImage::None()) { 663 MOZ_COUNT_CTOR(nsStyleList); 664 MOZ_ASSERT(NS_IsMainThread()); 665 } 666 667 nsStyleList::nsStyleList(const nsStyleList& aSource) 668 : mListStylePosition(aSource.mListStylePosition), 669 mListStyleType(aSource.mListStyleType), 670 mQuotes(aSource.mQuotes), 671 mListStyleImage(aSource.mListStyleImage) { 672 MOZ_COUNT_CTOR(nsStyleList); 673 } 674 675 void nsStyleList::TriggerImageLoads(Document& aDocument, 676 const nsStyleList* aOldStyle) { 677 MOZ_ASSERT(NS_IsMainThread()); 678 mListStyleImage.ResolveImage( 679 aDocument, aOldStyle ? &aOldStyle->mListStyleImage : nullptr); 680 } 681 682 nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aNewData, 683 const ComputedStyle& aOldStyle) const { 684 // If the quotes implementation is ever going to change we might not need 685 // a framechange here and a reflow should be sufficient. See bug 35768. 686 if (mQuotes != aNewData.mQuotes) { 687 return nsChangeHint_ReconstructFrame; 688 } 689 nsChangeHint hint = nsChangeHint(0); 690 // Only elements whose display value is list-item can be affected by 691 // list-style-{position,type,image}. This also relies on that when the display 692 // value changes from something else to list-item, that change itself would 693 // cause ReconstructFrame. 694 if (mListStylePosition != aNewData.mListStylePosition || 695 mListStyleType != aNewData.mListStyleType || 696 mListStyleImage != aNewData.mListStyleImage) { 697 if (aOldStyle.StyleDisplay()->IsListItem()) { 698 return nsChangeHint_ReconstructFrame; 699 } 700 // list-style-image may affect nsImageFrame for XUL elements, but that is 701 // dealt with explicitly in nsImageFrame::DidSetComputedStyle. 702 hint = nsChangeHint_NeutralChange; 703 } 704 return hint; 705 } 706 707 // -------------------- 708 // nsStyleXUL 709 // 710 nsStyleXUL::nsStyleXUL() 711 : mBoxFlex(0.0f), 712 mBoxOrdinal(1), 713 mBoxAlign(StyleBoxAlign::Stretch), 714 mBoxDirection(StyleBoxDirection::Normal), 715 mBoxOrient(StyleBoxOrient::Horizontal), 716 mBoxPack(StyleBoxPack::Start) { 717 MOZ_COUNT_CTOR(nsStyleXUL); 718 } 719 720 nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource) 721 : mBoxFlex(aSource.mBoxFlex), 722 mBoxOrdinal(aSource.mBoxOrdinal), 723 mBoxAlign(aSource.mBoxAlign), 724 mBoxDirection(aSource.mBoxDirection), 725 mBoxOrient(aSource.mBoxOrient), 726 mBoxPack(aSource.mBoxPack) { 727 MOZ_COUNT_CTOR(nsStyleXUL); 728 } 729 730 nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const { 731 if (mBoxAlign == aNewData.mBoxAlign && 732 mBoxDirection == aNewData.mBoxDirection && 733 mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient && 734 mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal) { 735 return nsChangeHint(0); 736 } 737 if (mBoxOrdinal != aNewData.mBoxOrdinal) { 738 return nsChangeHint_ReconstructFrame; 739 } 740 return NS_STYLE_HINT_REFLOW; 741 } 742 743 // -------------------- 744 // nsStyleColumn 745 // 746 747 nsStyleColumn::nsStyleColumn() 748 : mColumnWidth(LengthOrAuto::Auto()), 749 mColumnRuleColor(StyleColor::CurrentColor()), 750 mColumnRuleStyle(StyleBorderStyle::None), 751 mColumnRuleWidth(kMediumBorderWidth) { 752 MOZ_COUNT_CTOR(nsStyleColumn); 753 } 754 755 nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) 756 : mColumnCount(aSource.mColumnCount), 757 mColumnWidth(aSource.mColumnWidth), 758 mColumnRuleColor(aSource.mColumnRuleColor), 759 mColumnRuleStyle(aSource.mColumnRuleStyle), 760 mColumnFill(aSource.mColumnFill), 761 mColumnSpan(aSource.mColumnSpan), 762 mColumnRuleWidth(aSource.mColumnRuleWidth) { 763 MOZ_COUNT_CTOR(nsStyleColumn); 764 } 765 766 nsChangeHint nsStyleColumn::CalcDifference( 767 const nsStyleColumn& aNewData) const { 768 if (mColumnWidth.IsAuto() != aNewData.mColumnWidth.IsAuto() || 769 mColumnCount != aNewData.mColumnCount || 770 mColumnSpan != aNewData.mColumnSpan) { 771 // We force column count changes to do a reframe, because it's tricky to 772 // handle some edge cases where the column count gets smaller and content 773 // overflows. 774 // XXX not ideal 775 return nsChangeHint_ReconstructFrame; 776 } 777 778 if (mColumnWidth != aNewData.mColumnWidth || 779 mColumnFill != aNewData.mColumnFill) { 780 return NS_STYLE_HINT_REFLOW; 781 } 782 783 if (mColumnRuleWidth != aNewData.mColumnRuleWidth || 784 mColumnRuleStyle != aNewData.mColumnRuleStyle || 785 mColumnRuleColor != aNewData.mColumnRuleColor) { 786 return NS_STYLE_HINT_VISUAL; 787 } 788 789 return nsChangeHint(0); 790 } 791 792 using SVGPaintFallback = StyleGenericSVGPaintFallback<StyleColor>; 793 794 // -------------------- 795 // nsStyleSVG 796 // 797 nsStyleSVG::nsStyleSVG() 798 : mFill{StyleSVGPaintKind::Color(StyleColor::Black()), 799 SVGPaintFallback::Unset()}, 800 mStroke{StyleSVGPaintKind::None(), SVGPaintFallback::Unset()}, 801 mMarkerEnd(StyleUrlOrNone::None()), 802 mMarkerMid(StyleUrlOrNone::None()), 803 mMarkerStart(StyleUrlOrNone::None()), 804 mMozContextProperties{{}, {0}}, 805 mStrokeDasharray(StyleSVGStrokeDashArray::Values({})), 806 mStrokeDashoffset( 807 StyleSVGLength::LengthPercentage(LengthPercentage::Zero())), 808 mStrokeWidth( 809 StyleSVGWidth::LengthPercentage(LengthPercentage::FromPixels(1.0f))), 810 mFillOpacity(StyleSVGOpacity::Opacity(1.0f)), 811 mStrokeMiterlimit(4.0f), 812 mStrokeOpacity(StyleSVGOpacity::Opacity(1.0f)), 813 mClipRule(StyleFillRule::Nonzero), 814 mColorInterpolation(StyleColorInterpolation::Srgb), 815 mColorInterpolationFilters(StyleColorInterpolation::Linearrgb), 816 mFillRule(StyleFillRule::Nonzero), 817 mPaintOrder(0), 818 mShapeRendering(StyleShapeRendering::Auto), 819 mStrokeLinecap(StyleStrokeLinecap::Butt), 820 mStrokeLinejoin(StyleStrokeLinejoin::Miter), 821 mDominantBaseline(StyleDominantBaseline::Auto), 822 mTextAnchor(StyleTextAnchor::Start) { 823 MOZ_COUNT_CTOR(nsStyleSVG); 824 } 825 826 nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) 827 : mFill(aSource.mFill), 828 mStroke(aSource.mStroke), 829 mMarkerEnd(aSource.mMarkerEnd), 830 mMarkerMid(aSource.mMarkerMid), 831 mMarkerStart(aSource.mMarkerStart), 832 mMozContextProperties(aSource.mMozContextProperties), 833 mStrokeDasharray(aSource.mStrokeDasharray), 834 mStrokeDashoffset(aSource.mStrokeDashoffset), 835 mStrokeWidth(aSource.mStrokeWidth), 836 mFillOpacity(aSource.mFillOpacity), 837 mStrokeMiterlimit(aSource.mStrokeMiterlimit), 838 mStrokeOpacity(aSource.mStrokeOpacity), 839 mClipRule(aSource.mClipRule), 840 mColorInterpolation(aSource.mColorInterpolation), 841 mColorInterpolationFilters(aSource.mColorInterpolationFilters), 842 mFillRule(aSource.mFillRule), 843 mPaintOrder(aSource.mPaintOrder), 844 mShapeRendering(aSource.mShapeRendering), 845 mStrokeLinecap(aSource.mStrokeLinecap), 846 mStrokeLinejoin(aSource.mStrokeLinejoin), 847 mDominantBaseline(aSource.mDominantBaseline), 848 mTextAnchor(aSource.mTextAnchor) { 849 MOZ_COUNT_CTOR(nsStyleSVG); 850 } 851 852 static bool PaintURIChanged(const StyleSVGPaint& aPaint1, 853 const StyleSVGPaint& aPaint2) { 854 if (aPaint1.kind.IsPaintServer() != aPaint2.kind.IsPaintServer()) { 855 return true; 856 } 857 return aPaint1.kind.IsPaintServer() && 858 aPaint1.kind.AsPaintServer() != aPaint2.kind.AsPaintServer(); 859 } 860 861 nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const { 862 nsChangeHint hint = nsChangeHint(0); 863 864 if (mMarkerEnd != aNewData.mMarkerEnd || mMarkerMid != aNewData.mMarkerMid || 865 mMarkerStart != aNewData.mMarkerStart) { 866 // Markers currently contribute to SVGGeometryFrame::mRect, 867 // so we need a reflow as well as a repaint. No intrinsic sizes need 868 // to change, so nsChangeHint_NeedReflow is sufficient. 869 return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow | 870 nsChangeHint_RepaintFrame; 871 } 872 873 if (mFill != aNewData.mFill || mStroke != aNewData.mStroke || 874 mFillOpacity != aNewData.mFillOpacity || 875 mStrokeOpacity != aNewData.mStrokeOpacity) { 876 hint |= nsChangeHint_RepaintFrame; 877 if (HasStroke() != aNewData.HasStroke() || 878 (!HasStroke() && HasFill() != aNewData.HasFill())) { 879 // Frame bounds and overflow rects depend on whether we "have" fill or 880 // stroke. Whether we have stroke or not just changed, or else we have no 881 // stroke (in which case whether we have fill or not is significant to 882 // frame bounds) and whether we have fill or not just changed. In either 883 // case we need to reflow so the frame rect is updated. 884 // XXXperf this is a waste on non SVGGeometryFrames. 885 hint |= nsChangeHint_NeedReflow; 886 } 887 if (PaintURIChanged(mFill, aNewData.mFill) || 888 PaintURIChanged(mStroke, aNewData.mStroke)) { 889 hint |= nsChangeHint_UpdateEffects; 890 } 891 } 892 893 // Stroke currently contributes to SVGGeometryFrame::mRect, so 894 // we need a reflow here. No intrinsic sizes need to change, so 895 // nsChangeHint_NeedReflow is sufficient. 896 // Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect. 897 // text-anchor and dominant-baseline changes also require a reflow since 898 // they change frames' rects. 899 if (mStrokeWidth != aNewData.mStrokeWidth || 900 mStrokeMiterlimit != aNewData.mStrokeMiterlimit || 901 mStrokeLinecap != aNewData.mStrokeLinecap || 902 mStrokeLinejoin != aNewData.mStrokeLinejoin || 903 mDominantBaseline != aNewData.mDominantBaseline || 904 mTextAnchor != aNewData.mTextAnchor) { 905 return hint | nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame; 906 } 907 908 if (hint & nsChangeHint_RepaintFrame) { 909 return hint; // we don't add anything else below 910 } 911 912 if (mStrokeDashoffset != aNewData.mStrokeDashoffset || 913 mClipRule != aNewData.mClipRule || 914 mColorInterpolation != aNewData.mColorInterpolation || 915 mColorInterpolationFilters != aNewData.mColorInterpolationFilters || 916 mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder || 917 mShapeRendering != aNewData.mShapeRendering || 918 mStrokeDasharray != aNewData.mStrokeDasharray || 919 mMozContextProperties.bits != aNewData.mMozContextProperties.bits) { 920 return hint | nsChangeHint_RepaintFrame; 921 } 922 923 if (!hint) { 924 if (mMozContextProperties.idents != aNewData.mMozContextProperties.idents) { 925 hint = nsChangeHint_NeutralChange; 926 } 927 } 928 929 return hint; 930 } 931 932 // -------------------- 933 // nsStyleSVGReset 934 // 935 nsStyleSVGReset::nsStyleSVGReset() 936 : mX(LengthPercentage::Zero()), 937 mY(LengthPercentage::Zero()), 938 mCx(LengthPercentage::Zero()), 939 mCy(LengthPercentage::Zero()), 940 mRx(NonNegativeLengthPercentageOrAuto::Auto()), 941 mRy(NonNegativeLengthPercentageOrAuto::Auto()), 942 mR(NonNegativeLengthPercentage::Zero()), 943 mMask(nsStyleImageLayers::LayerType::Mask), 944 mClipPath(StyleClipPath::None()), 945 mStopColor(StyleColor::Black()), 946 mFloodColor(StyleColor::Black()), 947 mLightingColor(StyleColor::White()), 948 mStopOpacity(1.0f), 949 mFloodOpacity(1.0f), 950 mVectorEffect(StyleVectorEffect::NONE), 951 mMaskType(StyleMaskType::Luminance), 952 mD(StyleDProperty::None()) { 953 MOZ_COUNT_CTOR(nsStyleSVGReset); 954 } 955 956 nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) 957 : mX(aSource.mX), 958 mY(aSource.mY), 959 mCx(aSource.mCx), 960 mCy(aSource.mCy), 961 mRx(aSource.mRx), 962 mRy(aSource.mRy), 963 mR(aSource.mR), 964 mMask(aSource.mMask), 965 mClipPath(aSource.mClipPath), 966 mStopColor(aSource.mStopColor), 967 mFloodColor(aSource.mFloodColor), 968 mLightingColor(aSource.mLightingColor), 969 mStopOpacity(aSource.mStopOpacity), 970 mFloodOpacity(aSource.mFloodOpacity), 971 mVectorEffect(aSource.mVectorEffect), 972 mMaskType(aSource.mMaskType), 973 mD(aSource.mD) { 974 MOZ_COUNT_CTOR(nsStyleSVGReset); 975 } 976 977 void nsStyleSVGReset::TriggerImageLoads(Document& aDocument, 978 const nsStyleSVGReset* aOldStyle) { 979 MOZ_ASSERT(NS_IsMainThread()); 980 // NOTE(emilio): we intentionally don't call TriggerImageLoads for clip-path. 981 982 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) { 983 auto& image = mMask.mLayers[i].mImage; 984 if (!image.IsImageRequestType()) { 985 continue; 986 } 987 const auto* url = image.GetImageRequestURLValue(); 988 // If the url is a local ref, it must be a <mask-resource>, so we don't 989 // need to resolve the style image. 990 if (url->IsLocalRef()) { 991 continue; 992 } 993 #if 0 994 // XXX The old style system also checks whether this is a reference to 995 // the current document with reference, but it doesn't seem to be a 996 // behavior mentioned anywhere, so we comment out the code for now. 997 nsIURI* docURI = aPresContext->Document()->GetDocumentURI(); 998 if (url->EqualsExceptRef(docURI)) { 999 continue; 1000 } 1001 #endif 1002 // Otherwise, we may need the image even if it has a reference, in case 1003 // the referenced element isn't a valid SVG <mask> element. 1004 const auto* oldImage = (aOldStyle && aOldStyle->mMask.mLayers.Length() > i) 1005 ? &aOldStyle->mMask.mLayers[i].mImage 1006 : nullptr; 1007 1008 image.ResolveImage(aDocument, oldImage); 1009 } 1010 } 1011 1012 nsChangeHint nsStyleSVGReset::CalcDifference( 1013 const nsStyleSVGReset& aNewData) const { 1014 nsChangeHint hint = nsChangeHint(0); 1015 1016 if (mX != aNewData.mX || mY != aNewData.mY || mCx != aNewData.mCx || 1017 mCy != aNewData.mCy || mR != aNewData.mR || mRx != aNewData.mRx || 1018 mRy != aNewData.mRy || mD != aNewData.mD) { 1019 hint |= nsChangeHint_InvalidateRenderingObservers | nsChangeHint_NeedReflow; 1020 } 1021 1022 if (mClipPath != aNewData.mClipPath) { 1023 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; 1024 } 1025 1026 if (mVectorEffect != aNewData.mVectorEffect) { 1027 // Stroke currently affects SVGGeometryFrame::mRect, and 1028 // vector-effect affect stroke. As a result we need to reflow if 1029 // vector-effect changes in order to have SVGGeometryFrame:: 1030 // ReflowSVG called to update its mRect. No intrinsic sizes need 1031 // to change so nsChangeHint_NeedReflow is sufficient. 1032 hint |= nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame; 1033 } else if (mStopColor != aNewData.mStopColor || 1034 mFloodColor != aNewData.mFloodColor || 1035 mLightingColor != aNewData.mLightingColor || 1036 mStopOpacity != aNewData.mStopOpacity || 1037 mFloodOpacity != aNewData.mFloodOpacity || 1038 mMaskType != aNewData.mMaskType || mD != aNewData.mD) { 1039 hint |= nsChangeHint_RepaintFrame; 1040 } 1041 1042 hint |= 1043 mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask); 1044 1045 return hint; 1046 } 1047 1048 bool nsStyleSVGReset::HasMask() const { 1049 for (uint32_t i = 0; i < mMask.mImageCount; i++) { 1050 if (!mMask.mLayers[i].mImage.IsNone()) { 1051 return true; 1052 } 1053 } 1054 1055 return false; 1056 } 1057 1058 // -------------------- 1059 // nsStylePage 1060 // 1061 1062 nsStylePage::nsStylePage(const nsStylePage& aSrc) 1063 : mSize(aSrc.mSize), 1064 mPage(aSrc.mPage), 1065 mPageOrientation(aSrc.mPageOrientation) { 1066 MOZ_COUNT_CTOR(nsStylePage); 1067 } 1068 1069 nsChangeHint nsStylePage::CalcDifference(const nsStylePage& aNewData) const { 1070 // Page rule styling only matters when printing or using print preview. 1071 if (aNewData.mSize != mSize || aNewData.mPage != mPage || 1072 aNewData.mPageOrientation != mPageOrientation) { 1073 return nsChangeHint_NeutralChange; 1074 } 1075 return nsChangeHint_Empty; 1076 } 1077 1078 // -------------------- 1079 // nsStylePosition 1080 // 1081 nsStylePosition::nsStylePosition() 1082 : mObjectPosition(Position::FromPercentage(0.5f)), 1083 mOffset(StyleRectWithAllSides(StyleInset::Auto())), 1084 mWidth(StyleSize::Auto()), 1085 mMinWidth(StyleSize::Auto()), 1086 mMaxWidth(StyleMaxSize::None()), 1087 mHeight(StyleSize::Auto()), 1088 mMinHeight(StyleSize::Auto()), 1089 mMaxHeight(StyleMaxSize::None()), 1090 mPositionAnchor(StylePositionAnchor::None()), 1091 mPositionVisibility(StylePositionVisibility::ANCHORS_VISIBLE), 1092 mPositionTryFallbacks(StylePositionTryFallbacks()), 1093 mPositionTryOrder(StylePositionTryOrder::Normal), 1094 mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())), 1095 mAspectRatio(StyleAspectRatio::Auto()), 1096 mGridAutoFlow(StyleGridAutoFlow::ROW), 1097 mMasonryAutoFlow( 1098 {StyleMasonryPlacement::Pack, StyleMasonryItemOrder::DefiniteFirst}), 1099 mAlignContent({StyleAlignFlags::NORMAL}), 1100 mAlignItems({StyleAlignFlags::NORMAL}), 1101 mAlignSelf({StyleAlignFlags::AUTO}), 1102 mJustifyContent({StyleAlignFlags::NORMAL}), 1103 mJustifyItems({{StyleAlignFlags::LEGACY}, {StyleAlignFlags::NORMAL}}), 1104 mJustifySelf({StyleAlignFlags::AUTO}), 1105 mFlexDirection(StyleFlexDirection::Row), 1106 mFlexWrap(StyleFlexWrap::Nowrap), 1107 mObjectFit(StyleObjectFit::Fill), 1108 mBoxSizing(StyleBoxSizing::Content), 1109 mOrder(0), 1110 mFlexGrow(0.0f), 1111 mFlexShrink(1.0f), 1112 mZIndex(StyleZIndex::Auto()), 1113 mGridTemplateColumns(StyleGridTemplateComponent::None()), 1114 mGridTemplateRows(StyleGridTemplateComponent::None()), 1115 mGridTemplateAreas(StyleGridTemplateAreas::None()), 1116 mColumnGap(NonNegativeLengthPercentageOrNormal::Normal()), 1117 mRowGap(NonNegativeLengthPercentageOrNormal::Normal()), 1118 mContainIntrinsicWidth(StyleContainIntrinsicSize::None()), 1119 mContainIntrinsicHeight(StyleContainIntrinsicSize::None()) { 1120 MOZ_COUNT_CTOR(nsStylePosition); 1121 1122 // The initial value of grid-auto-columns and grid-auto-rows is 'auto', 1123 // which computes to 'minmax(auto, auto)'. 1124 1125 // Other members get their default constructors 1126 // which initialize them to representations of their respective initial value. 1127 // mGridTemplate{Rows,Columns}: false and empty arrays for 'none' 1128 // mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto' 1129 } 1130 1131 nsStylePosition::nsStylePosition(const nsStylePosition& aSource) 1132 : mObjectPosition(aSource.mObjectPosition), 1133 mOffset(aSource.mOffset), 1134 mWidth(aSource.mWidth), 1135 mMinWidth(aSource.mMinWidth), 1136 mMaxWidth(aSource.mMaxWidth), 1137 mHeight(aSource.mHeight), 1138 mMinHeight(aSource.mMinHeight), 1139 mMaxHeight(aSource.mMaxHeight), 1140 mPositionAnchor(aSource.mPositionAnchor), 1141 mPositionArea(aSource.mPositionArea), 1142 mPositionVisibility(aSource.mPositionVisibility), 1143 mPositionTryFallbacks(aSource.mPositionTryFallbacks), 1144 mPositionTryOrder(aSource.mPositionTryOrder), 1145 mFlexBasis(aSource.mFlexBasis), 1146 mGridAutoColumns(aSource.mGridAutoColumns), 1147 mGridAutoRows(aSource.mGridAutoRows), 1148 mAspectRatio(aSource.mAspectRatio), 1149 mGridAutoFlow(aSource.mGridAutoFlow), 1150 mMasonryAutoFlow(aSource.mMasonryAutoFlow), 1151 mAlignContent(aSource.mAlignContent), 1152 mAlignItems(aSource.mAlignItems), 1153 mAlignSelf(aSource.mAlignSelf), 1154 mJustifyContent(aSource.mJustifyContent), 1155 mJustifyItems(aSource.mJustifyItems), 1156 mJustifySelf(aSource.mJustifySelf), 1157 mFlexDirection(aSource.mFlexDirection), 1158 mFlexWrap(aSource.mFlexWrap), 1159 mObjectFit(aSource.mObjectFit), 1160 mBoxSizing(aSource.mBoxSizing), 1161 mOrder(aSource.mOrder), 1162 mFlexGrow(aSource.mFlexGrow), 1163 mFlexShrink(aSource.mFlexShrink), 1164 mZIndex(aSource.mZIndex), 1165 mGridTemplateColumns(aSource.mGridTemplateColumns), 1166 mGridTemplateRows(aSource.mGridTemplateRows), 1167 mGridTemplateAreas(aSource.mGridTemplateAreas), 1168 mGridColumnStart(aSource.mGridColumnStart), 1169 mGridColumnEnd(aSource.mGridColumnEnd), 1170 mGridRowStart(aSource.mGridRowStart), 1171 mGridRowEnd(aSource.mGridRowEnd), 1172 mColumnGap(aSource.mColumnGap), 1173 mRowGap(aSource.mRowGap), 1174 mContainIntrinsicWidth(aSource.mContainIntrinsicWidth), 1175 mContainIntrinsicHeight(aSource.mContainIntrinsicHeight) { 1176 MOZ_COUNT_CTOR(nsStylePosition); 1177 } 1178 1179 static bool IsEqualInsetType(const StyleRect<StyleInset>& aSides1, 1180 const StyleRect<StyleInset>& aSides2) { 1181 for (const auto side : mozilla::AllPhysicalSides()) { 1182 if (aSides1.Get(side).tag != aSides2.Get(side).tag) { 1183 return false; 1184 } 1185 } 1186 return true; 1187 } 1188 1189 nsChangeHint nsStylePosition::CalcDifference( 1190 const nsStylePosition& aNewData, const ComputedStyle& aOldStyle) const { 1191 if (mGridTemplateColumns.IsMasonry() != 1192 aNewData.mGridTemplateColumns.IsMasonry() || 1193 mGridTemplateRows.IsMasonry() != aNewData.mGridTemplateRows.IsMasonry()) { 1194 // XXXmats this could be optimized to AllReflowHints with a bit of work, 1195 // but I'll assume this is a very rare use case in practice. (bug 1623886) 1196 return nsChangeHint_ReconstructFrame; 1197 } 1198 1199 nsChangeHint hint = nsChangeHint(0); 1200 1201 // Changes to "z-index" require a repaint. 1202 if (mZIndex != aNewData.mZIndex) { 1203 hint |= nsChangeHint_RepaintFrame; 1204 } 1205 1206 // Changes to "object-fit" & "object-position" require a repaint. They 1207 // may also require a reflow, if we have a nsSubDocumentFrame, so that we 1208 // can adjust the size & position of the subdocument. 1209 if (mObjectFit != aNewData.mObjectFit || 1210 mObjectPosition != aNewData.mObjectPosition) { 1211 hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow; 1212 } 1213 1214 if (mContainIntrinsicWidth != aNewData.mContainIntrinsicWidth || 1215 mContainIntrinsicHeight != aNewData.mContainIntrinsicHeight) { 1216 hint |= NS_STYLE_HINT_REFLOW; 1217 } 1218 1219 if (mOrder != aNewData.mOrder) { 1220 // "order" impacts both layout order and stacking order, so we need both a 1221 // reflow and a repaint when it changes. (Technically, we only need a 1222 // reflow if we're in a multi-line flexbox (which we can't be sure about, 1223 // since that's determined by styling on our parent) -- there, "order" can 1224 // affect which flex line we end up on, & hence can affect our sizing by 1225 // changing the group of flex items we're competing with for space.) 1226 return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints; 1227 } 1228 1229 if (mBoxSizing != aNewData.mBoxSizing) { 1230 // Can affect both widths and heights; just a bad scene. 1231 return hint | nsChangeHint_AllReflowHints; 1232 } 1233 1234 if (mAlignItems != aNewData.mAlignItems || 1235 mAlignSelf != aNewData.mAlignSelf) { 1236 return hint | nsChangeHint_AllReflowHints; 1237 } 1238 1239 // Properties that apply to flex items: 1240 // XXXdholbert These should probably be more targeted (bug 819536) 1241 if (mFlexBasis != aNewData.mFlexBasis || mFlexGrow != aNewData.mFlexGrow || 1242 mFlexShrink != aNewData.mFlexShrink) { 1243 return hint | nsChangeHint_AllReflowHints; 1244 } 1245 1246 // Properties that apply to flex containers: 1247 // - flex-direction can swap a flex container between vertical & horizontal. 1248 // - flex-wrap changes whether a flex container's children are wrapped, which 1249 // impacts their sizing/positioning and hence impacts the container's size. 1250 if (mFlexDirection != aNewData.mFlexDirection || 1251 mFlexWrap != aNewData.mFlexWrap) { 1252 return hint | nsChangeHint_AllReflowHints; 1253 } 1254 1255 // Properties that apply to grid containers: 1256 // FIXME: only for grid containers 1257 // (ie. 'display: grid' or 'display: inline-grid') 1258 if (mGridTemplateColumns != aNewData.mGridTemplateColumns || 1259 mGridTemplateRows != aNewData.mGridTemplateRows || 1260 mGridTemplateAreas != aNewData.mGridTemplateAreas || 1261 mGridAutoColumns != aNewData.mGridAutoColumns || 1262 mGridAutoRows != aNewData.mGridAutoRows || 1263 mGridAutoFlow != aNewData.mGridAutoFlow || 1264 mMasonryAutoFlow != aNewData.mMasonryAutoFlow) { 1265 return hint | nsChangeHint_AllReflowHints; 1266 } 1267 1268 // Properties that apply to grid items: 1269 // FIXME: only for grid items 1270 // (ie. parent frame is 'display: grid' or 'display: inline-grid') 1271 if (mGridColumnStart != aNewData.mGridColumnStart || 1272 mGridColumnEnd != aNewData.mGridColumnEnd || 1273 mGridRowStart != aNewData.mGridRowStart || 1274 mGridRowEnd != aNewData.mGridRowEnd || 1275 mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) { 1276 return hint | nsChangeHint_AllReflowHints; 1277 } 1278 1279 // Changing 'justify-content/items/self' might affect the positioning, 1280 // but it won't affect any sizing. 1281 if (mJustifyContent != aNewData.mJustifyContent || 1282 mJustifyItems.computed != aNewData.mJustifyItems.computed || 1283 mJustifySelf != aNewData.mJustifySelf) { 1284 hint |= nsChangeHint_NeedReflow; 1285 } 1286 1287 // No need to do anything if specified justify-items changes, as long as the 1288 // computed one (tested above) is unchanged. 1289 if (mJustifyItems.specified != aNewData.mJustifyItems.specified) { 1290 hint |= nsChangeHint_NeutralChange; 1291 } 1292 1293 // 'align-content' doesn't apply to a single-line flexbox but we don't know 1294 // if we're a flex container at this point so we can't optimize for that. 1295 if (mAlignContent != aNewData.mAlignContent) { 1296 hint |= nsChangeHint_NeedReflow; 1297 } 1298 1299 bool widthChanged = mWidth != aNewData.mWidth || 1300 mMinWidth != aNewData.mMinWidth || 1301 mMaxWidth != aNewData.mMaxWidth; 1302 bool heightChanged = mHeight != aNewData.mHeight || 1303 mMinHeight != aNewData.mMinHeight || 1304 mMaxHeight != aNewData.mMaxHeight; 1305 1306 if (widthChanged || heightChanged) { 1307 // It doesn't matter whether we're looking at the old or new visibility 1308 // struct, since a change between vertical and horizontal writing-mode will 1309 // cause a reframe. 1310 const bool isVertical = aOldStyle.StyleVisibility()->mWritingMode != 1311 StyleWritingModeProperty::HorizontalTb; 1312 if (isVertical ? widthChanged : heightChanged) { 1313 hint |= nsChangeHint_ReflowHintsForBSizeChange; 1314 } 1315 if (isVertical ? heightChanged : widthChanged) { 1316 hint |= nsChangeHint_ReflowHintsForISizeChange; 1317 } 1318 } 1319 1320 // Note(dshin): Following hints based on changes in `position-*` 1321 // is conditional on being absolutely positioned, but we don't have 1322 // enough information here. 1323 if (mPositionVisibility != aNewData.mPositionVisibility) { 1324 // position-visibility doesn't affect layout boxes. 1325 hint |= nsChangeHint_RepaintFrame; 1326 } 1327 1328 if (mPositionAnchor != aNewData.mPositionAnchor || 1329 mPositionTryFallbacks != aNewData.mPositionTryFallbacks || 1330 mPositionTryOrder != aNewData.mPositionTryOrder || 1331 mPositionArea != aNewData.mPositionArea) { 1332 // We need to reflow in order to update the `AnchorPosReferences` 1333 // property at minimum. 1334 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition; 1335 } 1336 1337 if (mAspectRatio != aNewData.mAspectRatio) { 1338 hint |= nsChangeHint_ReflowHintsForISizeChange | 1339 nsChangeHint_ReflowHintsForBSizeChange; 1340 } 1341 1342 // If any of the offsets have changed, then return the respective hints 1343 // so that we would hopefully be able to avoid reflowing. 1344 // Note that it is possible that we'll need to reflow when processing 1345 // restyles, but we don't have enough information to make a good decision 1346 // right now. 1347 // Don't try to handle changes between types efficiently; at least for 1348 // changing into/out of `auto`, we will hardly ever be able to avoid a reflow. 1349 if (mOffset != aNewData.mOffset) { 1350 if (IsEqualInsetType(mOffset, aNewData.mOffset) && 1351 aNewData.mOffset.All([](const StyleInset& aInset) { 1352 // Anchor positioning invalidation depends on `AnchorPosReferences` 1353 // being updated, which happens during reflow. 1354 return !aInset.HasAnchorPositioningFunction(); 1355 })) { 1356 hint |= 1357 nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow; 1358 } else { 1359 hint |= 1360 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition; 1361 } 1362 } 1363 return hint; 1364 } 1365 1366 const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicBSize( 1367 const WritingMode& aWM) const { 1368 return aWM.IsVertical() ? mContainIntrinsicWidth : mContainIntrinsicHeight; 1369 } 1370 1371 const StyleContainIntrinsicSize& nsStylePosition::ContainIntrinsicISize( 1372 const WritingMode& aWM) const { 1373 return aWM.IsVertical() ? mContainIntrinsicHeight : mContainIntrinsicWidth; 1374 } 1375 1376 StyleSelfAlignment nsStylePosition::UsedAlignSelf( 1377 const ComputedStyle* aParent) const { 1378 if (mAlignSelf._0 != StyleAlignFlags::AUTO) { 1379 return mAlignSelf; 1380 } 1381 if (MOZ_LIKELY(aParent)) { 1382 auto parentAlignItems = aParent->StylePosition()->mAlignItems; 1383 MOZ_ASSERT(!(parentAlignItems._0 & StyleAlignFlags::LEGACY), 1384 "align-items can't have 'legacy'"); 1385 return {parentAlignItems._0}; 1386 } 1387 return {StyleAlignFlags::NORMAL}; 1388 } 1389 1390 StyleSelfAlignment nsStylePosition::UsedJustifySelf( 1391 const ComputedStyle* aParent) const { 1392 if (mJustifySelf._0 != StyleAlignFlags::AUTO) { 1393 return mJustifySelf; 1394 } 1395 if (MOZ_LIKELY(aParent)) { 1396 const auto& inheritedJustifyItems = 1397 aParent->StylePosition()->mJustifyItems.computed._0; 1398 return {inheritedJustifyItems._0 & ~StyleAlignFlags::LEGACY}; 1399 } 1400 return {StyleAlignFlags::NORMAL}; 1401 } 1402 1403 AnchorResolvedInset AnchorResolvedInsetHelper::ResolveAnchor( 1404 const mozilla::StyleInset& aValue, mozilla::StylePhysicalSide aSide, 1405 const AnchorPosOffsetResolutionParams& aParams) { 1406 MOZ_ASSERT(aValue.HasAnchorPositioningFunction(), 1407 "Calling anchor resolution without using it?"); 1408 switch (aValue.tag) { 1409 case StyleInset::Tag::AnchorContainingCalcFunction: { 1410 const auto& lp = aValue.AsAnchorContainingCalcFunction(); 1411 const auto& c = lp.AsCalc(); 1412 auto result = StyleCalcAnchorPositioningFunctionResolution::Invalid(); 1413 const auto allowed = 1414 StyleAllowAnchorPosResolutionInCalcPercentage::Both(aSide); 1415 Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &allowed, &aParams, 1416 &result); 1417 if (result.IsInvalid()) { 1418 return Auto(); 1419 } 1420 return MakeUniqueOfUniqueOrNonOwning<const StyleInset>(result.AsValid()); 1421 } 1422 case StyleInset::Tag::AnchorFunction: { 1423 auto resolved = StyleAnchorPositioningFunctionResolution::Invalid(); 1424 Servo_ResolveAnchorFunction(&*aValue.AsAnchorFunction(), &aParams, aSide, 1425 &resolved); 1426 if (resolved.IsInvalid()) { 1427 return Auto(); 1428 } 1429 if (resolved.IsResolvedReference()) { 1430 return MakeUniqueOfUniqueOrNonOwning<const StyleInset>( 1431 *resolved.AsResolvedReference()); 1432 } 1433 return AnchorResolvedInset{ 1434 MakeUniqueOfUniqueOrNonOwning<const StyleInset>( 1435 resolved.AsResolved())}; 1436 } 1437 case StyleInset::Tag::AnchorSizeFunction: { 1438 auto resolved = StyleAnchorPositioningFunctionResolution::Invalid(); 1439 Servo_ResolveAnchorSizeFunctionForInset( 1440 &*aValue.AsAnchorSizeFunction(), &aParams, ToStylePhysicalAxis(aSide), 1441 &resolved); 1442 if (resolved.IsInvalid()) { 1443 return Auto(); 1444 } 1445 if (resolved.IsResolvedReference()) { 1446 return MakeUniqueOfUniqueOrNonOwning<const StyleInset>( 1447 *resolved.AsResolvedReference()); 1448 } 1449 return MakeUniqueOfUniqueOrNonOwning<const StyleInset>( 1450 resolved.AsResolved()); 1451 } 1452 default: 1453 MOZ_ASSERT_UNREACHABLE("Unhandled inset type"); 1454 return Auto(); 1455 } 1456 } 1457 1458 AnchorResolvedSize AnchorResolvedSizeHelper::ResolveAnchor( 1459 const mozilla::StyleSize& aValue, StylePhysicalAxis aAxis, 1460 const AnchorPosResolutionParams& aParams) { 1461 MOZ_ASSERT(aValue.HasAnchorPositioningFunction(), 1462 "Calling anchor resolution without using it?"); 1463 if (aValue.IsAnchorSizeFunction()) { 1464 auto resolved = StyleAnchorPositioningFunctionResolution::Invalid(); 1465 Servo_ResolveAnchorSizeFunctionForSize(&*aValue.AsAnchorSizeFunction(), 1466 &aParams, aAxis, &resolved); 1467 if (resolved.IsInvalid()) { 1468 return Auto(); 1469 } 1470 if (resolved.IsResolvedReference()) { 1471 return MakeUniqueOfUniqueOrNonOwning<const StyleSize>( 1472 *resolved.AsResolvedReference()); 1473 } 1474 return MakeUniqueOfUniqueOrNonOwning<const StyleSize>( 1475 resolved.AsResolved()); 1476 } 1477 1478 const auto& lp = aValue.AsAnchorContainingCalcFunction(); 1479 // Follows the same reasoning as anchor resolved insets. 1480 const auto& c = lp.AsCalc(); 1481 auto result = StyleCalcAnchorPositioningFunctionResolution::Invalid(); 1482 AnchorPosOffsetResolutionParams params = 1483 AnchorPosOffsetResolutionParams::UseCBFrameSize(aParams); 1484 const auto allowed = 1485 StyleAllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(aAxis); 1486 Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &allowed, ¶ms, &result); 1487 if (result.IsInvalid()) { 1488 return Auto(); 1489 } 1490 return MakeUniqueOfUniqueOrNonOwning<const StyleSize>(result.AsValid()); 1491 } 1492 1493 AnchorResolvedMaxSize AnchorResolvedMaxSizeHelper::ResolveAnchor( 1494 const mozilla::StyleMaxSize& aValue, StylePhysicalAxis aAxis, 1495 const AnchorPosResolutionParams& aParams) { 1496 MOZ_ASSERT(aValue.HasAnchorPositioningFunction(), 1497 "Calling anchor resolution without using it?"); 1498 if (aValue.IsAnchorSizeFunction()) { 1499 auto resolved = StyleAnchorPositioningFunctionResolution::Invalid(); 1500 Servo_ResolveAnchorSizeFunctionForMaxSize(&*aValue.AsAnchorSizeFunction(), 1501 &aParams, aAxis, &resolved); 1502 if (resolved.IsInvalid()) { 1503 return None(); 1504 } 1505 if (resolved.IsResolvedReference()) { 1506 return MakeUniqueOfUniqueOrNonOwning<const StyleMaxSize>( 1507 *resolved.AsResolvedReference()); 1508 } 1509 return MakeUniqueOfUniqueOrNonOwning<const StyleMaxSize>( 1510 resolved.AsResolved()); 1511 } 1512 1513 const auto& lp = aValue.AsAnchorContainingCalcFunction(); 1514 // Follows the same reasoning as anchor resolved insets. 1515 const auto& c = lp.AsCalc(); 1516 auto result = StyleCalcAnchorPositioningFunctionResolution::Invalid(); 1517 AnchorPosOffsetResolutionParams params = 1518 AnchorPosOffsetResolutionParams::UseCBFrameSize(aParams); 1519 const auto allowed = 1520 StyleAllowAnchorPosResolutionInCalcPercentage::AnchorSizeOnly(aAxis); 1521 Servo_ResolveAnchorFunctionsInCalcPercentage(&c, &allowed, ¶ms, &result); 1522 if (result.IsInvalid()) { 1523 return None(); 1524 } 1525 return MakeUniqueOfUniqueOrNonOwning<const StyleMaxSize>(result.AsValid()); 1526 } 1527 1528 // -------------------- 1529 // nsStyleTable 1530 // 1531 1532 nsStyleTable::nsStyleTable() 1533 : mLayoutStrategy(StyleTableLayout::Auto), mXSpan(1) { 1534 MOZ_COUNT_CTOR(nsStyleTable); 1535 } 1536 1537 nsStyleTable::nsStyleTable(const nsStyleTable& aSource) 1538 : mLayoutStrategy(aSource.mLayoutStrategy), mXSpan(aSource.mXSpan) { 1539 MOZ_COUNT_CTOR(nsStyleTable); 1540 } 1541 1542 nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const { 1543 if (mXSpan != aNewData.mXSpan || 1544 mLayoutStrategy != aNewData.mLayoutStrategy) { 1545 return nsChangeHint_ReconstructFrame; 1546 } 1547 return nsChangeHint(0); 1548 } 1549 1550 // ----------------------- 1551 // nsStyleTableBorder 1552 1553 nsStyleTableBorder::nsStyleTableBorder() 1554 : mBorderSpacing{Length::Zero(), Length::Zero()}, 1555 mBorderCollapse(StyleBorderCollapse::Separate), 1556 mCaptionSide(StyleCaptionSide::Top), 1557 mEmptyCells(StyleEmptyCells::Show) { 1558 MOZ_COUNT_CTOR(nsStyleTableBorder); 1559 } 1560 1561 nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource) 1562 : mBorderSpacing(aSource.mBorderSpacing), 1563 mBorderCollapse(aSource.mBorderCollapse), 1564 mCaptionSide(aSource.mCaptionSide), 1565 mEmptyCells(aSource.mEmptyCells) { 1566 MOZ_COUNT_CTOR(nsStyleTableBorder); 1567 } 1568 1569 nsChangeHint nsStyleTableBorder::CalcDifference( 1570 const nsStyleTableBorder& aNewData) const { 1571 // Border-collapse changes need a reframe, because we use a different frame 1572 // class for table cells in the collapsed border model. This is used to 1573 // conserve memory when using the separated border model (collapsed borders 1574 // require extra state to be stored). 1575 if (mBorderCollapse != aNewData.mBorderCollapse) { 1576 return nsChangeHint_ReconstructFrame; 1577 } 1578 if (mCaptionSide != aNewData.mCaptionSide || 1579 mBorderSpacing != aNewData.mBorderSpacing) { 1580 return NS_STYLE_HINT_REFLOW; 1581 } 1582 if (mEmptyCells != aNewData.mEmptyCells) { 1583 return NS_STYLE_HINT_VISUAL; 1584 } 1585 return nsChangeHint(0); 1586 } 1587 1588 template <typename T> 1589 static bool GradientItemsAreOpaque( 1590 Span<const StyleGenericGradientItem<StyleColor, T>> aItems) { 1591 for (auto& stop : aItems) { 1592 if (stop.IsInterpolationHint()) { 1593 continue; 1594 } 1595 1596 auto& color = stop.IsSimpleColorStop() ? stop.AsSimpleColorStop() 1597 : stop.AsComplexColorStop().color; 1598 if (color.MaybeTransparent()) { 1599 // We don't know the foreground color here, so if it's being used 1600 // we must assume it might be transparent. 1601 return false; 1602 } 1603 } 1604 1605 return true; 1606 } 1607 1608 template <> 1609 bool StyleGradient::IsOpaque() const { 1610 switch (tag) { 1611 case Tag::Linear: 1612 return GradientItemsAreOpaque(AsLinear().items.AsSpan()); 1613 case Tag::Radial: 1614 return GradientItemsAreOpaque(AsRadial().items.AsSpan()); 1615 case Tag::Conic: 1616 return GradientItemsAreOpaque(AsConic().items.AsSpan()); 1617 } 1618 MOZ_ASSERT_UNREACHABLE("Unexpected gradient type"); 1619 return false; 1620 } 1621 1622 template <> 1623 bool StyleImage::IsOpaque() const { 1624 switch (tag) { 1625 case Tag::ImageSet: 1626 return FinalImage().IsOpaque(); 1627 case Tag::Gradient: 1628 return AsGradient()->IsOpaque(); 1629 case Tag::Url: { 1630 if (!IsComplete()) { 1631 return false; 1632 } 1633 MOZ_ASSERT(GetImageRequest(), "should've returned earlier above"); 1634 nsCOMPtr<imgIContainer> imageContainer; 1635 GetImageRequest()->GetImage(getter_AddRefs(imageContainer)); 1636 MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready"); 1637 return imageContainer->WillDrawOpaqueNow(); 1638 } 1639 case Tag::CrossFade: 1640 for (const auto& el : AsCrossFade()->elements.AsSpan()) { 1641 if (el.image.IsColor()) { 1642 if (el.image.AsColor().MaybeTransparent()) { 1643 return false; 1644 } 1645 continue; 1646 } 1647 MOZ_ASSERT(el.image.IsImage()); 1648 if (!el.image.AsImage().IsOpaque()) { 1649 return false; 1650 } 1651 } 1652 return true; 1653 case Tag::LightDark: 1654 MOZ_FALLTHROUGH_ASSERT("Should be computed already"); 1655 case Tag::Element: 1656 case Tag::MozSymbolicIcon: 1657 case Tag::None: 1658 break; 1659 } 1660 return false; 1661 } 1662 1663 template <> 1664 bool StyleImage::IsComplete() const { 1665 switch (tag) { 1666 case Tag::None: 1667 return false; 1668 case Tag::Gradient: 1669 case Tag::Element: 1670 case Tag::MozSymbolicIcon: 1671 return true; 1672 case Tag::Url: { 1673 if (!IsResolved()) { 1674 return false; 1675 } 1676 imgRequestProxy* req = GetImageRequest(); 1677 if (!req) { 1678 return false; 1679 } 1680 uint32_t status = imgIRequest::STATUS_ERROR; 1681 return NS_SUCCEEDED(req->GetImageStatus(&status)) && 1682 (status & imgIRequest::STATUS_SIZE_AVAILABLE) && 1683 (status & imgIRequest::STATUS_FRAME_COMPLETE); 1684 } 1685 case Tag::ImageSet: 1686 return FinalImage().IsComplete(); 1687 // Bug 546052 cross-fade not yet implemented. 1688 case Tag::CrossFade: 1689 return true; 1690 case Tag::LightDark: 1691 MOZ_ASSERT_UNREACHABLE("light-dark() should be computed already"); 1692 break; 1693 } 1694 MOZ_ASSERT_UNREACHABLE("unexpected image type"); 1695 return false; 1696 } 1697 1698 template <> 1699 bool StyleImage::IsSizeAvailable() const { 1700 switch (tag) { 1701 case Tag::None: 1702 return false; 1703 case Tag::Gradient: 1704 case Tag::Element: 1705 case Tag::MozSymbolicIcon: 1706 return true; 1707 case Tag::Url: { 1708 imgRequestProxy* req = GetImageRequest(); 1709 if (!req) { 1710 return false; 1711 } 1712 uint32_t status = imgIRequest::STATUS_ERROR; 1713 return NS_SUCCEEDED(req->GetImageStatus(&status)) && 1714 !(status & imgIRequest::STATUS_ERROR) && 1715 (status & imgIRequest::STATUS_SIZE_AVAILABLE); 1716 } 1717 case Tag::ImageSet: 1718 return FinalImage().IsSizeAvailable(); 1719 case Tag::CrossFade: 1720 // TODO: Bug 546052 cross-fade not yet implemented. 1721 return true; 1722 case Tag::LightDark: 1723 MOZ_ASSERT_UNREACHABLE("light-dark() should be computed already"); 1724 break; 1725 } 1726 MOZ_ASSERT_UNREACHABLE("unexpected image type"); 1727 return false; 1728 } 1729 1730 template <> 1731 void StyleImage::ResolveImage(Document& aDoc, const StyleImage* aOld) { 1732 if (IsResolved()) { 1733 return; 1734 } 1735 const auto* old = aOld ? aOld->GetImageRequestURLValue() : nullptr; 1736 const auto* url = GetImageRequestURLValue(); 1737 // We could avoid this const_cast generating more code but it's not really 1738 // worth it. 1739 const_cast<StyleComputedUrl*>(url)->ResolveImage(aDoc, old); 1740 } 1741 1742 template <> 1743 ImageResolution StyleImage::GetResolution( 1744 const ComputedStyle* aStyleForZoom) const { 1745 ImageResolution resolution; 1746 if (imgRequestProxy* request = GetImageRequest()) { 1747 RefPtr<imgIContainer> image; 1748 request->GetImage(getter_AddRefs(image)); 1749 if (image) { 1750 resolution = image->GetResolution(); 1751 } 1752 } 1753 if (IsImageSet()) { 1754 const auto& set = *AsImageSet(); 1755 auto items = set.items.AsSpan(); 1756 if (MOZ_LIKELY(set.selected_index < items.Length())) { 1757 float r = items[set.selected_index].resolution._0; 1758 resolution.ScaleBy(r); 1759 } 1760 } 1761 if (aStyleForZoom && aStyleForZoom->EffectiveZoom() != StyleZoom::ONE) { 1762 resolution.ScaleBy(1.0f / aStyleForZoom->EffectiveZoom().ToFloat()); 1763 } 1764 return resolution; 1765 } 1766 1767 // -------------------- 1768 // nsStyleImageLayers 1769 // 1770 1771 const NonCustomCSSPropertyId nsStyleImageLayers::kBackgroundLayerTable[] = { 1772 eCSSProperty_background, // shorthand 1773 eCSSProperty_background_color, // color 1774 eCSSProperty_background_image, // image 1775 eCSSProperty_background_repeat, // repeat 1776 eCSSProperty_background_position_x, // positionX 1777 eCSSProperty_background_position_y, // positionY 1778 eCSSProperty_background_clip, // clip 1779 eCSSProperty_background_origin, // origin 1780 eCSSProperty_background_size, // size 1781 eCSSProperty_background_attachment, // attachment 1782 eCSSProperty_UNKNOWN, // maskMode 1783 eCSSProperty_UNKNOWN // composite 1784 }; 1785 1786 const NonCustomCSSPropertyId nsStyleImageLayers::kMaskLayerTable[] = { 1787 eCSSProperty_mask, // shorthand 1788 eCSSProperty_UNKNOWN, // color 1789 eCSSProperty_mask_image, // image 1790 eCSSProperty_mask_repeat, // repeat 1791 eCSSProperty_mask_position_x, // positionX 1792 eCSSProperty_mask_position_y, // positionY 1793 eCSSProperty_mask_clip, // clip 1794 eCSSProperty_mask_origin, // origin 1795 eCSSProperty_mask_size, // size 1796 eCSSProperty_UNKNOWN, // attachment 1797 eCSSProperty_mask_mode, // maskMode 1798 eCSSProperty_mask_composite // composite 1799 }; 1800 1801 nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType) 1802 : mAttachmentCount(1), 1803 mClipCount(1), 1804 mOriginCount(1), 1805 mRepeatCount(1), 1806 mPositionXCount(1), 1807 mPositionYCount(1), 1808 mImageCount(1), 1809 mSizeCount(1), 1810 mMaskModeCount(1), 1811 mBlendModeCount(1), 1812 mCompositeCount(1), 1813 mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT) { 1814 // Ensure first layer is initialized as specified layer type 1815 mLayers[0].Initialize(aType); 1816 } 1817 1818 nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers& aSource) 1819 : mAttachmentCount(aSource.mAttachmentCount), 1820 mClipCount(aSource.mClipCount), 1821 mOriginCount(aSource.mOriginCount), 1822 mRepeatCount(aSource.mRepeatCount), 1823 mPositionXCount(aSource.mPositionXCount), 1824 mPositionYCount(aSource.mPositionYCount), 1825 mImageCount(aSource.mImageCount), 1826 mSizeCount(aSource.mSizeCount), 1827 mMaskModeCount(aSource.mMaskModeCount), 1828 mBlendModeCount(aSource.mBlendModeCount), 1829 mCompositeCount(aSource.mCompositeCount), 1830 mLayers(aSource.mLayers.Clone()) {} 1831 1832 static bool AnyLayerIsElementImage(const nsStyleImageLayers& aLayers) { 1833 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aLayers) { 1834 if (aLayers.mLayers[i].mImage.FinalImage().IsElement()) { 1835 return true; 1836 } 1837 } 1838 return false; 1839 } 1840 1841 nsChangeHint nsStyleImageLayers::CalcDifference( 1842 const nsStyleImageLayers& aNewLayers, LayerType aType) const { 1843 nsChangeHint hint = nsChangeHint(0); 1844 1845 // If the number of visible images changes, then it's easy-peasy. 1846 if (mImageCount != aNewLayers.mImageCount) { 1847 hint |= nsChangeHint_RepaintFrame; 1848 if (aType == nsStyleImageLayers::LayerType::Mask || 1849 AnyLayerIsElementImage(*this) || AnyLayerIsElementImage(aNewLayers)) { 1850 hint |= nsChangeHint_UpdateEffects; 1851 } 1852 return hint; 1853 } 1854 1855 const nsStyleImageLayers& moreLayers = 1856 mLayers.Length() > aNewLayers.mLayers.Length() ? *this : aNewLayers; 1857 const nsStyleImageLayers& lessLayers = 1858 mLayers.Length() > aNewLayers.mLayers.Length() ? aNewLayers : *this; 1859 1860 for (size_t i = 0; i < moreLayers.mLayers.Length(); ++i) { 1861 const Layer& moreLayersLayer = moreLayers.mLayers[i]; 1862 if (i < moreLayers.mImageCount) { 1863 // This is a visible image we're diffing, we may need to repaint. 1864 const Layer& lessLayersLayer = lessLayers.mLayers[i]; 1865 nsChangeHint layerDifference = 1866 moreLayersLayer.CalcDifference(lessLayersLayer); 1867 if (layerDifference && 1868 (moreLayersLayer.mImage.FinalImage().IsElement() || 1869 lessLayersLayer.mImage.FinalImage().IsElement())) { 1870 layerDifference |= 1871 nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; 1872 } 1873 hint |= layerDifference; 1874 continue; 1875 } 1876 if (hint) { 1877 // If they're different by now, we're done. 1878 return hint; 1879 } 1880 if (i >= lessLayers.mLayers.Length()) { 1881 // The layer count differs, we know some property has changed, but if we 1882 // got here we know it won't affect rendering. 1883 return nsChangeHint_NeutralChange; 1884 } 1885 1886 const Layer& lessLayersLayer = lessLayers.mLayers[i]; 1887 MOZ_ASSERT(moreLayersLayer.mImage.IsNone()); 1888 MOZ_ASSERT(lessLayersLayer.mImage.IsNone()); 1889 if (moreLayersLayer.CalcDifference(lessLayersLayer)) { 1890 // We don't care about the difference returned, we know it's not visible, 1891 // but if something changed, then we need to return the neutral change. 1892 return nsChangeHint_NeutralChange; 1893 } 1894 } 1895 1896 if (hint) { 1897 // If they're different by now, we're done. 1898 return hint; 1899 } 1900 1901 // We could have same layers and values, but different count still. 1902 if (mAttachmentCount != aNewLayers.mAttachmentCount || 1903 mBlendModeCount != aNewLayers.mBlendModeCount || 1904 mClipCount != aNewLayers.mClipCount || 1905 mCompositeCount != aNewLayers.mCompositeCount || 1906 mMaskModeCount != aNewLayers.mMaskModeCount || 1907 mOriginCount != aNewLayers.mOriginCount || 1908 mRepeatCount != aNewLayers.mRepeatCount || 1909 mPositionXCount != aNewLayers.mPositionXCount || 1910 mPositionYCount != aNewLayers.mPositionYCount || 1911 mSizeCount != aNewLayers.mSizeCount) { 1912 hint |= nsChangeHint_NeutralChange; 1913 } 1914 1915 return hint; 1916 } 1917 1918 nsStyleImageLayers& nsStyleImageLayers::operator=( 1919 const nsStyleImageLayers& aOther) { 1920 mAttachmentCount = aOther.mAttachmentCount; 1921 mClipCount = aOther.mClipCount; 1922 mOriginCount = aOther.mOriginCount; 1923 mRepeatCount = aOther.mRepeatCount; 1924 mPositionXCount = aOther.mPositionXCount; 1925 mPositionYCount = aOther.mPositionYCount; 1926 mImageCount = aOther.mImageCount; 1927 mSizeCount = aOther.mSizeCount; 1928 mMaskModeCount = aOther.mMaskModeCount; 1929 mBlendModeCount = aOther.mBlendModeCount; 1930 mCompositeCount = aOther.mCompositeCount; 1931 mLayers = aOther.mLayers.Clone(); 1932 1933 return *this; 1934 } 1935 1936 bool nsStyleImageLayers::operator==(const nsStyleImageLayers& aOther) const { 1937 if (mAttachmentCount != aOther.mAttachmentCount || 1938 mClipCount != aOther.mClipCount || mOriginCount != aOther.mOriginCount || 1939 mRepeatCount != aOther.mRepeatCount || 1940 mPositionXCount != aOther.mPositionXCount || 1941 mPositionYCount != aOther.mPositionYCount || 1942 mImageCount != aOther.mImageCount || mSizeCount != aOther.mSizeCount || 1943 mMaskModeCount != aOther.mMaskModeCount || 1944 mBlendModeCount != aOther.mBlendModeCount) { 1945 return false; 1946 } 1947 1948 if (mLayers.Length() != aOther.mLayers.Length()) { 1949 return false; 1950 } 1951 1952 for (uint32_t i = 0; i < mLayers.Length(); i++) { 1953 if (mLayers[i].mPosition != aOther.mLayers[i].mPosition || 1954 mLayers[i].mImage != aOther.mLayers[i].mImage || 1955 mLayers[i].mSize != aOther.mLayers[i].mSize || 1956 mLayers[i].mClip != aOther.mLayers[i].mClip || 1957 mLayers[i].mOrigin != aOther.mLayers[i].mOrigin || 1958 mLayers[i].mAttachment != aOther.mLayers[i].mAttachment || 1959 mLayers[i].mBlendMode != aOther.mLayers[i].mBlendMode || 1960 mLayers[i].mComposite != aOther.mLayers[i].mComposite || 1961 mLayers[i].mMaskMode != aOther.mLayers[i].mMaskMode || 1962 mLayers[i].mRepeat != aOther.mLayers[i].mRepeat) { 1963 return false; 1964 } 1965 } 1966 1967 return true; 1968 } 1969 1970 static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize, 1971 const StyleImage& aImage) { 1972 MOZ_ASSERT(!aImage.IsNone(), "caller should have handled this"); 1973 MOZ_ASSERT(!aImage.IsImageSet(), "caller should have handled this"); 1974 1975 // Contain and cover straightforwardly depend on frame size. 1976 if (aSize.IsCover() || aSize.IsContain()) { 1977 return true; 1978 } 1979 1980 MOZ_ASSERT(aSize.IsExplicitSize()); 1981 const auto& size = aSize.AsExplicitSize(); 1982 1983 // If either dimension contains a non-zero percentage, rendering for that 1984 // dimension straightforwardly depends on frame size. 1985 if (size.width.HasPercent() || size.height.HasPercent()) { 1986 return true; 1987 } 1988 1989 // If both dimensions are fixed lengths, there's no dependency. 1990 if (!size.width.IsAuto() && !size.height.IsAuto()) { 1991 return false; 1992 } 1993 1994 // Gradient rendering depends on frame size when auto is involved because 1995 // gradients have no intrinsic ratio or dimensions, and therefore the relevant 1996 // dimension is "treat[ed] as 100%". 1997 if (aImage.IsGradient()) { 1998 return true; 1999 } 2000 2001 // XXX Element rendering for auto or fixed length doesn't depend on frame size 2002 // according to the spec. However, we don't implement the spec yet, so 2003 // for now we bail and say element() plus auto affects ultimate size. 2004 if (aImage.IsElement()) { 2005 return true; 2006 } 2007 2008 MOZ_ASSERT(aImage.IsImageRequestType(), "Missed some image"); 2009 if (auto* request = aImage.GetImageRequest()) { 2010 nsCOMPtr<imgIContainer> imgContainer; 2011 request->GetImage(getter_AddRefs(imgContainer)); 2012 if (imgContainer) { 2013 CSSIntSize imageSize; 2014 AspectRatio imageRatio; 2015 bool hasWidth, hasHeight; 2016 // We could bother getting the right resolution here but it doesn't matter 2017 // since we ignore `imageSize`. 2018 nsLayoutUtils::ComputeSizeForDrawing(imgContainer, ImageResolution(), 2019 imageSize, imageRatio, hasWidth, 2020 hasHeight); 2021 2022 // If the image has a fixed width and height, rendering never depends on 2023 // the frame size. 2024 if (hasWidth && hasHeight) { 2025 return false; 2026 } 2027 2028 // If the image has an intrinsic ratio, rendering will depend on frame 2029 // size when background-size is all auto. 2030 if (imageRatio) { 2031 return size.width.IsAuto() == size.height.IsAuto(); 2032 } 2033 2034 // Otherwise, rendering depends on frame size when the image dimensions 2035 // and background-size don't complement each other. 2036 return !(hasWidth && size.width.IsLengthPercentage()) && 2037 !(hasHeight && size.height.IsLengthPercentage()); 2038 } 2039 } 2040 2041 // Passed the gauntlet: no dependency. 2042 return false; 2043 } 2044 2045 nsStyleImageLayers::Layer::Layer() 2046 : mImage(StyleImage::None()), 2047 mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(), 2048 LengthPercentageOrAuto::Auto())), 2049 2050 mClip(StyleGeometryBox::BorderBox), 2051 mAttachment(StyleImageLayerAttachment::Scroll), 2052 mBlendMode(StyleBlend::Normal), 2053 mComposite(StyleMaskComposite::Add), 2054 mMaskMode(StyleMaskMode::MatchSource) {} 2055 2056 nsStyleImageLayers::Layer::~Layer() = default; 2057 2058 void nsStyleImageLayers::Layer::Initialize( 2059 nsStyleImageLayers::LayerType aType) { 2060 mPosition = Position::FromPercentage(0.); 2061 2062 if (aType == LayerType::Background) { 2063 mOrigin = StyleGeometryBox::PaddingBox; 2064 } else { 2065 MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type."); 2066 mOrigin = StyleGeometryBox::BorderBox; 2067 } 2068 } 2069 2070 bool nsStyleImageLayers::Layer:: 2071 RenderingMightDependOnPositioningAreaSizeChange() const { 2072 // Do we even have an image? 2073 if (mImage.IsNone()) { 2074 return false; 2075 } 2076 2077 return mPosition.DependsOnPositioningAreaSize() || 2078 SizeDependsOnPositioningAreaSize(mSize, mImage.FinalImage()) || 2079 mRepeat.DependsOnPositioningAreaSize(); 2080 } 2081 2082 bool nsStyleImageLayers::Layer::operator==(const Layer& aOther) const { 2083 return mAttachment == aOther.mAttachment && mClip == aOther.mClip && 2084 mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat && 2085 mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition && 2086 mSize == aOther.mSize && mImage == aOther.mImage && 2087 mMaskMode == aOther.mMaskMode && mComposite == aOther.mComposite; 2088 } 2089 2090 template <class ComputedValueItem> 2091 static void FillImageLayerList( 2092 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, 2093 ComputedValueItem nsStyleImageLayers::Layer::* aResultLocation, 2094 uint32_t aItemCount, uint32_t aFillCount) { 2095 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length"); 2096 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount; 2097 ++sourceLayer, ++destLayer) { 2098 aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation; 2099 } 2100 } 2101 2102 // The same as FillImageLayerList, but for values stored in 2103 // layer.mPosition.*aResultLocation instead of layer.*aResultLocation. 2104 static void FillImageLayerPositionCoordList( 2105 nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, 2106 LengthPercentage Position::* aResultLocation, uint32_t aItemCount, 2107 uint32_t aFillCount) { 2108 MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length"); 2109 for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount; 2110 ++sourceLayer, ++destLayer) { 2111 aLayers[destLayer].mPosition.*aResultLocation = 2112 aLayers[sourceLayer].mPosition.*aResultLocation; 2113 } 2114 } 2115 2116 void nsStyleImageLayers::FillAllLayers(uint32_t aMaxItemCount) { 2117 // Delete any extra items. We need to keep layers in which any 2118 // property was specified. 2119 mLayers.TruncateLengthNonZero(aMaxItemCount); 2120 2121 uint32_t fillCount = mImageCount; 2122 FillImageLayerList(mLayers, &Layer::mImage, mImageCount, fillCount); 2123 FillImageLayerList(mLayers, &Layer::mRepeat, mRepeatCount, fillCount); 2124 FillImageLayerList(mLayers, &Layer::mAttachment, mAttachmentCount, fillCount); 2125 FillImageLayerList(mLayers, &Layer::mClip, mClipCount, fillCount); 2126 FillImageLayerList(mLayers, &Layer::mBlendMode, mBlendModeCount, fillCount); 2127 FillImageLayerList(mLayers, &Layer::mOrigin, mOriginCount, fillCount); 2128 FillImageLayerPositionCoordList(mLayers, &Position::horizontal, 2129 mPositionXCount, fillCount); 2130 FillImageLayerPositionCoordList(mLayers, &Position::vertical, mPositionYCount, 2131 fillCount); 2132 FillImageLayerList(mLayers, &Layer::mSize, mSizeCount, fillCount); 2133 FillImageLayerList(mLayers, &Layer::mMaskMode, mMaskModeCount, fillCount); 2134 FillImageLayerList(mLayers, &Layer::mComposite, mCompositeCount, fillCount); 2135 } 2136 2137 static bool UrlValuesEqual(const StyleImage& aImage, 2138 const StyleImage& aOtherImage) { 2139 const auto* url = aImage.GetImageRequestURLValue(); 2140 const auto* other = aOtherImage.GetImageRequestURLValue(); 2141 return url == other || (url && other && *url == *other); 2142 } 2143 2144 nsChangeHint nsStyleImageLayers::Layer::CalcDifference( 2145 const nsStyleImageLayers::Layer& aNewLayer) const { 2146 nsChangeHint hint = nsChangeHint(0); 2147 if (!UrlValuesEqual(mImage, aNewLayer.mImage)) { 2148 hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects; 2149 } else if (mAttachment != aNewLayer.mAttachment || mClip != aNewLayer.mClip || 2150 mOrigin != aNewLayer.mOrigin || mRepeat != aNewLayer.mRepeat || 2151 mBlendMode != aNewLayer.mBlendMode || mSize != aNewLayer.mSize || 2152 mImage != aNewLayer.mImage || mMaskMode != aNewLayer.mMaskMode || 2153 mComposite != aNewLayer.mComposite) { 2154 hint |= nsChangeHint_RepaintFrame; 2155 } 2156 2157 if (mPosition != aNewLayer.mPosition) { 2158 hint |= nsChangeHint_UpdateBackgroundPosition; 2159 } 2160 2161 return hint; 2162 } 2163 2164 // -------------------- 2165 // nsStyleBackground 2166 // 2167 2168 nsStyleBackground::nsStyleBackground() 2169 : mImage(nsStyleImageLayers::LayerType::Background), 2170 mBackgroundColor(StyleColor::Transparent()) { 2171 MOZ_COUNT_CTOR(nsStyleBackground); 2172 } 2173 2174 nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) 2175 : mImage(aSource.mImage), mBackgroundColor(aSource.mBackgroundColor) { 2176 MOZ_COUNT_CTOR(nsStyleBackground); 2177 } 2178 2179 void nsStyleBackground::TriggerImageLoads(Document& aDocument, 2180 const nsStyleBackground* aOldStyle) { 2181 MOZ_ASSERT(NS_IsMainThread()); 2182 mImage.ResolveImages(aDocument, aOldStyle ? &aOldStyle->mImage : nullptr); 2183 } 2184 2185 nsChangeHint nsStyleBackground::CalcDifference( 2186 const nsStyleBackground& aNewData) const { 2187 nsChangeHint hint = nsChangeHint(0); 2188 if (mBackgroundColor != aNewData.mBackgroundColor) { 2189 hint |= nsChangeHint_RepaintFrame; 2190 } 2191 2192 hint |= mImage.CalcDifference(aNewData.mImage, 2193 nsStyleImageLayers::LayerType::Background); 2194 2195 return hint; 2196 } 2197 2198 bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const { 2199 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) { 2200 const nsStyleImageLayers::Layer& layer = mImage.mLayers[i]; 2201 if (layer.mAttachment == StyleImageLayerAttachment::Fixed && 2202 !layer.mImage.IsNone() && !nsLayoutUtils::IsTransformed(aFrame)) { 2203 return true; 2204 } 2205 } 2206 return false; 2207 } 2208 2209 nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const { 2210 return mBackgroundColor.CalcColor(aFrame); 2211 } 2212 2213 nscolor nsStyleBackground::BackgroundColor(const ComputedStyle* aStyle) const { 2214 return mBackgroundColor.CalcColor(*aStyle); 2215 } 2216 2217 bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const { 2218 return IsTransparent(aFrame->Style()); 2219 } 2220 2221 bool nsStyleBackground::IsTransparent(const ComputedStyle* aStyle) const { 2222 return BottomLayer().mImage.IsNone() && mImage.mImageCount == 1 && 2223 NS_GET_A(BackgroundColor(aStyle)) == 0; 2224 } 2225 2226 StyleTransition::StyleTransition(const StyleTransition& aCopy) = default; 2227 2228 bool StyleTransition::operator==(const StyleTransition& aOther) const { 2229 return mTimingFunction == aOther.mTimingFunction && 2230 mDuration == aOther.mDuration && mDelay == aOther.mDelay && 2231 mProperty == aOther.mProperty && mBehavior == aOther.mBehavior; 2232 } 2233 2234 StyleAnimation::StyleAnimation(const StyleAnimation& aCopy) = default; 2235 2236 bool StyleAnimation::operator==(const StyleAnimation& aOther) const { 2237 return mTimingFunction == aOther.mTimingFunction && 2238 mDuration == aOther.mDuration && mDelay == aOther.mDelay && 2239 mName == aOther.mName && mDirection == aOther.mDirection && 2240 mFillMode == aOther.mFillMode && mPlayState == aOther.mPlayState && 2241 mIterationCount == aOther.mIterationCount && 2242 mComposition == aOther.mComposition && mTimeline == aOther.mTimeline; 2243 } 2244 2245 // -------------------- 2246 // nsStyleDisplay 2247 // 2248 nsStyleDisplay::nsStyleDisplay() 2249 : mDisplay(StyleDisplay::Inline), 2250 mOriginalDisplay(StyleDisplay::Inline), 2251 mContentVisibility(StyleContentVisibility::Visible), 2252 mContainerType(StyleContainerType::NORMAL), 2253 mAppearance(StyleAppearance::None), 2254 mContain(StyleContain::NONE), 2255 mEffectiveContainment(StyleContain::NONE), 2256 mDefaultAppearance(StyleAppearance::None), 2257 mPosition(StylePositionProperty::Static), 2258 mFloat(StyleFloat::None), 2259 mClear(StyleClear::None), 2260 mBreakInside(StyleBreakWithin::Auto), 2261 mBreakBefore(StyleBreakBetween::Auto), 2262 mBreakAfter(StyleBreakBetween::Auto), 2263 mOverflowX(StyleOverflow::Visible), 2264 mOverflowY(StyleOverflow::Visible), 2265 mScrollbarGutter(StyleScrollbarGutter::AUTO), 2266 mResize(StyleResize::None), 2267 mOrient(StyleOrient::Inline), 2268 mIsolation(StyleIsolation::Auto), 2269 mTopLayer(StyleTopLayer::None), 2270 mTouchAction(StyleTouchAction::AUTO), 2271 mScrollBehavior(StyleScrollBehavior::Auto), 2272 mOverscrollBehaviorX(StyleOverscrollBehavior::Auto), 2273 mOverscrollBehaviorY(StyleOverscrollBehavior::Auto), 2274 mOverflowAnchor(StyleOverflowAnchor::Auto), 2275 mScrollSnapAlign{StyleScrollSnapAlignKeyword::None, 2276 StyleScrollSnapAlignKeyword::None}, 2277 mScrollSnapStop{StyleScrollSnapStop::Normal}, 2278 mScrollSnapType{StyleScrollSnapAxis::Both, 2279 StyleScrollSnapStrictness::None}, 2280 mBackfaceVisibility(StyleBackfaceVisibility::Visible), 2281 mTransformStyle(StyleTransformStyle::Flat), 2282 mTransformBox(StyleTransformBox::ViewBox), 2283 mRotate(StyleRotate::None()), 2284 mTranslate(StyleTranslate::None()), 2285 mScale(StyleScale::None()), 2286 mWillChange{{}, {0}}, 2287 mOffsetPath(StyleOffsetPath::None()), 2288 mOffsetDistance(LengthPercentage::Zero()), 2289 mOffsetRotate{true, StyleAngle{0.0}}, 2290 mOffsetAnchor(StylePositionOrAuto::Auto()), 2291 mOffsetPosition(StyleOffsetPosition::Normal()), 2292 mTransformOrigin{LengthPercentage::FromPercentage(0.5), 2293 LengthPercentage::FromPercentage(0.5), 2294 {0.}}, 2295 mChildPerspective(StylePerspective::None()), 2296 mPerspectiveOrigin(Position::FromPercentage(0.5f)), 2297 mVerticalAlign( 2298 StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)), 2299 mBaselineSource(StyleBaselineSource::Auto), 2300 mWebkitLineClamp(0), 2301 mShapeMargin(LengthPercentage::Zero()), 2302 mShapeOutside(StyleShapeOutside::None()), 2303 mAnchorScope(StyleAnchorScope::None()) { 2304 MOZ_COUNT_CTOR(nsStyleDisplay); 2305 } 2306 2307 nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) 2308 : mDisplay(aSource.mDisplay), 2309 mOriginalDisplay(aSource.mOriginalDisplay), 2310 mContentVisibility(aSource.mContentVisibility), 2311 mContainerType(aSource.mContainerType), 2312 mAppearance(aSource.mAppearance), 2313 mContain(aSource.mContain), 2314 mEffectiveContainment(aSource.mEffectiveContainment), 2315 mDefaultAppearance(aSource.mDefaultAppearance), 2316 mPosition(aSource.mPosition), 2317 mFloat(aSource.mFloat), 2318 mClear(aSource.mClear), 2319 mBreakInside(aSource.mBreakInside), 2320 mBreakBefore(aSource.mBreakBefore), 2321 mBreakAfter(aSource.mBreakAfter), 2322 mOverflowX(aSource.mOverflowX), 2323 mOverflowY(aSource.mOverflowY), 2324 mScrollbarGutter(aSource.mScrollbarGutter), 2325 mResize(aSource.mResize), 2326 mOrient(aSource.mOrient), 2327 mIsolation(aSource.mIsolation), 2328 mTopLayer(aSource.mTopLayer), 2329 mTouchAction(aSource.mTouchAction), 2330 mScrollBehavior(aSource.mScrollBehavior), 2331 mOverscrollBehaviorX(aSource.mOverscrollBehaviorX), 2332 mOverscrollBehaviorY(aSource.mOverscrollBehaviorY), 2333 mOverflowAnchor(aSource.mOverflowAnchor), 2334 mScrollSnapAlign(aSource.mScrollSnapAlign), 2335 mScrollSnapStop(aSource.mScrollSnapStop), 2336 mScrollSnapType(aSource.mScrollSnapType), 2337 mBackfaceVisibility(aSource.mBackfaceVisibility), 2338 mTransformStyle(aSource.mTransformStyle), 2339 mTransformBox(aSource.mTransformBox), 2340 mTransform(aSource.mTransform), 2341 mRotate(aSource.mRotate), 2342 mTranslate(aSource.mTranslate), 2343 mScale(aSource.mScale), 2344 mContainerName(aSource.mContainerName), 2345 mWillChange(aSource.mWillChange), 2346 mOffsetPath(aSource.mOffsetPath), 2347 mOffsetDistance(aSource.mOffsetDistance), 2348 mOffsetRotate(aSource.mOffsetRotate), 2349 mOffsetAnchor(aSource.mOffsetAnchor), 2350 mOffsetPosition(aSource.mOffsetPosition), 2351 mTransformOrigin(aSource.mTransformOrigin), 2352 mChildPerspective(aSource.mChildPerspective), 2353 mPerspectiveOrigin(aSource.mPerspectiveOrigin), 2354 mVerticalAlign(aSource.mVerticalAlign), 2355 mBaselineSource(aSource.mBaselineSource), 2356 mWebkitLineClamp(aSource.mWebkitLineClamp), 2357 mShapeImageThreshold(aSource.mShapeImageThreshold), 2358 mShapeMargin(aSource.mShapeMargin), 2359 mShapeOutside(aSource.mShapeOutside), 2360 mAnchorName(aSource.mAnchorName), 2361 mAnchorScope(aSource.mAnchorScope) { 2362 MOZ_COUNT_CTOR(nsStyleDisplay); 2363 } 2364 2365 void nsStyleDisplay::TriggerImageLoads(Document& aDocument, 2366 const nsStyleDisplay* aOldStyle) { 2367 MOZ_ASSERT(NS_IsMainThread()); 2368 2369 if (mShapeOutside.IsImage()) { 2370 const auto* old = aOldStyle && aOldStyle->mShapeOutside.IsImage() 2371 ? &aOldStyle->mShapeOutside.AsImage() 2372 : nullptr; 2373 // Const-cast is ugly but legit, we could avoid it by generating mut-casts 2374 // with cbindgen. 2375 const_cast<StyleImage&>(mShapeOutside.AsImage()) 2376 .ResolveImage(aDocument, old); 2377 } 2378 } 2379 2380 template <typename TransformLike> 2381 static inline nsChangeHint CompareTransformValues( 2382 const TransformLike& aOldTransform, const TransformLike& aNewTransform) { 2383 nsChangeHint result = nsChangeHint(0); 2384 2385 // Note: If we add a new change hint for transform changes here, we have to 2386 // modify KeyframeEffect::CalculateCumulativeChangeHint too! 2387 if (aOldTransform != aNewTransform) { 2388 result |= nsChangeHint_UpdateTransformLayer; 2389 if (!aOldTransform.IsNone() && !aNewTransform.IsNone()) { 2390 result |= nsChangeHint_UpdatePostTransformOverflow; 2391 } else { 2392 result |= nsChangeHint_UpdateOverflow; 2393 } 2394 } 2395 2396 return result; 2397 } 2398 2399 static inline nsChangeHint CompareMotionValues( 2400 const nsStyleDisplay& aDisplay, const nsStyleDisplay& aNewDisplay) { 2401 if (aDisplay.mOffsetPath == aNewDisplay.mOffsetPath) { 2402 if (aDisplay.mOffsetDistance == aNewDisplay.mOffsetDistance && 2403 aDisplay.mOffsetRotate == aNewDisplay.mOffsetRotate && 2404 aDisplay.mOffsetAnchor == aNewDisplay.mOffsetAnchor && 2405 aDisplay.mOffsetPosition == aNewDisplay.mOffsetPosition) { 2406 return nsChangeHint(0); 2407 } 2408 2409 // No motion path transform is applied. 2410 if (aDisplay.mOffsetPath.IsNone()) { 2411 return nsChangeHint_NeutralChange; 2412 } 2413 } 2414 2415 // TODO: Bug 1482737: This probably doesn't need to UpdateOverflow 2416 // (or UpdateTransformLayer) if there's already a transform. 2417 // Set the same hints as what we use for transform because motion path is 2418 // a kind of transform and will be combined with other transforms. 2419 nsChangeHint result = nsChangeHint_UpdateTransformLayer; 2420 if (!aDisplay.mOffsetPath.IsNone() && !aNewDisplay.mOffsetPath.IsNone()) { 2421 result |= nsChangeHint_UpdatePostTransformOverflow; 2422 } else { 2423 result |= nsChangeHint_UpdateOverflow; 2424 } 2425 return result; 2426 } 2427 2428 static bool ScrollbarGenerationChanged(const nsStyleDisplay& aOld, 2429 const nsStyleDisplay& aNew) { 2430 auto changed = [](StyleOverflow aOld, StyleOverflow aNew) { 2431 return aOld != aNew && 2432 (aOld == StyleOverflow::Hidden || aNew == StyleOverflow::Hidden); 2433 }; 2434 return changed(aOld.mOverflowX, aNew.mOverflowX) || 2435 changed(aOld.mOverflowY, aNew.mOverflowY); 2436 } 2437 2438 static bool AppearanceValueAffectsFrames(StyleAppearance aAppearance, 2439 StyleAppearance aDefaultAppearance) { 2440 switch (aAppearance) { 2441 case StyleAppearance::None: 2442 // Checkbox / radio with appearance none doesn't construct an 2443 // nsCheckboxRadioFrame. 2444 return aDefaultAppearance == StyleAppearance::Checkbox || 2445 aDefaultAppearance == StyleAppearance::Radio; 2446 case StyleAppearance::Textfield: 2447 // This is for <input type=number/search> where we allow authors to 2448 // specify a |-moz-appearance:textfield| to get a control without buttons. 2449 // We need to reframe since this affects the spinbox creation in 2450 // nsNumberControlFrame::CreateAnonymousContent. 2451 return aDefaultAppearance == StyleAppearance::NumberInput || 2452 aDefaultAppearance == StyleAppearance::PasswordInput; 2453 case StyleAppearance::Menulist: 2454 // This affects the menulist button creation. 2455 return aDefaultAppearance == StyleAppearance::Menulist; 2456 default: 2457 return false; 2458 } 2459 } 2460 2461 nsChangeHint nsStyleDisplay::CalcDifference( 2462 const nsStyleDisplay& aNewData, const ComputedStyle& aOldStyle) const { 2463 if (mDisplay != aNewData.mDisplay || 2464 (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None) || 2465 mTopLayer != aNewData.mTopLayer || mResize != aNewData.mResize) { 2466 return nsChangeHint_ReconstructFrame; 2467 } 2468 2469 auto oldAppearance = EffectiveAppearance(); 2470 auto newAppearance = aNewData.EffectiveAppearance(); 2471 if (oldAppearance != newAppearance) { 2472 // Changes to the mDefaultAppearance values handled in 2473 // AppearanceValueAffectsFrames reconstruct their frames via other means. 2474 // E.g. switching the <input> type attribute reframes via 2475 // GetAttributeChangeHint. Thus, it doesn't matter whether we pick 2476 // mDefaultAppearance or aNewData.mDefaultAppearance for the check below. 2477 if (AppearanceValueAffectsFrames(oldAppearance, mDefaultAppearance) || 2478 AppearanceValueAffectsFrames(newAppearance, mDefaultAppearance)) { 2479 return nsChangeHint_ReconstructFrame; 2480 } 2481 } 2482 2483 auto hint = nsChangeHint(0); 2484 const auto containmentDiff = 2485 mEffectiveContainment ^ aNewData.mEffectiveContainment; 2486 if (containmentDiff) { 2487 if (containmentDiff & StyleContain::STYLE) { 2488 // Style containment affects counters so we need to re-frame. 2489 return nsChangeHint_ReconstructFrame; 2490 } 2491 if (containmentDiff & (StyleContain::PAINT | StyleContain::LAYOUT)) { 2492 // Paint and layout containment boxes are absolutely/fixed positioning 2493 // containers. 2494 hint |= nsChangeHint_UpdateContainingBlock; 2495 } 2496 // The other container types only need a reflow. 2497 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; 2498 } 2499 if (mPosition != aNewData.mPosition) { 2500 if (IsAbsolutelyPositionedStyle() || 2501 aNewData.IsAbsolutelyPositionedStyle()) { 2502 // This changes our parent relationship on the frame tree and / or needs 2503 // to create a placeholder, so gotta reframe. There are some cases (when 2504 // switching from fixed to absolute or viceversa, if our containing block 2505 // happens to remain the same, i.e., if it has a transform or such) where 2506 // this wouldn't really be needed (though we'd still need to move the 2507 // frame from one child list to another). In any case we don't have a hand 2508 // to that information from here, and it doesn't seem like a case worth 2509 // optimizing for. 2510 return nsChangeHint_ReconstructFrame; 2511 } 2512 // We start or stop being a containing block for abspos descendants. This 2513 // also causes painting to change, as we'd become a pseudo-stacking context. 2514 if (IsRelativelyOrStickyPositionedStyle() != 2515 aNewData.IsRelativelyOrStickyPositionedStyle()) { 2516 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_RepaintFrame; 2517 } 2518 if (IsPositionForcingStackingContext() != 2519 aNewData.IsPositionForcingStackingContext()) { 2520 hint |= nsChangeHint_RepaintFrame; 2521 } 2522 // On top of that: if the above ends up not reframing, we need a reflow to 2523 // compute our relative, static or sticky position. 2524 hint |= nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition; 2525 } 2526 2527 if (mScrollSnapAlign != aNewData.mScrollSnapAlign || 2528 mScrollSnapType != aNewData.mScrollSnapType || 2529 mScrollSnapStop != aNewData.mScrollSnapStop) { 2530 hint |= nsChangeHint_RepaintFrame; 2531 } 2532 if (mScrollBehavior != aNewData.mScrollBehavior) { 2533 hint |= nsChangeHint_NeutralChange; 2534 } 2535 2536 if (mOverflowX != aNewData.mOverflowX || mOverflowY != aNewData.mOverflowY) { 2537 const bool isScrollable = IsScrollableOverflow(); 2538 if (isScrollable != aNewData.IsScrollableOverflow()) { 2539 // We may need to construct or destroy a scroll frame as a result of this 2540 // change. If we don't, we still need to update our overflow in some cases 2541 // (like svg:foreignObject), which ignore the scrollable-ness of our 2542 // overflow. 2543 hint |= nsChangeHint_ScrollbarChange | nsChangeHint_UpdateOverflow | 2544 nsChangeHint_RepaintFrame; 2545 } else if (isScrollable) { 2546 if (ScrollbarGenerationChanged(*this, aNewData)) { 2547 // We might need to reframe in the case of hidden -> non-hidden case 2548 // though, since ScrollContainerFrame::CreateAnonymousContent avoids 2549 // creating scrollbars altogether for overflow: hidden. That seems it 2550 // could create some interesting perf cliffs... 2551 hint |= nsChangeHint_ScrollbarChange; 2552 } else { 2553 // Otherwise, for changes where both overflow values are scrollable, 2554 // means that scrollbars may appear or disappear. We need to reflow, 2555 // since reflow is what determines which scrollbars if any are visible. 2556 hint |= nsChangeHint_ReflowHintsForScrollbarChange; 2557 } 2558 } else { 2559 // Otherwise this is a change between 'visible' and 'clip'. 2560 // Here only whether we have a 'clip' changes, so just repaint and 2561 // update our overflow areas in that case. 2562 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame; 2563 } 2564 } 2565 2566 if (mScrollbarGutter != aNewData.mScrollbarGutter) { 2567 if (IsScrollableOverflow() || aOldStyle.IsRootElementStyle()) { 2568 // Changing scrollbar-gutter affects available inline-size of a inner 2569 // scrolled frame, so we need a reflow for scrollbar change. Note that the 2570 // root is always scrollable in HTML, even if its style doesn't say so. 2571 hint |= nsChangeHint_ReflowHintsForScrollbarChange; 2572 } else { 2573 // scrollbar-gutter only applies to scroll containers. 2574 hint |= nsChangeHint_NeutralChange; 2575 } 2576 } 2577 2578 if (mFloat != aNewData.mFloat) { 2579 // Changing which side we're floating on (float:none was handled above). 2580 hint |= nsChangeHint_ReflowHintsForFloatAreaChange; 2581 } 2582 2583 if (mShapeOutside != aNewData.mShapeOutside || 2584 mShapeMargin != aNewData.mShapeMargin || 2585 mShapeImageThreshold != aNewData.mShapeImageThreshold) { 2586 if (aNewData.mFloat != StyleFloat::None) { 2587 // If we are floating, and our shape-outside, shape-margin, or 2588 // shape-image-threshold are changed, our descendants are not impacted, 2589 // but our ancestor and siblings are. 2590 hint |= nsChangeHint_ReflowHintsForFloatAreaChange; 2591 } else { 2592 // shape-outside or shape-margin or shape-image-threshold changed, 2593 // but we don't need to reflow because we're not floating. 2594 hint |= nsChangeHint_NeutralChange; 2595 } 2596 } 2597 2598 if (mWebkitLineClamp != aNewData.mWebkitLineClamp || 2599 mVerticalAlign != aNewData.mVerticalAlign || 2600 mBaselineSource != aNewData.mBaselineSource) { 2601 // XXX Can this just be AllReflowHints + RepaintFrame, and be included in 2602 // the block below? 2603 hint |= NS_STYLE_HINT_REFLOW; 2604 } 2605 2606 // XXX the following is conservative, for now: changing float breaking 2607 // shouldn't necessarily require a repaint, reflow should suffice. 2608 // 2609 // FIXME(emilio): We definitely change the frame tree in nsCSSFrameConstructor 2610 // based on break-before / break-after... Shouldn't that reframe? 2611 if (mClear != aNewData.mClear || mBreakInside != aNewData.mBreakInside || 2612 mBreakBefore != aNewData.mBreakBefore || 2613 mBreakAfter != aNewData.mBreakAfter || 2614 mAppearance != aNewData.mAppearance || 2615 mDefaultAppearance != aNewData.mDefaultAppearance || 2616 mOrient != aNewData.mOrient) { 2617 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; 2618 } 2619 2620 if (mIsolation != aNewData.mIsolation) { 2621 hint |= nsChangeHint_RepaintFrame; 2622 } 2623 2624 /* If we've added or removed the transform property, we need to reconstruct 2625 * the frame to add or remove the view object, and also to handle abs-pos and 2626 * fixed-pos containers. 2627 */ 2628 if (HasTransformStyle() != aNewData.HasTransformStyle()) { 2629 hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform; 2630 } else { 2631 /* Otherwise, if we've kept the property lying around and we already had a 2632 * transform, we need to see whether or not we've changed the transform. 2633 * If so, we need to recompute its overflow rect (which probably changed 2634 * if the transform changed) and to redraw within the bounds of that new 2635 * overflow rect. 2636 * 2637 * If the property isn't present in either style struct, we still do the 2638 * comparisons but turn all the resulting change hints into 2639 * nsChangeHint_NeutralChange. 2640 */ 2641 nsChangeHint transformHint = CalcTransformPropertyDifference(aNewData); 2642 2643 if (transformHint) { 2644 if (HasTransformStyle()) { 2645 hint |= transformHint; 2646 } else { 2647 hint |= nsChangeHint_NeutralChange; 2648 } 2649 } 2650 } 2651 2652 if (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) { 2653 // A change from/to being a containing block for position:fixed. 2654 hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_UpdateOverflow | 2655 nsChangeHint_RepaintFrame; 2656 } else if (mChildPerspective != aNewData.mChildPerspective) { 2657 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame; 2658 } 2659 2660 // Note that the HasTransformStyle() != aNewData.HasTransformStyle() 2661 // test above handles relevant changes in the StyleWillChangeBit_TRANSFORM 2662 // bit, which in turn handles frame reconstruction for changes in the 2663 // containing block of fixed-positioned elements. 2664 auto willChangeBitsChanged = mWillChange.bits ^ aNewData.mWillChange.bits; 2665 2666 if (willChangeBitsChanged & 2667 (StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL | 2668 StyleWillChangeBits::SCROLL | StyleWillChangeBits::OPACITY | 2669 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::TRANSFORM | 2670 StyleWillChangeBits::Z_INDEX)) { 2671 hint |= nsChangeHint_RepaintFrame; 2672 } 2673 2674 if (willChangeBitsChanged & 2675 (StyleWillChangeBits::FIXPOS_CB_NON_SVG | StyleWillChangeBits::TRANSFORM | 2676 StyleWillChangeBits::PERSPECTIVE | StyleWillChangeBits::POSITION | 2677 StyleWillChangeBits::CONTAIN)) { 2678 hint |= nsChangeHint_UpdateContainingBlock; 2679 } 2680 2681 // If touch-action is changed, we need to regenerate the event regions on 2682 // the layers and send it over to the compositor for APZ to handle. 2683 if (mTouchAction != aNewData.mTouchAction) { 2684 hint |= nsChangeHint_RepaintFrame; 2685 } 2686 2687 // If overscroll-behavior has changed, the changes are picked up 2688 // during a repaint. 2689 if (mOverscrollBehaviorX != aNewData.mOverscrollBehaviorX || 2690 mOverscrollBehaviorY != aNewData.mOverscrollBehaviorY) { 2691 hint |= nsChangeHint_SchedulePaint; 2692 } 2693 2694 if (mOriginalDisplay != aNewData.mOriginalDisplay) { 2695 // Our hypothetical box position may have changed. 2696 // 2697 // Note that it doesn't matter if we look at the old or the new struct, 2698 // since a change on whether we need a hypothetical position would trigger 2699 // reflow anyway. 2700 if (IsAbsolutelyPositionedStyle() && 2701 aOldStyle.StylePosition()->NeedsHypotheticalPositionIfAbsPos()) { 2702 hint |= 2703 nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition; 2704 } else { 2705 hint |= nsChangeHint_NeutralChange; 2706 } 2707 } 2708 2709 // Note: Our current behavior for handling changes to the 2710 // transition-duration, transition-delay, and transition-timing-function 2711 // properties is to do nothing. In other words, the transition 2712 // property that matters is what it is when the transition begins, and 2713 // we don't stop a transition later because the transition property 2714 // changed. 2715 // We do handle changes to transition-property, but we don't need to 2716 // bother with anything here, since the transition manager is notified 2717 // of any ComputedStyle change anyway. 2718 2719 // Note: Likewise, for animation-*, the animation manager gets 2720 // notified about every new ComputedStyle constructed, and it uses 2721 // that opportunity to handle dynamic changes appropriately. 2722 2723 // But we still need to return nsChangeHint_NeutralChange for these 2724 // properties, since some data did change in the style struct. 2725 2726 // TODO(emilio): Figure out change hints for container-name, maybe it needs to 2727 // be handled by the style system as a special-case (since it changes 2728 // container-query selection on descendants). 2729 // container-type / contain / content-visibility are handled by the 2730 // mEffectiveContainment check. 2731 if (!hint && (mWillChange != aNewData.mWillChange || 2732 mOverflowAnchor != aNewData.mOverflowAnchor || 2733 mContentVisibility != aNewData.mContentVisibility || 2734 mContainerType != aNewData.mContainerType || 2735 mContain != aNewData.mContain || 2736 mContainerName != aNewData.mContainerName || 2737 mAnchorName != aNewData.mAnchorName || 2738 mAnchorScope != aNewData.mAnchorScope)) { 2739 hint |= nsChangeHint_NeutralChange; 2740 } 2741 2742 return hint; 2743 } 2744 2745 nsChangeHint nsStyleDisplay::CalcTransformPropertyDifference( 2746 const nsStyleDisplay& aNewData) const { 2747 nsChangeHint transformHint = nsChangeHint(0); 2748 2749 transformHint |= CompareTransformValues(mTransform, aNewData.mTransform); 2750 transformHint |= CompareTransformValues(mRotate, aNewData.mRotate); 2751 transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate); 2752 transformHint |= CompareTransformValues(mScale, aNewData.mScale); 2753 transformHint |= CompareMotionValues(*this, aNewData); 2754 2755 if (mTransformOrigin != aNewData.mTransformOrigin) { 2756 transformHint |= nsChangeHint_UpdateTransformLayer | 2757 nsChangeHint_UpdatePostTransformOverflow; 2758 } 2759 2760 if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin || 2761 mTransformStyle != aNewData.mTransformStyle || 2762 mTransformBox != aNewData.mTransformBox) { 2763 transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame; 2764 } 2765 2766 if (mBackfaceVisibility != aNewData.mBackfaceVisibility) { 2767 transformHint |= nsChangeHint_RepaintFrame; 2768 } 2769 2770 return transformHint; 2771 } 2772 2773 // -------------------- 2774 // nsStyleVisibility 2775 // 2776 2777 nsStyleVisibility::nsStyleVisibility(const Document& aDocument) 2778 : mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL 2779 ? StyleDirection::Rtl 2780 : StyleDirection::Ltr), 2781 mVisible(StyleVisibility::Visible), 2782 mImageRendering(StyleImageRendering::Auto), 2783 mWritingMode(StyleWritingModeProperty::HorizontalTb), 2784 mTextOrientation(StyleTextOrientation::Mixed), 2785 mMozBoxCollapse(StyleMozBoxCollapse::Flex), 2786 mPrintColorAdjust(StylePrintColorAdjust::Economy), 2787 mImageOrientation(StyleImageOrientation::FromImage) { 2788 MOZ_COUNT_CTOR(nsStyleVisibility); 2789 } 2790 2791 nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) 2792 : mDirection(aSource.mDirection), 2793 mVisible(aSource.mVisible), 2794 mImageRendering(aSource.mImageRendering), 2795 mWritingMode(aSource.mWritingMode), 2796 mTextOrientation(aSource.mTextOrientation), 2797 mMozBoxCollapse(aSource.mMozBoxCollapse), 2798 mPrintColorAdjust(aSource.mPrintColorAdjust), 2799 mImageOrientation(aSource.mImageOrientation) { 2800 MOZ_COUNT_CTOR(nsStyleVisibility); 2801 } 2802 2803 nsChangeHint nsStyleVisibility::CalcDifference( 2804 const nsStyleVisibility& aNewData) const { 2805 nsChangeHint hint = nsChangeHint(0); 2806 2807 if (mDirection != aNewData.mDirection || 2808 mWritingMode != aNewData.mWritingMode) { 2809 // It's important that a change in mWritingMode results in frame 2810 // reconstruction, because it may affect intrinsic size (see 2811 // nsSubDocumentFrame::GetIntrinsicISize/BSize). 2812 // Also, the used writing-mode value is now a field on nsIFrame and some 2813 // classes (e.g. table rows/cells) copy their value from an ancestor. 2814 return nsChangeHint_ReconstructFrame; 2815 } 2816 if (mImageOrientation != aNewData.mImageOrientation) { 2817 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; 2818 } 2819 if (mVisible != aNewData.mVisible) { 2820 if (mVisible == StyleVisibility::Visible || 2821 aNewData.mVisible == StyleVisibility::Visible) { 2822 hint |= nsChangeHint_VisibilityChange; 2823 } 2824 if (mVisible == StyleVisibility::Collapse || 2825 aNewData.mVisible == StyleVisibility::Collapse) { 2826 hint |= NS_STYLE_HINT_REFLOW; 2827 } else { 2828 hint |= NS_STYLE_HINT_VISUAL; 2829 } 2830 } 2831 if (mTextOrientation != aNewData.mTextOrientation || 2832 mMozBoxCollapse != aNewData.mMozBoxCollapse) { 2833 hint |= NS_STYLE_HINT_REFLOW; 2834 } 2835 if (mImageRendering != aNewData.mImageRendering) { 2836 hint |= nsChangeHint_RepaintFrame; 2837 } 2838 if (mPrintColorAdjust != aNewData.mPrintColorAdjust) { 2839 // color-adjust only affects media where dynamic changes can't happen. 2840 hint |= nsChangeHint_NeutralChange; 2841 } 2842 return hint; 2843 } 2844 2845 StyleImageOrientation nsStyleVisibility::UsedImageOrientation( 2846 imgIRequest* aRequest, StyleImageOrientation aOrientation) { 2847 if (aOrientation == StyleImageOrientation::FromImage || !aRequest) { 2848 return aOrientation; 2849 } 2850 2851 nsCOMPtr<nsIPrincipal> triggeringPrincipal = 2852 aRequest->GetTriggeringPrincipal(); 2853 2854 // If the request was for a blob, the request may not have a triggering 2855 // principal and we should use the input orientation. 2856 if (!triggeringPrincipal) { 2857 return aOrientation; 2858 } 2859 2860 nsCOMPtr<nsIURI> uri = aRequest->GetURI(); 2861 // If the image request is a data uri, then treat the request as a 2862 // same origin request. 2863 bool isSameOrigin = 2864 uri->SchemeIs("data") || triggeringPrincipal->IsSameOrigin(uri); 2865 2866 // If the image request is a cross-origin request that does not use CORS, 2867 // do not enforce the image orientation found in the style. Use the image 2868 // orientation found in the exif data. 2869 if (!isSameOrigin && !nsLayoutUtils::ImageRequestUsesCORS(aRequest)) { 2870 return StyleImageOrientation::FromImage; 2871 } 2872 2873 return aOrientation; 2874 } 2875 2876 //----------------------- 2877 // nsStyleContent 2878 // 2879 2880 nsStyleContent::nsStyleContent() : mContent(StyleContent::Normal()) { 2881 MOZ_COUNT_CTOR(nsStyleContent); 2882 } 2883 2884 nsStyleContent::nsStyleContent(const nsStyleContent& aSource) 2885 : mContent(aSource.mContent), 2886 mCounterIncrement(aSource.mCounterIncrement), 2887 mCounterReset(aSource.mCounterReset), 2888 mCounterSet(aSource.mCounterSet) { 2889 MOZ_COUNT_CTOR(nsStyleContent); 2890 } 2891 2892 nsChangeHint nsStyleContent::CalcDifference( 2893 const nsStyleContent& aNewData) const { 2894 // Unfortunately we need to reframe even if the content lengths are the same; 2895 // a simple reflow will not pick up different text or different image URLs, 2896 // since we set all that up in the CSSFrameConstructor 2897 if (mContent != aNewData.mContent || 2898 mCounterIncrement != aNewData.mCounterIncrement || 2899 mCounterReset != aNewData.mCounterReset || 2900 mCounterSet != aNewData.mCounterSet) { 2901 return nsChangeHint_ReconstructFrame; 2902 } 2903 2904 return nsChangeHint(0); 2905 } 2906 2907 void nsStyleContent::TriggerImageLoads(Document& aDoc, 2908 const nsStyleContent* aOld) { 2909 if (!mContent.IsItems()) { 2910 return; 2911 } 2912 2913 Span<const StyleContentItem> oldItems; 2914 if (aOld) { 2915 oldItems = aOld->NonAltContentItems(); 2916 } 2917 2918 auto items = NonAltContentItems(); 2919 for (size_t i = 0; i < items.Length(); ++i) { 2920 const auto& item = items[i]; 2921 if (!item.IsImage()) { 2922 continue; 2923 } 2924 const auto& image = item.AsImage(); 2925 const auto* oldImage = i < oldItems.Length() && oldItems[i].IsImage() 2926 ? &oldItems[i].AsImage() 2927 : nullptr; 2928 const_cast<StyleImage&>(image).ResolveImage(aDoc, oldImage); 2929 } 2930 } 2931 2932 // -------------------- 2933 // nsStyleTextReset 2934 // 2935 2936 nsStyleTextReset::nsStyleTextReset() 2937 : mTextDecorationLine(StyleTextDecorationLine::NONE), 2938 mTextDecorationStyle(StyleTextDecorationStyle::Solid), 2939 mUnicodeBidi(StyleUnicodeBidi::Normal), 2940 mInitialLetter{0, 0}, 2941 mTextDecorationColor(StyleColor::CurrentColor()), 2942 mTextDecorationThickness(StyleTextDecorationLength::Auto()), 2943 mTextDecorationInset(StyleTextDecorationInset::Length( 2944 StyleLength::Zero(), StyleLength::Zero())) { 2945 MOZ_COUNT_CTOR(nsStyleTextReset); 2946 } 2947 2948 nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource) 2949 : mTextOverflow(aSource.mTextOverflow), 2950 mTextDecorationLine(aSource.mTextDecorationLine), 2951 mTextDecorationStyle(aSource.mTextDecorationStyle), 2952 mUnicodeBidi(aSource.mUnicodeBidi), 2953 mInitialLetter(aSource.mInitialLetter), 2954 mTextDecorationColor(aSource.mTextDecorationColor), 2955 mTextDecorationThickness(aSource.mTextDecorationThickness), 2956 mTextDecorationInset(aSource.mTextDecorationInset) { 2957 MOZ_COUNT_CTOR(nsStyleTextReset); 2958 } 2959 2960 nsChangeHint nsStyleTextReset::CalcDifference( 2961 const nsStyleTextReset& aNewData) const { 2962 if (mUnicodeBidi != aNewData.mUnicodeBidi || 2963 mInitialLetter != aNewData.mInitialLetter) { 2964 return NS_STYLE_HINT_REFLOW; 2965 } 2966 2967 if (mTextDecorationLine != aNewData.mTextDecorationLine || 2968 mTextDecorationStyle != aNewData.mTextDecorationStyle || 2969 mTextDecorationThickness != aNewData.mTextDecorationThickness || 2970 mTextDecorationInset != aNewData.mTextDecorationInset) { 2971 // Changes to our text-decoration line can impact our overflow area & 2972 // also our descendants' overflow areas (particularly for text-frame 2973 // descendants). So, we update those areas & trigger a repaint. 2974 return nsChangeHint_RepaintFrame | nsChangeHint_UpdateSubtreeOverflow | 2975 nsChangeHint_SchedulePaint; 2976 } 2977 2978 // Repaint for decoration color changes or text-overflow. 2979 if (mTextDecorationColor != aNewData.mTextDecorationColor || 2980 mTextOverflow != aNewData.mTextOverflow) { 2981 return nsChangeHint_RepaintFrame; 2982 } 2983 2984 return nsChangeHint(0); 2985 } 2986 2987 // -------------------- 2988 // nsStyleText 2989 // 2990 2991 static StyleAbsoluteColor DefaultColor(const Document& aDocument) { 2992 return StyleAbsoluteColor::FromColor( 2993 PreferenceSheet::PrefsFor(aDocument) 2994 .ColorsFor(aDocument.DefaultColorScheme()) 2995 .mDefault); 2996 } 2997 2998 nsStyleText::nsStyleText(const Document& aDocument) 2999 : mColor(DefaultColor(aDocument)), 3000 mForcedColorAdjust(StyleForcedColorAdjust::Auto), 3001 mTextTransform(StyleTextTransform::NONE), 3002 mTextAlign(StyleTextAlign::Start), 3003 mTextAlignLast(StyleTextAlignLast::Auto), 3004 mTextJustify(StyleTextJustify::Auto), 3005 mHyphens(StyleHyphens::Manual), 3006 mRubyAlign(StyleRubyAlign::SpaceAround), 3007 mRubyPosition(StyleRubyPosition::AlternateOver), 3008 mTextSizeAdjust(StyleTextSizeAdjust::Auto), 3009 mTextCombineUpright(StyleTextCombineUpright::None), 3010 mMozControlCharacterVisibility( 3011 StaticPrefs::layout_css_control_characters_visible() 3012 ? StyleMozControlCharacterVisibility::Visible 3013 : StyleMozControlCharacterVisibility::Hidden), 3014 mTextEmphasisPosition(StyleTextEmphasisPosition::AUTO), 3015 mTextRendering(StyleTextRendering::Auto), 3016 mTextEmphasisColor(StyleColor::CurrentColor()), 3017 mWebkitTextFillColor(StyleColor::CurrentColor()), 3018 mWebkitTextStrokeColor(StyleColor::CurrentColor()), 3019 mTabSize(StyleNonNegativeLengthOrNumber::Number(8.f)), 3020 mWordSpacing(LengthPercentage::Zero()), 3021 mLetterSpacing(LengthPercentage::Zero()), 3022 mTextUnderlineOffset(LengthPercentageOrAuto::Auto()), 3023 mTextDecorationSkipInk(StyleTextDecorationSkipInk::Auto), 3024 mTextUnderlinePosition(StyleTextUnderlinePosition::AUTO), 3025 mWebkitTextStrokeWidth(0), 3026 mTextEmphasisStyle(StyleTextEmphasisStyle::None()) { 3027 MOZ_COUNT_CTOR(nsStyleText); 3028 } 3029 3030 nsStyleText::nsStyleText(const nsStyleText& aSource) 3031 : mColor(aSource.mColor), 3032 mForcedColorAdjust(aSource.mForcedColorAdjust), 3033 mTextTransform(aSource.mTextTransform), 3034 mTextAlign(aSource.mTextAlign), 3035 mTextAlignLast(aSource.mTextAlignLast), 3036 mTextJustify(aSource.mTextJustify), 3037 mWhiteSpaceCollapse(aSource.mWhiteSpaceCollapse), 3038 mTextWrapMode(aSource.mTextWrapMode), 3039 mLineBreak(aSource.mLineBreak), 3040 mWordBreak(aSource.mWordBreak), 3041 mOverflowWrap(aSource.mOverflowWrap), 3042 mTextAutospace(aSource.mTextAutospace), 3043 mHyphens(aSource.mHyphens), 3044 mRubyAlign(aSource.mRubyAlign), 3045 mRubyPosition(aSource.mRubyPosition), 3046 mTextSizeAdjust(aSource.mTextSizeAdjust), 3047 mTextCombineUpright(aSource.mTextCombineUpright), 3048 mMozControlCharacterVisibility(aSource.mMozControlCharacterVisibility), 3049 mTextEmphasisPosition(aSource.mTextEmphasisPosition), 3050 mTextRendering(aSource.mTextRendering), 3051 mTextEmphasisColor(aSource.mTextEmphasisColor), 3052 mWebkitTextFillColor(aSource.mWebkitTextFillColor), 3053 mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor), 3054 mTabSize(aSource.mTabSize), 3055 mWordSpacing(aSource.mWordSpacing), 3056 mLetterSpacing(aSource.mLetterSpacing), 3057 mTextIndent(aSource.mTextIndent), 3058 mTextUnderlineOffset(aSource.mTextUnderlineOffset), 3059 mTextDecorationSkipInk(aSource.mTextDecorationSkipInk), 3060 mTextUnderlinePosition(aSource.mTextUnderlinePosition), 3061 mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth), 3062 mTextShadow(aSource.mTextShadow), 3063 mTextEmphasisStyle(aSource.mTextEmphasisStyle), 3064 mHyphenateCharacter(aSource.mHyphenateCharacter), 3065 mHyphenateLimitChars(aSource.mHyphenateLimitChars), 3066 mWebkitTextSecurity(aSource.mWebkitTextSecurity), 3067 mTextWrapStyle(aSource.mTextWrapStyle) { 3068 MOZ_COUNT_CTOR(nsStyleText); 3069 } 3070 3071 nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const { 3072 if (WhiteSpaceOrNewlineIsSignificant() != 3073 aNewData.WhiteSpaceOrNewlineIsSignificant()) { 3074 // This may require construction of suppressed text frames 3075 return nsChangeHint_ReconstructFrame; 3076 } 3077 3078 if (mTextCombineUpright != aNewData.mTextCombineUpright || 3079 mMozControlCharacterVisibility != 3080 aNewData.mMozControlCharacterVisibility) { 3081 return nsChangeHint_ReconstructFrame; 3082 } 3083 3084 if ((mTextAlign != aNewData.mTextAlign) || 3085 (mTextAlignLast != aNewData.mTextAlignLast) || 3086 (mTextTransform != aNewData.mTextTransform) || 3087 (mWhiteSpaceCollapse != aNewData.mWhiteSpaceCollapse) || 3088 (mTextWrapMode != aNewData.mTextWrapMode) || 3089 (mLineBreak != aNewData.mLineBreak) || 3090 (mWordBreak != aNewData.mWordBreak) || 3091 (mOverflowWrap != aNewData.mOverflowWrap) || 3092 (mHyphens != aNewData.mHyphens) || (mRubyAlign != aNewData.mRubyAlign) || 3093 (mRubyPosition != aNewData.mRubyPosition) || 3094 (mTextSizeAdjust != aNewData.mTextSizeAdjust) || 3095 (mLetterSpacing != aNewData.mLetterSpacing) || 3096 (mTextIndent != aNewData.mTextIndent) || 3097 (mTextJustify != aNewData.mTextJustify) || 3098 (mWordSpacing != aNewData.mWordSpacing) || 3099 (mTabSize != aNewData.mTabSize) || 3100 (mHyphenateCharacter != aNewData.mHyphenateCharacter) || 3101 (mHyphenateLimitChars != aNewData.mHyphenateLimitChars) || 3102 (mWebkitTextSecurity != aNewData.mWebkitTextSecurity) || 3103 (mTextWrapStyle != aNewData.mTextWrapStyle) || 3104 (mTextAutospace != aNewData.mTextAutospace)) { 3105 return NS_STYLE_HINT_REFLOW; 3106 } 3107 3108 if (HasEffectiveTextEmphasis() != aNewData.HasEffectiveTextEmphasis() || 3109 (HasEffectiveTextEmphasis() && 3110 mTextEmphasisPosition != aNewData.mTextEmphasisPosition)) { 3111 // Text emphasis position change could affect line height calculation. 3112 return nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; 3113 } 3114 3115 nsChangeHint hint = nsChangeHint(0); 3116 3117 // text-rendering changes require a reflow since they change SVG 3118 // frames' rects. 3119 if (mTextRendering != aNewData.mTextRendering) { 3120 hint |= nsChangeHint_NeedReflow | nsChangeHint_RepaintFrame; 3121 } 3122 3123 if (mTextShadow != aNewData.mTextShadow || 3124 mTextEmphasisStyle != aNewData.mTextEmphasisStyle || 3125 mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth || 3126 mTextUnderlineOffset != aNewData.mTextUnderlineOffset || 3127 mTextDecorationSkipInk != aNewData.mTextDecorationSkipInk || 3128 mTextUnderlinePosition != aNewData.mTextUnderlinePosition) { 3129 hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint | 3130 nsChangeHint_RepaintFrame; 3131 3132 // We don't add any other hints below. 3133 return hint; 3134 } 3135 3136 if (mColor != aNewData.mColor) { 3137 hint |= nsChangeHint_RepaintFrame; 3138 } 3139 3140 if (mTextEmphasisColor != aNewData.mTextEmphasisColor || 3141 mWebkitTextFillColor != aNewData.mWebkitTextFillColor || 3142 mWebkitTextStrokeColor != aNewData.mWebkitTextStrokeColor) { 3143 hint |= nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame; 3144 } 3145 3146 if (hint) { 3147 return hint; 3148 } 3149 3150 if (mTextEmphasisPosition != aNewData.mTextEmphasisPosition || 3151 mForcedColorAdjust != aNewData.mForcedColorAdjust) { 3152 return nsChangeHint_NeutralChange; 3153 } 3154 3155 return nsChangeHint(0); 3156 } 3157 3158 LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM, 3159 const nsAtom* aLanguage) const { 3160 mozilla::Side side; 3161 if (mTextEmphasisPosition & StyleTextEmphasisPosition::AUTO) { 3162 // 'auto' resolves to 'under right' for Chinese, 'over right' otherwise. 3163 if (aWM.IsVertical()) { 3164 side = eSideRight; 3165 } else { 3166 if (nsStyleUtil::MatchesLanguagePrefix(aLanguage, u"zh")) { 3167 side = eSideBottom; 3168 } else { 3169 side = eSideTop; 3170 } 3171 } 3172 } else { 3173 if (aWM.IsVertical()) { 3174 side = mTextEmphasisPosition & StyleTextEmphasisPosition::LEFT 3175 ? eSideLeft 3176 : eSideRight; 3177 } else { 3178 side = mTextEmphasisPosition & StyleTextEmphasisPosition::OVER 3179 ? eSideTop 3180 : eSideBottom; 3181 } 3182 } 3183 3184 LogicalSide result = aWM.LogicalSideForPhysicalSide(side); 3185 MOZ_ASSERT(IsBlock(result)); 3186 return result; 3187 } 3188 3189 //----------------------- 3190 // nsStyleUI 3191 // 3192 3193 nsStyleUI::nsStyleUI() 3194 : mInert(StyleInert::None), 3195 mMozTheme(StyleMozTheme::Auto), 3196 mUserFocus(StyleUserFocus::Normal), 3197 mPointerEvents(StylePointerEvents::Auto), 3198 mCursor{{}, StyleCursorKind::Auto}, 3199 mAccentColor(StyleColorOrAuto::Auto()), 3200 mCaretColor(StyleColorOrAuto::Auto()), 3201 mScrollbarColor(StyleScrollbarColor::Auto()), 3202 mColorScheme(StyleColorScheme{{}, {}}) { 3203 MOZ_COUNT_CTOR(nsStyleUI); 3204 } 3205 3206 nsStyleUI::nsStyleUI(const nsStyleUI& aSource) 3207 : mInert(aSource.mInert), 3208 mMozTheme(aSource.mMozTheme), 3209 mUserFocus(aSource.mUserFocus), 3210 mPointerEvents(aSource.mPointerEvents), 3211 mCursor(aSource.mCursor), 3212 mAccentColor(aSource.mAccentColor), 3213 mCaretColor(aSource.mCaretColor), 3214 mScrollbarColor(aSource.mScrollbarColor), 3215 mColorScheme(aSource.mColorScheme) { 3216 MOZ_COUNT_CTOR(nsStyleUI); 3217 } 3218 3219 void nsStyleUI::TriggerImageLoads(Document& aDocument, 3220 const nsStyleUI* aOldStyle) { 3221 MOZ_ASSERT(NS_IsMainThread()); 3222 3223 auto cursorImages = mCursor.images.AsSpan(); 3224 auto oldCursorImages = aOldStyle ? aOldStyle->mCursor.images.AsSpan() 3225 : Span<const StyleCursorImage>(); 3226 for (size_t i = 0; i < cursorImages.Length(); ++i) { 3227 const auto& cursor = cursorImages[i]; 3228 const auto* oldCursorImage = 3229 oldCursorImages.Length() > i ? &oldCursorImages[i].image : nullptr; 3230 const_cast<StyleCursorImage&>(cursor).image.ResolveImage(aDocument, 3231 oldCursorImage); 3232 } 3233 } 3234 3235 nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const { 3236 // SVGGeometryFrame's mRect depends on stroke _and_ on the value of 3237 // pointer-events. See SVGGeometryFrame::ReflowSVG's use of GetHitTestFlags. 3238 // (Only a reflow, no visual change.) 3239 // 3240 // pointer-events changes can change event regions overrides on layers and 3241 // so needs a repaint. 3242 const auto kPointerEventsHint = 3243 nsChangeHint_NeedReflow | nsChangeHint_SchedulePaint; 3244 3245 nsChangeHint hint = nsChangeHint(0); 3246 if (mCursor != aNewData.mCursor) { 3247 hint |= nsChangeHint_UpdateCursor; 3248 } 3249 3250 if (mPointerEvents != aNewData.mPointerEvents) { 3251 hint |= kPointerEventsHint; 3252 } 3253 3254 if (mInert != aNewData.mInert) { 3255 // inert affects pointer-events, user-select, user-focus. 3256 // Do the union of all them (minus 3257 // nsChangeHint_NeutralChange which isn't needed if there's any other hint). 3258 hint |= NS_STYLE_HINT_VISUAL | kPointerEventsHint; 3259 } 3260 3261 if (mUserFocus != aNewData.mUserFocus) { 3262 hint |= nsChangeHint_NeutralChange; 3263 } 3264 3265 if (mCaretColor != aNewData.mCaretColor || 3266 mAccentColor != aNewData.mAccentColor || 3267 mScrollbarColor != aNewData.mScrollbarColor || 3268 mMozTheme != aNewData.mMozTheme || 3269 mColorScheme != aNewData.mColorScheme) { 3270 hint |= nsChangeHint_RepaintFrame; 3271 } 3272 3273 return hint; 3274 } 3275 3276 //----------------------- 3277 // nsStyleUIReset 3278 // 3279 3280 nsStyleUIReset::nsStyleUIReset() 3281 : mUserSelect(StyleUserSelect::Auto), 3282 mScrollbarWidth(StyleScrollbarWidth::Auto), 3283 mMozForceBrokenImageIcon(false), 3284 mMozSubtreeHiddenOnlyVisually(false), 3285 mIMEMode(StyleImeMode::Auto), 3286 mWindowDragging(StyleWindowDragging::Default), 3287 mWindowShadow(StyleWindowShadow::Auto), 3288 mWindowOpacity(1.0), 3289 mMozWindowInputRegionMargin(StyleLength::Zero()), 3290 mTransitions( 3291 nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT), 3292 mTransitionTimingFunctionCount(1), 3293 mTransitionDurationCount(1), 3294 mTransitionDelayCount(1), 3295 mTransitionPropertyCount(1), 3296 mTransitionBehaviorCount(1), 3297 mAnimations( 3298 nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT), 3299 mAnimationTimingFunctionCount(1), 3300 mAnimationDurationCount(1), 3301 mAnimationDelayCount(1), 3302 mAnimationNameCount(1), 3303 mAnimationDirectionCount(1), 3304 mAnimationFillModeCount(1), 3305 mAnimationPlayStateCount(1), 3306 mAnimationIterationCountCount(1), 3307 mAnimationCompositionCount(1), 3308 mAnimationTimelineCount(1), 3309 mScrollTimelines( 3310 nsStyleAutoArray<StyleScrollTimeline>::WITH_SINGLE_INITIAL_ELEMENT), 3311 mScrollTimelineNameCount(1), 3312 mScrollTimelineAxisCount(1), 3313 mViewTimelines( 3314 nsStyleAutoArray<StyleViewTimeline>::WITH_SINGLE_INITIAL_ELEMENT), 3315 mViewTimelineNameCount(1), 3316 mViewTimelineAxisCount(1), 3317 mViewTimelineInsetCount(1), 3318 mFieldSizing(StyleFieldSizing::Fixed), 3319 mViewTransitionName(StyleViewTransitionName::None()) { 3320 MOZ_COUNT_CTOR(nsStyleUIReset); 3321 } 3322 3323 nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource) 3324 : mUserSelect(aSource.mUserSelect), 3325 mScrollbarWidth(aSource.mScrollbarWidth), 3326 mMozForceBrokenImageIcon(aSource.mMozForceBrokenImageIcon), 3327 mMozSubtreeHiddenOnlyVisually(aSource.mMozSubtreeHiddenOnlyVisually), 3328 mIMEMode(aSource.mIMEMode), 3329 mWindowDragging(aSource.mWindowDragging), 3330 mWindowShadow(aSource.mWindowShadow), 3331 mWindowOpacity(aSource.mWindowOpacity), 3332 mMozWindowInputRegionMargin(aSource.mMozWindowInputRegionMargin), 3333 mMozWindowTransform(aSource.mMozWindowTransform), 3334 mTransitions(aSource.mTransitions.Clone()), 3335 mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount), 3336 mTransitionDurationCount(aSource.mTransitionDurationCount), 3337 mTransitionDelayCount(aSource.mTransitionDelayCount), 3338 mTransitionPropertyCount(aSource.mTransitionPropertyCount), 3339 mTransitionBehaviorCount(aSource.mTransitionBehaviorCount), 3340 mAnimations(aSource.mAnimations.Clone()), 3341 mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount), 3342 mAnimationDurationCount(aSource.mAnimationDurationCount), 3343 mAnimationDelayCount(aSource.mAnimationDelayCount), 3344 mAnimationNameCount(aSource.mAnimationNameCount), 3345 mAnimationDirectionCount(aSource.mAnimationDirectionCount), 3346 mAnimationFillModeCount(aSource.mAnimationFillModeCount), 3347 mAnimationPlayStateCount(aSource.mAnimationPlayStateCount), 3348 mAnimationIterationCountCount(aSource.mAnimationIterationCountCount), 3349 mAnimationCompositionCount(aSource.mAnimationCompositionCount), 3350 mAnimationTimelineCount(aSource.mAnimationTimelineCount), 3351 mScrollTimelines(aSource.mScrollTimelines.Clone()), 3352 mScrollTimelineNameCount(aSource.mScrollTimelineNameCount), 3353 mScrollTimelineAxisCount(aSource.mScrollTimelineAxisCount), 3354 mViewTimelines(aSource.mViewTimelines.Clone()), 3355 mViewTimelineNameCount(aSource.mViewTimelineNameCount), 3356 mViewTimelineAxisCount(aSource.mViewTimelineAxisCount), 3357 mViewTimelineInsetCount(aSource.mViewTimelineInsetCount), 3358 mFieldSizing(aSource.mFieldSizing), 3359 mViewTransitionName(aSource.mViewTransitionName), 3360 mViewTransitionClass(aSource.mViewTransitionClass) { 3361 MOZ_COUNT_CTOR(nsStyleUIReset); 3362 } 3363 3364 nsChangeHint nsStyleUIReset::CalcDifference( 3365 const nsStyleUIReset& aNewData) const { 3366 nsChangeHint hint = nsChangeHint(0); 3367 3368 if (mMozForceBrokenImageIcon != aNewData.mMozForceBrokenImageIcon) { 3369 hint |= nsChangeHint_ReconstructFrame; 3370 } 3371 if (mMozSubtreeHiddenOnlyVisually != aNewData.mMozSubtreeHiddenOnlyVisually) { 3372 hint |= nsChangeHint_RepaintFrame; 3373 } 3374 if (mFieldSizing != aNewData.mFieldSizing) { 3375 hint |= nsChangeHint_NeutralChange; 3376 } 3377 if (mScrollbarWidth != aNewData.mScrollbarWidth) { 3378 // For scrollbar-width change, we need some special handling similar 3379 // to overflow properties. Specifically, we may need to reconstruct 3380 // the scrollbar or force reflow of the viewport scrollbar. 3381 hint |= nsChangeHint_ScrollbarChange; 3382 } 3383 if (mWindowShadow != aNewData.mWindowShadow) { 3384 // We really need just an nsChangeHint_SyncFrameView, except 3385 // on an ancestor of the frame, so we get that by doing a 3386 // reflow. 3387 hint |= NS_STYLE_HINT_REFLOW; 3388 } 3389 if (mUserSelect != aNewData.mUserSelect) { 3390 hint |= NS_STYLE_HINT_VISUAL; 3391 } 3392 3393 if (mWindowDragging != aNewData.mWindowDragging) { 3394 hint |= nsChangeHint_SchedulePaint; 3395 } 3396 3397 if (mViewTransitionName != aNewData.mViewTransitionName) { 3398 if (HasViewTransitionName() != aNewData.HasViewTransitionName()) { 3399 hint |= nsChangeHint_RepaintFrame; 3400 } else { 3401 hint |= nsChangeHint_NeutralChange; 3402 } 3403 } 3404 3405 if (mViewTransitionClass != aNewData.mViewTransitionClass) { 3406 hint |= nsChangeHint_NeutralChange; 3407 } 3408 3409 if (!hint && 3410 (mTransitions != aNewData.mTransitions || 3411 mTransitionTimingFunctionCount != 3412 aNewData.mTransitionTimingFunctionCount || 3413 mTransitionDurationCount != aNewData.mTransitionDurationCount || 3414 mTransitionDelayCount != aNewData.mTransitionDelayCount || 3415 mTransitionPropertyCount != aNewData.mTransitionPropertyCount || 3416 mTransitionBehaviorCount != aNewData.mTransitionBehaviorCount || 3417 mAnimations != aNewData.mAnimations || 3418 mAnimationTimingFunctionCount != 3419 aNewData.mAnimationTimingFunctionCount || 3420 mAnimationDurationCount != aNewData.mAnimationDurationCount || 3421 mAnimationDelayCount != aNewData.mAnimationDelayCount || 3422 mAnimationNameCount != aNewData.mAnimationNameCount || 3423 mAnimationDirectionCount != aNewData.mAnimationDirectionCount || 3424 mAnimationFillModeCount != aNewData.mAnimationFillModeCount || 3425 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount || 3426 mAnimationIterationCountCount != 3427 aNewData.mAnimationIterationCountCount || 3428 mAnimationCompositionCount != aNewData.mAnimationCompositionCount || 3429 mAnimationTimelineCount != aNewData.mAnimationTimelineCount || 3430 mIMEMode != aNewData.mIMEMode || 3431 mWindowOpacity != aNewData.mWindowOpacity || 3432 mMozWindowInputRegionMargin != aNewData.mMozWindowInputRegionMargin || 3433 mMozWindowTransform != aNewData.mMozWindowTransform || 3434 mScrollTimelines != aNewData.mScrollTimelines || 3435 mScrollTimelineNameCount != aNewData.mScrollTimelineNameCount || 3436 mScrollTimelineAxisCount != aNewData.mScrollTimelineAxisCount || 3437 mViewTimelines != aNewData.mViewTimelines || 3438 mViewTimelineNameCount != aNewData.mViewTimelineNameCount || 3439 mViewTimelineAxisCount != aNewData.mViewTimelineAxisCount || 3440 mViewTimelineInsetCount != aNewData.mViewTimelineInsetCount)) { 3441 hint |= nsChangeHint_NeutralChange; 3442 } 3443 3444 return hint; 3445 } 3446 3447 StyleScrollbarWidth nsStyleUIReset::ScrollbarWidth() const { 3448 if (MOZ_UNLIKELY(StaticPrefs::layout_css_scrollbar_width_thin_disabled())) { 3449 if (mScrollbarWidth == StyleScrollbarWidth::Thin) { 3450 return StyleScrollbarWidth::Auto; 3451 } 3452 } 3453 return mScrollbarWidth; 3454 } 3455 3456 //----------------------- 3457 // nsStyleEffects 3458 // 3459 3460 nsStyleEffects::nsStyleEffects() 3461 : mClip(StyleClipRectOrAuto::Auto()), 3462 mOpacity(1.0f), 3463 mMixBlendMode(StyleBlend::Normal) { 3464 MOZ_COUNT_CTOR(nsStyleEffects); 3465 } 3466 3467 nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource) 3468 : mFilters(aSource.mFilters), 3469 mBoxShadow(aSource.mBoxShadow), 3470 mBackdropFilters(aSource.mBackdropFilters), 3471 mClip(aSource.mClip), 3472 mOpacity(aSource.mOpacity), 3473 mMixBlendMode(aSource.mMixBlendMode) { 3474 MOZ_COUNT_CTOR(nsStyleEffects); 3475 } 3476 3477 static bool AnyAutonessChanged(const StyleClipRectOrAuto& aOld, 3478 const StyleClipRectOrAuto& aNew) { 3479 if (aOld.IsAuto() != aNew.IsAuto()) { 3480 return true; 3481 } 3482 if (aOld.IsAuto()) { 3483 return false; 3484 } 3485 const auto& oldRect = aOld.AsRect(); 3486 const auto& newRect = aNew.AsRect(); 3487 return oldRect.top.IsAuto() != newRect.top.IsAuto() || 3488 oldRect.right.IsAuto() != newRect.right.IsAuto() || 3489 oldRect.bottom.IsAuto() != newRect.bottom.IsAuto() || 3490 oldRect.left.IsAuto() != newRect.left.IsAuto(); 3491 } 3492 3493 nsChangeHint nsStyleEffects::CalcDifference( 3494 const nsStyleEffects& aNewData) const { 3495 nsChangeHint hint = nsChangeHint(0); 3496 3497 if (mBoxShadow != aNewData.mBoxShadow) { 3498 // Update overflow regions & trigger DLBI to be sure it's noticed. 3499 // Also request a repaint, since it's possible that only the color 3500 // of the shadow is changing (and UpdateOverflow/SchedulePaint won't 3501 // repaint for that, since they won't know what needs invalidating.) 3502 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint | 3503 nsChangeHint_RepaintFrame; 3504 } 3505 3506 if (AnyAutonessChanged(mClip, aNewData.mClip)) { 3507 hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame; 3508 } else if (mClip != aNewData.mClip) { 3509 // If the clip has changed, we just need to update overflow areas. DLBI 3510 // will handle the invalidation. 3511 hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint; 3512 } 3513 3514 if (mOpacity != aNewData.mOpacity) { 3515 hint |= nsChangeHint_UpdateOpacityLayer; 3516 3517 // If we're going from the optimized >=0.99 opacity value to 1.0 or back, 3518 // then repaint the frame because DLBI will not catch the invalidation. 3519 // Otherwise, just update the opacity layer. 3520 if ((mOpacity >= 0.99f && mOpacity < 1.0f && aNewData.mOpacity == 1.0f) || 3521 (aNewData.mOpacity >= 0.99f && aNewData.mOpacity < 1.0f && 3522 mOpacity == 1.0f)) { 3523 hint |= nsChangeHint_RepaintFrame; 3524 } else { 3525 if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) { 3526 hint |= nsChangeHint_UpdateUsesOpacity; 3527 } 3528 } 3529 } 3530 3531 if (HasFilters() != aNewData.HasFilters()) { 3532 // A change from/to being a containing block for position:fixed. 3533 hint |= nsChangeHint_UpdateContainingBlock; 3534 } 3535 3536 if (mFilters != aNewData.mFilters) { 3537 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame | 3538 nsChangeHint_UpdateOverflow; 3539 } 3540 3541 if (mMixBlendMode != aNewData.mMixBlendMode) { 3542 hint |= nsChangeHint_RepaintFrame; 3543 } 3544 3545 if (HasBackdropFilters() != aNewData.HasBackdropFilters()) { 3546 // A change from/to being a containing block for position:fixed. 3547 hint |= nsChangeHint_UpdateContainingBlock; 3548 } 3549 3550 if (mBackdropFilters != aNewData.mBackdropFilters) { 3551 hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame; 3552 } 3553 3554 return hint; 3555 } 3556 3557 static bool TransformOperationHasPercent(const StyleTransformOperation& aOp) { 3558 switch (aOp.tag) { 3559 case StyleTransformOperation::Tag::TranslateX: 3560 return aOp.AsTranslateX().HasPercent(); 3561 case StyleTransformOperation::Tag::TranslateY: 3562 return aOp.AsTranslateY().HasPercent(); 3563 case StyleTransformOperation::Tag::TranslateZ: 3564 return false; 3565 case StyleTransformOperation::Tag::Translate3D: { 3566 const auto& translate = aOp.AsTranslate3D(); 3567 // NOTE(emilio): z translation is a `<length>`, so can't have percentages. 3568 return translate._0.HasPercent() || translate._1.HasPercent(); 3569 } 3570 case StyleTransformOperation::Tag::Translate: { 3571 const auto& translate = aOp.AsTranslate(); 3572 return translate._0.HasPercent() || translate._1.HasPercent(); 3573 } 3574 case StyleTransformOperation::Tag::AccumulateMatrix: { 3575 const auto& accum = aOp.AsAccumulateMatrix(); 3576 return accum.from_list.HasPercent() || accum.to_list.HasPercent(); 3577 } 3578 case StyleTransformOperation::Tag::InterpolateMatrix: { 3579 const auto& interpolate = aOp.AsInterpolateMatrix(); 3580 return interpolate.from_list.HasPercent() || 3581 interpolate.to_list.HasPercent(); 3582 } 3583 case StyleTransformOperation::Tag::Perspective: 3584 case StyleTransformOperation::Tag::RotateX: 3585 case StyleTransformOperation::Tag::RotateY: 3586 case StyleTransformOperation::Tag::RotateZ: 3587 case StyleTransformOperation::Tag::Rotate: 3588 case StyleTransformOperation::Tag::Rotate3D: 3589 case StyleTransformOperation::Tag::SkewX: 3590 case StyleTransformOperation::Tag::SkewY: 3591 case StyleTransformOperation::Tag::Skew: 3592 case StyleTransformOperation::Tag::ScaleX: 3593 case StyleTransformOperation::Tag::ScaleY: 3594 case StyleTransformOperation::Tag::ScaleZ: 3595 case StyleTransformOperation::Tag::Scale: 3596 case StyleTransformOperation::Tag::Scale3D: 3597 case StyleTransformOperation::Tag::Matrix: 3598 case StyleTransformOperation::Tag::Matrix3D: 3599 return false; 3600 default: 3601 MOZ_ASSERT_UNREACHABLE("Unknown transform operation"); 3602 return false; 3603 } 3604 } 3605 3606 template <> 3607 bool StyleTransform::HasPercent() const { 3608 for (const auto& op : Operations()) { 3609 if (TransformOperationHasPercent(op)) { 3610 return true; 3611 } 3612 } 3613 return false; 3614 } 3615 3616 template <> 3617 void StyleCalcNode::ScaleLengthsBy(float aScale) { 3618 auto ScaleNode = [aScale](const StyleCalcNode& aNode) { 3619 // This const_cast could be removed by generating more mut-casts, if 3620 // needed. 3621 const_cast<StyleCalcNode&>(aNode).ScaleLengthsBy(aScale); 3622 }; 3623 3624 switch (tag) { 3625 case Tag::Leaf: { 3626 const auto& leaf = AsLeaf(); 3627 if (leaf.IsLength()) { 3628 // This const_cast could be removed by generating more mut-casts, if 3629 // needed. 3630 const_cast<Length&>(leaf.AsLength()).ScaleBy(aScale); 3631 } 3632 break; 3633 } 3634 case Tag::Clamp: { 3635 const auto& clamp = AsClamp(); 3636 ScaleNode(*clamp.min); 3637 ScaleNode(*clamp.center); 3638 ScaleNode(*clamp.max); 3639 break; 3640 } 3641 case Tag::Round: { 3642 const auto& round = AsRound(); 3643 ScaleNode(*round.value); 3644 ScaleNode(*round.step); 3645 break; 3646 } 3647 case Tag::ModRem: { 3648 const auto& modRem = AsModRem(); 3649 ScaleNode(*modRem.dividend); 3650 ScaleNode(*modRem.divisor); 3651 break; 3652 } 3653 case Tag::MinMax: { 3654 for (const auto& child : AsMinMax()._0.AsSpan()) { 3655 ScaleNode(child); 3656 } 3657 break; 3658 } 3659 case Tag::Sum: { 3660 for (const auto& child : AsSum().AsSpan()) { 3661 ScaleNode(child); 3662 } 3663 break; 3664 } 3665 case Tag::Product: { 3666 for (const auto& child : AsProduct().AsSpan()) { 3667 ScaleNode(child); 3668 } 3669 break; 3670 } 3671 case Tag::Negate: { 3672 const auto& negate = AsNegate(); 3673 ScaleNode(*negate); 3674 break; 3675 } 3676 case Tag::Invert: { 3677 const auto& invert = AsInvert(); 3678 ScaleNode(*invert); 3679 break; 3680 } 3681 case Tag::Hypot: { 3682 for (const auto& child : AsHypot().AsSpan()) { 3683 ScaleNode(child); 3684 } 3685 break; 3686 } 3687 case Tag::Abs: { 3688 const auto& abs = AsAbs(); 3689 ScaleNode(*abs); 3690 break; 3691 } 3692 case Tag::Sign: { 3693 const auto& sign = AsSign(); 3694 ScaleNode(*sign); 3695 break; 3696 } 3697 case Tag::Anchor: { 3698 MOZ_ASSERT_UNREACHABLE("Unresolved anchor() function"); 3699 break; 3700 } 3701 case Tag::AnchorSize: { 3702 MOZ_ASSERT_UNREACHABLE("Unresolved anchor-size() function"); 3703 break; 3704 } 3705 } 3706 } 3707 3708 bool nsStyleDisplay::PrecludesSizeContainmentOrContentVisibilityWithFrame( 3709 const nsIFrame& aFrame) const { 3710 // The spec says that in the case of SVG, the contain property only applies 3711 // to <svg> elements that have an associated CSS layout box. 3712 // https://drafts.csswg.org/css-contain/#contain-property 3713 // Internal SVG elements do not use the standard CSS box model, and wouldn't 3714 // be affected by size containment. By disabling it we prevent them from 3715 // becoming query containers for size features. 3716 if (aFrame.HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { 3717 return true; 3718 } 3719 3720 // Note: The spec for size containment says it should have no effect 3721 // - on non-atomic, inline-level boxes. 3722 // - on internal ruby boxes. 3723 // - if inner display type is table. 3724 // - on internal table boxes. 3725 // https://drafts.csswg.org/css-contain/#containment-size 3726 bool isNonReplacedInline = aFrame.IsLineParticipant() && !aFrame.IsReplaced(); 3727 return isNonReplacedInline || IsInternalRubyDisplayType() || 3728 DisplayInside() == mozilla::StyleDisplayInside::Table || 3729 IsInnerTableStyle(); 3730 } 3731 3732 ContainSizeAxes nsStyleDisplay::GetContainSizeAxes( 3733 const nsIFrame& aFrame) const { 3734 // Short circuit for no containment whatsoever 3735 if (MOZ_LIKELY(!mEffectiveContainment)) { 3736 return ContainSizeAxes(false, false); 3737 } 3738 3739 if (PrecludesSizeContainmentOrContentVisibilityWithFrame(aFrame)) { 3740 return ContainSizeAxes(false, false); 3741 } 3742 3743 // https://drafts.csswg.org/css-contain-2/#content-visibility 3744 // If this content skips its content via content-visibility, it always has 3745 // size containment. 3746 if (MOZ_LIKELY(!(mEffectiveContainment & StyleContain::SIZE)) && 3747 MOZ_UNLIKELY(aFrame.HidesContent())) { 3748 return ContainSizeAxes(true, true); 3749 } 3750 3751 return ContainSizeAxes( 3752 static_cast<bool>(mEffectiveContainment & StyleContain::INLINE_SIZE), 3753 static_cast<bool>(mEffectiveContainment & StyleContain::BLOCK_SIZE)); 3754 } 3755 3756 StyleContentVisibility nsStyleDisplay::ContentVisibility( 3757 const nsIFrame& aFrame) const { 3758 if (MOZ_LIKELY(mContentVisibility == StyleContentVisibility::Visible)) { 3759 return StyleContentVisibility::Visible; 3760 } 3761 // content-visibility applies to elements for which size containment applies. 3762 // https://drafts.csswg.org/css-contain/#content-visibility 3763 if (PrecludesSizeContainmentOrContentVisibilityWithFrame(aFrame)) { 3764 return StyleContentVisibility::Visible; 3765 } 3766 // If we're in print/print-preview, or being used as an image, we should 3767 // always treat `auto` as `visible`. 3768 if (mContentVisibility == StyleContentVisibility::Auto && 3769 (aFrame.PresContext()->IsPrintingOrPrintPreview() || 3770 aFrame.PresContext()->Document()->IsBeingUsedAsImage())) { 3771 return StyleContentVisibility::Visible; 3772 } 3773 return mContentVisibility; 3774 } 3775 3776 static nscoord Resolve(const StyleContainIntrinsicSize& aSize, 3777 nscoord aNoneValue, const nsIFrame& aFrame, 3778 LogicalAxis aAxis) { 3779 if (aSize.IsNone()) { 3780 return aNoneValue; 3781 } 3782 if (aSize.IsLength()) { 3783 return aSize.AsLength().ToAppUnits(); 3784 } 3785 MOZ_ASSERT(aSize.HasAuto()); 3786 if (const auto* element = Element::FromNodeOrNull(aFrame.GetContent())) { 3787 Maybe<float> lastSize = aAxis == LogicalAxis::Block 3788 ? element->GetLastRememberedBSize() 3789 : element->GetLastRememberedISize(); 3790 if (lastSize && aFrame.HidesContent()) { 3791 return CSSPixel::ToAppUnits(*lastSize); 3792 } 3793 } 3794 if (aSize.IsAutoNone()) { 3795 return aNoneValue; 3796 } 3797 return aSize.AsAutoLength().ToAppUnits(); 3798 } 3799 3800 Maybe<nscoord> ContainSizeAxes::ContainIntrinsicBSize( 3801 const nsIFrame& aFrame, nscoord aNoneValue) const { 3802 if (!mBContained) { 3803 return Nothing(); 3804 } 3805 const StyleContainIntrinsicSize& bSize = 3806 aFrame.StylePosition()->ContainIntrinsicBSize(aFrame.GetWritingMode()); 3807 return Some(Resolve(bSize, aNoneValue, aFrame, LogicalAxis::Block)); 3808 } 3809 3810 Maybe<nscoord> ContainSizeAxes::ContainIntrinsicISize( 3811 const nsIFrame& aFrame, nscoord aNoneValue) const { 3812 if (!mIContained) { 3813 return Nothing(); 3814 } 3815 const StyleContainIntrinsicSize& iSize = 3816 aFrame.StylePosition()->ContainIntrinsicISize(aFrame.GetWritingMode()); 3817 return Some(Resolve(iSize, aNoneValue, aFrame, LogicalAxis::Inline)); 3818 } 3819 3820 nsSize ContainSizeAxes::ContainSize(const nsSize& aUncontainedSize, 3821 const nsIFrame& aFrame) const { 3822 if (!IsAny()) { 3823 return aUncontainedSize; 3824 } 3825 if (aFrame.GetWritingMode().IsVertical()) { 3826 return nsSize( 3827 ContainIntrinsicBSize(aFrame).valueOr(aUncontainedSize.Width()), 3828 ContainIntrinsicISize(aFrame).valueOr(aUncontainedSize.Height())); 3829 } 3830 return nsSize( 3831 ContainIntrinsicISize(aFrame).valueOr(aUncontainedSize.Width()), 3832 ContainIntrinsicBSize(aFrame).valueOr(aUncontainedSize.Height())); 3833 } 3834 3835 IntrinsicSize ContainSizeAxes::ContainIntrinsicSize( 3836 const IntrinsicSize& aUncontainedSize, const nsIFrame& aFrame) const { 3837 if (!IsAny()) { 3838 return aUncontainedSize; 3839 } 3840 IntrinsicSize result(aUncontainedSize); 3841 const auto wm = aFrame.GetWritingMode(); 3842 if (Maybe<nscoord> containBSize = ContainIntrinsicBSize(aFrame)) { 3843 result.BSize(wm) = containBSize; 3844 } 3845 if (Maybe<nscoord> containISize = ContainIntrinsicISize(aFrame)) { 3846 result.ISize(wm) = containISize; 3847 } 3848 return result; 3849 }