forked from mirrors/gecko-dev
		
	 a4b19fcd61
			
		
	
	
		a4b19fcd61
		
	
	
	
	
		
			
			--HG-- rename : browser/components/nsBrowserContentHandler.js => browser/components/BrowserContentHandler.jsm
		
			
				
	
	
		
			4170 lines
		
	
	
	
		
			145 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			4170 lines
		
	
	
	
		
			145 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * structs that contain the data provided by ComputedStyle, the
 | |
|  * internal API for computed style data for an element
 | |
|  */
 | |
| 
 | |
| #include "nsStyleStruct.h"
 | |
| #include "nsStyleStructInlines.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsString.h"
 | |
| #include "nsPresContext.h"
 | |
| #include "nsIAppShellService.h"
 | |
| #include "nsIWidget.h"
 | |
| #include "nsCRTGlue.h"
 | |
| #include "nsCSSProps.h"
 | |
| #include "nsDeviceContext.h"
 | |
| #include "nsStyleUtil.h"
 | |
| 
 | |
| #include "nsCOMPtr.h"
 | |
| 
 | |
| #include "nsBidiUtils.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| 
 | |
| #include "imgIRequest.h"
 | |
| #include "imgIContainer.h"
 | |
| #include "CounterStyleManager.h"
 | |
| 
 | |
| #include "mozilla/dom/AnimationEffectBinding.h"  // for PlaybackDirection
 | |
| #include "mozilla/dom/DocGroup.h"
 | |
| #include "mozilla/dom/ImageTracker.h"
 | |
| #include "mozilla/CORSMode.h"
 | |
| #include "mozilla/ClearOnShutdown.h"
 | |
| #include "mozilla/Likely.h"
 | |
| #include "nsIURI.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include <algorithm>
 | |
| #include "ImageLoader.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| /* static */ const int32_t nsStyleGridLine::kMinLine;
 | |
| /* static */ const int32_t nsStyleGridLine::kMaxLine;
 | |
| 
 | |
| static const nscoord kMediumBorderWidth = nsPresContext::CSSPixelsToAppUnits(3);
 | |
| 
 | |
| // We set the size limit of style structs to 504 bytes so that when they
 | |
| // are allocated by Servo side with Arc, the total size doesn't exceed
 | |
| // 512 bytes, which minimizes allocator slop.
 | |
| static constexpr size_t kStyleStructSizeLimit = 504;
 | |
| #define STYLE_STRUCT(name_)                                      \
 | |
|   static_assert(sizeof(nsStyle##name_) <= kStyleStructSizeLimit, \
 | |
|                 "nsStyle" #name_ " became larger than the size limit");
 | |
| #include "nsStyleStructList.h"
 | |
| #undef STYLE_STRUCT
 | |
| 
 | |
| static bool DefinitelyEqualURIs(const css::URLValue* aURI1,
 | |
|                                 const css::URLValue* aURI2) {
 | |
|   return aURI1 == aURI2 ||
 | |
|          (aURI1 && aURI2 && aURI1->DefinitelyEqualURIs(*aURI2));
 | |
| }
 | |
| 
 | |
| static bool DefinitelyEqualURIsAndPrincipal(const css::URLValue* aURI1,
 | |
|                                             const css::URLValue* aURI2) {
 | |
|   return aURI1 == aURI2 ||
 | |
|          (aURI1 && aURI2 && aURI1->DefinitelyEqualURIsAndPrincipal(*aURI2));
 | |
| }
 | |
| 
 | |
| static bool DefinitelyEqualImages(const nsStyleImageRequest* aRequest1,
 | |
|                                   const nsStyleImageRequest* aRequest2) {
 | |
|   if (aRequest1 == aRequest2) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (!aRequest1 || !aRequest2) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return aRequest1->DefinitelyEquals(*aRequest2);
 | |
| }
 | |
| 
 | |
| static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs);
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleFont
 | |
| //
 | |
| nsStyleFont::nsStyleFont(const nsStyleFont& aSrc)
 | |
|     : mFont(aSrc.mFont),
 | |
|       mSize(aSrc.mSize),
 | |
|       mFontSizeFactor(aSrc.mFontSizeFactor),
 | |
|       mFontSizeOffset(aSrc.mFontSizeOffset),
 | |
|       mFontSizeKeyword(aSrc.mFontSizeKeyword),
 | |
|       mGenericID(aSrc.mGenericID),
 | |
|       mScriptLevel(aSrc.mScriptLevel),
 | |
|       mMathVariant(aSrc.mMathVariant),
 | |
|       mMathDisplay(aSrc.mMathDisplay),
 | |
|       mMinFontSizeRatio(aSrc.mMinFontSizeRatio),
 | |
|       mExplicitLanguage(aSrc.mExplicitLanguage),
 | |
|       mAllowZoom(aSrc.mAllowZoom),
 | |
|       mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize),
 | |
|       mScriptMinSize(aSrc.mScriptMinSize),
 | |
|       mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier),
 | |
|       mLanguage(aSrc.mLanguage) {
 | |
|   MOZ_COUNT_CTOR(nsStyleFont);
 | |
| }
 | |
| 
 | |
| nsStyleFont::nsStyleFont(const Document& aDocument)
 | |
|     : mFont(*aDocument.GetFontPrefsForLang(nullptr)->GetDefaultFont(
 | |
|           kPresContext_DefaultVariableFont_ID)),
 | |
|       mSize(ZoomText(aDocument, mFont.size)),
 | |
|       mFontSizeFactor(1.0),
 | |
|       mFontSizeOffset(0),
 | |
|       mFontSizeKeyword(NS_STYLE_FONT_SIZE_MEDIUM),
 | |
|       mGenericID(kGenericFont_NONE),
 | |
|       mScriptLevel(0),
 | |
|       mMathVariant(NS_MATHML_MATHVARIANT_NONE),
 | |
|       mMathDisplay(NS_MATHML_DISPLAYSTYLE_INLINE),
 | |
|       mMinFontSizeRatio(100),  // 100%
 | |
|       mExplicitLanguage(false),
 | |
|       mAllowZoom(true),
 | |
|       mScriptUnconstrainedSize(mSize),
 | |
|       mScriptMinSize(nsPresContext::CSSTwipsToAppUnits(
 | |
|           NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))),
 | |
|       mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER),
 | |
|       mLanguage(aDocument.GetLanguageForStyle()) {
 | |
|   MOZ_COUNT_CTOR(nsStyleFont);
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   mFont.size = mSize;
 | |
|   if (!nsContentUtils::IsChromeDoc(&aDocument)) {
 | |
|     nscoord minimumFontSize =
 | |
|         aDocument.GetFontPrefsForLang(mLanguage)->mMinimumFontSize;
 | |
|     mFont.size = std::max(mSize, minimumFontSize);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aNewData) const {
 | |
|   MOZ_ASSERT(mAllowZoom == aNewData.mAllowZoom,
 | |
|              "expected mAllowZoom to be the same on both nsStyleFonts");
 | |
|   if (mSize != aNewData.mSize || mLanguage != aNewData.mLanguage ||
 | |
|       mExplicitLanguage != aNewData.mExplicitLanguage ||
 | |
|       mMathVariant != aNewData.mMathVariant ||
 | |
|       mMathDisplay != aNewData.mMathDisplay ||
 | |
|       mMinFontSizeRatio != aNewData.mMinFontSizeRatio) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   switch (mFont.CalcDifference(aNewData.mFont)) {
 | |
|     case nsFont::MaxDifference::eLayoutAffecting:
 | |
|       return NS_STYLE_HINT_REFLOW;
 | |
| 
 | |
|     case nsFont::MaxDifference::eVisual:
 | |
|       return NS_STYLE_HINT_VISUAL;
 | |
| 
 | |
|     case nsFont::MaxDifference::eNone:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   // XXX Should any of these cause a non-nsChangeHint_NeutralChange change?
 | |
|   if (mGenericID != aNewData.mGenericID ||
 | |
|       mScriptLevel != aNewData.mScriptLevel ||
 | |
|       mScriptUnconstrainedSize != aNewData.mScriptUnconstrainedSize ||
 | |
|       mScriptMinSize != aNewData.mScriptMinSize ||
 | |
|       mScriptSizeMultiplier != aNewData.mScriptSizeMultiplier) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| nscoord nsStyleFont::ZoomText(const Document& aDocument, nscoord aSize) {
 | |
|   float textZoom = 1.0;
 | |
|   if (auto* pc = aDocument.GetPresContext()) {
 | |
|     textZoom = pc->EffectiveTextZoom();
 | |
|   }
 | |
|   // aSize can be negative (e.g.: calc(-1px)) so we can't assert that here.
 | |
|   // The caller is expected deal with that.
 | |
|   return NSToCoordTruncClamped(float(aSize) * textZoom);
 | |
| }
 | |
| 
 | |
| template <typename T>
 | |
| static StyleRect<T> StyleRectWithAllSides(const T& aSide) {
 | |
|   return {aSide, aSide, aSide, aSide};
 | |
| }
 | |
| 
 | |
| nsStyleMargin::nsStyleMargin(const Document& aDocument)
 | |
|     : mMargin(StyleRectWithAllSides(
 | |
|           LengthPercentageOrAuto::LengthPercentage(LengthPercentage::Zero()))) {
 | |
|   MOZ_COUNT_CTOR(nsStyleMargin);
 | |
| }
 | |
| 
 | |
| nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc)
 | |
|     : mMargin(aSrc.mMargin) {
 | |
|   MOZ_COUNT_CTOR(nsStyleMargin);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleMargin::CalcDifference(
 | |
|     const nsStyleMargin& aNewData) const {
 | |
|   if (mMargin == aNewData.mMargin) {
 | |
|     return nsChangeHint(0);
 | |
|   }
 | |
|   // Margin differences can't affect descendant intrinsic sizes and
 | |
|   // don't need to force children to reflow.
 | |
|   return nsChangeHint_NeedReflow | nsChangeHint_ReflowChangesSizeOrPosition |
 | |
|          nsChangeHint_ClearAncestorIntrinsics;
 | |
| }
 | |
| 
 | |
| nsStylePadding::nsStylePadding(const Document& aDocument)
 | |
|     : mPadding(StyleRectWithAllSides(LengthPercentage::Zero())) {
 | |
|   MOZ_COUNT_CTOR(nsStylePadding);
 | |
| }
 | |
| 
 | |
| nsStylePadding::nsStylePadding(const nsStylePadding& aSrc)
 | |
|     : mPadding(aSrc.mPadding) {
 | |
|   MOZ_COUNT_CTOR(nsStylePadding);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStylePadding::CalcDifference(
 | |
|     const nsStylePadding& aNewData) const {
 | |
|   if (mPadding == aNewData.mPadding) {
 | |
|     return nsChangeHint(0);
 | |
|   }
 | |
|   // Padding differences can't affect descendant intrinsic sizes, but do need
 | |
|   // to force children to reflow so that we can reposition them, since their
 | |
|   // offsets are from our frame bounds but our content rect's position within
 | |
|   // those bounds is moving.
 | |
|   // FIXME: It would be good to return a weaker hint here that doesn't
 | |
|   // force reflow of all descendants, but the hint would need to force
 | |
|   // reflow of the frame's children (see how
 | |
|   // ReflowInput::InitResizeFlags initializes the inline-resize flag).
 | |
|   return NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
 | |
| }
 | |
| 
 | |
| static nscoord TwipsPerPixel(const Document& aDocument) {
 | |
|   auto* pc = aDocument.GetPresContext();
 | |
|   return pc ? pc->AppUnitsPerDevPixel() : mozilla::AppUnitsPerCSSPixel();
 | |
| }
 | |
| 
 | |
| nsStyleBorder::nsStyleBorder(const Document& aDocument)
 | |
|     : mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL),
 | |
|       mBorderImageRepeatH(StyleBorderImageRepeat::Stretch),
 | |
|       mBorderImageRepeatV(StyleBorderImageRepeat::Stretch),
 | |
|       mFloatEdge(StyleFloatEdge::ContentBox),
 | |
|       mBoxDecorationBreak(StyleBoxDecorationBreak::Slice),
 | |
|       mBorderTopColor(StyleComplexColor::CurrentColor()),
 | |
|       mBorderRightColor(StyleComplexColor::CurrentColor()),
 | |
|       mBorderBottomColor(StyleComplexColor::CurrentColor()),
 | |
|       mBorderLeftColor(StyleComplexColor::CurrentColor()),
 | |
|       mComputedBorder(0, 0, 0, 0),
 | |
|       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
 | |
|   MOZ_COUNT_CTOR(nsStyleBorder);
 | |
| 
 | |
|   NS_FOR_CSS_HALF_CORNERS(corner) {
 | |
|     mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor));
 | |
|   }
 | |
| 
 | |
|   nscoord medium = kMediumBorderWidth;
 | |
|   NS_FOR_CSS_SIDES(side) {
 | |
|     mBorderImageSlice.Set(side, nsStyleCoord(1.0f, eStyleUnit_Percent));
 | |
|     mBorderImageWidth.Set(side, nsStyleCoord(1.0f, eStyleUnit_Factor));
 | |
|     mBorderImageOutset.Set(side, nsStyleCoord(0.0f, eStyleUnit_Factor));
 | |
| 
 | |
|     mBorder.Side(side) = medium;
 | |
|     mBorderStyle[side] = StyleBorderStyle::None;
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc)
 | |
|     : mBorderRadius(aSrc.mBorderRadius),
 | |
|       mBorderImageSource(aSrc.mBorderImageSource),
 | |
|       mBorderImageSlice(aSrc.mBorderImageSlice),
 | |
|       mBorderImageWidth(aSrc.mBorderImageWidth),
 | |
|       mBorderImageOutset(aSrc.mBorderImageOutset),
 | |
|       mBorderImageFill(aSrc.mBorderImageFill),
 | |
|       mBorderImageRepeatH(aSrc.mBorderImageRepeatH),
 | |
|       mBorderImageRepeatV(aSrc.mBorderImageRepeatV),
 | |
|       mFloatEdge(aSrc.mFloatEdge),
 | |
|       mBoxDecorationBreak(aSrc.mBoxDecorationBreak),
 | |
|       mBorderTopColor(aSrc.mBorderTopColor),
 | |
|       mBorderRightColor(aSrc.mBorderRightColor),
 | |
|       mBorderBottomColor(aSrc.mBorderBottomColor),
 | |
|       mBorderLeftColor(aSrc.mBorderLeftColor),
 | |
|       mComputedBorder(aSrc.mComputedBorder),
 | |
|       mBorder(aSrc.mBorder),
 | |
|       mTwipsPerPixel(aSrc.mTwipsPerPixel) {
 | |
|   MOZ_COUNT_CTOR(nsStyleBorder);
 | |
|   NS_FOR_CSS_SIDES(side) { mBorderStyle[side] = aSrc.mBorderStyle[side]; }
 | |
| }
 | |
| 
 | |
| nsStyleBorder::~nsStyleBorder() { MOZ_COUNT_DTOR(nsStyleBorder); }
 | |
| 
 | |
| void nsStyleBorder::TriggerImageLoads(Document& aDocument,
 | |
|                                       const nsStyleBorder* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   mBorderImageSource.ResolveImage(
 | |
|       aDocument, aOldStyle ? &aOldStyle->mBorderImageSource : nullptr);
 | |
| }
 | |
| 
 | |
| nsMargin nsStyleBorder::GetImageOutset() const {
 | |
|   // We don't check whether there is a border-image (which is OK since
 | |
|   // the initial values yields 0 outset) so that we don't have to
 | |
|   // reflow to update overflow areas when an image loads.
 | |
|   nsMargin outset;
 | |
|   NS_FOR_CSS_SIDES(s) {
 | |
|     nsStyleCoord coord = mBorderImageOutset.Get(s);
 | |
|     nscoord value;
 | |
|     switch (coord.GetUnit()) {
 | |
|       case eStyleUnit_Coord:
 | |
|         value = coord.GetCoordValue();
 | |
|         break;
 | |
|       case eStyleUnit_Factor:
 | |
|         value = coord.GetFactorValue() * mComputedBorder.Side(s);
 | |
|         break;
 | |
|       default:
 | |
|         MOZ_ASSERT_UNREACHABLE("unexpected CSS unit for image outset");
 | |
|         value = 0;
 | |
|         break;
 | |
|     }
 | |
|     outset.Side(s) = value;
 | |
|   }
 | |
|   return outset;
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleBorder::CalcDifference(
 | |
|     const nsStyleBorder& aNewData) const {
 | |
|   // FIXME: XXXbz: As in nsStylePadding::CalcDifference, many of these
 | |
|   // differences should not need to clear descendant intrinsics.
 | |
|   // FIXME: It would be good to return a weaker hint for the
 | |
|   // GetComputedBorder() differences (and perhaps others) that doesn't
 | |
|   // force reflow of all descendants, but the hint would need to force
 | |
|   // reflow of the frame's children (see how
 | |
|   // ReflowInput::InitResizeFlags initializes the inline-resize flag).
 | |
|   if (mTwipsPerPixel != aNewData.mTwipsPerPixel ||
 | |
|       GetComputedBorder() != aNewData.GetComputedBorder() ||
 | |
|       mFloatEdge != aNewData.mFloatEdge ||
 | |
|       mBorderImageOutset != aNewData.mBorderImageOutset ||
 | |
|       mBoxDecorationBreak != aNewData.mBoxDecorationBreak) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   NS_FOR_CSS_SIDES(ix) {
 | |
|     // See the explanation in nsChangeHint.h of
 | |
|     // nsChangeHint_BorderStyleNoneChange .
 | |
|     // Furthermore, even though we know *this* side is 0 width, just
 | |
|     // assume a repaint hint for some other change rather than bother
 | |
|     // tracking this result through the rest of the function.
 | |
|     if (HasVisibleStyle(ix) != aNewData.HasVisibleStyle(ix)) {
 | |
|       return nsChangeHint_RepaintFrame | nsChangeHint_BorderStyleNoneChange;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Note that mBorderStyle stores not only the border style but also
 | |
|   // color-related flags.  Given that we've already done an mComputedBorder
 | |
|   // comparison, border-style differences can only lead to a repaint hint.  So
 | |
|   // it's OK to just compare the values directly -- if either the actual
 | |
|   // style or the color flags differ we want to repaint.
 | |
|   NS_FOR_CSS_SIDES(ix) {
 | |
|     if (mBorderStyle[ix] != aNewData.mBorderStyle[ix] ||
 | |
|         BorderColorFor(ix) != aNewData.BorderColorFor(ix)) {
 | |
|       return nsChangeHint_RepaintFrame;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mBorderRadius != aNewData.mBorderRadius) {
 | |
|     return nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   // Loading status of the border image can be accessed in main thread only
 | |
|   // while CalcDifference might be executed on a background thread. As a
 | |
|   // result, we have to check mBorderImage* fields even before border image was
 | |
|   // actually loaded.
 | |
|   if (!mBorderImageSource.IsEmpty() || !aNewData.mBorderImageSource.IsEmpty()) {
 | |
|     if (mBorderImageSource != aNewData.mBorderImageSource ||
 | |
|         mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
 | |
|         mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
 | |
|         mBorderImageSlice != aNewData.mBorderImageSlice ||
 | |
|         mBorderImageFill != aNewData.mBorderImageFill ||
 | |
|         mBorderImageWidth != aNewData.mBorderImageWidth) {
 | |
|       return nsChangeHint_RepaintFrame;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // mBorder is the specified border value.  Changes to this don't
 | |
|   // need any change processing, since we operate on the computed
 | |
|   // border values instead.
 | |
|   if (mBorder != aNewData.mBorder) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   // mBorderImage* fields are checked only when border-image is not 'none'.
 | |
|   if (mBorderImageSource != aNewData.mBorderImageSource ||
 | |
|       mBorderImageRepeatH != aNewData.mBorderImageRepeatH ||
 | |
|       mBorderImageRepeatV != aNewData.mBorderImageRepeatV ||
 | |
|       mBorderImageSlice != aNewData.mBorderImageSlice ||
 | |
|       mBorderImageFill != aNewData.mBorderImageFill ||
 | |
|       mBorderImageWidth != aNewData.mBorderImageWidth) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| nsStyleOutline::nsStyleOutline(const Document& aDocument)
 | |
|     : mOutlineWidth(kMediumBorderWidth),
 | |
|       mOutlineOffset(0),
 | |
|       mOutlineColor(StyleComplexColor::CurrentColor()),
 | |
|       mOutlineStyle(StyleOutlineStyle::BorderStyle(StyleBorderStyle::None)),
 | |
|       mActualOutlineWidth(0),
 | |
|       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
 | |
|   MOZ_COUNT_CTOR(nsStyleOutline);
 | |
|   // spacing values not inherited
 | |
|   nsStyleCoord zero(0, nsStyleCoord::CoordConstructor);
 | |
|   NS_FOR_CSS_HALF_CORNERS(corner) { mOutlineRadius.Set(corner, zero); }
 | |
| }
 | |
| 
 | |
| nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc)
 | |
|     : mOutlineRadius(aSrc.mOutlineRadius),
 | |
|       mOutlineWidth(aSrc.mOutlineWidth),
 | |
|       mOutlineOffset(aSrc.mOutlineOffset),
 | |
|       mOutlineColor(aSrc.mOutlineColor),
 | |
|       mOutlineStyle(aSrc.mOutlineStyle),
 | |
|       mActualOutlineWidth(aSrc.mActualOutlineWidth),
 | |
|       mTwipsPerPixel(aSrc.mTwipsPerPixel) {
 | |
|   MOZ_COUNT_CTOR(nsStyleOutline);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleOutline::CalcDifference(
 | |
|     const nsStyleOutline& aNewData) const {
 | |
|   if (mActualOutlineWidth != aNewData.mActualOutlineWidth ||
 | |
|       (mActualOutlineWidth > 0 && mOutlineOffset != aNewData.mOutlineOffset)) {
 | |
|     return nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
 | |
|            nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mOutlineStyle != aNewData.mOutlineStyle ||
 | |
|       mOutlineColor != aNewData.mOutlineColor ||
 | |
|       mOutlineRadius != aNewData.mOutlineRadius) {
 | |
|     if (mActualOutlineWidth > 0) {
 | |
|       return nsChangeHint_RepaintFrame;
 | |
|     }
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   if (mOutlineWidth != aNewData.mOutlineWidth ||
 | |
|       mOutlineOffset != aNewData.mOutlineOffset ||
 | |
|       mTwipsPerPixel != aNewData.mTwipsPerPixel) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleList
 | |
| //
 | |
| nsStyleList::nsStyleList(const Document& aDocument)
 | |
|     : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) {
 | |
|   MOZ_COUNT_CTOR(nsStyleList);
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   mCounterStyle = nsGkAtoms::disc;
 | |
|   mQuotes = Servo_Quotes_GetInitialValue().Consume();
 | |
| }
 | |
| 
 | |
| nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); }
 | |
| 
 | |
| nsStyleList::nsStyleList(const nsStyleList& aSource)
 | |
|     : mListStylePosition(aSource.mListStylePosition),
 | |
|       mListStyleImage(aSource.mListStyleImage),
 | |
|       mCounterStyle(aSource.mCounterStyle),
 | |
|       mQuotes(aSource.mQuotes),
 | |
|       mImageRegion(aSource.mImageRegion) {
 | |
|   MOZ_COUNT_CTOR(nsStyleList);
 | |
| }
 | |
| 
 | |
| void nsStyleList::TriggerImageLoads(Document& aDocument,
 | |
|                                     const nsStyleList* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   if (mListStyleImage && !mListStyleImage->IsResolved()) {
 | |
|     mListStyleImage->Resolve(
 | |
|         aDocument, aOldStyle ? aOldStyle->mListStyleImage.get() : nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleList::CalcDifference(
 | |
|     const nsStyleList& aNewData, const nsStyleDisplay& aOldDisplay) const {
 | |
|   // If the quotes implementation is ever going to change we might not need
 | |
|   // a framechange here and a reflow should be sufficient.  See bug 35768.
 | |
|   if (mQuotes != aNewData.mQuotes &&
 | |
|       !Servo_Quotes_Equal(mQuotes.get(), aNewData.mQuotes.get())) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
|   // Only elements whose display value is list-item can be affected by
 | |
|   // list-style-position and list-style-type. If the old display struct
 | |
|   // doesn't exist, assume it isn't affected by display value at all,
 | |
|   // and thus these properties should not affect it either. This also
 | |
|   // relies on that when the display value changes from something else
 | |
|   // to list-item, that change itself would cause ReconstructFrame.
 | |
|   if (aOldDisplay.mDisplay == StyleDisplay::ListItem) {
 | |
|     if (mListStylePosition != aNewData.mListStylePosition) {
 | |
|       return nsChangeHint_ReconstructFrame;
 | |
|     }
 | |
|     if (mCounterStyle != aNewData.mCounterStyle) {
 | |
|       return NS_STYLE_HINT_REFLOW;
 | |
|     }
 | |
|   } else if (mListStylePosition != aNewData.mListStylePosition ||
 | |
|              mCounterStyle != aNewData.mCounterStyle) {
 | |
|     hint = nsChangeHint_NeutralChange;
 | |
|   }
 | |
|   // list-style-image and -moz-image-region may affect some XUL elements
 | |
|   // regardless of display value, so we still need to check them.
 | |
|   if (!DefinitelyEqualImages(mListStyleImage, aNewData.mListStyleImage)) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
|   if (!mImageRegion.IsEqualInterior(aNewData.mImageRegion)) {
 | |
|     if (mImageRegion.width != aNewData.mImageRegion.width ||
 | |
|         mImageRegion.height != aNewData.mImageRegion.height) {
 | |
|       return NS_STYLE_HINT_REFLOW;
 | |
|     }
 | |
|     return NS_STYLE_HINT_VISUAL;
 | |
|   }
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsIURI> nsStyleList::GetListStyleImageURI() const {
 | |
|   if (!mListStyleImage) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri = mListStyleImage->GetImageURI();
 | |
|   return uri.forget();
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleXUL
 | |
| //
 | |
| nsStyleXUL::nsStyleXUL(const Document& aDocument)
 | |
|     : mBoxFlex(0.0f),
 | |
|       mBoxOrdinal(1),
 | |
|       mBoxAlign(StyleBoxAlign::Stretch),
 | |
|       mBoxDirection(StyleBoxDirection::Normal),
 | |
|       mBoxOrient(StyleBoxOrient::Horizontal),
 | |
|       mBoxPack(StyleBoxPack::Start),
 | |
|       mStackSizing(StyleStackSizing::StretchToFit) {
 | |
|   MOZ_COUNT_CTOR(nsStyleXUL);
 | |
| }
 | |
| 
 | |
| nsStyleXUL::~nsStyleXUL() { MOZ_COUNT_DTOR(nsStyleXUL); }
 | |
| 
 | |
| nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource)
 | |
|     : mBoxFlex(aSource.mBoxFlex),
 | |
|       mBoxOrdinal(aSource.mBoxOrdinal),
 | |
|       mBoxAlign(aSource.mBoxAlign),
 | |
|       mBoxDirection(aSource.mBoxDirection),
 | |
|       mBoxOrient(aSource.mBoxOrient),
 | |
|       mBoxPack(aSource.mBoxPack),
 | |
|       mStackSizing(aSource.mStackSizing) {
 | |
|   MOZ_COUNT_CTOR(nsStyleXUL);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aNewData) const {
 | |
|   if (mBoxAlign == aNewData.mBoxAlign &&
 | |
|       mBoxDirection == aNewData.mBoxDirection &&
 | |
|       mBoxFlex == aNewData.mBoxFlex && mBoxOrient == aNewData.mBoxOrient &&
 | |
|       mBoxPack == aNewData.mBoxPack && mBoxOrdinal == aNewData.mBoxOrdinal &&
 | |
|       mStackSizing == aNewData.mStackSizing) {
 | |
|     return nsChangeHint(0);
 | |
|   }
 | |
|   if (mBoxOrdinal != aNewData.mBoxOrdinal) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
|   return NS_STYLE_HINT_REFLOW;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleColumn
 | |
| //
 | |
| /* static */ const uint32_t nsStyleColumn::kMaxColumnCount;
 | |
| /* static */ const uint32_t nsStyleColumn::kColumnCountAuto;
 | |
| 
 | |
| nsStyleColumn::nsStyleColumn(const Document& aDocument)
 | |
|     : mColumnWidth(eStyleUnit_Auto),
 | |
|       mColumnRuleColor(StyleComplexColor::CurrentColor()),
 | |
|       mColumnRuleStyle(StyleBorderStyle::None),
 | |
|       mColumnRuleWidth(kMediumBorderWidth),
 | |
|       mTwipsPerPixel(TwipsPerPixel(aDocument)) {
 | |
|   MOZ_COUNT_CTOR(nsStyleColumn);
 | |
| }
 | |
| 
 | |
| nsStyleColumn::~nsStyleColumn() { MOZ_COUNT_DTOR(nsStyleColumn); }
 | |
| 
 | |
| nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource)
 | |
|     : mColumnCount(aSource.mColumnCount),
 | |
|       mColumnWidth(aSource.mColumnWidth),
 | |
|       mColumnRuleColor(aSource.mColumnRuleColor),
 | |
|       mColumnRuleStyle(aSource.mColumnRuleStyle),
 | |
|       mColumnFill(aSource.mColumnFill),
 | |
|       mColumnSpan(aSource.mColumnSpan),
 | |
|       mColumnRuleWidth(aSource.mColumnRuleWidth),
 | |
|       mTwipsPerPixel(aSource.mTwipsPerPixel) {
 | |
|   MOZ_COUNT_CTOR(nsStyleColumn);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleColumn::CalcDifference(
 | |
|     const nsStyleColumn& aNewData) const {
 | |
|   if ((mColumnWidth.GetUnit() == eStyleUnit_Auto) !=
 | |
|           (aNewData.mColumnWidth.GetUnit() == eStyleUnit_Auto) ||
 | |
|       mColumnCount != aNewData.mColumnCount ||
 | |
|       mColumnSpan != aNewData.mColumnSpan) {
 | |
|     // We force column count changes to do a reframe, because it's tricky to
 | |
|     // handle some edge cases where the column count gets smaller and content
 | |
|     // overflows.
 | |
|     // XXX not ideal
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if (mColumnWidth != aNewData.mColumnWidth ||
 | |
|       mColumnFill != aNewData.mColumnFill) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   if (GetComputedColumnRuleWidth() != aNewData.GetComputedColumnRuleWidth() ||
 | |
|       mColumnRuleStyle != aNewData.mColumnRuleStyle ||
 | |
|       mColumnRuleColor != aNewData.mColumnRuleColor) {
 | |
|     return NS_STYLE_HINT_VISUAL;
 | |
|   }
 | |
| 
 | |
|   // XXX Is it right that we never check mTwipsPerPixel to return a
 | |
|   // non-nsChangeHint_NeutralChange hint?
 | |
|   if (mColumnRuleWidth != aNewData.mColumnRuleWidth ||
 | |
|       mTwipsPerPixel != aNewData.mTwipsPerPixel) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleSVG
 | |
| //
 | |
| nsStyleSVG::nsStyleSVG(const Document& aDocument)
 | |
|     : mFill(eStyleSVGPaintType_Color),  // Will be initialized to NS_RGB(0,0,0)
 | |
|       mStroke(eStyleSVGPaintType_None),
 | |
|       mStrokeDashoffset(0, nsStyleCoord::CoordConstructor),
 | |
|       mStrokeWidth(nsPresContext::CSSPixelsToAppUnits(1),
 | |
|                    nsStyleCoord::CoordConstructor),
 | |
|       mFillOpacity(1.0f),
 | |
|       mStrokeMiterlimit(4.0f),
 | |
|       mStrokeOpacity(1.0f),
 | |
|       mClipRule(StyleFillRule::Nonzero),
 | |
|       mColorInterpolation(NS_STYLE_COLOR_INTERPOLATION_SRGB),
 | |
|       mColorInterpolationFilters(NS_STYLE_COLOR_INTERPOLATION_LINEARRGB),
 | |
|       mFillRule(StyleFillRule::Nonzero),
 | |
|       mPaintOrder(NS_STYLE_PAINT_ORDER_NORMAL),
 | |
|       mShapeRendering(NS_STYLE_SHAPE_RENDERING_AUTO),
 | |
|       mStrokeLinecap(NS_STYLE_STROKE_LINECAP_BUTT),
 | |
|       mStrokeLinejoin(NS_STYLE_STROKE_LINEJOIN_MITER),
 | |
|       mTextAnchor(NS_STYLE_TEXT_ANCHOR_START),
 | |
|       mContextPropsBits(0),
 | |
|       mContextFlags(
 | |
|           (eStyleSVGOpacitySource_Normal << FILL_OPACITY_SOURCE_SHIFT) |
 | |
|           (eStyleSVGOpacitySource_Normal << STROKE_OPACITY_SOURCE_SHIFT)) {
 | |
|   MOZ_COUNT_CTOR(nsStyleSVG);
 | |
| }
 | |
| 
 | |
| nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); }
 | |
| 
 | |
| nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource)
 | |
|     : mFill(aSource.mFill),
 | |
|       mStroke(aSource.mStroke),
 | |
|       mMarkerEnd(aSource.mMarkerEnd),
 | |
|       mMarkerMid(aSource.mMarkerMid),
 | |
|       mMarkerStart(aSource.mMarkerStart),
 | |
|       mStrokeDasharray(aSource.mStrokeDasharray),
 | |
|       mContextProps(aSource.mContextProps),
 | |
|       mStrokeDashoffset(aSource.mStrokeDashoffset),
 | |
|       mStrokeWidth(aSource.mStrokeWidth),
 | |
|       mFillOpacity(aSource.mFillOpacity),
 | |
|       mStrokeMiterlimit(aSource.mStrokeMiterlimit),
 | |
|       mStrokeOpacity(aSource.mStrokeOpacity),
 | |
|       mClipRule(aSource.mClipRule),
 | |
|       mColorInterpolation(aSource.mColorInterpolation),
 | |
|       mColorInterpolationFilters(aSource.mColorInterpolationFilters),
 | |
|       mFillRule(aSource.mFillRule),
 | |
|       mPaintOrder(aSource.mPaintOrder),
 | |
|       mShapeRendering(aSource.mShapeRendering),
 | |
|       mStrokeLinecap(aSource.mStrokeLinecap),
 | |
|       mStrokeLinejoin(aSource.mStrokeLinejoin),
 | |
|       mTextAnchor(aSource.mTextAnchor),
 | |
|       mContextPropsBits(aSource.mContextPropsBits),
 | |
|       mContextFlags(aSource.mContextFlags) {
 | |
|   MOZ_COUNT_CTOR(nsStyleSVG);
 | |
| }
 | |
| 
 | |
| static bool PaintURIChanged(const nsStyleSVGPaint& aPaint1,
 | |
|                             const nsStyleSVGPaint& aPaint2) {
 | |
|   if (aPaint1.Type() != aPaint2.Type()) {
 | |
|     return aPaint1.Type() == eStyleSVGPaintType_Server ||
 | |
|            aPaint2.Type() == eStyleSVGPaintType_Server;
 | |
|   }
 | |
|   return aPaint1.Type() == eStyleSVGPaintType_Server &&
 | |
|          !DefinitelyEqualURIs(aPaint1.GetPaintServer(),
 | |
|                               aPaint2.GetPaintServer());
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (!DefinitelyEqualURIs(mMarkerEnd, aNewData.mMarkerEnd) ||
 | |
|       !DefinitelyEqualURIs(mMarkerMid, aNewData.mMarkerMid) ||
 | |
|       !DefinitelyEqualURIs(mMarkerStart, aNewData.mMarkerStart)) {
 | |
|     // Markers currently contribute to SVGGeometryFrame::mRect,
 | |
|     // so we need a reflow as well as a repaint. No intrinsic sizes need
 | |
|     // to change, so nsChangeHint_NeedReflow is sufficient.
 | |
|     return nsChangeHint_UpdateEffects | nsChangeHint_NeedReflow |
 | |
|            nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
 | |
|            nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mFill != aNewData.mFill || mStroke != aNewData.mStroke ||
 | |
|       mFillOpacity != aNewData.mFillOpacity ||
 | |
|       mStrokeOpacity != aNewData.mStrokeOpacity) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|     if (HasStroke() != aNewData.HasStroke() ||
 | |
|         (!HasStroke() && HasFill() != aNewData.HasFill())) {
 | |
|       // Frame bounds and overflow rects depend on whether we "have" fill or
 | |
|       // stroke. Whether we have stroke or not just changed, or else we have no
 | |
|       // stroke (in which case whether we have fill or not is significant to
 | |
|       // frame bounds) and whether we have fill or not just changed. In either
 | |
|       // case we need to reflow so the frame rect is updated.
 | |
|       // XXXperf this is a waste on non SVGGeometryFrames.
 | |
|       hint |= nsChangeHint_NeedReflow |
 | |
|               nsChangeHint_NeedDirtyReflow;  // XXX remove me: bug 876085
 | |
|     }
 | |
|     if (PaintURIChanged(mFill, aNewData.mFill) ||
 | |
|         PaintURIChanged(mStroke, aNewData.mStroke)) {
 | |
|       hint |= nsChangeHint_UpdateEffects;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Stroke currently contributes to SVGGeometryFrame::mRect, so
 | |
|   // we need a reflow here. No intrinsic sizes need to change, so
 | |
|   // nsChangeHint_NeedReflow is sufficient.
 | |
|   // Note that stroke-dashoffset does not affect SVGGeometryFrame::mRect.
 | |
|   // text-anchor changes also require a reflow since it changes frames' rects.
 | |
|   if (mStrokeWidth != aNewData.mStrokeWidth ||
 | |
|       mStrokeMiterlimit != aNewData.mStrokeMiterlimit ||
 | |
|       mStrokeLinecap != aNewData.mStrokeLinecap ||
 | |
|       mStrokeLinejoin != aNewData.mStrokeLinejoin ||
 | |
|       mTextAnchor != aNewData.mTextAnchor) {
 | |
|     return hint | nsChangeHint_NeedReflow |
 | |
|            nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
 | |
|            nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (hint & nsChangeHint_RepaintFrame) {
 | |
|     return hint;  // we don't add anything else below
 | |
|   }
 | |
| 
 | |
|   if (mStrokeDashoffset != aNewData.mStrokeDashoffset ||
 | |
|       mClipRule != aNewData.mClipRule ||
 | |
|       mColorInterpolation != aNewData.mColorInterpolation ||
 | |
|       mColorInterpolationFilters != aNewData.mColorInterpolationFilters ||
 | |
|       mFillRule != aNewData.mFillRule || mPaintOrder != aNewData.mPaintOrder ||
 | |
|       mShapeRendering != aNewData.mShapeRendering ||
 | |
|       mStrokeDasharray != aNewData.mStrokeDasharray ||
 | |
|       mContextFlags != aNewData.mContextFlags ||
 | |
|       mContextPropsBits != aNewData.mContextPropsBits) {
 | |
|     return hint | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (!hint) {
 | |
|     if (mContextProps != aNewData.mContextProps) {
 | |
|       hint = nsChangeHint_NeutralChange;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // StyleBasicShape
 | |
| 
 | |
| nsCSSKeyword StyleBasicShape::GetShapeTypeName() const {
 | |
|   switch (mType) {
 | |
|     case StyleBasicShapeType::Polygon:
 | |
|       return eCSSKeyword_polygon;
 | |
|     case StyleBasicShapeType::Circle:
 | |
|       return eCSSKeyword_circle;
 | |
|     case StyleBasicShapeType::Ellipse:
 | |
|       return eCSSKeyword_ellipse;
 | |
|     case StyleBasicShapeType::Inset:
 | |
|       return eCSSKeyword_inset;
 | |
|   }
 | |
|   MOZ_ASSERT_UNREACHABLE("unexpected type");
 | |
|   return eCSSKeyword_UNKNOWN;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // StyleShapeSource
 | |
| StyleShapeSource::StyleShapeSource() : mBasicShape() {}
 | |
| 
 | |
| StyleShapeSource::StyleShapeSource(const StyleShapeSource& aSource) {
 | |
|   DoCopy(aSource);
 | |
| }
 | |
| 
 | |
| StyleShapeSource::~StyleShapeSource() { DoDestroy(); }
 | |
| 
 | |
| StyleShapeSource& StyleShapeSource::operator=(const StyleShapeSource& aOther) {
 | |
|   if (this != &aOther) {
 | |
|     DoCopy(aOther);
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool StyleShapeSource::operator==(const StyleShapeSource& aOther) const {
 | |
|   if (mType != aOther.mType) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   switch (mType) {
 | |
|     case StyleShapeSourceType::None:
 | |
|       return true;
 | |
| 
 | |
|     case StyleShapeSourceType::URL:
 | |
|     case StyleShapeSourceType::Image:
 | |
|       return *mShapeImage == *aOther.mShapeImage;
 | |
| 
 | |
|     case StyleShapeSourceType::Shape:
 | |
|       return *mBasicShape == *aOther.mBasicShape &&
 | |
|              mReferenceBox == aOther.mReferenceBox;
 | |
| 
 | |
|     case StyleShapeSourceType::Box:
 | |
|       return mReferenceBox == aOther.mReferenceBox;
 | |
| 
 | |
|     case StyleShapeSourceType::Path:
 | |
|       return *mSVGPath == *aOther.mSVGPath;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT_UNREACHABLE("Unexpected shape source type!");
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::SetURL(const css::URLValue& aValue) {
 | |
|   if (mType != StyleShapeSourceType::Image &&
 | |
|       mType != StyleShapeSourceType::URL) {
 | |
|     DoDestroy();
 | |
|     new (&mShapeImage) UniquePtr<nsStyleImage>(new nsStyleImage());
 | |
|   }
 | |
|   mShapeImage->SetURLValue(do_AddRef(&aValue));
 | |
|   mType = StyleShapeSourceType::URL;
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::SetShapeImage(UniquePtr<nsStyleImage> aShapeImage) {
 | |
|   MOZ_ASSERT(aShapeImage);
 | |
|   DoDestroy();
 | |
|   new (&mShapeImage) UniquePtr<nsStyleImage>(std::move(aShapeImage));
 | |
|   mType = StyleShapeSourceType::Image;
 | |
| }
 | |
| 
 | |
| imgIRequest* StyleShapeSource::GetShapeImageData() const {
 | |
|   if (mType != StyleShapeSourceType::Image) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (mShapeImage->GetType() != eStyleImageType_Image) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return mShapeImage->GetImageData();
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::SetBasicShape(UniquePtr<StyleBasicShape> aBasicShape,
 | |
|                                      StyleGeometryBox aReferenceBox) {
 | |
|   MOZ_ASSERT(aBasicShape);
 | |
|   DoDestroy();
 | |
|   new (&mBasicShape) UniquePtr<StyleBasicShape>(std::move(aBasicShape));
 | |
|   mReferenceBox = aReferenceBox;
 | |
|   mType = StyleShapeSourceType::Shape;
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::SetPath(UniquePtr<StyleSVGPath> aPath) {
 | |
|   MOZ_ASSERT(aPath);
 | |
|   DoDestroy();
 | |
|   new (&mSVGPath) UniquePtr<StyleSVGPath>(std::move(aPath));
 | |
|   mType = StyleShapeSourceType::Path;
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::TriggerImageLoads(
 | |
|     Document& aDocument, const StyleShapeSource* aOldShapeSource) {
 | |
|   if (GetType() != StyleShapeSourceType::Image) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto* oldShapeImage = (aOldShapeSource && aOldShapeSource->GetType() ==
 | |
|                                                 StyleShapeSourceType::Image)
 | |
|                             ? &aOldShapeSource->ShapeImage()
 | |
|                             : nullptr;
 | |
|   mShapeImage->ResolveImage(aDocument, oldShapeImage);
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::SetReferenceBox(StyleGeometryBox aReferenceBox) {
 | |
|   DoDestroy();
 | |
|   mReferenceBox = aReferenceBox;
 | |
|   mType = StyleShapeSourceType::Box;
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::DoCopy(const StyleShapeSource& aOther) {
 | |
|   switch (aOther.mType) {
 | |
|     case StyleShapeSourceType::None:
 | |
|       mReferenceBox = StyleGeometryBox::NoBox;
 | |
|       mType = StyleShapeSourceType::None;
 | |
|       break;
 | |
| 
 | |
|     case StyleShapeSourceType::URL:
 | |
|       SetURL(aOther.URL());
 | |
|       break;
 | |
| 
 | |
|     case StyleShapeSourceType::Image:
 | |
|       SetShapeImage(MakeUnique<nsStyleImage>(aOther.ShapeImage()));
 | |
|       break;
 | |
| 
 | |
|     case StyleShapeSourceType::Shape:
 | |
|       SetBasicShape(MakeUnique<StyleBasicShape>(aOther.BasicShape()),
 | |
|                     aOther.GetReferenceBox());
 | |
|       break;
 | |
| 
 | |
|     case StyleShapeSourceType::Box:
 | |
|       SetReferenceBox(aOther.GetReferenceBox());
 | |
|       break;
 | |
| 
 | |
|     case StyleShapeSourceType::Path:
 | |
|       SetPath(MakeUnique<StyleSVGPath>(aOther.Path()));
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void StyleShapeSource::DoDestroy() {
 | |
|   switch (mType) {
 | |
|     case StyleShapeSourceType::Shape:
 | |
|       mBasicShape.~UniquePtr<StyleBasicShape>();
 | |
|       break;
 | |
|     case StyleShapeSourceType::Image:
 | |
|     case StyleShapeSourceType::URL:
 | |
|       mShapeImage.~UniquePtr<nsStyleImage>();
 | |
|       break;
 | |
|     case StyleShapeSourceType::Path:
 | |
|       mSVGPath.~UniquePtr<StyleSVGPath>();
 | |
|       break;
 | |
|     case StyleShapeSourceType::None:
 | |
|     case StyleShapeSourceType::Box:
 | |
|       // Not a union type, so do nothing.
 | |
|       break;
 | |
|   }
 | |
|   mType = StyleShapeSourceType::None;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleFilter
 | |
| //
 | |
| nsStyleFilter::nsStyleFilter()
 | |
|     : mType(NS_STYLE_FILTER_NONE), mDropShadow(nullptr) {
 | |
|   MOZ_COUNT_CTOR(nsStyleFilter);
 | |
| }
 | |
| 
 | |
| nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource)
 | |
|     : mType(NS_STYLE_FILTER_NONE), mDropShadow(nullptr) {
 | |
|   MOZ_COUNT_CTOR(nsStyleFilter);
 | |
|   if (aSource.mType == NS_STYLE_FILTER_URL) {
 | |
|     SetURL(aSource.mURL);
 | |
|   } else if (aSource.mType == NS_STYLE_FILTER_DROP_SHADOW) {
 | |
|     SetDropShadow(aSource.mDropShadow);
 | |
|   } else if (aSource.mType != NS_STYLE_FILTER_NONE) {
 | |
|     SetFilterParameter(aSource.mFilterParameter, aSource.mType);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleFilter::~nsStyleFilter() {
 | |
|   ReleaseRef();
 | |
|   MOZ_COUNT_DTOR(nsStyleFilter);
 | |
| }
 | |
| 
 | |
| nsStyleFilter& nsStyleFilter::operator=(const nsStyleFilter& aOther) {
 | |
|   if (this == &aOther) {
 | |
|     return *this;
 | |
|   }
 | |
| 
 | |
|   if (aOther.mType == NS_STYLE_FILTER_URL) {
 | |
|     SetURL(aOther.mURL);
 | |
|   } else if (aOther.mType == NS_STYLE_FILTER_DROP_SHADOW) {
 | |
|     SetDropShadow(aOther.mDropShadow);
 | |
|   } else if (aOther.mType != NS_STYLE_FILTER_NONE) {
 | |
|     SetFilterParameter(aOther.mFilterParameter, aOther.mType);
 | |
|   } else {
 | |
|     ReleaseRef();
 | |
|     mType = NS_STYLE_FILTER_NONE;
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool nsStyleFilter::operator==(const nsStyleFilter& aOther) const {
 | |
|   if (mType != aOther.mType) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mType == NS_STYLE_FILTER_URL) {
 | |
|     return DefinitelyEqualURIs(mURL, aOther.mURL);
 | |
|   } else if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
 | |
|     return *mDropShadow == *aOther.mDropShadow;
 | |
|   } else if (mType != NS_STYLE_FILTER_NONE) {
 | |
|     return mFilterParameter == aOther.mFilterParameter;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void nsStyleFilter::ReleaseRef() {
 | |
|   if (mType == NS_STYLE_FILTER_DROP_SHADOW) {
 | |
|     NS_ASSERTION(mDropShadow, "expected pointer");
 | |
|     mDropShadow->Release();
 | |
|   } else if (mType == NS_STYLE_FILTER_URL) {
 | |
|     NS_ASSERTION(mURL, "expected pointer");
 | |
|     mURL->Release();
 | |
|   }
 | |
|   mURL = nullptr;
 | |
| }
 | |
| 
 | |
| void nsStyleFilter::SetFilterParameter(const nsStyleCoord& aFilterParameter,
 | |
|                                        int32_t aType) {
 | |
|   ReleaseRef();
 | |
|   mFilterParameter = aFilterParameter;
 | |
|   mType = aType;
 | |
| }
 | |
| 
 | |
| bool nsStyleFilter::SetURL(css::URLValue* aURL) {
 | |
|   ReleaseRef();
 | |
|   mURL = aURL;
 | |
|   mURL->AddRef();
 | |
|   mType = NS_STYLE_FILTER_URL;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void nsStyleFilter::SetDropShadow(nsCSSShadowArray* aDropShadow) {
 | |
|   NS_ASSERTION(aDropShadow, "expected pointer");
 | |
|   ReleaseRef();
 | |
|   mDropShadow = aDropShadow;
 | |
|   mDropShadow->AddRef();
 | |
|   mType = NS_STYLE_FILTER_DROP_SHADOW;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleSVGReset
 | |
| //
 | |
| nsStyleSVGReset::nsStyleSVGReset(const Document& aDocument)
 | |
|     : mMask(nsStyleImageLayers::LayerType::Mask),
 | |
|       mStopColor(StyleComplexColor::Black()),
 | |
|       mFloodColor(StyleComplexColor::Black()),
 | |
|       mLightingColor(StyleComplexColor::White()),
 | |
|       mStopOpacity(1.0f),
 | |
|       mFloodOpacity(1.0f),
 | |
|       mDominantBaseline(NS_STYLE_DOMINANT_BASELINE_AUTO),
 | |
|       mVectorEffect(NS_STYLE_VECTOR_EFFECT_NONE),
 | |
|       mMaskType(NS_STYLE_MASK_TYPE_LUMINANCE) {
 | |
|   MOZ_COUNT_CTOR(nsStyleSVGReset);
 | |
| }
 | |
| 
 | |
| nsStyleSVGReset::~nsStyleSVGReset() { MOZ_COUNT_DTOR(nsStyleSVGReset); }
 | |
| 
 | |
| nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource)
 | |
|     : mMask(aSource.mMask),
 | |
|       mClipPath(aSource.mClipPath),
 | |
|       mStopColor(aSource.mStopColor),
 | |
|       mFloodColor(aSource.mFloodColor),
 | |
|       mLightingColor(aSource.mLightingColor),
 | |
|       mStopOpacity(aSource.mStopOpacity),
 | |
|       mFloodOpacity(aSource.mFloodOpacity),
 | |
|       mDominantBaseline(aSource.mDominantBaseline),
 | |
|       mVectorEffect(aSource.mVectorEffect),
 | |
|       mMaskType(aSource.mMaskType) {
 | |
|   MOZ_COUNT_CTOR(nsStyleSVGReset);
 | |
| }
 | |
| 
 | |
| void nsStyleSVGReset::TriggerImageLoads(Document& aDocument,
 | |
|                                         const nsStyleSVGReset* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mMask) {
 | |
|     nsStyleImage& image = mMask.mLayers[i].mImage;
 | |
|     if (image.GetType() == eStyleImageType_Image) {
 | |
|       const auto* url = image.GetURLValue();
 | |
|       // If the url is a local ref, it must be a <mask-resource>, so we don't
 | |
|       // need to resolve the style image.
 | |
|       if (url->IsLocalRef()) {
 | |
|         continue;
 | |
|       }
 | |
| #if 0
 | |
|       // XXX The old style system also checks whether this is a reference to
 | |
|       // the current document with reference, but it doesn't seem to be a
 | |
|       // behavior mentioned anywhere, so we comment out the code for now.
 | |
|       nsIURI* docURI = aPresContext->Document()->GetDocumentURI();
 | |
|       if (url->EqualsExceptRef(docURI)) {
 | |
|         continue;
 | |
|       }
 | |
| #endif
 | |
| 
 | |
|       // Otherwise, we may need the image even if it has a reference, in case
 | |
|       // the referenced element isn't a valid SVG <mask> element.
 | |
|       const nsStyleImage* oldImage =
 | |
|           (aOldStyle && aOldStyle->mMask.mLayers.Length() > i)
 | |
|               ? &aOldStyle->mMask.mLayers[i].mImage
 | |
|               : nullptr;
 | |
| 
 | |
|       image.ResolveImage(aDocument, oldImage);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleSVGReset::CalcDifference(
 | |
|     const nsStyleSVGReset& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (mClipPath != aNewData.mClipPath) {
 | |
|     hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mDominantBaseline != aNewData.mDominantBaseline) {
 | |
|     // XXXjwatt: why NS_STYLE_HINT_REFLOW? Isn't that excessive?
 | |
|     hint |= NS_STYLE_HINT_REFLOW;
 | |
|   } else if (mVectorEffect != aNewData.mVectorEffect) {
 | |
|     // Stroke currently affects SVGGeometryFrame::mRect, and
 | |
|     // vector-effect affect stroke. As a result we need to reflow if
 | |
|     // vector-effect changes in order to have SVGGeometryFrame::
 | |
|     // ReflowSVG called to update its mRect. No intrinsic sizes need
 | |
|     // to change so nsChangeHint_NeedReflow is sufficient.
 | |
|     hint |= nsChangeHint_NeedReflow |
 | |
|             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
 | |
|             nsChangeHint_RepaintFrame;
 | |
|   } else if (mStopColor != aNewData.mStopColor ||
 | |
|              mFloodColor != aNewData.mFloodColor ||
 | |
|              mLightingColor != aNewData.mLightingColor ||
 | |
|              mStopOpacity != aNewData.mStopOpacity ||
 | |
|              mFloodOpacity != aNewData.mFloodOpacity ||
 | |
|              mMaskType != aNewData.mMaskType) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   hint |=
 | |
|       mMask.CalcDifference(aNewData.mMask, nsStyleImageLayers::LayerType::Mask);
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| bool nsStyleSVGReset::HasMask() const {
 | |
|   for (uint32_t i = 0; i < mMask.mImageCount; i++) {
 | |
|     if (!mMask.mLayers[i].mImage.IsEmpty()) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // nsStyleSVGPaint implementation
 | |
| nsStyleSVGPaint::nsStyleSVGPaint(nsStyleSVGPaintType aType)
 | |
|     : mPaint(StyleComplexColor::Black()),
 | |
|       mType(aType),
 | |
|       mFallbackType(eStyleSVGFallbackType_NotSet),
 | |
|       mFallbackColor(StyleComplexColor::Black()) {
 | |
|   MOZ_ASSERT(aType == nsStyleSVGPaintType(0) ||
 | |
|              aType == eStyleSVGPaintType_None ||
 | |
|              aType == eStyleSVGPaintType_Color);
 | |
| }
 | |
| 
 | |
| nsStyleSVGPaint::nsStyleSVGPaint(const nsStyleSVGPaint& aSource)
 | |
|     : nsStyleSVGPaint(nsStyleSVGPaintType(0)) {
 | |
|   Assign(aSource);
 | |
| }
 | |
| 
 | |
| nsStyleSVGPaint::~nsStyleSVGPaint() { Reset(); }
 | |
| 
 | |
| void nsStyleSVGPaint::Reset() {
 | |
|   switch (mType) {
 | |
|     case eStyleSVGPaintType_None:
 | |
|       break;
 | |
|     case eStyleSVGPaintType_Color:
 | |
|       mPaint.mColor = StyleComplexColor::Black();
 | |
|       break;
 | |
|     case eStyleSVGPaintType_Server:
 | |
|       mPaint.mPaintServer->Release();
 | |
|       mPaint.mPaintServer = nullptr;
 | |
|       MOZ_FALLTHROUGH;
 | |
|     case eStyleSVGPaintType_ContextFill:
 | |
|     case eStyleSVGPaintType_ContextStroke:
 | |
|       mFallbackType = eStyleSVGFallbackType_NotSet;
 | |
|       mFallbackColor = StyleComplexColor::Black();
 | |
|       break;
 | |
|   }
 | |
|   mType = nsStyleSVGPaintType(0);
 | |
| }
 | |
| 
 | |
| nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) {
 | |
|   if (this != &aOther) {
 | |
|     Assign(aOther);
 | |
|   }
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| void nsStyleSVGPaint::Assign(const nsStyleSVGPaint& aOther) {
 | |
|   MOZ_ASSERT(aOther.mType != nsStyleSVGPaintType(0),
 | |
|              "shouldn't copy uninitialized nsStyleSVGPaint");
 | |
| 
 | |
|   switch (aOther.mType) {
 | |
|     case eStyleSVGPaintType_None:
 | |
|       SetNone();
 | |
|       break;
 | |
|     case eStyleSVGPaintType_Color:
 | |
|       SetColor(aOther.mPaint.mColor);
 | |
|       break;
 | |
|     case eStyleSVGPaintType_Server:
 | |
|       SetPaintServer(aOther.mPaint.mPaintServer, aOther.mFallbackType,
 | |
|                      aOther.mFallbackColor);
 | |
|       break;
 | |
|     case eStyleSVGPaintType_ContextFill:
 | |
|     case eStyleSVGPaintType_ContextStroke:
 | |
|       SetContextValue(aOther.mType, aOther.mFallbackType,
 | |
|                       aOther.mFallbackColor);
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsStyleSVGPaint::SetNone() {
 | |
|   Reset();
 | |
|   mType = eStyleSVGPaintType_None;
 | |
| }
 | |
| 
 | |
| void nsStyleSVGPaint::SetContextValue(nsStyleSVGPaintType aType,
 | |
|                                       nsStyleSVGFallbackType aFallbackType,
 | |
|                                       StyleComplexColor aFallbackColor) {
 | |
|   MOZ_ASSERT(aType == eStyleSVGPaintType_ContextFill ||
 | |
|              aType == eStyleSVGPaintType_ContextStroke);
 | |
|   Reset();
 | |
|   mType = aType;
 | |
|   mFallbackType = aFallbackType;
 | |
|   mFallbackColor = aFallbackColor;
 | |
| }
 | |
| 
 | |
| void nsStyleSVGPaint::SetColor(StyleComplexColor aColor) {
 | |
|   Reset();
 | |
|   mType = eStyleSVGPaintType_Color;
 | |
|   mPaint.mColor = aColor;
 | |
| }
 | |
| 
 | |
| void nsStyleSVGPaint::SetPaintServer(css::URLValue* aPaintServer,
 | |
|                                      nsStyleSVGFallbackType aFallbackType,
 | |
|                                      StyleComplexColor aFallbackColor) {
 | |
|   MOZ_ASSERT(aPaintServer);
 | |
|   Reset();
 | |
|   mType = eStyleSVGPaintType_Server;
 | |
|   mPaint.mPaintServer = aPaintServer;
 | |
|   mPaint.mPaintServer->AddRef();
 | |
|   mFallbackType = aFallbackType;
 | |
|   mFallbackColor = aFallbackColor;
 | |
| }
 | |
| 
 | |
| bool nsStyleSVGPaint::operator==(const nsStyleSVGPaint& aOther) const {
 | |
|   if (mType != aOther.mType) {
 | |
|     return false;
 | |
|   }
 | |
|   switch (mType) {
 | |
|     case eStyleSVGPaintType_Color:
 | |
|       return mPaint.mColor == aOther.mPaint.mColor;
 | |
|     case eStyleSVGPaintType_Server:
 | |
|       return DefinitelyEqualURIs(mPaint.mPaintServer,
 | |
|                                  aOther.mPaint.mPaintServer) &&
 | |
|              mFallbackType == aOther.mFallbackType &&
 | |
|              mFallbackColor == aOther.mFallbackColor;
 | |
|     case eStyleSVGPaintType_ContextFill:
 | |
|     case eStyleSVGPaintType_ContextStroke:
 | |
|       return mFallbackType == aOther.mFallbackType &&
 | |
|              mFallbackColor == aOther.mFallbackColor;
 | |
|     default:
 | |
|       MOZ_ASSERT(mType == eStyleSVGPaintType_None, "Unexpected SVG paint type");
 | |
|       return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStylePosition
 | |
| //
 | |
| nsStylePosition::nsStylePosition(const Document& aDocument)
 | |
|     : mObjectPosition(Position::FromPercentage(0.5f)),
 | |
|       mOffset(StyleRectWithAllSides(LengthPercentageOrAuto::Auto())),
 | |
|       mWidth(StyleSize::Auto()),
 | |
|       mMinWidth(StyleSize::Auto()),
 | |
|       mMaxWidth(StyleMaxSize::None()),
 | |
|       mHeight(StyleSize::Auto()),
 | |
|       mMinHeight(StyleSize::Auto()),
 | |
|       mMaxHeight(StyleMaxSize::None()),
 | |
|       mFlexBasis(StyleFlexBasis::Size(StyleSize::Auto())),
 | |
|       mGridAutoColumnsMin(eStyleUnit_Auto),
 | |
|       mGridAutoColumnsMax(eStyleUnit_Auto),
 | |
|       mGridAutoRowsMin(eStyleUnit_Auto),
 | |
|       mGridAutoRowsMax(eStyleUnit_Auto),
 | |
|       mGridAutoFlow(NS_STYLE_GRID_AUTO_FLOW_ROW),
 | |
|       mBoxSizing(StyleBoxSizing::Content),
 | |
|       mAlignContent(NS_STYLE_ALIGN_NORMAL),
 | |
|       mAlignItems(NS_STYLE_ALIGN_NORMAL),
 | |
|       mAlignSelf(NS_STYLE_ALIGN_AUTO),
 | |
|       mJustifyContent(NS_STYLE_JUSTIFY_NORMAL),
 | |
|       mSpecifiedJustifyItems(NS_STYLE_JUSTIFY_LEGACY),
 | |
|       mJustifyItems(NS_STYLE_JUSTIFY_NORMAL),
 | |
|       mJustifySelf(NS_STYLE_JUSTIFY_AUTO),
 | |
|       mFlexDirection(NS_STYLE_FLEX_DIRECTION_ROW),
 | |
|       mFlexWrap(NS_STYLE_FLEX_WRAP_NOWRAP),
 | |
|       mObjectFit(NS_STYLE_OBJECT_FIT_FILL),
 | |
|       mOrder(NS_STYLE_ORDER_INITIAL),
 | |
|       mFlexGrow(0.0f),
 | |
|       mFlexShrink(1.0f),
 | |
|       mZIndex(eStyleUnit_Auto),
 | |
|       mColumnGap(eStyleUnit_Normal),
 | |
|       mRowGap(eStyleUnit_Normal) {
 | |
|   MOZ_COUNT_CTOR(nsStylePosition);
 | |
| 
 | |
|   // The initial value of grid-auto-columns and grid-auto-rows is 'auto',
 | |
|   // which computes to 'minmax(auto, auto)'.
 | |
| 
 | |
|   // Other members get their default constructors
 | |
|   // which initialize them to representations of their respective initial value.
 | |
|   // mGridTemplateAreas: nullptr for 'none'
 | |
|   // mGridTemplate{Rows,Columns}: false and empty arrays for 'none'
 | |
|   // mGrid{Column,Row}{Start,End}: false/0/empty values for 'auto'
 | |
| }
 | |
| 
 | |
| nsStylePosition::~nsStylePosition() { MOZ_COUNT_DTOR(nsStylePosition); }
 | |
| 
 | |
| nsStylePosition::nsStylePosition(const nsStylePosition& aSource)
 | |
|     : mObjectPosition(aSource.mObjectPosition),
 | |
|       mOffset(aSource.mOffset),
 | |
|       mWidth(aSource.mWidth),
 | |
|       mMinWidth(aSource.mMinWidth),
 | |
|       mMaxWidth(aSource.mMaxWidth),
 | |
|       mHeight(aSource.mHeight),
 | |
|       mMinHeight(aSource.mMinHeight),
 | |
|       mMaxHeight(aSource.mMaxHeight),
 | |
|       mFlexBasis(aSource.mFlexBasis),
 | |
|       mGridAutoColumnsMin(aSource.mGridAutoColumnsMin),
 | |
|       mGridAutoColumnsMax(aSource.mGridAutoColumnsMax),
 | |
|       mGridAutoRowsMin(aSource.mGridAutoRowsMin),
 | |
|       mGridAutoRowsMax(aSource.mGridAutoRowsMax),
 | |
|       mGridAutoFlow(aSource.mGridAutoFlow),
 | |
|       mBoxSizing(aSource.mBoxSizing),
 | |
|       mAlignContent(aSource.mAlignContent),
 | |
|       mAlignItems(aSource.mAlignItems),
 | |
|       mAlignSelf(aSource.mAlignSelf),
 | |
|       mJustifyContent(aSource.mJustifyContent),
 | |
|       mSpecifiedJustifyItems(aSource.mSpecifiedJustifyItems),
 | |
|       mJustifyItems(aSource.mJustifyItems),
 | |
|       mJustifySelf(aSource.mJustifySelf),
 | |
|       mFlexDirection(aSource.mFlexDirection),
 | |
|       mFlexWrap(aSource.mFlexWrap),
 | |
|       mObjectFit(aSource.mObjectFit),
 | |
|       mOrder(aSource.mOrder),
 | |
|       mFlexGrow(aSource.mFlexGrow),
 | |
|       mFlexShrink(aSource.mFlexShrink),
 | |
|       mZIndex(aSource.mZIndex),
 | |
|       mGridTemplateAreas(aSource.mGridTemplateAreas),
 | |
|       mGridColumnStart(aSource.mGridColumnStart),
 | |
|       mGridColumnEnd(aSource.mGridColumnEnd),
 | |
|       mGridRowStart(aSource.mGridRowStart),
 | |
|       mGridRowEnd(aSource.mGridRowEnd),
 | |
|       mColumnGap(aSource.mColumnGap),
 | |
|       mRowGap(aSource.mRowGap) {
 | |
|   MOZ_COUNT_CTOR(nsStylePosition);
 | |
| 
 | |
|   if (aSource.mGridTemplateColumns) {
 | |
|     mGridTemplateColumns =
 | |
|         MakeUnique<nsStyleGridTemplate>(*aSource.mGridTemplateColumns);
 | |
|   }
 | |
|   if (aSource.mGridTemplateRows) {
 | |
|     mGridTemplateRows =
 | |
|         MakeUnique<nsStyleGridTemplate>(*aSource.mGridTemplateRows);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool IsAutonessEqual(const StyleRect<LengthPercentageOrAuto>& aSides1,
 | |
|                             const StyleRect<LengthPercentageOrAuto>& aSides2) {
 | |
|   NS_FOR_CSS_SIDES(side) {
 | |
|     if (aSides1.Get(side).IsAuto() != aSides2.Get(side).IsAuto()) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static bool IsGridTemplateEqual(
 | |
|     const UniquePtr<nsStyleGridTemplate>& aOldData,
 | |
|     const UniquePtr<nsStyleGridTemplate>& aNewData) {
 | |
|   if (aOldData == aNewData) {
 | |
|     return true;
 | |
|   }
 | |
|   if (!aOldData || !aNewData) {
 | |
|     return false;
 | |
|   }
 | |
|   return *aOldData == *aNewData;
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStylePosition::CalcDifference(
 | |
|     const nsStylePosition& aNewData,
 | |
|     const nsStyleVisibility& aOldStyleVisibility) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   // Changes to "z-index" require a repaint.
 | |
|   if (mZIndex != aNewData.mZIndex) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   // Changes to "object-fit" & "object-position" require a repaint.  They
 | |
|   // may also require a reflow, if we have a nsSubDocumentFrame, so that we
 | |
|   // can adjust the size & position of the subdocument.
 | |
|   if (mObjectFit != aNewData.mObjectFit ||
 | |
|       mObjectPosition != aNewData.mObjectPosition) {
 | |
|     hint |= nsChangeHint_RepaintFrame | nsChangeHint_NeedReflow;
 | |
|   }
 | |
| 
 | |
|   if (mOrder != aNewData.mOrder) {
 | |
|     // "order" impacts both layout order and stacking order, so we need both a
 | |
|     // reflow and a repaint when it changes.  (Technically, we only need a
 | |
|     // reflow if we're in a multi-line flexbox (which we can't be sure about,
 | |
|     // since that's determined by styling on our parent) -- there, "order" can
 | |
|     // affect which flex line we end up on, & hence can affect our sizing by
 | |
|     // changing the group of flex items we're competing with for space.)
 | |
|     return hint | nsChangeHint_RepaintFrame | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   if (mBoxSizing != aNewData.mBoxSizing) {
 | |
|     // Can affect both widths and heights; just a bad scene.
 | |
|     return hint | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   // Properties that apply to flex items:
 | |
|   // XXXdholbert These should probably be more targeted (bug 819536)
 | |
|   if (mAlignSelf != aNewData.mAlignSelf || mFlexBasis != aNewData.mFlexBasis ||
 | |
|       mFlexGrow != aNewData.mFlexGrow || mFlexShrink != aNewData.mFlexShrink) {
 | |
|     return hint | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   // Properties that apply to flex containers:
 | |
|   // - flex-direction can swap a flex container between vertical & horizontal.
 | |
|   // - align-items can change the sizing of a flex container & the positioning
 | |
|   //   of its children.
 | |
|   // - flex-wrap changes whether a flex container's children are wrapped, which
 | |
|   //   impacts their sizing/positioning and hence impacts the container's size.
 | |
|   if (mAlignItems != aNewData.mAlignItems ||
 | |
|       mFlexDirection != aNewData.mFlexDirection ||
 | |
|       mFlexWrap != aNewData.mFlexWrap) {
 | |
|     return hint | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   // Properties that apply to grid containers:
 | |
|   // FIXME: only for grid containers
 | |
|   // (ie. 'display: grid' or 'display: inline-grid')
 | |
|   if (!IsGridTemplateEqual(mGridTemplateColumns,
 | |
|                            aNewData.mGridTemplateColumns) ||
 | |
|       !IsGridTemplateEqual(mGridTemplateRows, aNewData.mGridTemplateRows) ||
 | |
|       mGridTemplateAreas != aNewData.mGridTemplateAreas ||
 | |
|       mGridAutoColumnsMin != aNewData.mGridAutoColumnsMin ||
 | |
|       mGridAutoColumnsMax != aNewData.mGridAutoColumnsMax ||
 | |
|       mGridAutoRowsMin != aNewData.mGridAutoRowsMin ||
 | |
|       mGridAutoRowsMax != aNewData.mGridAutoRowsMax ||
 | |
|       mGridAutoFlow != aNewData.mGridAutoFlow) {
 | |
|     return hint | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   // Properties that apply to grid items:
 | |
|   // FIXME: only for grid items
 | |
|   // (ie. parent frame is 'display: grid' or 'display: inline-grid')
 | |
|   if (mGridColumnStart != aNewData.mGridColumnStart ||
 | |
|       mGridColumnEnd != aNewData.mGridColumnEnd ||
 | |
|       mGridRowStart != aNewData.mGridRowStart ||
 | |
|       mGridRowEnd != aNewData.mGridRowEnd ||
 | |
|       mColumnGap != aNewData.mColumnGap || mRowGap != aNewData.mRowGap) {
 | |
|     return hint | nsChangeHint_AllReflowHints;
 | |
|   }
 | |
| 
 | |
|   // Changing 'justify-content/items/self' might affect the positioning,
 | |
|   // but it won't affect any sizing.
 | |
|   if (mJustifyContent != aNewData.mJustifyContent ||
 | |
|       mJustifyItems != aNewData.mJustifyItems ||
 | |
|       mJustifySelf != aNewData.mJustifySelf) {
 | |
|     hint |= nsChangeHint_NeedReflow;
 | |
|   }
 | |
| 
 | |
|   // No need to do anything if mSpecifiedJustifyItems changes, as long as
 | |
|   // mJustifyItems (tested above) is unchanged.
 | |
|   if (mSpecifiedJustifyItems != aNewData.mSpecifiedJustifyItems) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   // 'align-content' doesn't apply to a single-line flexbox but we don't know
 | |
|   // if we're a flex container at this point so we can't optimize for that.
 | |
|   if (mAlignContent != aNewData.mAlignContent) {
 | |
|     hint |= nsChangeHint_NeedReflow;
 | |
|   }
 | |
| 
 | |
|   bool widthChanged = mWidth != aNewData.mWidth ||
 | |
|                       mMinWidth != aNewData.mMinWidth ||
 | |
|                       mMaxWidth != aNewData.mMaxWidth;
 | |
|   bool heightChanged = mHeight != aNewData.mHeight ||
 | |
|                        mMinHeight != aNewData.mMinHeight ||
 | |
|                        mMaxHeight != aNewData.mMaxHeight;
 | |
| 
 | |
|   // Note that we pass an nsStyleVisibility here because we don't want
 | |
|   // to cause a new struct to be computed during
 | |
|   // ComputedStyle::CalcStyleDifference, which can lead to incorrect
 | |
|   // style data.
 | |
|   // It doesn't matter whether we're looking at the old or new
 | |
|   // visibility struct, since a change between vertical and horizontal
 | |
|   // writing-mode will cause a reframe, and it's easier to pass the old.
 | |
|   bool isVertical = WritingMode(&aOldStyleVisibility).IsVertical();
 | |
|   if (isVertical ? widthChanged : heightChanged) {
 | |
|     hint |= nsChangeHint_ReflowHintsForBSizeChange;
 | |
|   }
 | |
| 
 | |
|   if (isVertical ? heightChanged : widthChanged) {
 | |
|     hint |= nsChangeHint_ReflowHintsForISizeChange;
 | |
|   }
 | |
| 
 | |
|   // If any of the offsets have changed, then return the respective hints
 | |
|   // so that we would hopefully be able to avoid reflowing.
 | |
|   // Note that it is possible that we'll need to reflow when processing
 | |
|   // restyles, but we don't have enough information to make a good decision
 | |
|   // right now.
 | |
|   // Don't try to handle changes between "auto" and non-auto efficiently;
 | |
|   // that's tricky to do and will hardly ever be able to avoid a reflow.
 | |
|   if (mOffset != aNewData.mOffset) {
 | |
|     if (IsAutonessEqual(mOffset, aNewData.mOffset)) {
 | |
|       hint |=
 | |
|           nsChangeHint_RecomputePosition | nsChangeHint_UpdateParentOverflow;
 | |
|     } else {
 | |
|       hint |= nsChangeHint_AllReflowHints;
 | |
|     }
 | |
|   }
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| uint8_t nsStylePosition::UsedAlignSelf(ComputedStyle* aParent) const {
 | |
|   if (mAlignSelf != NS_STYLE_ALIGN_AUTO) {
 | |
|     return mAlignSelf;
 | |
|   }
 | |
|   if (MOZ_LIKELY(aParent)) {
 | |
|     auto parentAlignItems = aParent->StylePosition()->mAlignItems;
 | |
|     MOZ_ASSERT(!(parentAlignItems & NS_STYLE_ALIGN_LEGACY),
 | |
|                "align-items can't have 'legacy'");
 | |
|     return parentAlignItems;
 | |
|   }
 | |
|   return NS_STYLE_ALIGN_NORMAL;
 | |
| }
 | |
| 
 | |
| uint8_t nsStylePosition::UsedJustifySelf(ComputedStyle* aParent) const {
 | |
|   if (mJustifySelf != NS_STYLE_JUSTIFY_AUTO) {
 | |
|     return mJustifySelf;
 | |
|   }
 | |
|   if (MOZ_LIKELY(aParent)) {
 | |
|     auto inheritedJustifyItems = aParent->StylePosition()->mJustifyItems;
 | |
|     return inheritedJustifyItems & ~NS_STYLE_JUSTIFY_LEGACY;
 | |
|   }
 | |
|   return NS_STYLE_JUSTIFY_NORMAL;
 | |
| }
 | |
| 
 | |
| static StaticAutoPtr<nsStyleGridTemplate> sDefaultGridTemplate;
 | |
| 
 | |
| static const nsStyleGridTemplate& DefaultGridTemplate() {
 | |
|   if (!sDefaultGridTemplate) {
 | |
|     sDefaultGridTemplate = new nsStyleGridTemplate;
 | |
|     ClearOnShutdown(&sDefaultGridTemplate);
 | |
|   }
 | |
|   return *sDefaultGridTemplate;
 | |
| }
 | |
| 
 | |
| const nsStyleGridTemplate& nsStylePosition::GridTemplateColumns() const {
 | |
|   return mGridTemplateColumns ? *mGridTemplateColumns : DefaultGridTemplate();
 | |
| }
 | |
| 
 | |
| const nsStyleGridTemplate& nsStylePosition::GridTemplateRows() const {
 | |
|   return mGridTemplateRows ? *mGridTemplateRows : DefaultGridTemplate();
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleTable
 | |
| //
 | |
| 
 | |
| nsStyleTable::nsStyleTable(const Document& aDocument)
 | |
|     : mLayoutStrategy(NS_STYLE_TABLE_LAYOUT_AUTO), mSpan(1) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTable);
 | |
| }
 | |
| 
 | |
| nsStyleTable::~nsStyleTable() { MOZ_COUNT_DTOR(nsStyleTable); }
 | |
| 
 | |
| nsStyleTable::nsStyleTable(const nsStyleTable& aSource)
 | |
|     : mLayoutStrategy(aSource.mLayoutStrategy), mSpan(aSource.mSpan) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTable);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aNewData) const {
 | |
|   if (mSpan != aNewData.mSpan || mLayoutStrategy != aNewData.mLayoutStrategy) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| // -----------------------
 | |
| // nsStyleTableBorder
 | |
| 
 | |
| nsStyleTableBorder::nsStyleTableBorder(const Document& aDocument)
 | |
|     : mBorderSpacingCol(0),
 | |
|       mBorderSpacingRow(0),
 | |
|       mBorderCollapse(NS_STYLE_BORDER_SEPARATE),
 | |
|       mCaptionSide(NS_STYLE_CAPTION_SIDE_TOP),
 | |
|       mEmptyCells(NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTableBorder);
 | |
| }
 | |
| 
 | |
| nsStyleTableBorder::~nsStyleTableBorder() {
 | |
|   MOZ_COUNT_DTOR(nsStyleTableBorder);
 | |
| }
 | |
| 
 | |
| nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource)
 | |
|     : mBorderSpacingCol(aSource.mBorderSpacingCol),
 | |
|       mBorderSpacingRow(aSource.mBorderSpacingRow),
 | |
|       mBorderCollapse(aSource.mBorderCollapse),
 | |
|       mCaptionSide(aSource.mCaptionSide),
 | |
|       mEmptyCells(aSource.mEmptyCells) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTableBorder);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleTableBorder::CalcDifference(
 | |
|     const nsStyleTableBorder& aNewData) const {
 | |
|   // Border-collapse changes need a reframe, because we use a different frame
 | |
|   // class for table cells in the collapsed border model.  This is used to
 | |
|   // conserve memory when using the separated border model (collapsed borders
 | |
|   // require extra state to be stored).
 | |
|   if (mBorderCollapse != aNewData.mBorderCollapse) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if ((mCaptionSide == aNewData.mCaptionSide) &&
 | |
|       (mBorderSpacingCol == aNewData.mBorderSpacingCol) &&
 | |
|       (mBorderSpacingRow == aNewData.mBorderSpacingRow)) {
 | |
|     if (mEmptyCells == aNewData.mEmptyCells) {
 | |
|       return nsChangeHint(0);
 | |
|     }
 | |
|     return NS_STYLE_HINT_VISUAL;
 | |
|   } else {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleColor
 | |
| //
 | |
| 
 | |
| static nscolor DefaultColor(const Document& aDocument) {
 | |
|   auto* pc = aDocument.GetPresContext();
 | |
|   return pc ? pc->DefaultColor() : NS_RGB(0, 0, 0);
 | |
| }
 | |
| 
 | |
| nsStyleColor::nsStyleColor(const Document& aDocument)
 | |
|     : mColor(DefaultColor(aDocument)) {
 | |
|   MOZ_COUNT_CTOR(nsStyleColor);
 | |
| }
 | |
| 
 | |
| nsStyleColor::nsStyleColor(const nsStyleColor& aSource)
 | |
|     : mColor(aSource.mColor) {
 | |
|   MOZ_COUNT_CTOR(nsStyleColor);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleColor::CalcDifference(const nsStyleColor& aNewData) const {
 | |
|   if (mColor == aNewData.mColor) {
 | |
|     return nsChangeHint(0);
 | |
|   }
 | |
|   return nsChangeHint_RepaintFrame;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleGradient
 | |
| //
 | |
| bool nsStyleGradient::operator==(const nsStyleGradient& aOther) const {
 | |
|   MOZ_ASSERT(mSize == NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER ||
 | |
|                  mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR,
 | |
|              "incorrect combination of shape and size");
 | |
|   MOZ_ASSERT(aOther.mSize == NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER ||
 | |
|                  aOther.mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR,
 | |
|              "incorrect combination of shape and size");
 | |
| 
 | |
|   if (mShape != aOther.mShape || mSize != aOther.mSize ||
 | |
|       mRepeating != aOther.mRepeating ||
 | |
|       mLegacySyntax != aOther.mLegacySyntax ||
 | |
|       mMozLegacySyntax != aOther.mMozLegacySyntax ||
 | |
|       mBgPosX != aOther.mBgPosX || mBgPosY != aOther.mBgPosY ||
 | |
|       mAngle != aOther.mAngle || mRadiusX != aOther.mRadiusX ||
 | |
|       mRadiusY != aOther.mRadiusY) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mStops.Length() != aOther.mStops.Length()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < mStops.Length(); i++) {
 | |
|     const auto& stop1 = mStops[i];
 | |
|     const auto& stop2 = aOther.mStops[i];
 | |
|     if (stop1.mLocation != stop2.mLocation ||
 | |
|         stop1.mIsInterpolationHint != stop2.mIsInterpolationHint ||
 | |
|         (!stop1.mIsInterpolationHint && stop1.mColor != stop2.mColor)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| nsStyleGradient::nsStyleGradient()
 | |
|     : mShape(NS_STYLE_GRADIENT_SHAPE_LINEAR),
 | |
|       mSize(NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER),
 | |
|       mRepeating(false),
 | |
|       mLegacySyntax(false),
 | |
|       mMozLegacySyntax(false) {}
 | |
| 
 | |
| bool nsStyleGradient::IsOpaque() {
 | |
|   for (uint32_t i = 0; i < mStops.Length(); i++) {
 | |
|     if (mStops[i].mColor.MaybeTransparent()) {
 | |
|       // We don't know the foreground color here, so if it's being used
 | |
|       // we must assume it might be transparent.
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool nsStyleGradient::HasCalc() {
 | |
|   for (uint32_t i = 0; i < mStops.Length(); i++) {
 | |
|     if (mStops[i].mLocation.IsCalcUnit()) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return mBgPosX.IsCalcUnit() || mBgPosY.IsCalcUnit() || mAngle.IsCalcUnit() ||
 | |
|          mRadiusX.IsCalcUnit() || mRadiusY.IsCalcUnit();
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleImageRequest
 | |
| 
 | |
| /**
 | |
|  * Runnable to release the nsStyleImageRequest's mRequestProxy
 | |
|  * and mImageTracker on the main thread, and to perform
 | |
|  * any necessary unlocking and untracking of the image.
 | |
|  */
 | |
| class StyleImageRequestCleanupTask : public mozilla::Runnable {
 | |
|  public:
 | |
|   typedef nsStyleImageRequest::Mode Mode;
 | |
| 
 | |
|   StyleImageRequestCleanupTask(Mode aModeFlags,
 | |
|                                already_AddRefed<imgRequestProxy> aRequestProxy,
 | |
|                                already_AddRefed<ImageTracker> aImageTracker)
 | |
|       : mozilla::Runnable("StyleImageRequestCleanupTask"),
 | |
|         mModeFlags(aModeFlags),
 | |
|         mRequestProxy(aRequestProxy),
 | |
|         mImageTracker(aImageTracker) {}
 | |
| 
 | |
|   NS_IMETHOD Run() final {
 | |
|     MOZ_ASSERT(!mRequestProxy || NS_IsMainThread(),
 | |
|                "If mRequestProxy is non-null, we need to run on main thread!");
 | |
| 
 | |
|     if (!mRequestProxy) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     if (mModeFlags & Mode::Track) {
 | |
|       MOZ_ASSERT(mImageTracker);
 | |
|       mImageTracker->Remove(mRequestProxy);
 | |
|     } else {
 | |
|       mRequestProxy->UnlockImage();
 | |
|     }
 | |
| 
 | |
|     if (mModeFlags & Mode::Discard) {
 | |
|       mRequestProxy->RequestDiscard();
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   virtual ~StyleImageRequestCleanupTask() {
 | |
|     MOZ_ASSERT((!mRequestProxy && !mImageTracker) || NS_IsMainThread(),
 | |
|                "mRequestProxy and mImageTracker's destructor need to run "
 | |
|                "on the main thread!");
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   Mode mModeFlags;
 | |
|   // Since we always dispatch this runnable to the main thread, these will be
 | |
|   // released on the main thread when the runnable itself is released.
 | |
|   RefPtr<imgRequestProxy> mRequestProxy;
 | |
|   RefPtr<ImageTracker> mImageTracker;
 | |
| };
 | |
| 
 | |
| nsStyleImageRequest::nsStyleImageRequest(Mode aModeFlags,
 | |
|                                          css::URLValue* aImageValue)
 | |
|     : mImageValue(aImageValue), mModeFlags(aModeFlags), mResolved(false) {}
 | |
| 
 | |
| nsStyleImageRequest::~nsStyleImageRequest() {
 | |
|   // We may or may not be being destroyed on the main thread.  To clean
 | |
|   // up, we must untrack and unlock the image (depending on mModeFlags),
 | |
|   // and release mRequestProxy and mImageTracker, all on the main thread.
 | |
|   {
 | |
|     RefPtr<StyleImageRequestCleanupTask> task =
 | |
|         new StyleImageRequestCleanupTask(mModeFlags, mRequestProxy.forget(),
 | |
|                                          mImageTracker.forget());
 | |
|     if (NS_IsMainThread()) {
 | |
|       task->Run();
 | |
|     } else {
 | |
|       if (mDocGroup) {
 | |
|         mDocGroup->Dispatch(TaskCategory::Other, task.forget());
 | |
|       } else {
 | |
|         // if Resolve was not called at some point, mDocGroup is not set.
 | |
|         SystemGroup::Dispatch(TaskCategory::Other, task.forget());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(!mRequestProxy);
 | |
|   MOZ_ASSERT(!mImageTracker);
 | |
| }
 | |
| 
 | |
| bool nsStyleImageRequest::Resolve(Document& aDocument,
 | |
|                                   const nsStyleImageRequest* aOldImageRequest) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(!IsResolved(), "already resolved");
 | |
| 
 | |
|   mResolved = true;
 | |
| 
 | |
|   nsIURI* docURI = aDocument.GetDocumentURI();
 | |
|   if (GetImageValue()->HasRef()) {
 | |
|     bool isEqualExceptRef = false;
 | |
|     RefPtr<nsIURI> imageURI = GetImageURI();
 | |
|     if (!imageURI) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     if (NS_SUCCEEDED(imageURI->EqualsExceptRef(docURI, &isEqualExceptRef)) &&
 | |
|         isEqualExceptRef) {
 | |
|       // Prevent loading an internal resource.
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // TODO(emilio, bug 1440442): This is a hackaround to avoid flickering due the
 | |
|   // lack of non-http image caching in imagelib (bug 1406134), which causes
 | |
|   // stuff like bug 1439285. Cleanest fix if that doesn't get fixed is bug
 | |
|   // 1440305, but that seems too risky, and a lot of work to do before 60.
 | |
|   //
 | |
|   // Once that's fixed, the "old style" argument to TriggerImageLoads can go
 | |
|   // away.
 | |
|   if (nsContentUtils::IsChromeDoc(&aDocument) && aOldImageRequest &&
 | |
|       aOldImageRequest->IsResolved() && DefinitelyEquals(*aOldImageRequest)) {
 | |
|     MOZ_ASSERT(aOldImageRequest->mDocGroup == aDocument.GetDocGroup());
 | |
|     MOZ_ASSERT(mModeFlags == aOldImageRequest->mModeFlags);
 | |
| 
 | |
|     mDocGroup = aOldImageRequest->mDocGroup;
 | |
|     mImageValue = aOldImageRequest->mImageValue;
 | |
|     mRequestProxy = aOldImageRequest->mRequestProxy;
 | |
|   } else {
 | |
|     mDocGroup = aDocument.GetDocGroup();
 | |
|     imgRequestProxy* request = mImageValue->LoadImage(&aDocument);
 | |
|     bool isPrint = !!aDocument.GetOriginalDocument();
 | |
|     if (!isPrint) {
 | |
|       mRequestProxy = request;
 | |
|     } else if (request) {
 | |
|       request->GetStaticRequest(&aDocument, getter_AddRefs(mRequestProxy));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!mRequestProxy) {
 | |
|     // The URL resolution or image load failed.
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mModeFlags & Mode::Track) {
 | |
|     mImageTracker = aDocument.ImageTracker();
 | |
|   }
 | |
| 
 | |
|   MaybeTrackAndLock();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void nsStyleImageRequest::MaybeTrackAndLock() {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(IsResolved());
 | |
|   MOZ_ASSERT(mRequestProxy);
 | |
| 
 | |
|   if (mModeFlags & Mode::Track) {
 | |
|     MOZ_ASSERT(mImageTracker);
 | |
|     mImageTracker->Add(mRequestProxy);
 | |
|   } else {
 | |
|     MOZ_ASSERT(!mImageTracker);
 | |
|     mRequestProxy->LockImage();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsStyleImageRequest::DefinitelyEquals(
 | |
|     const nsStyleImageRequest& aOther) const {
 | |
|   return DefinitelyEqualURIs(mImageValue, aOther.mImageValue);
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // CachedBorderImageData
 | |
| //
 | |
| void CachedBorderImageData::SetCachedSVGViewportSize(
 | |
|     const mozilla::Maybe<nsSize>& aSVGViewportSize) {
 | |
|   mCachedSVGViewportSize = aSVGViewportSize;
 | |
| }
 | |
| 
 | |
| const mozilla::Maybe<nsSize>&
 | |
| CachedBorderImageData::GetCachedSVGViewportSize() {
 | |
|   return mCachedSVGViewportSize;
 | |
| }
 | |
| 
 | |
| struct PurgeCachedImagesTask : mozilla::Runnable {
 | |
|   PurgeCachedImagesTask() : mozilla::Runnable("PurgeCachedImagesTask") {}
 | |
|   NS_IMETHOD Run() final {
 | |
|     mSubImages.Clear();
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsCOMArray<imgIContainer> mSubImages;
 | |
| };
 | |
| 
 | |
| void CachedBorderImageData::PurgeCachedImages() {
 | |
|   if (ServoStyleSet::IsInServoTraversal()) {
 | |
|     RefPtr<PurgeCachedImagesTask> task = new PurgeCachedImagesTask();
 | |
|     task->mSubImages.SwapElements(mSubImages);
 | |
|     // This will run the task immediately if we're already on the main thread,
 | |
|     // but that is fine.
 | |
|     NS_DispatchToMainThread(task.forget());
 | |
|   } else {
 | |
|     mSubImages.Clear();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CachedBorderImageData::SetSubImage(uint8_t aIndex,
 | |
|                                         imgIContainer* aSubImage) {
 | |
|   mSubImages.ReplaceObjectAt(aSubImage, aIndex);
 | |
| }
 | |
| 
 | |
| imgIContainer* CachedBorderImageData::GetSubImage(uint8_t aIndex) {
 | |
|   imgIContainer* subImage = nullptr;
 | |
|   if (aIndex < mSubImages.Count()) subImage = mSubImages[aIndex];
 | |
|   return subImage;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleImage
 | |
| //
 | |
| 
 | |
| nsStyleImage::nsStyleImage()
 | |
|     : mType(eStyleImageType_Null), mImage(nullptr), mCropRect(nullptr) {
 | |
|   MOZ_COUNT_CTOR(nsStyleImage);
 | |
| }
 | |
| 
 | |
| nsStyleImage::~nsStyleImage() {
 | |
|   MOZ_COUNT_DTOR(nsStyleImage);
 | |
|   if (mType != eStyleImageType_Null) {
 | |
|     SetNull();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleImage::nsStyleImage(const nsStyleImage& aOther)
 | |
|     : mType(eStyleImageType_Null), mCropRect(nullptr) {
 | |
|   // We need our own copy constructor because we don't want
 | |
|   // to copy the reference count
 | |
|   MOZ_COUNT_CTOR(nsStyleImage);
 | |
|   DoCopy(aOther);
 | |
| }
 | |
| 
 | |
| nsStyleImage& nsStyleImage::operator=(const nsStyleImage& aOther) {
 | |
|   if (this != &aOther) {
 | |
|     DoCopy(aOther);
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| void nsStyleImage::DoCopy(const nsStyleImage& aOther) {
 | |
|   SetNull();
 | |
| 
 | |
|   if (aOther.mType == eStyleImageType_Image) {
 | |
|     SetImageRequest(do_AddRef(aOther.mImage));
 | |
|   } else if (aOther.mType == eStyleImageType_Gradient) {
 | |
|     SetGradientData(aOther.mGradient);
 | |
|   } else if (aOther.mType == eStyleImageType_Element) {
 | |
|     SetElementId(do_AddRef(aOther.mElementId));
 | |
|   } else if (aOther.mType == eStyleImageType_URL) {
 | |
|     SetURLValue(do_AddRef(aOther.mURLValue));
 | |
|   }
 | |
| 
 | |
|   UniquePtr<nsStyleSides> cropRectCopy;
 | |
|   if (aOther.mCropRect) {
 | |
|     cropRectCopy = MakeUnique<nsStyleSides>(*aOther.mCropRect.get());
 | |
|   }
 | |
|   SetCropRect(std::move(cropRectCopy));
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetNull() {
 | |
|   if (mType == eStyleImageType_Gradient) {
 | |
|     mGradient->Release();
 | |
|   } else if (mType == eStyleImageType_Image) {
 | |
|     NS_RELEASE(mImage);
 | |
|   } else if (mType == eStyleImageType_Element) {
 | |
|     NS_RELEASE(mElementId);
 | |
|   } else if (mType == eStyleImageType_URL) {
 | |
|     // FIXME: NS_RELEASE doesn't handle const gracefully (unlike RefPtr).
 | |
|     const_cast<css::URLValue*>(mURLValue)->Release();
 | |
|     mURLValue = nullptr;
 | |
|   }
 | |
| 
 | |
|   mType = eStyleImageType_Null;
 | |
|   mCropRect = nullptr;
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetImageRequest(
 | |
|     already_AddRefed<nsStyleImageRequest> aImage) {
 | |
|   RefPtr<nsStyleImageRequest> image = aImage;
 | |
| 
 | |
|   if (mType != eStyleImageType_Null) {
 | |
|     SetNull();
 | |
|   }
 | |
| 
 | |
|   if (image) {
 | |
|     mImage = image.forget().take();
 | |
|     mType = eStyleImageType_Image;
 | |
|   }
 | |
|   if (mCachedBIData) {
 | |
|     mCachedBIData->PurgeCachedImages();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetGradientData(nsStyleGradient* aGradient) {
 | |
|   if (aGradient) {
 | |
|     aGradient->AddRef();
 | |
|   }
 | |
| 
 | |
|   if (mType != eStyleImageType_Null) {
 | |
|     SetNull();
 | |
|   }
 | |
| 
 | |
|   if (aGradient) {
 | |
|     mGradient = aGradient;
 | |
|     mType = eStyleImageType_Gradient;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetElementId(already_AddRefed<nsAtom> aElementId) {
 | |
|   if (mType != eStyleImageType_Null) {
 | |
|     SetNull();
 | |
|   }
 | |
| 
 | |
|   if (RefPtr<nsAtom> atom = aElementId) {
 | |
|     mElementId = atom.forget().take();
 | |
|     mType = eStyleImageType_Element;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetCropRect(UniquePtr<nsStyleSides> aCropRect) {
 | |
|   mCropRect = std::move(aCropRect);
 | |
| }
 | |
| 
 | |
| void nsStyleImage::SetURLValue(already_AddRefed<const URLValue> aValue) {
 | |
|   RefPtr<const URLValue> value = aValue;
 | |
| 
 | |
|   if (mType != eStyleImageType_Null) {
 | |
|     SetNull();
 | |
|   }
 | |
| 
 | |
|   if (value) {
 | |
|     mURLValue = value.forget().take();
 | |
|     mType = eStyleImageType_URL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static int32_t ConvertToPixelCoord(const nsStyleCoord& aCoord,
 | |
|                                    int32_t aPercentScale) {
 | |
|   double pixelValue;
 | |
|   switch (aCoord.GetUnit()) {
 | |
|     case eStyleUnit_Percent:
 | |
|       pixelValue = aCoord.GetPercentValue() * aPercentScale;
 | |
|       break;
 | |
|     case eStyleUnit_Factor:
 | |
|       pixelValue = aCoord.GetFactorValue();
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("unexpected unit for image crop rect");
 | |
|       return 0;
 | |
|   }
 | |
|   MOZ_ASSERT(pixelValue >= 0, "we ensured non-negative while parsing");
 | |
|   pixelValue = std::min(pixelValue, double(INT32_MAX));  // avoid overflow
 | |
|   return NS_lround(pixelValue);
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsIURI> nsStyleImageRequest::GetImageURI() const {
 | |
|   nsCOMPtr<nsIURI> uri;
 | |
| 
 | |
|   if (mRequestProxy) {
 | |
|     mRequestProxy->GetURI(getter_AddRefs(uri));
 | |
|     if (uri) {
 | |
|       return uri.forget();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If we had some problem resolving the mRequestProxy, use the URL stored
 | |
|   // in the mImageValue.
 | |
|   if (!mImageValue) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   uri = mImageValue->GetURI();
 | |
|   return uri.forget();
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect,
 | |
|                                          bool* aIsEntireImage) const {
 | |
|   MOZ_ASSERT(mType == eStyleImageType_Image,
 | |
|              "This function is designed to be used only when mType"
 | |
|              "is eStyleImageType_Image.");
 | |
| 
 | |
|   imgRequestProxy* req = GetImageData();
 | |
|   if (!req) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<imgIContainer> imageContainer;
 | |
|   req->GetImage(getter_AddRefs(imageContainer));
 | |
|   if (!imageContainer) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsIntSize imageSize;
 | |
|   imageContainer->GetWidth(&imageSize.width);
 | |
|   imageContainer->GetHeight(&imageSize.height);
 | |
|   if (imageSize.width <= 0 || imageSize.height <= 0) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   int32_t left = ConvertToPixelCoord(mCropRect->GetLeft(), imageSize.width);
 | |
|   int32_t top = ConvertToPixelCoord(mCropRect->GetTop(), imageSize.height);
 | |
|   int32_t right = ConvertToPixelCoord(mCropRect->GetRight(), imageSize.width);
 | |
|   int32_t bottom =
 | |
|       ConvertToPixelCoord(mCropRect->GetBottom(), imageSize.height);
 | |
| 
 | |
|   // IntersectRect() returns an empty rect if we get negative width or height
 | |
|   nsIntRect cropRect(left, top, right - left, bottom - top);
 | |
|   nsIntRect imageRect(nsIntPoint(0, 0), imageSize);
 | |
|   aActualCropRect.IntersectRect(imageRect, cropRect);
 | |
| 
 | |
|   if (aIsEntireImage) {
 | |
|     *aIsEntireImage = aActualCropRect.IsEqualInterior(imageRect);
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::StartDecoding() const {
 | |
|   if (mType == eStyleImageType_Image) {
 | |
|     imgRequestProxy* req = GetImageData();
 | |
|     if (!req) {
 | |
|       return false;
 | |
|     }
 | |
|     return req->StartDecodingWithResult(imgIContainer::FLAG_ASYNC_NOTIFY);
 | |
|   }
 | |
|   // null image types always return false from IsComplete, so we do the same
 | |
|   // here.
 | |
|   return mType != eStyleImageType_Null ? true : false;
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::IsOpaque() const {
 | |
|   if (!IsComplete()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_Gradient) {
 | |
|     return mGradient->IsOpaque();
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_Element || mType == eStyleImageType_URL) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(mType == eStyleImageType_Image, "unexpected image type");
 | |
|   MOZ_ASSERT(GetImageData(), "should've returned earlier above");
 | |
| 
 | |
|   nsCOMPtr<imgIContainer> imageContainer;
 | |
|   GetImageData()->GetImage(getter_AddRefs(imageContainer));
 | |
|   MOZ_ASSERT(imageContainer, "IsComplete() said image container is ready");
 | |
| 
 | |
|   // Check if the crop region of the image is opaque.
 | |
|   if (imageContainer->WillDrawOpaqueNow()) {
 | |
|     if (!mCropRect) {
 | |
|       return true;
 | |
|     }
 | |
| 
 | |
|     // Must make sure if mCropRect contains at least a pixel.
 | |
|     // XXX Is this optimization worth it? Maybe I should just return false.
 | |
|     nsIntRect actualCropRect;
 | |
|     return ComputeActualCropRect(actualCropRect) && !actualCropRect.IsEmpty();
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::IsComplete() const {
 | |
|   switch (mType) {
 | |
|     case eStyleImageType_Null:
 | |
|       return false;
 | |
|     case eStyleImageType_Gradient:
 | |
|     case eStyleImageType_Element:
 | |
|     case eStyleImageType_URL:
 | |
|       return true;
 | |
|     case eStyleImageType_Image: {
 | |
|       if (!IsResolved()) {
 | |
|         return false;
 | |
|       }
 | |
|       imgRequestProxy* req = GetImageData();
 | |
|       if (!req) {
 | |
|         return false;
 | |
|       }
 | |
|       uint32_t status = imgIRequest::STATUS_ERROR;
 | |
|       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
 | |
|              (status & imgIRequest::STATUS_SIZE_AVAILABLE) &&
 | |
|              (status & imgIRequest::STATUS_FRAME_COMPLETE);
 | |
|     }
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("unexpected image type");
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::IsLoaded() const {
 | |
|   switch (mType) {
 | |
|     case eStyleImageType_Null:
 | |
|       return false;
 | |
|     case eStyleImageType_Gradient:
 | |
|     case eStyleImageType_Element:
 | |
|     case eStyleImageType_URL:
 | |
|       return true;
 | |
|     case eStyleImageType_Image: {
 | |
|       imgRequestProxy* req = GetImageData();
 | |
|       if (!req) {
 | |
|         return false;
 | |
|       }
 | |
|       uint32_t status = imgIRequest::STATUS_ERROR;
 | |
|       return NS_SUCCEEDED(req->GetImageStatus(&status)) &&
 | |
|              !(status & imgIRequest::STATUS_ERROR) &&
 | |
|              (status & imgIRequest::STATUS_LOAD_COMPLETE);
 | |
|     }
 | |
|     default:
 | |
|       MOZ_ASSERT_UNREACHABLE("unexpected image type");
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static inline bool EqualRects(const UniquePtr<nsStyleSides>& aRect1,
 | |
|                               const UniquePtr<nsStyleSides>& aRect2) {
 | |
|   return aRect1 == aRect2 || /* handles null== null, and optimize */
 | |
|          (aRect1 && aRect2 && *aRect1 == *aRect2);
 | |
| }
 | |
| 
 | |
| bool nsStyleImage::operator==(const nsStyleImage& aOther) const {
 | |
|   if (mType != aOther.mType) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!EqualRects(mCropRect, aOther.mCropRect)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_Image) {
 | |
|     return DefinitelyEqualImages(mImage, aOther.mImage);
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_Gradient) {
 | |
|     return *mGradient == *aOther.mGradient;
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_Element) {
 | |
|     return mElementId == aOther.mElementId;
 | |
|   }
 | |
| 
 | |
|   if (mType == eStyleImageType_URL) {
 | |
|     return DefinitelyEqualURIs(mURLValue, aOther.mURLValue);
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void nsStyleImage::PurgeCacheForViewportChange(
 | |
|     const mozilla::Maybe<nsSize>& aSVGViewportSize,
 | |
|     const bool aHasIntrinsicRatio) const {
 | |
|   EnsureCachedBIData();
 | |
| 
 | |
|   // If we're redrawing with a different viewport-size than we used for our
 | |
|   // cached subimages, then we can't trust that our subimages are valid;
 | |
|   // any percent sizes/positions in our SVG doc may be different now. Purge!
 | |
|   // (We don't have to purge if the SVG document has an intrinsic ratio,
 | |
|   // though, because the actual size of elements in SVG documant's coordinate
 | |
|   // axis are fixed in this case.)
 | |
|   if (aSVGViewportSize != mCachedBIData->GetCachedSVGViewportSize() &&
 | |
|       !aHasIntrinsicRatio) {
 | |
|     mCachedBIData->PurgeCachedImages();
 | |
|     mCachedBIData->SetCachedSVGViewportSize(aSVGViewportSize);
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsIURI> nsStyleImage::GetImageURI() const {
 | |
|   if (mType != eStyleImageType_Image) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIURI> uri = mImage->GetImageURI();
 | |
|   return uri.forget();
 | |
| }
 | |
| 
 | |
| const css::URLValue* nsStyleImage::GetURLValue() const {
 | |
|   if (mType == eStyleImageType_Image) {
 | |
|     return mImage->GetImageValue();
 | |
|   }
 | |
|   if (mType == eStyleImageType_URL) {
 | |
|     return mURLValue;
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleImageLayers
 | |
| //
 | |
| 
 | |
| const nsCSSPropertyID nsStyleImageLayers::kBackgroundLayerTable[] = {
 | |
|     eCSSProperty_background,             // shorthand
 | |
|     eCSSProperty_background_color,       // color
 | |
|     eCSSProperty_background_image,       // image
 | |
|     eCSSProperty_background_repeat,      // repeat
 | |
|     eCSSProperty_background_position_x,  // positionX
 | |
|     eCSSProperty_background_position_y,  // positionY
 | |
|     eCSSProperty_background_clip,        // clip
 | |
|     eCSSProperty_background_origin,      // origin
 | |
|     eCSSProperty_background_size,        // size
 | |
|     eCSSProperty_background_attachment,  // attachment
 | |
|     eCSSProperty_UNKNOWN,                // maskMode
 | |
|     eCSSProperty_UNKNOWN                 // composite
 | |
| };
 | |
| 
 | |
| const nsCSSPropertyID nsStyleImageLayers::kMaskLayerTable[] = {
 | |
|     eCSSProperty_mask,             // shorthand
 | |
|     eCSSProperty_UNKNOWN,          // color
 | |
|     eCSSProperty_mask_image,       // image
 | |
|     eCSSProperty_mask_repeat,      // repeat
 | |
|     eCSSProperty_mask_position_x,  // positionX
 | |
|     eCSSProperty_mask_position_y,  // positionY
 | |
|     eCSSProperty_mask_clip,        // clip
 | |
|     eCSSProperty_mask_origin,      // origin
 | |
|     eCSSProperty_mask_size,        // size
 | |
|     eCSSProperty_UNKNOWN,          // attachment
 | |
|     eCSSProperty_mask_mode,        // maskMode
 | |
|     eCSSProperty_mask_composite    // composite
 | |
| };
 | |
| 
 | |
| nsStyleImageLayers::nsStyleImageLayers(nsStyleImageLayers::LayerType aType)
 | |
|     : mAttachmentCount(1),
 | |
|       mClipCount(1),
 | |
|       mOriginCount(1),
 | |
|       mRepeatCount(1),
 | |
|       mPositionXCount(1),
 | |
|       mPositionYCount(1),
 | |
|       mImageCount(1),
 | |
|       mSizeCount(1),
 | |
|       mMaskModeCount(1),
 | |
|       mBlendModeCount(1),
 | |
|       mCompositeCount(1),
 | |
|       mLayers(nsStyleAutoArray<Layer>::WITH_SINGLE_INITIAL_ELEMENT) {
 | |
|   MOZ_COUNT_CTOR(nsStyleImageLayers);
 | |
| 
 | |
|   // Ensure first layer is initialized as specified layer type
 | |
|   mLayers[0].Initialize(aType);
 | |
| }
 | |
| 
 | |
| nsStyleImageLayers::nsStyleImageLayers(const nsStyleImageLayers& aSource)
 | |
|     : mAttachmentCount(aSource.mAttachmentCount),
 | |
|       mClipCount(aSource.mClipCount),
 | |
|       mOriginCount(aSource.mOriginCount),
 | |
|       mRepeatCount(aSource.mRepeatCount),
 | |
|       mPositionXCount(aSource.mPositionXCount),
 | |
|       mPositionYCount(aSource.mPositionYCount),
 | |
|       mImageCount(aSource.mImageCount),
 | |
|       mSizeCount(aSource.mSizeCount),
 | |
|       mMaskModeCount(aSource.mMaskModeCount),
 | |
|       mBlendModeCount(aSource.mBlendModeCount),
 | |
|       mCompositeCount(aSource.mCompositeCount),
 | |
|       mLayers(aSource.mLayers)  // deep copy
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsStyleImageLayers);
 | |
|   // If the deep copy of mLayers failed, truncate the counts.
 | |
|   uint32_t count = mLayers.Length();
 | |
|   if (count != aSource.mLayers.Length()) {
 | |
|     NS_WARNING("truncating counts due to out-of-memory");
 | |
|     mAttachmentCount = std::max(mAttachmentCount, count);
 | |
|     mClipCount = std::max(mClipCount, count);
 | |
|     mOriginCount = std::max(mOriginCount, count);
 | |
|     mRepeatCount = std::max(mRepeatCount, count);
 | |
|     mPositionXCount = std::max(mPositionXCount, count);
 | |
|     mPositionYCount = std::max(mPositionYCount, count);
 | |
|     mImageCount = std::max(mImageCount, count);
 | |
|     mSizeCount = std::max(mSizeCount, count);
 | |
|     mMaskModeCount = std::max(mMaskModeCount, count);
 | |
|     mBlendModeCount = std::max(mBlendModeCount, count);
 | |
|     mCompositeCount = std::max(mCompositeCount, count);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleImageLayers::CalcDifference(
 | |
|     const nsStyleImageLayers& aNewLayers,
 | |
|     nsStyleImageLayers::LayerType aType) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   const nsStyleImageLayers& moreLayers =
 | |
|       mImageCount > aNewLayers.mImageCount ? *this : aNewLayers;
 | |
|   const nsStyleImageLayers& lessLayers =
 | |
|       mImageCount > aNewLayers.mImageCount ? aNewLayers : *this;
 | |
| 
 | |
|   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, moreLayers) {
 | |
|     if (i < lessLayers.mImageCount) {
 | |
|       nsChangeHint layerDifference =
 | |
|           moreLayers.mLayers[i].CalcDifference(lessLayers.mLayers[i]);
 | |
|       hint |= layerDifference;
 | |
|       if (layerDifference && ((moreLayers.mLayers[i].mImage.GetType() ==
 | |
|                                eStyleImageType_Element) ||
 | |
|                               (lessLayers.mLayers[i].mImage.GetType() ==
 | |
|                                eStyleImageType_Element))) {
 | |
|         hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
 | |
|       }
 | |
|     } else {
 | |
|       hint |= nsChangeHint_RepaintFrame;
 | |
|       if (moreLayers.mLayers[i].mImage.GetType() == eStyleImageType_Element) {
 | |
|         hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aType == nsStyleImageLayers::LayerType::Mask &&
 | |
|       mImageCount != aNewLayers.mImageCount) {
 | |
|     hint |= nsChangeHint_UpdateEffects;
 | |
|   }
 | |
| 
 | |
|   if (hint) {
 | |
|     return hint;
 | |
|   }
 | |
| 
 | |
|   if (mAttachmentCount != aNewLayers.mAttachmentCount ||
 | |
|       mBlendModeCount != aNewLayers.mBlendModeCount ||
 | |
|       mClipCount != aNewLayers.mClipCount ||
 | |
|       mCompositeCount != aNewLayers.mCompositeCount ||
 | |
|       mMaskModeCount != aNewLayers.mMaskModeCount ||
 | |
|       mOriginCount != aNewLayers.mOriginCount ||
 | |
|       mRepeatCount != aNewLayers.mRepeatCount ||
 | |
|       mPositionXCount != aNewLayers.mPositionXCount ||
 | |
|       mPositionYCount != aNewLayers.mPositionYCount ||
 | |
|       mSizeCount != aNewLayers.mSizeCount) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| nsStyleImageLayers& nsStyleImageLayers::operator=(
 | |
|     const nsStyleImageLayers& aOther) {
 | |
|   mAttachmentCount = aOther.mAttachmentCount;
 | |
|   mClipCount = aOther.mClipCount;
 | |
|   mOriginCount = aOther.mOriginCount;
 | |
|   mRepeatCount = aOther.mRepeatCount;
 | |
|   mPositionXCount = aOther.mPositionXCount;
 | |
|   mPositionYCount = aOther.mPositionYCount;
 | |
|   mImageCount = aOther.mImageCount;
 | |
|   mSizeCount = aOther.mSizeCount;
 | |
|   mMaskModeCount = aOther.mMaskModeCount;
 | |
|   mBlendModeCount = aOther.mBlendModeCount;
 | |
|   mCompositeCount = aOther.mCompositeCount;
 | |
|   mLayers = aOther.mLayers;
 | |
| 
 | |
|   uint32_t count = mLayers.Length();
 | |
|   if (count != aOther.mLayers.Length()) {
 | |
|     NS_WARNING("truncating counts due to out-of-memory");
 | |
|     mAttachmentCount = std::max(mAttachmentCount, count);
 | |
|     mClipCount = std::max(mClipCount, count);
 | |
|     mOriginCount = std::max(mOriginCount, count);
 | |
|     mRepeatCount = std::max(mRepeatCount, count);
 | |
|     mPositionXCount = std::max(mPositionXCount, count);
 | |
|     mPositionYCount = std::max(mPositionYCount, count);
 | |
|     mImageCount = std::max(mImageCount, count);
 | |
|     mSizeCount = std::max(mSizeCount, count);
 | |
|     mMaskModeCount = std::max(mMaskModeCount, count);
 | |
|     mBlendModeCount = std::max(mBlendModeCount, count);
 | |
|     mCompositeCount = std::max(mCompositeCount, count);
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| nsStyleImageLayers& nsStyleImageLayers::operator=(nsStyleImageLayers&& aOther) {
 | |
|   mAttachmentCount = aOther.mAttachmentCount;
 | |
|   mClipCount = aOther.mClipCount;
 | |
|   mOriginCount = aOther.mOriginCount;
 | |
|   mRepeatCount = aOther.mRepeatCount;
 | |
|   mPositionXCount = aOther.mPositionXCount;
 | |
|   mPositionYCount = aOther.mPositionYCount;
 | |
|   mImageCount = aOther.mImageCount;
 | |
|   mSizeCount = aOther.mSizeCount;
 | |
|   mMaskModeCount = aOther.mMaskModeCount;
 | |
|   mBlendModeCount = aOther.mBlendModeCount;
 | |
|   mCompositeCount = aOther.mCompositeCount;
 | |
|   mLayers = std::move(aOther.mLayers);
 | |
| 
 | |
|   uint32_t count = mLayers.Length();
 | |
|   if (count != aOther.mLayers.Length()) {
 | |
|     NS_WARNING("truncating counts due to out-of-memory");
 | |
|     mAttachmentCount = std::max(mAttachmentCount, count);
 | |
|     mClipCount = std::max(mClipCount, count);
 | |
|     mOriginCount = std::max(mOriginCount, count);
 | |
|     mRepeatCount = std::max(mRepeatCount, count);
 | |
|     mPositionXCount = std::max(mPositionXCount, count);
 | |
|     mPositionYCount = std::max(mPositionYCount, count);
 | |
|     mImageCount = std::max(mImageCount, count);
 | |
|     mSizeCount = std::max(mSizeCount, count);
 | |
|     mMaskModeCount = std::max(mMaskModeCount, count);
 | |
|     mBlendModeCount = std::max(mBlendModeCount, count);
 | |
|     mCompositeCount = std::max(mCompositeCount, count);
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool nsStyleImageLayers::operator==(const nsStyleImageLayers& aOther) const {
 | |
|   if (mAttachmentCount != aOther.mAttachmentCount ||
 | |
|       mClipCount != aOther.mClipCount || mOriginCount != aOther.mOriginCount ||
 | |
|       mRepeatCount != aOther.mRepeatCount ||
 | |
|       mPositionXCount != aOther.mPositionXCount ||
 | |
|       mPositionYCount != aOther.mPositionYCount ||
 | |
|       mImageCount != aOther.mImageCount || mSizeCount != aOther.mSizeCount ||
 | |
|       mMaskModeCount != aOther.mMaskModeCount ||
 | |
|       mBlendModeCount != aOther.mBlendModeCount) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (mLayers.Length() != aOther.mLayers.Length()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < mLayers.Length(); i++) {
 | |
|     if (mLayers[i].mPosition != aOther.mLayers[i].mPosition ||
 | |
|         !DefinitelyEqualURIs(mLayers[i].mImage.GetURLValue(),
 | |
|                              aOther.mLayers[i].mImage.GetURLValue()) ||
 | |
|         mLayers[i].mImage != aOther.mLayers[i].mImage ||
 | |
|         mLayers[i].mSize != aOther.mLayers[i].mSize ||
 | |
|         mLayers[i].mClip != aOther.mLayers[i].mClip ||
 | |
|         mLayers[i].mOrigin != aOther.mLayers[i].mOrigin ||
 | |
|         mLayers[i].mAttachment != aOther.mLayers[i].mAttachment ||
 | |
|         mLayers[i].mBlendMode != aOther.mLayers[i].mBlendMode ||
 | |
|         mLayers[i].mComposite != aOther.mLayers[i].mComposite ||
 | |
|         mLayers[i].mMaskMode != aOther.mLayers[i].mMaskMode ||
 | |
|         mLayers[i].mRepeat != aOther.mLayers[i].mRepeat) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool nsStyleImageLayers::IsInitialPositionForLayerType(Position aPosition,
 | |
|                                                        LayerType aType) {
 | |
|   return aPosition == Position::FromPercentage(0.);
 | |
| }
 | |
| 
 | |
| static bool SizeDependsOnPositioningAreaSize(const StyleBackgroundSize& aSize,
 | |
|                                              const nsStyleImage& aImage) {
 | |
|   MOZ_ASSERT(aImage.GetType() != eStyleImageType_Null,
 | |
|              "caller should have handled this");
 | |
| 
 | |
|   // Contain and cover straightforwardly depend on frame size.
 | |
|   if (aSize.IsCover() || aSize.IsContain()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(aSize.IsExplicitSize());
 | |
|   auto& size = aSize.explicit_size;
 | |
| 
 | |
|   // If either dimension contains a non-zero percentage, rendering for that
 | |
|   // dimension straightforwardly depends on frame size.
 | |
|   if (size.width.HasPercent() || size.height.HasPercent()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // If both dimensions are fixed lengths, there's no dependency.
 | |
|   if (!size.width.IsAuto() && !size.height.IsAuto()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsStyleImageType type = aImage.GetType();
 | |
| 
 | |
|   // Gradient rendering depends on frame size when auto is involved because
 | |
|   // gradients have no intrinsic ratio or dimensions, and therefore the relevant
 | |
|   // dimension is "treat[ed] as 100%".
 | |
|   if (type == eStyleImageType_Gradient) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // XXX Element rendering for auto or fixed length doesn't depend on frame size
 | |
|   //     according to the spec.  However, we don't implement the spec yet, so
 | |
|   //     for now we bail and say element() plus auto affects ultimate size.
 | |
|   if (type == eStyleImageType_Element) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (type == eStyleImageType_Image) {
 | |
|     nsCOMPtr<imgIContainer> imgContainer;
 | |
|     if (imgRequestProxy* req = aImage.GetImageData()) {
 | |
|       req->GetImage(getter_AddRefs(imgContainer));
 | |
|     }
 | |
|     if (imgContainer) {
 | |
|       CSSIntSize imageSize;
 | |
|       nsSize imageRatio;
 | |
|       bool hasWidth, hasHeight;
 | |
|       nsLayoutUtils::ComputeSizeForDrawing(imgContainer, imageSize, imageRatio,
 | |
|                                            hasWidth, hasHeight);
 | |
| 
 | |
|       // If the image has a fixed width and height, rendering never depends on
 | |
|       // the frame size.
 | |
|       if (hasWidth && hasHeight) {
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       // If the image has an intrinsic ratio, rendering will depend on frame
 | |
|       // size when background-size is all auto.
 | |
|       if (imageRatio != nsSize(0, 0)) {
 | |
|         return size.width.IsAuto() == size.height.IsAuto();
 | |
|       }
 | |
| 
 | |
|       // Otherwise, rendering depends on frame size when the image dimensions
 | |
|       // and background-size don't complement each other.
 | |
|       return !(hasWidth && size.width.IsLengthPercentage()) &&
 | |
|              !(hasHeight && size.height.IsLengthPercentage());
 | |
|     }
 | |
|   } else {
 | |
|     MOZ_ASSERT_UNREACHABLE("missed an enum value");
 | |
|   }
 | |
| 
 | |
|   // Passed the gauntlet: no dependency.
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| nsStyleImageLayers::Layer::Layer()
 | |
|     : mSize(StyleBackgroundSize::ExplicitSize(LengthPercentageOrAuto::Auto(),
 | |
|                                               LengthPercentageOrAuto::Auto())),
 | |
|       mClip(StyleGeometryBox::BorderBox),
 | |
|       mAttachment(StyleImageLayerAttachment::Scroll),
 | |
|       mBlendMode(NS_STYLE_BLEND_NORMAL),
 | |
|       mComposite(NS_STYLE_MASK_COMPOSITE_ADD),
 | |
|       mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) {
 | |
|   mImage.SetNull();
 | |
| }
 | |
| 
 | |
| nsStyleImageLayers::Layer::~Layer() {}
 | |
| 
 | |
| void nsStyleImageLayers::Layer::Initialize(
 | |
|     nsStyleImageLayers::LayerType aType) {
 | |
|   mRepeat.SetInitialValues();
 | |
| 
 | |
|   mPosition = Position::FromPercentage(0.);
 | |
| 
 | |
|   if (aType == LayerType::Background) {
 | |
|     mOrigin = StyleGeometryBox::PaddingBox;
 | |
|   } else {
 | |
|     MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type.");
 | |
|     mOrigin = StyleGeometryBox::BorderBox;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsStyleImageLayers::Layer::
 | |
|     RenderingMightDependOnPositioningAreaSizeChange() const {
 | |
|   // Do we even have an image?
 | |
|   if (mImage.IsEmpty()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return mPosition.DependsOnPositioningAreaSize() ||
 | |
|          SizeDependsOnPositioningAreaSize(mSize, mImage) ||
 | |
|          mRepeat.DependsOnPositioningAreaSize();
 | |
| }
 | |
| 
 | |
| bool nsStyleImageLayers::Layer::operator==(const Layer& aOther) const {
 | |
|   return mAttachment == aOther.mAttachment && mClip == aOther.mClip &&
 | |
|          mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat &&
 | |
|          mBlendMode == aOther.mBlendMode && mPosition == aOther.mPosition &&
 | |
|          mSize == aOther.mSize && mImage == aOther.mImage &&
 | |
|          mMaskMode == aOther.mMaskMode && mComposite == aOther.mComposite;
 | |
| }
 | |
| 
 | |
| template <class ComputedValueItem>
 | |
| static void FillImageLayerList(
 | |
|     nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
 | |
|     ComputedValueItem nsStyleImageLayers::Layer::*aResultLocation,
 | |
|     uint32_t aItemCount, uint32_t aFillCount) {
 | |
|   MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
 | |
|   for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
 | |
|        ++sourceLayer, ++destLayer) {
 | |
|     aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // The same as FillImageLayerList, but for values stored in
 | |
| // layer.mPosition.*aResultLocation instead of layer.*aResultLocation.
 | |
| static void FillImageLayerPositionCoordList(
 | |
|     nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers,
 | |
|     LengthPercentage Position::*aResultLocation, uint32_t aItemCount,
 | |
|     uint32_t aFillCount) {
 | |
|   MOZ_ASSERT(aFillCount <= aLayers.Length(), "unexpected array length");
 | |
|   for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount;
 | |
|        ++sourceLayer, ++destLayer) {
 | |
|     aLayers[destLayer].mPosition.*aResultLocation =
 | |
|         aLayers[sourceLayer].mPosition.*aResultLocation;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsStyleImageLayers::FillAllLayers(uint32_t aMaxItemCount) {
 | |
|   // Delete any extra items.  We need to keep layers in which any
 | |
|   // property was specified.
 | |
|   mLayers.TruncateLengthNonZero(aMaxItemCount);
 | |
| 
 | |
|   uint32_t fillCount = mImageCount;
 | |
|   FillImageLayerList(mLayers, &Layer::mImage, mImageCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mRepeat, mRepeatCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mAttachment, mAttachmentCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mClip, mClipCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mBlendMode, mBlendModeCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mOrigin, mOriginCount, fillCount);
 | |
|   FillImageLayerPositionCoordList(mLayers, &Position::horizontal,
 | |
|                                   mPositionXCount, fillCount);
 | |
|   FillImageLayerPositionCoordList(mLayers, &Position::vertical, mPositionYCount,
 | |
|                                   fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mSize, mSizeCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mMaskMode, mMaskModeCount, fillCount);
 | |
|   FillImageLayerList(mLayers, &Layer::mComposite, mCompositeCount, fillCount);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleImageLayers::Layer::CalcDifference(
 | |
|     const nsStyleImageLayers::Layer& aNewLayer) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
|   if (!DefinitelyEqualURIs(mImage.GetURLValue(),
 | |
|                            aNewLayer.mImage.GetURLValue())) {
 | |
|     hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects;
 | |
|   } else if (mAttachment != aNewLayer.mAttachment || mClip != aNewLayer.mClip ||
 | |
|              mOrigin != aNewLayer.mOrigin || mRepeat != aNewLayer.mRepeat ||
 | |
|              mBlendMode != aNewLayer.mBlendMode || mSize != aNewLayer.mSize ||
 | |
|              mImage != aNewLayer.mImage || mMaskMode != aNewLayer.mMaskMode ||
 | |
|              mComposite != aNewLayer.mComposite) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mPosition != aNewLayer.mPosition) {
 | |
|     hint |= nsChangeHint_UpdateBackgroundPosition;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleBackground
 | |
| //
 | |
| 
 | |
| nsStyleBackground::nsStyleBackground(const Document& aDocument)
 | |
|     : mImage(nsStyleImageLayers::LayerType::Background),
 | |
|       mBackgroundColor(StyleComplexColor::Transparent()) {
 | |
|   MOZ_COUNT_CTOR(nsStyleBackground);
 | |
| }
 | |
| 
 | |
| nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource)
 | |
|     : mImage(aSource.mImage), mBackgroundColor(aSource.mBackgroundColor) {
 | |
|   MOZ_COUNT_CTOR(nsStyleBackground);
 | |
| }
 | |
| 
 | |
| nsStyleBackground::~nsStyleBackground() { MOZ_COUNT_DTOR(nsStyleBackground); }
 | |
| 
 | |
| void nsStyleBackground::TriggerImageLoads(Document& aDocument,
 | |
|                                           const nsStyleBackground* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   mImage.ResolveImages(aDocument, aOldStyle ? &aOldStyle->mImage : nullptr);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleBackground::CalcDifference(
 | |
|     const nsStyleBackground& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
|   if (mBackgroundColor != aNewData.mBackgroundColor) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   hint |= mImage.CalcDifference(aNewData.mImage,
 | |
|                                 nsStyleImageLayers::LayerType::Background);
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| bool nsStyleBackground::HasFixedBackground(nsIFrame* aFrame) const {
 | |
|   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, mImage) {
 | |
|     const nsStyleImageLayers::Layer& layer = mImage.mLayers[i];
 | |
|     if (layer.mAttachment == StyleImageLayerAttachment::Fixed &&
 | |
|         !layer.mImage.IsEmpty() && !nsLayoutUtils::IsTransformed(aFrame)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| nscolor nsStyleBackground::BackgroundColor(const nsIFrame* aFrame) const {
 | |
|   return mBackgroundColor.CalcColor(aFrame);
 | |
| }
 | |
| 
 | |
| nscolor nsStyleBackground::BackgroundColor(
 | |
|     mozilla::ComputedStyle* aStyle) const {
 | |
|   return mBackgroundColor.CalcColor(aStyle);
 | |
| }
 | |
| 
 | |
| bool nsStyleBackground::IsTransparent(const nsIFrame* aFrame) const {
 | |
|   return IsTransparent(aFrame->Style());
 | |
| }
 | |
| 
 | |
| bool nsStyleBackground::IsTransparent(mozilla::ComputedStyle* aStyle) const {
 | |
|   return BottomLayer().mImage.IsEmpty() && mImage.mImageCount == 1 &&
 | |
|          NS_GET_A(BackgroundColor(aStyle)) == 0;
 | |
| }
 | |
| 
 | |
| StyleTransition::StyleTransition(const StyleTransition& aCopy)
 | |
|     : mTimingFunction(aCopy.mTimingFunction),
 | |
|       mDuration(aCopy.mDuration),
 | |
|       mDelay(aCopy.mDelay),
 | |
|       mProperty(aCopy.mProperty),
 | |
|       mUnknownProperty(aCopy.mUnknownProperty) {}
 | |
| 
 | |
| void StyleTransition::SetInitialValues() {
 | |
|   mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
 | |
|   mDuration = 0.0;
 | |
|   mDelay = 0.0;
 | |
|   mProperty = eCSSPropertyExtra_all_properties;
 | |
| }
 | |
| 
 | |
| bool StyleTransition::operator==(const StyleTransition& aOther) const {
 | |
|   return mTimingFunction == aOther.mTimingFunction &&
 | |
|          mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
 | |
|          mProperty == aOther.mProperty &&
 | |
|          (mProperty != eCSSProperty_UNKNOWN ||
 | |
|           mUnknownProperty == aOther.mUnknownProperty);
 | |
| }
 | |
| 
 | |
| StyleAnimation::StyleAnimation(const StyleAnimation& aCopy)
 | |
|     : mTimingFunction(aCopy.mTimingFunction),
 | |
|       mDuration(aCopy.mDuration),
 | |
|       mDelay(aCopy.mDelay),
 | |
|       mName(aCopy.mName),
 | |
|       mDirection(aCopy.mDirection),
 | |
|       mFillMode(aCopy.mFillMode),
 | |
|       mPlayState(aCopy.mPlayState),
 | |
|       mIterationCount(aCopy.mIterationCount) {}
 | |
| 
 | |
| void StyleAnimation::SetInitialValues() {
 | |
|   mTimingFunction = nsTimingFunction(StyleTimingKeyword::Ease);
 | |
|   mDuration = 0.0;
 | |
|   mDelay = 0.0;
 | |
|   mName = nsGkAtoms::_empty;
 | |
|   mDirection = dom::PlaybackDirection::Normal;
 | |
|   mFillMode = dom::FillMode::None;
 | |
|   mPlayState = StyleAnimationPlayState::Running;
 | |
|   mIterationCount = 1.0f;
 | |
| }
 | |
| 
 | |
| bool StyleAnimation::operator==(const StyleAnimation& aOther) const {
 | |
|   return mTimingFunction == aOther.mTimingFunction &&
 | |
|          mDuration == aOther.mDuration && mDelay == aOther.mDelay &&
 | |
|          mName == aOther.mName && mDirection == aOther.mDirection &&
 | |
|          mFillMode == aOther.mFillMode && mPlayState == aOther.mPlayState &&
 | |
|          mIterationCount == aOther.mIterationCount;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleDisplay
 | |
| //
 | |
| nsStyleDisplay::nsStyleDisplay(const Document& aDocument)
 | |
|     : mDisplay(StyleDisplay::Inline),
 | |
|       mOriginalDisplay(StyleDisplay::Inline),
 | |
|       mContain(NS_STYLE_CONTAIN_NONE),
 | |
|       mAppearance(StyleAppearance::None),
 | |
|       mPosition(NS_STYLE_POSITION_STATIC),
 | |
|       mFloat(StyleFloat::None),
 | |
|       mOriginalFloat(StyleFloat::None),
 | |
|       mBreakType(StyleClear::None),
 | |
|       mBreakInside(StyleBreakWithin::Auto),
 | |
|       mBreakBefore(StyleBreakBetween::Auto),
 | |
|       mBreakAfter(StyleBreakBetween::Auto),
 | |
|       mOverflowX(StyleOverflow::Visible),
 | |
|       mOverflowY(StyleOverflow::Visible),
 | |
|       mOverflowClipBoxBlock(StyleOverflowClipBox::PaddingBox),
 | |
|       mOverflowClipBoxInline(StyleOverflowClipBox::PaddingBox),
 | |
|       mResize(StyleResize::None),
 | |
|       mOrient(StyleOrient::Inline),
 | |
|       mIsolation(NS_STYLE_ISOLATION_AUTO),
 | |
|       mTopLayer(NS_STYLE_TOP_LAYER_NONE),
 | |
|       mWillChangeBitField(0),
 | |
|       mTouchAction(NS_STYLE_TOUCH_ACTION_AUTO),
 | |
|       mScrollBehavior(NS_STYLE_SCROLL_BEHAVIOR_AUTO),
 | |
|       mOverscrollBehaviorX(StyleOverscrollBehavior::Auto),
 | |
|       mOverscrollBehaviorY(StyleOverscrollBehavior::Auto),
 | |
|       mOverflowAnchor(StyleOverflowAnchor::Auto),
 | |
|       mScrollSnapTypeX(StyleScrollSnapType::None),
 | |
|       mScrollSnapTypeY(StyleScrollSnapType::None),
 | |
|       mScrollSnapPointsX(eStyleUnit_None),
 | |
|       mScrollSnapPointsY(eStyleUnit_None),
 | |
|       mScrollSnapDestination(
 | |
|           {LengthPercentage::Zero(), LengthPercentage::Zero()}),
 | |
|       mBackfaceVisibility(NS_STYLE_BACKFACE_VISIBILITY_VISIBLE),
 | |
|       mTransformStyle(NS_STYLE_TRANSFORM_STYLE_FLAT),
 | |
|       mTransformBox(StyleGeometryBox::BorderBox),
 | |
|       mTransformOrigin{
 | |
|           {0.5f, eStyleUnit_Percent},  // Transform is centered on origin
 | |
|           {0.5f, eStyleUnit_Percent},
 | |
|           {0, nsStyleCoord::CoordConstructor}},
 | |
|       mChildPerspective(eStyleUnit_None),
 | |
|       mPerspectiveOrigin{{0.5f, eStyleUnit_Percent},
 | |
|                          {0.5f, eStyleUnit_Percent}},
 | |
|       mVerticalAlign(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated),
 | |
|       mTransitions(
 | |
|           nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
 | |
|       mTransitionTimingFunctionCount(1),
 | |
|       mTransitionDurationCount(1),
 | |
|       mTransitionDelayCount(1),
 | |
|       mTransitionPropertyCount(1),
 | |
|       mAnimations(
 | |
|           nsStyleAutoArray<StyleAnimation>::WITH_SINGLE_INITIAL_ELEMENT),
 | |
|       mAnimationTimingFunctionCount(1),
 | |
|       mAnimationDurationCount(1),
 | |
|       mAnimationDelayCount(1),
 | |
|       mAnimationNameCount(1),
 | |
|       mAnimationDirectionCount(1),
 | |
|       mAnimationFillModeCount(1),
 | |
|       mAnimationPlayStateCount(1),
 | |
|       mAnimationIterationCountCount(1),
 | |
|       mShapeMargin(LengthPercentage::Zero()) {
 | |
|   MOZ_COUNT_CTOR(nsStyleDisplay);
 | |
| 
 | |
|   mTransitions[0].SetInitialValues();
 | |
|   mAnimations[0].SetInitialValues();
 | |
| }
 | |
| 
 | |
| nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource)
 | |
|     : mBinding(aSource.mBinding),
 | |
|       mDisplay(aSource.mDisplay),
 | |
|       mOriginalDisplay(aSource.mOriginalDisplay),
 | |
|       mContain(aSource.mContain),
 | |
|       mAppearance(aSource.mAppearance),
 | |
|       mPosition(aSource.mPosition),
 | |
|       mFloat(aSource.mFloat),
 | |
|       mOriginalFloat(aSource.mOriginalFloat),
 | |
|       mBreakType(aSource.mBreakType),
 | |
|       mBreakInside(aSource.mBreakInside),
 | |
|       mBreakBefore(aSource.mBreakBefore),
 | |
|       mBreakAfter(aSource.mBreakAfter),
 | |
|       mOverflowX(aSource.mOverflowX),
 | |
|       mOverflowY(aSource.mOverflowY),
 | |
|       mOverflowClipBoxBlock(aSource.mOverflowClipBoxBlock),
 | |
|       mOverflowClipBoxInline(aSource.mOverflowClipBoxInline),
 | |
|       mResize(aSource.mResize),
 | |
|       mOrient(aSource.mOrient),
 | |
|       mIsolation(aSource.mIsolation),
 | |
|       mTopLayer(aSource.mTopLayer),
 | |
|       mWillChangeBitField(aSource.mWillChangeBitField),
 | |
|       mWillChange(aSource.mWillChange),
 | |
|       mTouchAction(aSource.mTouchAction),
 | |
|       mScrollBehavior(aSource.mScrollBehavior),
 | |
|       mOverscrollBehaviorX(aSource.mOverscrollBehaviorX),
 | |
|       mOverscrollBehaviorY(aSource.mOverscrollBehaviorY),
 | |
|       mScrollSnapTypeX(aSource.mScrollSnapTypeX),
 | |
|       mScrollSnapTypeY(aSource.mScrollSnapTypeY),
 | |
|       mScrollSnapPointsX(aSource.mScrollSnapPointsX),
 | |
|       mScrollSnapPointsY(aSource.mScrollSnapPointsY),
 | |
|       mScrollSnapDestination(aSource.mScrollSnapDestination),
 | |
|       mScrollSnapCoordinate(aSource.mScrollSnapCoordinate),
 | |
|       mBackfaceVisibility(aSource.mBackfaceVisibility),
 | |
|       mTransformStyle(aSource.mTransformStyle),
 | |
|       mTransformBox(aSource.mTransformBox),
 | |
|       mSpecifiedTransform(aSource.mSpecifiedTransform),
 | |
|       mSpecifiedRotate(aSource.mSpecifiedRotate),
 | |
|       mSpecifiedTranslate(aSource.mSpecifiedTranslate),
 | |
|       mSpecifiedScale(aSource.mSpecifiedScale),
 | |
|       // We intentionally leave mIndividualTransform as null, is the caller's
 | |
|       // responsibility to call GenerateCombinedIndividualTransform when
 | |
|       // appropriate.
 | |
|       mMotion(aSource.mMotion ? MakeUnique<StyleMotion>(*aSource.mMotion)
 | |
|                               : nullptr),
 | |
|       mTransformOrigin{aSource.mTransformOrigin[0], aSource.mTransformOrigin[1],
 | |
|                        aSource.mTransformOrigin[2]},
 | |
|       mChildPerspective(aSource.mChildPerspective),
 | |
|       mPerspectiveOrigin{aSource.mPerspectiveOrigin[0],
 | |
|                          aSource.mPerspectiveOrigin[1]},
 | |
|       mVerticalAlign(aSource.mVerticalAlign),
 | |
|       mTransitions(aSource.mTransitions),
 | |
|       mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount),
 | |
|       mTransitionDurationCount(aSource.mTransitionDurationCount),
 | |
|       mTransitionDelayCount(aSource.mTransitionDelayCount),
 | |
|       mTransitionPropertyCount(aSource.mTransitionPropertyCount),
 | |
|       mAnimations(aSource.mAnimations),
 | |
|       mAnimationTimingFunctionCount(aSource.mAnimationTimingFunctionCount),
 | |
|       mAnimationDurationCount(aSource.mAnimationDurationCount),
 | |
|       mAnimationDelayCount(aSource.mAnimationDelayCount),
 | |
|       mAnimationNameCount(aSource.mAnimationNameCount),
 | |
|       mAnimationDirectionCount(aSource.mAnimationDirectionCount),
 | |
|       mAnimationFillModeCount(aSource.mAnimationFillModeCount),
 | |
|       mAnimationPlayStateCount(aSource.mAnimationPlayStateCount),
 | |
|       mAnimationIterationCountCount(aSource.mAnimationIterationCountCount),
 | |
|       mShapeImageThreshold(aSource.mShapeImageThreshold),
 | |
|       mShapeMargin(aSource.mShapeMargin),
 | |
|       mShapeOutside(aSource.mShapeOutside) {
 | |
|   MOZ_COUNT_CTOR(nsStyleDisplay);
 | |
| }
 | |
| 
 | |
| static void ReleaseSharedListOnMainThread(const char* aName,
 | |
|                                           RefPtr<nsCSSValueSharedList>& aList) {
 | |
|   // We don't allow releasing nsCSSValues with refcounted data in the Servo
 | |
|   // traversal, since the refcounts aren't threadsafe. Since Servo may trigger
 | |
|   // the deallocation of style structs during styling, we need to handle it
 | |
|   // here.
 | |
|   if (aList && ServoStyleSet::IsInServoTraversal()) {
 | |
|     // The default behavior of NS_ReleaseOnMainThreadSystemGroup is to only
 | |
|     // proxy the release if we're not already on the main thread. This is a nice
 | |
|     // optimization for the cases we happen to be doing a sequential traversal
 | |
|     // (i.e. a single-core machine), but it trips our assertions which check
 | |
|     // whether we're in a Servo traversal, parallel or not. So we
 | |
|     // unconditionally proxy in debug builds.
 | |
|     bool alwaysProxy =
 | |
| #ifdef DEBUG
 | |
|         true;
 | |
| #else
 | |
|         false;
 | |
| #endif
 | |
|     NS_ReleaseOnMainThreadSystemGroup(aName, aList.forget(), alwaysProxy);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleDisplay::~nsStyleDisplay() {
 | |
|   ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedTransform",
 | |
|                                 mSpecifiedTransform);
 | |
|   ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedRotate",
 | |
|                                 mSpecifiedRotate);
 | |
|   ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedTranslate",
 | |
|                                 mSpecifiedTranslate);
 | |
|   ReleaseSharedListOnMainThread("nsStyleDisplay::mSpecifiedScale",
 | |
|                                 mSpecifiedScale);
 | |
|   ReleaseSharedListOnMainThread("nsStyleDisplay::mIndividualTransform",
 | |
|                                 mIndividualTransform);
 | |
|   MOZ_COUNT_DTOR(nsStyleDisplay);
 | |
| }
 | |
| 
 | |
| void nsStyleDisplay::TriggerImageLoads(Document& aDocument,
 | |
|                                        const nsStyleDisplay* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   mShapeOutside.TriggerImageLoads(
 | |
|       aDocument, aOldStyle ? &aOldStyle->mShapeOutside : nullptr);
 | |
| }
 | |
| 
 | |
| static inline bool TransformListChanged(
 | |
|     const RefPtr<nsCSSValueSharedList>& aList,
 | |
|     const RefPtr<nsCSSValueSharedList>& aNewList) {
 | |
|   return !aList != !aNewList || (aList && *aList != *aNewList);
 | |
| }
 | |
| 
 | |
| static inline nsChangeHint CompareTransformValues(
 | |
|     const RefPtr<nsCSSValueSharedList>& aList,
 | |
|     const RefPtr<nsCSSValueSharedList>& aNewList) {
 | |
|   nsChangeHint result = nsChangeHint(0);
 | |
| 
 | |
|   // Note: If we add a new change hint for transform changes here, we have to
 | |
|   // modify KeyframeEffect::CalculateCumulativeChangeHint too!
 | |
|   if (!aList != !aNewList || (aList && *aList != *aNewList)) {
 | |
|     result |= nsChangeHint_UpdateTransformLayer;
 | |
|     if (aList && aNewList) {
 | |
|       result |= nsChangeHint_UpdatePostTransformOverflow;
 | |
|     } else {
 | |
|       result |= nsChangeHint_UpdateOverflow;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| static inline nsChangeHint CompareMotionValues(const StyleMotion* aMotion,
 | |
|                                                const StyleMotion* aNewMotion) {
 | |
|   nsChangeHint result = nsChangeHint(0);
 | |
| 
 | |
|   // TODO: Bug 1482737: This probably doesn't need to UpdateOverflow
 | |
|   // (or UpdateTransformLayer) if there's already a transform.
 | |
|   if (!aMotion != !aNewMotion || (aMotion && *aMotion != *aNewMotion)) {
 | |
|     // Set the same hints as what we use for transform because motion path is
 | |
|     // a kind of transform and will be combined with other transforms.
 | |
|     result |= nsChangeHint_UpdateTransformLayer;
 | |
|     if ((aMotion && aMotion->HasPath()) &&
 | |
|         (aNewMotion && aNewMotion->HasPath())) {
 | |
|       result |= nsChangeHint_UpdatePostTransformOverflow;
 | |
|     } else {
 | |
|       result |= nsChangeHint_UpdateOverflow;
 | |
|     }
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleDisplay::CalcDifference(
 | |
|     const nsStyleDisplay& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (!DefinitelyEqualURIsAndPrincipal(mBinding, aNewData.mBinding) ||
 | |
|       mPosition != aNewData.mPosition || mDisplay != aNewData.mDisplay ||
 | |
|       mContain != aNewData.mContain ||
 | |
|       (mFloat == StyleFloat::None) != (aNewData.mFloat == StyleFloat::None) ||
 | |
|       mScrollBehavior != aNewData.mScrollBehavior ||
 | |
|       mScrollSnapTypeX != aNewData.mScrollSnapTypeX ||
 | |
|       mScrollSnapTypeY != aNewData.mScrollSnapTypeY ||
 | |
|       mScrollSnapPointsX != aNewData.mScrollSnapPointsX ||
 | |
|       mScrollSnapPointsY != aNewData.mScrollSnapPointsY ||
 | |
|       mScrollSnapDestination != aNewData.mScrollSnapDestination ||
 | |
|       mTopLayer != aNewData.mTopLayer || mResize != aNewData.mResize) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if ((mAppearance == StyleAppearance::Textfield &&
 | |
|        aNewData.mAppearance != StyleAppearance::Textfield) ||
 | |
|       (mAppearance != StyleAppearance::Textfield &&
 | |
|        aNewData.mAppearance == StyleAppearance::Textfield)) {
 | |
|     // This is for <input type=number> where we allow authors to specify a
 | |
|     // |-moz-appearance:textfield| to get a control without a spinner. (The
 | |
|     // spinner is present for |-moz-appearance:number-input| but also other
 | |
|     // values such as 'none'.) We need to reframe since we want to use
 | |
|     // nsTextControlFrame instead of nsNumberControlFrame if the author
 | |
|     // specifies 'textfield'.
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if (mOverflowX != aNewData.mOverflowX || mOverflowY != aNewData.mOverflowY) {
 | |
|     hint |= nsChangeHint_ScrollbarChange;
 | |
|   }
 | |
| 
 | |
|   /* Note: When mScrollBehavior, mScrollSnapTypeX, mScrollSnapTypeY,
 | |
|    * mScrollSnapPointsX, mScrollSnapPointsY, or mScrollSnapDestination are
 | |
|    * changed, nsChangeHint_NeutralChange is not sufficient to enter
 | |
|    * nsCSSFrameConstructor::PropagateScrollToViewport. By using the same hint
 | |
|    * as used when the overflow css property changes,
 | |
|    * nsChangeHint_ReconstructFrame, PropagateScrollToViewport will be called.
 | |
|    *
 | |
|    * The scroll-behavior css property is not expected to change often (the
 | |
|    * CSSOM-View DOM methods are likely to be used in those cases); however,
 | |
|    * if this does become common perhaps a faster-path might be worth while.
 | |
|    */
 | |
| 
 | |
|   if (mFloat != aNewData.mFloat) {
 | |
|     // Changing which side we're floating on (float:none was handled above).
 | |
|     hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
 | |
|   }
 | |
| 
 | |
|   if (mShapeOutside != aNewData.mShapeOutside ||
 | |
|       mShapeMargin != aNewData.mShapeMargin ||
 | |
|       mShapeImageThreshold != aNewData.mShapeImageThreshold) {
 | |
|     if (aNewData.mFloat != StyleFloat::None) {
 | |
|       // If we are floating, and our shape-outside, shape-margin, or
 | |
|       // shape-image-threshold are changed, our descendants are not impacted,
 | |
|       // but our ancestor and siblings are.
 | |
|       hint |= nsChangeHint_ReflowHintsForFloatAreaChange;
 | |
|     } else {
 | |
|       // shape-outside or shape-margin or shape-image-threshold changed,
 | |
|       // but we don't need to reflow because we're not floating.
 | |
|       hint |= nsChangeHint_NeutralChange;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mVerticalAlign != aNewData.mVerticalAlign) {
 | |
|     // XXX Can this just be AllReflowHints + RepaintFrame, and be included in
 | |
|     // the block below?
 | |
|     hint |= NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   // XXX the following is conservative, for now: changing float breaking
 | |
|   // shouldn't necessarily require a repaint, reflow should suffice.
 | |
|   //
 | |
|   // FIXME(emilio): We definitely change the frame tree in nsCSSFrameConstructor
 | |
|   // based on break-before / break-after... Shouldn't that reframe?
 | |
|   if (mBreakType != aNewData.mBreakType ||
 | |
|       mBreakInside != aNewData.mBreakInside ||
 | |
|       mBreakBefore != aNewData.mBreakBefore ||
 | |
|       mBreakAfter != aNewData.mBreakAfter ||
 | |
|       mAppearance != aNewData.mAppearance || mOrient != aNewData.mOrient ||
 | |
|       mOverflowClipBoxBlock != aNewData.mOverflowClipBoxBlock ||
 | |
|       mOverflowClipBoxInline != aNewData.mOverflowClipBoxInline) {
 | |
|     hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mIsolation != aNewData.mIsolation) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   /* If we've added or removed the transform property, we need to reconstruct
 | |
|    * the frame to add or remove the view object, and also to handle abs-pos and
 | |
|    * fixed-pos containers.
 | |
|    */
 | |
|   if (HasTransformStyle() != aNewData.HasTransformStyle()) {
 | |
|     hint |= nsChangeHint_ComprehensiveAddOrRemoveTransform;
 | |
|   } else {
 | |
|     /* Otherwise, if we've kept the property lying around and we already had a
 | |
|      * transform, we need to see whether or not we've changed the transform.
 | |
|      * If so, we need to recompute its overflow rect (which probably changed
 | |
|      * if the transform changed) and to redraw within the bounds of that new
 | |
|      * overflow rect.
 | |
|      *
 | |
|      * If the property isn't present in either style struct, we still do the
 | |
|      * comparisons but turn all the resulting change hints into
 | |
|      * nsChangeHint_NeutralChange.
 | |
|      */
 | |
|     nsChangeHint transformHint = nsChangeHint(0);
 | |
| 
 | |
|     transformHint |= CompareTransformValues(mSpecifiedTransform,
 | |
|                                             aNewData.mSpecifiedTransform);
 | |
|     transformHint |=
 | |
|         CompareTransformValues(mSpecifiedRotate, aNewData.mSpecifiedRotate);
 | |
|     transformHint |= CompareTransformValues(mSpecifiedTranslate,
 | |
|                                             aNewData.mSpecifiedTranslate);
 | |
|     transformHint |=
 | |
|         CompareTransformValues(mSpecifiedScale, aNewData.mSpecifiedScale);
 | |
|     transformHint |= CompareMotionValues(mMotion.get(), aNewData.mMotion.get());
 | |
| 
 | |
|     const nsChangeHint kUpdateOverflowAndRepaintHint =
 | |
|         nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
 | |
|     for (uint8_t index = 0; index < 3; ++index) {
 | |
|       if (mTransformOrigin[index] != aNewData.mTransformOrigin[index]) {
 | |
|         transformHint |= nsChangeHint_UpdateTransformLayer |
 | |
|                          nsChangeHint_UpdatePostTransformOverflow;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     for (uint8_t index = 0; index < 2; ++index) {
 | |
|       if (mPerspectiveOrigin[index] != aNewData.mPerspectiveOrigin[index]) {
 | |
|         transformHint |= kUpdateOverflowAndRepaintHint;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (mTransformStyle != aNewData.mTransformStyle ||
 | |
|         mTransformBox != aNewData.mTransformBox) {
 | |
|       transformHint |= kUpdateOverflowAndRepaintHint;
 | |
|     }
 | |
| 
 | |
|     if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
 | |
|       transformHint |= nsChangeHint_RepaintFrame;
 | |
|     }
 | |
| 
 | |
|     if (transformHint) {
 | |
|       if (HasTransformStyle()) {
 | |
|         hint |= transformHint;
 | |
|       } else {
 | |
|         hint |= nsChangeHint_NeutralChange;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) {
 | |
|     // A change from/to being a containing block for position:fixed.
 | |
|     hint |= nsChangeHint_UpdateContainingBlock | nsChangeHint_UpdateOverflow |
 | |
|             nsChangeHint_RepaintFrame;
 | |
|   } else if (mChildPerspective != aNewData.mChildPerspective) {
 | |
|     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   // Note that the HasTransformStyle() != aNewData.HasTransformStyle()
 | |
|   // test above handles relevant changes in the
 | |
|   // NS_STYLE_WILL_CHANGE_TRANSFORM bit, which in turn handles frame
 | |
|   // reconstruction for changes in the containing block of
 | |
|   // fixed-positioned elements.
 | |
|   uint8_t willChangeBitsChanged =
 | |
|       mWillChangeBitField ^ aNewData.mWillChangeBitField;
 | |
|   if (willChangeBitsChanged &
 | |
|       (NS_STYLE_WILL_CHANGE_STACKING_CONTEXT | NS_STYLE_WILL_CHANGE_SCROLL |
 | |
|        NS_STYLE_WILL_CHANGE_OPACITY)) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (willChangeBitsChanged &
 | |
|       (NS_STYLE_WILL_CHANGE_FIXPOS_CB | NS_STYLE_WILL_CHANGE_ABSPOS_CB)) {
 | |
|     hint |= nsChangeHint_UpdateContainingBlock;
 | |
|   }
 | |
| 
 | |
|   // If touch-action is changed, we need to regenerate the event regions on
 | |
|   // the layers and send it over to the compositor for APZ to handle.
 | |
|   if (mTouchAction != aNewData.mTouchAction) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   // If overscroll-behavior has changed, the changes are picked up
 | |
|   // during a repaint.
 | |
|   if (mOverscrollBehaviorX != aNewData.mOverscrollBehaviorX ||
 | |
|       mOverscrollBehaviorY != aNewData.mOverscrollBehaviorY) {
 | |
|     hint |= nsChangeHint_SchedulePaint;
 | |
|   }
 | |
| 
 | |
|   // Note:  Our current behavior for handling changes to the
 | |
|   // transition-duration, transition-delay, and transition-timing-function
 | |
|   // properties is to do nothing.  In other words, the transition
 | |
|   // property that matters is what it is when the transition begins, and
 | |
|   // we don't stop a transition later because the transition property
 | |
|   // changed.
 | |
|   // We do handle changes to transition-property, but we don't need to
 | |
|   // bother with anything here, since the transition manager is notified
 | |
|   // of any ComputedStyle change anyway.
 | |
| 
 | |
|   // Note: Likewise, for animation-*, the animation manager gets
 | |
|   // notified about every new ComputedStyle constructed, and it uses
 | |
|   // that opportunity to handle dynamic changes appropriately.
 | |
| 
 | |
|   // But we still need to return nsChangeHint_NeutralChange for these
 | |
|   // properties, since some data did change in the style struct.
 | |
| 
 | |
|   if (!hint && (mOriginalDisplay != aNewData.mOriginalDisplay ||
 | |
|                 mOriginalFloat != aNewData.mOriginalFloat ||
 | |
|                 mTransitions != aNewData.mTransitions ||
 | |
|                 mTransitionTimingFunctionCount !=
 | |
|                     aNewData.mTransitionTimingFunctionCount ||
 | |
|                 mTransitionDurationCount != aNewData.mTransitionDurationCount ||
 | |
|                 mTransitionDelayCount != aNewData.mTransitionDelayCount ||
 | |
|                 mTransitionPropertyCount != aNewData.mTransitionPropertyCount ||
 | |
|                 mAnimations != aNewData.mAnimations ||
 | |
|                 mAnimationTimingFunctionCount !=
 | |
|                     aNewData.mAnimationTimingFunctionCount ||
 | |
|                 mAnimationDurationCount != aNewData.mAnimationDurationCount ||
 | |
|                 mAnimationDelayCount != aNewData.mAnimationDelayCount ||
 | |
|                 mAnimationNameCount != aNewData.mAnimationNameCount ||
 | |
|                 mAnimationDirectionCount != aNewData.mAnimationDirectionCount ||
 | |
|                 mAnimationFillModeCount != aNewData.mAnimationFillModeCount ||
 | |
|                 mAnimationPlayStateCount != aNewData.mAnimationPlayStateCount ||
 | |
|                 mAnimationIterationCountCount !=
 | |
|                     aNewData.mAnimationIterationCountCount ||
 | |
|                 mScrollSnapCoordinate != aNewData.mScrollSnapCoordinate ||
 | |
|                 mWillChange != aNewData.mWillChange)) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| bool nsStyleDisplay::TransformChanged(const nsStyleDisplay& aNewData) const {
 | |
|   return TransformListChanged(mSpecifiedTransform,
 | |
|                               aNewData.mSpecifiedTransform);
 | |
| }
 | |
| 
 | |
| void nsStyleDisplay::GenerateCombinedIndividualTransform() {
 | |
|   MOZ_ASSERT(!mIndividualTransform);
 | |
| 
 | |
|   // Follow the order defined in the spec to append transform functions.
 | |
|   // https://drafts.csswg.org/css-transforms-2/#ctm
 | |
|   AutoTArray<nsCSSValueSharedList*, 3> shareLists;
 | |
|   if (mSpecifiedTranslate) {
 | |
|     shareLists.AppendElement(mSpecifiedTranslate.get());
 | |
|   }
 | |
|   if (mSpecifiedRotate) {
 | |
|     shareLists.AppendElement(mSpecifiedRotate.get());
 | |
|   }
 | |
|   if (mSpecifiedScale) {
 | |
|     shareLists.AppendElement(mSpecifiedScale.get());
 | |
|   }
 | |
| 
 | |
|   if (shareLists.Length() == 0) {
 | |
|     return;
 | |
|   }
 | |
|   if (shareLists.Length() == 1) {
 | |
|     mIndividualTransform = shareLists[0];
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // In common, we may have 3 transform functions:
 | |
|   // 1. one rotate function in mSpecifiedRotate,
 | |
|   // 2. one translate function in mSpecifiedTranslate,
 | |
|   // 3. one scale function in mSpecifiedScale.
 | |
|   AutoTArray<nsCSSValueList*, 3> valueLists;
 | |
|   for (auto list : shareLists) {
 | |
|     if (list) {
 | |
|       valueLists.AppendElement(list->mHead->Clone());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Check we have at least one list or else valueLists.Length() - 1 below will
 | |
|   // underflow.
 | |
|   MOZ_ASSERT(valueLists.Length());
 | |
| 
 | |
|   for (uint32_t i = 0; i < valueLists.Length() - 1; i++) {
 | |
|     valueLists[i]->mNext = valueLists[i + 1];
 | |
|   }
 | |
| 
 | |
|   mIndividualTransform = new nsCSSValueSharedList(valueLists[0]);
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleVisibility
 | |
| //
 | |
| 
 | |
| nsStyleVisibility::nsStyleVisibility(const Document& aDocument)
 | |
|     : mDirection(aDocument.GetBidiOptions() == IBMBIDI_TEXTDIRECTION_RTL
 | |
|                      ? NS_STYLE_DIRECTION_RTL
 | |
|                      : NS_STYLE_DIRECTION_LTR),
 | |
|       mVisible(NS_STYLE_VISIBILITY_VISIBLE),
 | |
|       mImageRendering(NS_STYLE_IMAGE_RENDERING_AUTO),
 | |
|       mWritingMode(NS_STYLE_WRITING_MODE_HORIZONTAL_TB),
 | |
|       mTextOrientation(NS_STYLE_TEXT_ORIENTATION_MIXED),
 | |
|       mColorAdjust(StyleColorAdjust::Economy) {
 | |
|   MOZ_COUNT_CTOR(nsStyleVisibility);
 | |
| }
 | |
| 
 | |
| nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource)
 | |
|     : mImageOrientation(aSource.mImageOrientation),
 | |
|       mDirection(aSource.mDirection),
 | |
|       mVisible(aSource.mVisible),
 | |
|       mImageRendering(aSource.mImageRendering),
 | |
|       mWritingMode(aSource.mWritingMode),
 | |
|       mTextOrientation(aSource.mTextOrientation),
 | |
|       mColorAdjust(aSource.mColorAdjust) {
 | |
|   MOZ_COUNT_CTOR(nsStyleVisibility);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleVisibility::CalcDifference(
 | |
|     const nsStyleVisibility& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (mDirection != aNewData.mDirection ||
 | |
|       mWritingMode != aNewData.mWritingMode) {
 | |
|     // It's important that a change in mWritingMode results in frame
 | |
|     // reconstruction, because it may affect intrinsic size (see
 | |
|     // nsSubDocumentFrame::GetIntrinsicISize/BSize).
 | |
|     // Also, the used writing-mode value is now a field on nsIFrame and some
 | |
|     // classes (e.g. table rows/cells) copy their value from an ancestor.
 | |
|     hint |= nsChangeHint_ReconstructFrame;
 | |
|   } else {
 | |
|     if ((mImageOrientation != aNewData.mImageOrientation)) {
 | |
|       hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
 | |
|     }
 | |
|     if (mVisible != aNewData.mVisible) {
 | |
|       if (mVisible == NS_STYLE_VISIBILITY_VISIBLE ||
 | |
|           aNewData.mVisible == NS_STYLE_VISIBILITY_VISIBLE) {
 | |
|         hint |= nsChangeHint_VisibilityChange;
 | |
|       }
 | |
|       if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) ||
 | |
|           (NS_STYLE_VISIBILITY_COLLAPSE == aNewData.mVisible)) {
 | |
|         hint |= NS_STYLE_HINT_REFLOW;
 | |
|       } else {
 | |
|         hint |= NS_STYLE_HINT_VISUAL;
 | |
|       }
 | |
|     }
 | |
|     if (mTextOrientation != aNewData.mTextOrientation) {
 | |
|       hint |= NS_STYLE_HINT_REFLOW;
 | |
|     }
 | |
|     if (mImageRendering != aNewData.mImageRendering) {
 | |
|       hint |= nsChangeHint_RepaintFrame;
 | |
|     }
 | |
|     if (mColorAdjust != aNewData.mColorAdjust) {
 | |
|       // color-adjust only affects media where dynamic changes can't happen.
 | |
|       hint |= nsChangeHint_NeutralChange;
 | |
|     }
 | |
|   }
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| nsStyleContentData::~nsStyleContentData() {
 | |
|   MOZ_COUNT_DTOR(nsStyleContentData);
 | |
| 
 | |
|   if (mType == StyleContentType::Image) {
 | |
|     // FIXME(emilio): Is this needed now that URLs are not main thread only?
 | |
|     NS_ReleaseOnMainThreadSystemGroup("nsStyleContentData::mContent.mImage",
 | |
|                                       dont_AddRef(mContent.mImage));
 | |
|     mContent.mImage = nullptr;
 | |
|   } else if (mType == StyleContentType::Counter ||
 | |
|              mType == StyleContentType::Counters) {
 | |
|     mContent.mCounters->Release();
 | |
|   } else if (mType == StyleContentType::String) {
 | |
|     free(mContent.mString);
 | |
|   } else if (mType == StyleContentType::Attr) {
 | |
|     delete mContent.mAttr;
 | |
|   } else {
 | |
|     MOZ_ASSERT(mContent.mString == nullptr, "Leaking due to missing case");
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleContentData::nsStyleContentData(const nsStyleContentData& aOther)
 | |
|     : mType(aOther.mType) {
 | |
|   MOZ_COUNT_CTOR(nsStyleContentData);
 | |
|   switch (mType) {
 | |
|     case StyleContentType::Image:
 | |
|       mContent.mImage = aOther.mContent.mImage;
 | |
|       mContent.mImage->AddRef();
 | |
|       break;
 | |
|     case StyleContentType::Counter:
 | |
|     case StyleContentType::Counters:
 | |
|       mContent.mCounters = aOther.mContent.mCounters;
 | |
|       mContent.mCounters->AddRef();
 | |
|       break;
 | |
|     case StyleContentType::Attr:
 | |
|       mContent.mAttr = new nsStyleContentAttr(*aOther.mContent.mAttr);
 | |
|       break;
 | |
|     case StyleContentType::String:
 | |
|       mContent.mString = NS_xstrdup(aOther.mContent.mString);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_ASSERT(!aOther.mContent.mString);
 | |
|       mContent.mString = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsStyleContentData::CounterFunction::operator==(
 | |
|     const CounterFunction& aOther) const {
 | |
|   return mIdent == aOther.mIdent && mSeparator == aOther.mSeparator &&
 | |
|          mCounterStyle == aOther.mCounterStyle;
 | |
| }
 | |
| 
 | |
| nsStyleContentData& nsStyleContentData::operator=(
 | |
|     const nsStyleContentData& aOther) {
 | |
|   if (this == &aOther) {
 | |
|     return *this;
 | |
|   }
 | |
|   this->~nsStyleContentData();
 | |
|   new (this) nsStyleContentData(aOther);
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool nsStyleContentData::operator==(const nsStyleContentData& aOther) const {
 | |
|   if (mType != aOther.mType) {
 | |
|     return false;
 | |
|   }
 | |
|   if (mType == StyleContentType::Image) {
 | |
|     return DefinitelyEqualImages(mContent.mImage, aOther.mContent.mImage);
 | |
|   }
 | |
|   if (mType == StyleContentType::Attr) {
 | |
|     return *mContent.mAttr == *aOther.mContent.mAttr;
 | |
|   }
 | |
|   if (mType == StyleContentType::Counter ||
 | |
|       mType == StyleContentType::Counters) {
 | |
|     return *mContent.mCounters == *aOther.mContent.mCounters;
 | |
|   }
 | |
|   if (mType == StyleContentType::String) {
 | |
|     return NS_strcmp(mContent.mString, aOther.mContent.mString) == 0;
 | |
|   }
 | |
|   MOZ_ASSERT(!mContent.mString && !aOther.mContent.mString);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void nsStyleContentData::Resolve(Document& aDocument,
 | |
|                                  const nsStyleContentData* aOldStyle) {
 | |
|   if (mType != StyleContentType::Image) {
 | |
|     return;
 | |
|   }
 | |
|   if (!mContent.mImage->IsResolved()) {
 | |
|     const nsStyleImageRequest* oldRequest =
 | |
|         (aOldStyle && aOldStyle->mType == StyleContentType::Image)
 | |
|             ? aOldStyle->mContent.mImage
 | |
|             : nullptr;
 | |
|     mContent.mImage->Resolve(aDocument, oldRequest);
 | |
|   }
 | |
| }
 | |
| 
 | |
| //-----------------------
 | |
| // nsStyleContent
 | |
| //
 | |
| 
 | |
| nsStyleContent::nsStyleContent(const Document& aDocument) {
 | |
|   MOZ_COUNT_CTOR(nsStyleContent);
 | |
| }
 | |
| 
 | |
| nsStyleContent::~nsStyleContent() { MOZ_COUNT_DTOR(nsStyleContent); }
 | |
| 
 | |
| void nsStyleContent::TriggerImageLoads(Document& aDocument,
 | |
|                                        const nsStyleContent* aOldStyle) {
 | |
|   for (size_t i = 0; i < mContents.Length(); ++i) {
 | |
|     const nsStyleContentData* oldData =
 | |
|         (aOldStyle && aOldStyle->mContents.Length() > i)
 | |
|             ? &aOldStyle->mContents[i]
 | |
|             : nullptr;
 | |
|     mContents[i].Resolve(aDocument, oldData);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
 | |
|     : mContents(aSource.mContents),
 | |
|       mIncrements(aSource.mIncrements),
 | |
|       mResets(aSource.mResets) {
 | |
|   MOZ_COUNT_CTOR(nsStyleContent);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleContent::CalcDifference(
 | |
|     const nsStyleContent& aNewData) const {
 | |
|   // Unfortunately we need to reframe even if the content lengths are the same;
 | |
|   // a simple reflow will not pick up different text or different image URLs,
 | |
|   // since we set all that up in the CSSFrameConstructor
 | |
|   if (mContents != aNewData.mContents || mIncrements != aNewData.mIncrements ||
 | |
|       mResets != aNewData.mResets) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleTextReset
 | |
| //
 | |
| 
 | |
| nsStyleTextReset::nsStyleTextReset(const Document& aDocument)
 | |
|     : mTextOverflow(),
 | |
|       mTextDecorationLine(NS_STYLE_TEXT_DECORATION_LINE_NONE),
 | |
|       mTextDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID),
 | |
|       mUnicodeBidi(NS_STYLE_UNICODE_BIDI_NORMAL),
 | |
|       mInitialLetterSink(0),
 | |
|       mInitialLetterSize(0.0f),
 | |
|       mTextDecorationColor(StyleComplexColor::CurrentColor()) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTextReset);
 | |
| }
 | |
| 
 | |
| nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource)
 | |
|     : mTextOverflow(aSource.mTextOverflow),
 | |
|       mTextDecorationLine(aSource.mTextDecorationLine),
 | |
|       mTextDecorationStyle(aSource.mTextDecorationStyle),
 | |
|       mUnicodeBidi(aSource.mUnicodeBidi),
 | |
|       mInitialLetterSink(aSource.mInitialLetterSink),
 | |
|       mInitialLetterSize(aSource.mInitialLetterSize),
 | |
|       mTextDecorationColor(aSource.mTextDecorationColor) {
 | |
|   MOZ_COUNT_CTOR(nsStyleTextReset);
 | |
| }
 | |
| 
 | |
| nsStyleTextReset::~nsStyleTextReset() { MOZ_COUNT_DTOR(nsStyleTextReset); }
 | |
| 
 | |
| nsChangeHint nsStyleTextReset::CalcDifference(
 | |
|     const nsStyleTextReset& aNewData) const {
 | |
|   if (mUnicodeBidi != aNewData.mUnicodeBidi ||
 | |
|       mInitialLetterSink != aNewData.mInitialLetterSink ||
 | |
|       mInitialLetterSize != aNewData.mInitialLetterSize) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   if (mTextDecorationLine != aNewData.mTextDecorationLine ||
 | |
|       mTextDecorationStyle != aNewData.mTextDecorationStyle) {
 | |
|     // Changes to our text-decoration line can impact our overflow area &
 | |
|     // also our descendants' overflow areas (particularly for text-frame
 | |
|     // descendants).  So, we update those areas & trigger a repaint.
 | |
|     return nsChangeHint_RepaintFrame | nsChangeHint_UpdateSubtreeOverflow |
 | |
|            nsChangeHint_SchedulePaint;
 | |
|   }
 | |
| 
 | |
|   // Repaint for decoration color changes
 | |
|   if (mTextDecorationColor != aNewData.mTextDecorationColor) {
 | |
|     return nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mTextOverflow != aNewData.mTextOverflow) {
 | |
|     return nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| // Returns true if the given shadow-arrays are equal.
 | |
| static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs) {
 | |
|   if (lhs == rhs) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (!lhs || !rhs || lhs->Length() != rhs->Length()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   for (uint32_t i = 0; i < lhs->Length(); ++i) {
 | |
|     if (*lhs->ShadowAt(i) != *rhs->ShadowAt(i)) {
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // --------------------
 | |
| // nsStyleText
 | |
| //
 | |
| 
 | |
| nsStyleText::nsStyleText(const Document& aDocument)
 | |
|     : mTextAlign(NS_STYLE_TEXT_ALIGN_START),
 | |
|       mTextAlignLast(NS_STYLE_TEXT_ALIGN_AUTO),
 | |
|       mTextJustify(StyleTextJustify::Auto),
 | |
|       mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE),
 | |
|       mWhiteSpace(StyleWhiteSpace::Normal),
 | |
|       mWordBreak(NS_STYLE_WORDBREAK_NORMAL),
 | |
|       mOverflowWrap(StyleOverflowWrap::Normal),
 | |
|       mHyphens(StyleHyphens::Manual),
 | |
|       mRubyAlign(NS_STYLE_RUBY_ALIGN_SPACE_AROUND),
 | |
|       mRubyPosition(NS_STYLE_RUBY_POSITION_OVER),
 | |
|       mTextSizeAdjust(NS_STYLE_TEXT_SIZE_ADJUST_AUTO),
 | |
|       mTextCombineUpright(NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE),
 | |
|       mControlCharacterVisibility(
 | |
|           nsLayoutUtils::ControlCharVisibilityDefault()),
 | |
|       mTextEmphasisStyle(NS_STYLE_TEXT_EMPHASIS_STYLE_NONE),
 | |
|       mTextRendering(StyleTextRendering::Auto),
 | |
|       mTextEmphasisColor(StyleComplexColor::CurrentColor()),
 | |
|       mWebkitTextFillColor(StyleComplexColor::CurrentColor()),
 | |
|       mWebkitTextStrokeColor(StyleComplexColor::CurrentColor()),
 | |
|       mTabSize(float(NS_STYLE_TABSIZE_INITIAL), eStyleUnit_Factor),
 | |
|       mWordSpacing(0, nsStyleCoord::CoordConstructor),
 | |
|       mLetterSpacing(eStyleUnit_Normal),
 | |
|       mLineHeight(eStyleUnit_Normal),
 | |
|       mTextIndent(LengthPercentage::Zero()),
 | |
|       mWebkitTextStrokeWidth(0),
 | |
|       mTextShadow(nullptr) {
 | |
|   MOZ_COUNT_CTOR(nsStyleText);
 | |
|   RefPtr<nsAtom> language = aDocument.GetContentLanguageAsAtomForStyle();
 | |
|   mTextEmphasisPosition =
 | |
|       language && nsStyleUtil::MatchesLanguagePrefix(language, u"zh")
 | |
|           ? NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH
 | |
|           : NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT;
 | |
| }
 | |
| 
 | |
| nsStyleText::nsStyleText(const nsStyleText& aSource)
 | |
|     : mTextAlign(aSource.mTextAlign),
 | |
|       mTextAlignLast(aSource.mTextAlignLast),
 | |
|       mTextJustify(aSource.mTextJustify),
 | |
|       mTextTransform(aSource.mTextTransform),
 | |
|       mWhiteSpace(aSource.mWhiteSpace),
 | |
|       mWordBreak(aSource.mWordBreak),
 | |
|       mOverflowWrap(aSource.mOverflowWrap),
 | |
|       mHyphens(aSource.mHyphens),
 | |
|       mRubyAlign(aSource.mRubyAlign),
 | |
|       mRubyPosition(aSource.mRubyPosition),
 | |
|       mTextSizeAdjust(aSource.mTextSizeAdjust),
 | |
|       mTextCombineUpright(aSource.mTextCombineUpright),
 | |
|       mControlCharacterVisibility(aSource.mControlCharacterVisibility),
 | |
|       mTextEmphasisPosition(aSource.mTextEmphasisPosition),
 | |
|       mTextEmphasisStyle(aSource.mTextEmphasisStyle),
 | |
|       mTextRendering(aSource.mTextRendering),
 | |
|       mTextEmphasisColor(aSource.mTextEmphasisColor),
 | |
|       mWebkitTextFillColor(aSource.mWebkitTextFillColor),
 | |
|       mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor),
 | |
|       mTabSize(aSource.mTabSize),
 | |
|       mWordSpacing(aSource.mWordSpacing),
 | |
|       mLetterSpacing(aSource.mLetterSpacing),
 | |
|       mLineHeight(aSource.mLineHeight),
 | |
|       mTextIndent(aSource.mTextIndent),
 | |
|       mWebkitTextStrokeWidth(aSource.mWebkitTextStrokeWidth),
 | |
|       mTextShadow(aSource.mTextShadow),
 | |
|       mTextEmphasisStyleString(aSource.mTextEmphasisStyleString) {
 | |
|   MOZ_COUNT_CTOR(nsStyleText);
 | |
| }
 | |
| 
 | |
| nsStyleText::~nsStyleText() { MOZ_COUNT_DTOR(nsStyleText); }
 | |
| 
 | |
| nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aNewData) const {
 | |
|   if (WhiteSpaceOrNewlineIsSignificant() !=
 | |
|       aNewData.WhiteSpaceOrNewlineIsSignificant()) {
 | |
|     // This may require construction of suppressed text frames
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if (mTextCombineUpright != aNewData.mTextCombineUpright ||
 | |
|       mControlCharacterVisibility != aNewData.mControlCharacterVisibility) {
 | |
|     return nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
| 
 | |
|   if ((mTextAlign != aNewData.mTextAlign) ||
 | |
|       (mTextAlignLast != aNewData.mTextAlignLast) ||
 | |
|       (mTextTransform != aNewData.mTextTransform) ||
 | |
|       (mWhiteSpace != aNewData.mWhiteSpace) ||
 | |
|       (mWordBreak != aNewData.mWordBreak) ||
 | |
|       (mOverflowWrap != aNewData.mOverflowWrap) ||
 | |
|       (mHyphens != aNewData.mHyphens) || (mRubyAlign != aNewData.mRubyAlign) ||
 | |
|       (mRubyPosition != aNewData.mRubyPosition) ||
 | |
|       (mTextSizeAdjust != aNewData.mTextSizeAdjust) ||
 | |
|       (mLetterSpacing != aNewData.mLetterSpacing) ||
 | |
|       (mLineHeight != aNewData.mLineHeight) ||
 | |
|       (mTextIndent != aNewData.mTextIndent) ||
 | |
|       (mTextJustify != aNewData.mTextJustify) ||
 | |
|       (mWordSpacing != aNewData.mWordSpacing) ||
 | |
|       (mTabSize != aNewData.mTabSize)) {
 | |
|     return NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
| 
 | |
|   if (HasTextEmphasis() != aNewData.HasTextEmphasis() ||
 | |
|       (HasTextEmphasis() &&
 | |
|        mTextEmphasisPosition != aNewData.mTextEmphasisPosition)) {
 | |
|     // Text emphasis position change could affect line height calculation.
 | |
|     return nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   // text-rendering changes require a reflow since they change SVG
 | |
|   // frames' rects.
 | |
|   if (mTextRendering != aNewData.mTextRendering) {
 | |
|     hint |= nsChangeHint_NeedReflow |
 | |
|             nsChangeHint_NeedDirtyReflow |  // XXX remove me: bug 876085
 | |
|             nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (!AreShadowArraysEqual(mTextShadow, aNewData.mTextShadow) ||
 | |
|       mTextEmphasisStyle != aNewData.mTextEmphasisStyle ||
 | |
|       mTextEmphasisStyleString != aNewData.mTextEmphasisStyleString ||
 | |
|       mWebkitTextStrokeWidth != aNewData.mWebkitTextStrokeWidth) {
 | |
|     hint |= nsChangeHint_UpdateSubtreeOverflow | nsChangeHint_SchedulePaint |
 | |
|             nsChangeHint_RepaintFrame;
 | |
| 
 | |
|     // We don't add any other hints below.
 | |
|     return hint;
 | |
|   }
 | |
| 
 | |
|   if (mTextEmphasisColor != aNewData.mTextEmphasisColor ||
 | |
|       mWebkitTextFillColor != aNewData.mWebkitTextFillColor ||
 | |
|       mWebkitTextStrokeColor != aNewData.mWebkitTextStrokeColor) {
 | |
|     hint |= nsChangeHint_SchedulePaint | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (hint) {
 | |
|     return hint;
 | |
|   }
 | |
| 
 | |
|   if (mTextEmphasisPosition != aNewData.mTextEmphasisPosition) {
 | |
|     return nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return nsChangeHint(0);
 | |
| }
 | |
| 
 | |
| LogicalSide nsStyleText::TextEmphasisSide(WritingMode aWM) const {
 | |
|   MOZ_ASSERT(
 | |
|       (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT) !=
 | |
|        !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT)) &&
 | |
|       (!(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER) !=
 | |
|        !(mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER)));
 | |
|   mozilla::Side side =
 | |
|       aWM.IsVertical()
 | |
|           ? (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT
 | |
|                  ? eSideLeft
 | |
|                  : eSideRight)
 | |
|           : (mTextEmphasisPosition & NS_STYLE_TEXT_EMPHASIS_POSITION_OVER
 | |
|                  ? eSideTop
 | |
|                  : eSideBottom);
 | |
|   LogicalSide result = aWM.LogicalSideForPhysicalSide(side);
 | |
|   MOZ_ASSERT(IsBlock(result));
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| //-----------------------
 | |
| // nsStyleUI
 | |
| //
 | |
| 
 | |
| nsCursorImage::nsCursorImage()
 | |
|     : mHaveHotspot(false), mHotspotX(0.0f), mHotspotY(0.0f) {}
 | |
| 
 | |
| nsCursorImage::nsCursorImage(const nsCursorImage& aOther)
 | |
|     : mHaveHotspot(aOther.mHaveHotspot),
 | |
|       mHotspotX(aOther.mHotspotX),
 | |
|       mHotspotY(aOther.mHotspotY),
 | |
|       mImage(aOther.mImage) {}
 | |
| 
 | |
| nsCursorImage& nsCursorImage::operator=(const nsCursorImage& aOther) {
 | |
|   if (this != &aOther) {
 | |
|     mHaveHotspot = aOther.mHaveHotspot;
 | |
|     mHotspotX = aOther.mHotspotX;
 | |
|     mHotspotY = aOther.mHotspotY;
 | |
|     mImage = aOther.mImage;
 | |
|   }
 | |
| 
 | |
|   return *this;
 | |
| }
 | |
| 
 | |
| bool nsCursorImage::operator==(const nsCursorImage& aOther) const {
 | |
|   NS_ASSERTION(mHaveHotspot || (mHotspotX == 0 && mHotspotY == 0),
 | |
|                "expected mHotspot{X,Y} to be 0 when mHaveHotspot is false");
 | |
|   NS_ASSERTION(
 | |
|       aOther.mHaveHotspot || (aOther.mHotspotX == 0 && aOther.mHotspotY == 0),
 | |
|       "expected mHotspot{X,Y} to be 0 when mHaveHotspot is false");
 | |
|   return mHaveHotspot == aOther.mHaveHotspot && mHotspotX == aOther.mHotspotX &&
 | |
|          mHotspotY == aOther.mHotspotY &&
 | |
|          DefinitelyEqualImages(mImage, aOther.mImage);
 | |
| }
 | |
| 
 | |
| nsStyleUI::nsStyleUI(const Document& aDocument)
 | |
|     : mUserInput(StyleUserInput::Auto),
 | |
|       mUserModify(StyleUserModify::ReadOnly),
 | |
|       mUserFocus(StyleUserFocus::None),
 | |
|       mPointerEvents(NS_STYLE_POINTER_EVENTS_AUTO),
 | |
|       mCursor(StyleCursorKind::Auto),
 | |
|       mCaretColor(StyleComplexColor::Auto()),
 | |
|       mScrollbarFaceColor(StyleComplexColor::Auto()),
 | |
|       mScrollbarTrackColor(StyleComplexColor::Auto()) {
 | |
|   MOZ_COUNT_CTOR(nsStyleUI);
 | |
| }
 | |
| 
 | |
| nsStyleUI::nsStyleUI(const nsStyleUI& aSource)
 | |
|     : mUserInput(aSource.mUserInput),
 | |
|       mUserModify(aSource.mUserModify),
 | |
|       mUserFocus(aSource.mUserFocus),
 | |
|       mPointerEvents(aSource.mPointerEvents),
 | |
|       mCursor(aSource.mCursor),
 | |
|       mCursorImages(aSource.mCursorImages),
 | |
|       mCaretColor(aSource.mCaretColor),
 | |
|       mScrollbarFaceColor(aSource.mScrollbarFaceColor),
 | |
|       mScrollbarTrackColor(aSource.mScrollbarTrackColor) {
 | |
|   MOZ_COUNT_CTOR(nsStyleUI);
 | |
| }
 | |
| 
 | |
| nsStyleUI::~nsStyleUI() { MOZ_COUNT_DTOR(nsStyleUI); }
 | |
| 
 | |
| void nsStyleUI::TriggerImageLoads(Document& aDocument,
 | |
|                                   const nsStyleUI* aOldStyle) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   for (size_t i = 0; i < mCursorImages.Length(); ++i) {
 | |
|     nsCursorImage& cursor = mCursorImages[i];
 | |
| 
 | |
|     if (cursor.mImage && !cursor.mImage->IsResolved()) {
 | |
|       const nsCursorImage* oldCursor =
 | |
|           (aOldStyle && aOldStyle->mCursorImages.Length() > i)
 | |
|               ? &aOldStyle->mCursorImages[i]
 | |
|               : nullptr;
 | |
|       cursor.mImage->Resolve(aDocument,
 | |
|                              oldCursor ? oldCursor->mImage.get() : nullptr);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleUI::CalcDifference(const nsStyleUI& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
|   if (mCursor != aNewData.mCursor) {
 | |
|     hint |= nsChangeHint_UpdateCursor;
 | |
|   }
 | |
| 
 | |
|   // We could do better. But it wouldn't be worth it, URL-specified cursors are
 | |
|   // rare.
 | |
|   if (mCursorImages != aNewData.mCursorImages) {
 | |
|     hint |= nsChangeHint_UpdateCursor;
 | |
|   }
 | |
| 
 | |
|   if (mPointerEvents != aNewData.mPointerEvents) {
 | |
|     // SVGGeometryFrame's mRect depends on stroke _and_ on the value
 | |
|     // of pointer-events. See SVGGeometryFrame::ReflowSVG's use of
 | |
|     // GetHitTestFlags. (Only a reflow, no visual change.)
 | |
|     hint |= nsChangeHint_NeedReflow |
 | |
|             nsChangeHint_NeedDirtyReflow;  // XXX remove me: bug 876085
 | |
|   }
 | |
| 
 | |
|   if (mUserModify != aNewData.mUserModify) {
 | |
|     hint |= NS_STYLE_HINT_VISUAL;
 | |
|   }
 | |
| 
 | |
|   if (mUserInput != aNewData.mUserInput) {
 | |
|     if (StyleUserInput::None == mUserInput ||
 | |
|         StyleUserInput::None == aNewData.mUserInput) {
 | |
|       hint |= nsChangeHint_ReconstructFrame;
 | |
|     } else {
 | |
|       hint |= nsChangeHint_NeutralChange;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mUserFocus != aNewData.mUserFocus) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   if (mCaretColor != aNewData.mCaretColor ||
 | |
|       mScrollbarFaceColor != aNewData.mScrollbarFaceColor ||
 | |
|       mScrollbarTrackColor != aNewData.mScrollbarTrackColor) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| //-----------------------
 | |
| // nsStyleUIReset
 | |
| //
 | |
| 
 | |
| nsStyleUIReset::nsStyleUIReset(const Document& aDocument)
 | |
|     : mUserSelect(StyleUserSelect::Auto),
 | |
|       mScrollbarWidth(StyleScrollbarWidth::Auto),
 | |
|       mForceBrokenImageIcon(0),
 | |
|       mIMEMode(NS_STYLE_IME_MODE_AUTO),
 | |
|       mWindowDragging(StyleWindowDragging::Default),
 | |
|       mWindowShadow(NS_STYLE_WINDOW_SHADOW_DEFAULT),
 | |
|       mWindowOpacity(1.0),
 | |
|       mSpecifiedWindowTransform(nullptr),
 | |
|       mWindowTransformOrigin{
 | |
|           {0.5f, eStyleUnit_Percent},  // Transform is centered on origin
 | |
|           {0.5f, eStyleUnit_Percent}} {
 | |
|   MOZ_COUNT_CTOR(nsStyleUIReset);
 | |
| }
 | |
| 
 | |
| nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
 | |
|     : mUserSelect(aSource.mUserSelect),
 | |
|       mScrollbarWidth(aSource.mScrollbarWidth),
 | |
|       mForceBrokenImageIcon(aSource.mForceBrokenImageIcon),
 | |
|       mIMEMode(aSource.mIMEMode),
 | |
|       mWindowDragging(aSource.mWindowDragging),
 | |
|       mWindowShadow(aSource.mWindowShadow),
 | |
|       mWindowOpacity(aSource.mWindowOpacity),
 | |
|       mSpecifiedWindowTransform(aSource.mSpecifiedWindowTransform),
 | |
|       mWindowTransformOrigin{aSource.mWindowTransformOrigin[0],
 | |
|                              aSource.mWindowTransformOrigin[1]} {
 | |
|   MOZ_COUNT_CTOR(nsStyleUIReset);
 | |
| }
 | |
| 
 | |
| nsStyleUIReset::~nsStyleUIReset() {
 | |
|   MOZ_COUNT_DTOR(nsStyleUIReset);
 | |
| 
 | |
|   ReleaseSharedListOnMainThread("nsStyleUIReset::mSpecifiedWindowTransform",
 | |
|                                 mSpecifiedWindowTransform);
 | |
| }
 | |
| 
 | |
| nsChangeHint nsStyleUIReset::CalcDifference(
 | |
|     const nsStyleUIReset& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (mForceBrokenImageIcon != aNewData.mForceBrokenImageIcon) {
 | |
|     hint |= nsChangeHint_ReconstructFrame;
 | |
|   }
 | |
|   if (mScrollbarWidth != aNewData.mScrollbarWidth) {
 | |
|     // For scrollbar-width change, we need some special handling similar
 | |
|     // to overflow properties. Specifically, we may need to reconstruct
 | |
|     // the scrollbar or force reflow of the viewport scrollbar.
 | |
|     hint |= nsChangeHint_ScrollbarChange;
 | |
|   }
 | |
|   if (mWindowShadow != aNewData.mWindowShadow) {
 | |
|     // We really need just an nsChangeHint_SyncFrameView, except
 | |
|     // on an ancestor of the frame, so we get that by doing a
 | |
|     // reflow.
 | |
|     hint |= NS_STYLE_HINT_REFLOW;
 | |
|   }
 | |
|   if (mUserSelect != aNewData.mUserSelect) {
 | |
|     hint |= NS_STYLE_HINT_VISUAL;
 | |
|   }
 | |
| 
 | |
|   if (mWindowDragging != aNewData.mWindowDragging) {
 | |
|     hint |= nsChangeHint_SchedulePaint;
 | |
|   }
 | |
| 
 | |
|   if (mWindowOpacity != aNewData.mWindowOpacity ||
 | |
|       !mSpecifiedWindowTransform != !aNewData.mSpecifiedWindowTransform ||
 | |
|       (mSpecifiedWindowTransform &&
 | |
|        *mSpecifiedWindowTransform != *aNewData.mSpecifiedWindowTransform)) {
 | |
|     hint |= nsChangeHint_UpdateWidgetProperties;
 | |
|   } else {
 | |
|     for (uint8_t index = 0; index < 2; ++index) {
 | |
|       if (mWindowTransformOrigin[index] !=
 | |
|           aNewData.mWindowTransformOrigin[index]) {
 | |
|         hint |= nsChangeHint_UpdateWidgetProperties;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!hint && mIMEMode != aNewData.mIMEMode) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 | |
| 
 | |
| //-----------------------
 | |
| // nsStyleEffects
 | |
| //
 | |
| 
 | |
| nsStyleEffects::nsStyleEffects(const Document&)
 | |
|     : mBoxShadow(nullptr),
 | |
|       mClip(0, 0, 0, 0),
 | |
|       mOpacity(1.0f),
 | |
|       mClipFlags(NS_STYLE_CLIP_AUTO),
 | |
|       mMixBlendMode(NS_STYLE_BLEND_NORMAL) {
 | |
|   MOZ_COUNT_CTOR(nsStyleEffects);
 | |
| }
 | |
| 
 | |
| nsStyleEffects::nsStyleEffects(const nsStyleEffects& aSource)
 | |
|     : mFilters(aSource.mFilters),
 | |
|       mBoxShadow(aSource.mBoxShadow),
 | |
|       mClip(aSource.mClip),
 | |
|       mOpacity(aSource.mOpacity),
 | |
|       mClipFlags(aSource.mClipFlags),
 | |
|       mMixBlendMode(aSource.mMixBlendMode) {
 | |
|   MOZ_COUNT_CTOR(nsStyleEffects);
 | |
| }
 | |
| 
 | |
| nsStyleEffects::~nsStyleEffects() { MOZ_COUNT_DTOR(nsStyleEffects); }
 | |
| 
 | |
| nsChangeHint nsStyleEffects::CalcDifference(
 | |
|     const nsStyleEffects& aNewData) const {
 | |
|   nsChangeHint hint = nsChangeHint(0);
 | |
| 
 | |
|   if (!AreShadowArraysEqual(mBoxShadow, aNewData.mBoxShadow)) {
 | |
|     // Update overflow regions & trigger DLBI to be sure it's noticed.
 | |
|     // Also request a repaint, since it's possible that only the color
 | |
|     // of the shadow is changing (and UpdateOverflow/SchedulePaint won't
 | |
|     // repaint for that, since they won't know what needs invalidating.)
 | |
|     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint |
 | |
|             nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (mClipFlags != aNewData.mClipFlags) {
 | |
|     hint |= nsChangeHint_AllReflowHints | nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (!mClip.IsEqualInterior(aNewData.mClip)) {
 | |
|     // If the clip has changed, we just need to update overflow areas. DLBI
 | |
|     // will handle the invalidation.
 | |
|     hint |= nsChangeHint_UpdateOverflow | nsChangeHint_SchedulePaint;
 | |
|   }
 | |
| 
 | |
|   if (mOpacity != aNewData.mOpacity) {
 | |
|     // If we're going from the optimized >=0.99 opacity value to 1.0 or back,
 | |
|     // then repaint the frame because DLBI will not catch the invalidation.
 | |
|     // Otherwise, just update the opacity layer.
 | |
|     if ((mOpacity >= 0.99f && mOpacity < 1.0f && aNewData.mOpacity == 1.0f) ||
 | |
|         (aNewData.mOpacity >= 0.99f && aNewData.mOpacity < 1.0f &&
 | |
|          mOpacity == 1.0f)) {
 | |
|       hint |= nsChangeHint_RepaintFrame;
 | |
|     } else {
 | |
|       hint |= nsChangeHint_UpdateOpacityLayer;
 | |
|       if ((mOpacity == 1.0f) != (aNewData.mOpacity == 1.0f)) {
 | |
|         hint |= nsChangeHint_UpdateUsesOpacity;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (HasFilters() != aNewData.HasFilters()) {
 | |
|     // A change from/to being a containing block for position:fixed.
 | |
|     hint |= nsChangeHint_UpdateContainingBlock;
 | |
|   }
 | |
| 
 | |
|   if (mFilters != aNewData.mFilters) {
 | |
|     hint |= nsChangeHint_UpdateEffects | nsChangeHint_RepaintFrame |
 | |
|             nsChangeHint_UpdateOverflow;
 | |
|   }
 | |
| 
 | |
|   if (mMixBlendMode != aNewData.mMixBlendMode) {
 | |
|     hint |= nsChangeHint_RepaintFrame;
 | |
|   }
 | |
| 
 | |
|   if (!hint && !mClip.IsEqualEdges(aNewData.mClip)) {
 | |
|     hint |= nsChangeHint_NeutralChange;
 | |
|   }
 | |
| 
 | |
|   return hint;
 | |
| }
 |