diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/style/nsStyleStruct.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/style/nsStyleStruct.cpp')
-rw-r--r-- | layout/style/nsStyleStruct.cpp | 4261 |
1 files changed, 4261 insertions, 0 deletions
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp new file mode 100644 index 000000000..2f12d6201 --- /dev/null +++ b/layout/style/nsStyleStruct.cpp @@ -0,0 +1,4261 @@ +/* -*- 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 nsStyleContext, the + * internal API for computed style data for an element + */ + +#include "nsStyleStruct.h" +#include "nsStyleStructInlines.h" +#include "nsStyleConsts.h" +#include "nsThemeConstants.h" +#include "nsString.h" +#include "nsPresContext.h" +#include "nsIAppShellService.h" +#include "nsIWidget.h" +#include "nsCRTGlue.h" +#include "nsCSSParser.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/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection +#include "mozilla/dom/ImageTracker.h" +#include "mozilla/Likely.h" +#include "nsIURI.h" +#include "nsIDocument.h" +#include <algorithm> + +using namespace mozilla; + +static_assert((((1 << nsStyleStructID_Length) - 1) & + ~(NS_STYLE_INHERIT_MASK)) == 0, + "Not enough bits in NS_STYLE_INHERIT_MASK"); + +/* static */ const int32_t nsStyleGridLine::kMinLine; +/* static */ const int32_t nsStyleGridLine::kMaxLine; + +static bool +EqualURIs(nsIURI *aURI1, nsIURI *aURI2) +{ + bool eq; + return aURI1 == aURI2 || // handle null==null, and optimize + (aURI1 && aURI2 && + NS_SUCCEEDED(aURI1->Equals(aURI2, &eq)) && // not equal on fail + eq); +} + +static bool +DefinitelyEqualURIs(css::URLValueData* aURI1, + css::URLValueData* aURI2) +{ + return aURI1 == aURI2 || + (aURI1 && aURI2 && aURI1->DefinitelyEqualURIs(*aURI2)); +} + +static bool +DefinitelyEqualURIsAndPrincipal(css::URLValueData* aURI1, + css::URLValueData* aURI2) +{ + return aURI1 == aURI2 || + (aURI1 && aURI2 && aURI1->DefinitelyEqualURIsAndPrincipal(*aURI2)); +} + +static bool +EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) +{ + if (aImage1 == aImage2) { + return true; + } + + if (!aImage1 || !aImage2) { + return false; + } + + nsCOMPtr<nsIURI> uri1, uri2; + aImage1->GetURI(getter_AddRefs(uri1)); + aImage2->GetURI(getter_AddRefs(uri2)); + return EqualURIs(uri1, uri2); +} + +static bool +DefinitelyEqualImages(nsStyleImageRequest* aRequest1, + nsStyleImageRequest* aRequest2) +{ + if (aRequest1 == aRequest2) { + return true; + } + + if (!aRequest1 || !aRequest2) { + return false; + } + + return aRequest1->DefinitelyEquals(*aRequest2); +} + +// A nullsafe wrapper for strcmp. We depend on null-safety. +static int +safe_strcmp(const char16_t* a, const char16_t* b) +{ + if (!a || !b) { + return (int)(a - b); + } + return NS_strcmp(a, b); +} + +int32_t +StyleStructContext::AppUnitsPerDevPixel() +{ + return DeviceContext()->AppUnitsPerDevPixel(); +} + +nsDeviceContext* +StyleStructContext::HackilyFindSomeDeviceContext() +{ + nsCOMPtr<nsIAppShellService> appShell(do_GetService("@mozilla.org/appshell/appShellService;1")); + MOZ_ASSERT(appShell); + nsCOMPtr<mozIDOMWindowProxy> win; + appShell->GetHiddenDOMWindow(getter_AddRefs(win)); + return nsLayoutUtils::GetDeviceContextForScreenInfo(static_cast<nsPIDOMWindowOuter*>(win.get())); +} + +static bool AreShadowArraysEqual(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs); + +// -------------------- +// nsStyleFont +// +nsStyleFont::nsStyleFont(const nsFont& aFont, StyleStructContext aContext) + : mFont(aFont) + , mSize(nsStyleFont::ZoomText(aContext, mFont.size)) + , 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(GetLanguage(aContext)) +{ + MOZ_COUNT_CTOR(nsStyleFont); + mFont.size = mSize; +} + +nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) + : mFont(aSrc.mFont) + , mSize(aSrc.mSize) + , 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(StyleStructContext aContext) + : nsStyleFont(*aContext.GetDefaultFont(kPresContext_DefaultVariableFont_ID), + aContext) +{ +} + +void +nsStyleFont::Destroy(nsPresContext* aContext) { + this->~nsStyleFont(); + aContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStyleFont, this); +} + +void +nsStyleFont::EnableZoom(nsPresContext* aContext, bool aEnable) +{ + if (mAllowZoom == aEnable) { + return; + } + mAllowZoom = aEnable; + if (mAllowZoom) { + mSize = nsStyleFont::ZoomText(aContext, mSize); + mFont.size = nsStyleFont::ZoomText(aContext, mFont.size); + mScriptUnconstrainedSize = + nsStyleFont::ZoomText(aContext, mScriptUnconstrainedSize); + } else { + mSize = nsStyleFont::UnZoomText(aContext, mSize); + mFont.size = nsStyleFont::UnZoomText(aContext, mFont.size); + mScriptUnconstrainedSize = + nsStyleFont::UnZoomText(aContext, mScriptUnconstrainedSize); + } +} + +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 || + mFont != aNewData.mFont || + mLanguage != aNewData.mLanguage || + mExplicitLanguage != aNewData.mExplicitLanguage || + mMathVariant != aNewData.mMathVariant || + mMathDisplay != aNewData.mMathDisplay || + mMinFontSizeRatio != aNewData.mMinFontSizeRatio) { + return NS_STYLE_HINT_REFLOW; + } + + // 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); +} + +/* static */ nscoord +nsStyleFont::ZoomText(StyleStructContext aContext, nscoord aSize) +{ + // 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) * aContext.TextZoom()); +} + +/* static */ nscoord +nsStyleFont::UnZoomText(nsPresContext *aPresContext, nscoord aSize) +{ + // 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) / aPresContext->TextZoom()); +} + +/* static */ already_AddRefed<nsIAtom> +nsStyleFont::GetLanguage(StyleStructContext aContext) +{ + RefPtr<nsIAtom> language = aContext.GetContentLanguage(); + if (!language) { + // we didn't find a (usable) Content-Language, so we fall back + // to whatever the presContext guessed from the charset + // NOTE this should not be used elsewhere, because we want websites + // to use UTF-8 with proper language tag, instead of relying on + // deriving language from charset. See bug 1040668 comment 67. + language = aContext.GetLanguageFromCharset(); + } + return language.forget(); +} + +static nscoord +CalcCoord(const nsStyleCoord& aCoord, const nscoord* aEnumTable, int32_t aNumEnums) +{ + if (aCoord.GetUnit() == eStyleUnit_Enumerated) { + MOZ_ASSERT(aEnumTable, "must have enum table"); + int32_t value = aCoord.GetIntValue(); + if (0 <= value && value < aNumEnums) { + return aEnumTable[aCoord.GetIntValue()]; + } + NS_NOTREACHED("unexpected enum value"); + return 0; + } + MOZ_ASSERT(aCoord.ConvertsToLength(), "unexpected unit"); + return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); +} + +nsStyleMargin::nsStyleMargin(StyleStructContext aContext) +{ + MOZ_COUNT_CTOR(nsStyleMargin); + nsStyleCoord zero(0, nsStyleCoord::CoordConstructor); + NS_FOR_CSS_SIDES(side) { + mMargin.Set(side, zero); + } +} + +nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc) + : mMargin(aSrc.mMargin) +{ + MOZ_COUNT_CTOR(nsStyleMargin); +} + +void +nsStyleMargin::Destroy(nsPresContext* aContext) { + this->~nsStyleMargin(); + aContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStyleMargin, this); +} + +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(StyleStructContext aContext) +{ + MOZ_COUNT_CTOR(nsStylePadding); + nsStyleCoord zero(0, nsStyleCoord::CoordConstructor); + NS_FOR_CSS_SIDES(side) { + mPadding.Set(side, zero); + } +} + +nsStylePadding::nsStylePadding(const nsStylePadding& aSrc) + : mPadding(aSrc.mPadding) +{ + MOZ_COUNT_CTOR(nsStylePadding); +} + +void +nsStylePadding::Destroy(nsPresContext* aContext) { + this->~nsStylePadding(); + aContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStylePadding, this); +} + +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; +} + +nsStyleBorder::nsStyleBorder(StyleStructContext aContext) + : mBorderColors(nullptr) + , mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL) + , mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) + , mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) + , mFloatEdge(StyleFloatEdge::ContentBox) + , mBoxDecorationBreak(StyleBoxDecorationBreak::Slice) + , mComputedBorder(0, 0, 0, 0) +{ + MOZ_COUNT_CTOR(nsStyleBorder); + + NS_FOR_CSS_HALF_CORNERS (corner) { + mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor)); + } + + nscoord medium = + (StaticPresData::Get()->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; + 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] = NS_STYLE_BORDER_STYLE_NONE; + mBorderColor[side] = StyleComplexColor::CurrentColor(); + } + + mTwipsPerPixel = aContext.DevPixelsToAppUnits(1); +} + +nsBorderColors::~nsBorderColors() +{ + NS_CSS_DELETE_LIST_MEMBER(nsBorderColors, this, mNext); +} + +nsBorderColors* +nsBorderColors::Clone(bool aDeep) const +{ + nsBorderColors* result = new nsBorderColors(mColor); + if (MOZ_UNLIKELY(!result)) { + return result; + } + if (aDeep) { + NS_CSS_CLONE_LIST_MEMBER(nsBorderColors, this, mNext, result, (false)); + } + return result; +} + +nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) + : mBorderColors(nullptr) + , 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) + , mComputedBorder(aSrc.mComputedBorder) + , mBorder(aSrc.mBorder) + , mTwipsPerPixel(aSrc.mTwipsPerPixel) +{ + MOZ_COUNT_CTOR(nsStyleBorder); + if (aSrc.mBorderColors) { + EnsureBorderColors(); + for (int32_t i = 0; i < 4; i++) { + if (aSrc.mBorderColors[i]) { + mBorderColors[i] = aSrc.mBorderColors[i]->Clone(); + } else { + mBorderColors[i] = nullptr; + } + } + } + + NS_FOR_CSS_SIDES(side) { + mBorderStyle[side] = aSrc.mBorderStyle[side]; + mBorderColor[side] = aSrc.mBorderColor[side]; + } +} + +nsStyleBorder::~nsStyleBorder() +{ + MOZ_COUNT_DTOR(nsStyleBorder); + if (mBorderColors) { + for (int32_t i = 0; i < 4; i++) { + delete mBorderColors[i]; + } + delete [] mBorderColors; + } +} + +void +nsStyleBorder::FinishStyle(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPresContext->StyleSet()->IsServo()); + + mBorderImageSource.ResolveImage(aPresContext); +} + +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: + NS_NOTREACHED("unexpected CSS unit for image outset"); + value = 0; + break; + } + outset.Side(s) = value; + } + return outset; +} + +void +nsStyleBorder::Destroy(nsPresContext* aContext) +{ + this->~nsStyleBorder(); + aContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStyleBorder, this); +} + +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] || + mBorderColor[ix] != aNewData.mBorderColor[ix]) { + return nsChangeHint_RepaintFrame; + } + } + + if (mBorderRadius != aNewData.mBorderRadius || + !mBorderColors != !aNewData.mBorderColors) { + return nsChangeHint_RepaintFrame; + } + + if (IsBorderImageLoaded() || aNewData.IsBorderImageLoaded()) { + if (mBorderImageSource != aNewData.mBorderImageSource || + mBorderImageRepeatH != aNewData.mBorderImageRepeatH || + mBorderImageRepeatV != aNewData.mBorderImageRepeatV || + mBorderImageSlice != aNewData.mBorderImageSlice || + mBorderImageFill != aNewData.mBorderImageFill || + mBorderImageWidth != aNewData.mBorderImageWidth || + mBorderImageOutset != aNewData.mBorderImageOutset) { + return nsChangeHint_RepaintFrame; + } + } + + // Note that at this point if mBorderColors is non-null so is + // aNewData.mBorderColors + if (mBorderColors) { + NS_FOR_CSS_SIDES(ix) { + if (!nsBorderColors::Equal(mBorderColors[ix], + aNewData.mBorderColors[ix])) { + 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; + } + + return nsChangeHint(0); +} + +nsStyleOutline::nsStyleOutline(StyleStructContext aContext) + : mOutlineWidth(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated) + , mOutlineOffset(0) + , mOutlineColor(StyleComplexColor::CurrentColor()) + , mOutlineStyle(NS_STYLE_BORDER_STYLE_NONE) + , mActualOutlineWidth(0) + , mTwipsPerPixel(aContext.DevPixelsToAppUnits(1)) +{ + 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); +} + +void +nsStyleOutline::RecalcData() +{ + if (NS_STYLE_BORDER_STYLE_NONE == mOutlineStyle) { + mActualOutlineWidth = 0; + } else { + MOZ_ASSERT(mOutlineWidth.ConvertsToLength() || + mOutlineWidth.GetUnit() == eStyleUnit_Enumerated); + // Clamp negative calc() to 0. + mActualOutlineWidth = + std::max(CalcCoord(mOutlineWidth, + StaticPresData::Get()->GetBorderWidthTable(), 3), 0); + mActualOutlineWidth = + NS_ROUND_BORDER_TO_PIXELS(mActualOutlineWidth, mTwipsPerPixel); + } +} + +nsChangeHint +nsStyleOutline::CalcDifference(const nsStyleOutline& aNewData) const +{ + if (mActualOutlineWidth != aNewData.mActualOutlineWidth || + (mActualOutlineWidth > 0 && + mOutlineOffset != aNewData.mOutlineOffset)) { + return nsChangeHint_UpdateOverflow | + nsChangeHint_SchedulePaint; + } + + 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(StyleStructContext aContext) + : mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) + , mCounterStyle(aContext.BuildCounterStyle(NS_LITERAL_STRING("disc"))) +{ + MOZ_COUNT_CTOR(nsStyleList); + SetQuotesInitial(); +} + +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::FinishStyle(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPresContext->StyleSet()->IsServo()); + + if (mListStyleImage && !mListStyleImage->IsResolved()) { + mListStyleImage->Resolve(aPresContext); + } +} + +void +nsStyleList::SetQuotesInherit(const nsStyleList* aOther) +{ + mQuotes = aOther->mQuotes; +} + +void +nsStyleList::SetQuotesInitial() +{ + if (!sInitialQuotes) { + // The initial value for quotes is the en-US typographic convention: + // outermost are LEFT and RIGHT DOUBLE QUOTATION MARK, alternating + // with LEFT and RIGHT SINGLE QUOTATION MARK. + static const char16_t initialQuotes[8] = { + 0x201C, 0, 0x201D, 0, 0x2018, 0, 0x2019, 0 + }; + + sInitialQuotes = new nsStyleQuoteValues; + sInitialQuotes->mQuotePairs.AppendElement( + std::make_pair(nsDependentString(&initialQuotes[0], 1), + nsDependentString(&initialQuotes[2], 1))); + sInitialQuotes->mQuotePairs.AppendElement( + std::make_pair(nsDependentString(&initialQuotes[4], 1), + nsDependentString(&initialQuotes[6], 1))); + } + + mQuotes = sInitialQuotes; +} + +void +nsStyleList::SetQuotesNone() +{ + if (!sNoneQuotes) { + sNoneQuotes = new nsStyleQuoteValues; + } + mQuotes = sNoneQuotes; +} + +void +nsStyleList::SetQuotes(nsStyleQuoteValues::QuotePairArray&& aValues) +{ + mQuotes = new nsStyleQuoteValues; + mQuotes->mQuotePairs = Move(aValues); +} + +const nsStyleQuoteValues::QuotePairArray& +nsStyleList::GetQuotePairs() const +{ + return mQuotes->mQuotePairs; +} + +nsChangeHint +nsStyleList::CalcDifference(const nsStyleList& aNewData) 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 && + (mQuotes || aNewData.mQuotes) && + GetQuotePairs() != aNewData.GetQuotePairs()) { + return nsChangeHint_ReconstructFrame; + } + if (mListStylePosition != aNewData.mListStylePosition) { + return nsChangeHint_ReconstructFrame; + } + if (DefinitelyEqualImages(mListStyleImage, aNewData.mListStyleImage) && + mCounterStyle == aNewData.mCounterStyle) { + if (mImageRegion.IsEqualInterior(aNewData.mImageRegion)) { + return nsChangeHint(0); + } + if (mImageRegion.width == aNewData.mImageRegion.width && + mImageRegion.height == aNewData.mImageRegion.height) { + return NS_STYLE_HINT_VISUAL; + } + } + return NS_STYLE_HINT_REFLOW; +} + +StaticRefPtr<nsStyleQuoteValues> +nsStyleList::sInitialQuotes; + +StaticRefPtr<nsStyleQuoteValues> +nsStyleList::sNoneQuotes; + + +// -------------------- +// nsStyleXUL +// +nsStyleXUL::nsStyleXUL(StyleStructContext aContext) + : mBoxFlex(0.0f) + , mBoxOrdinal(1) + , mBoxAlign(StyleBoxAlign::Stretch) + , mBoxDirection(StyleBoxDirection::Normal) + , mBoxOrient(StyleBoxOrient::Horizontal) + , mBoxPack(StyleBoxPack::Start) + , mStretchStack(true) +{ + 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) + , mStretchStack(aSource.mStretchStack) +{ + 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 && + mStretchStack == aNewData.mStretchStack) { + return nsChangeHint(0); + } + if (mBoxOrdinal != aNewData.mBoxOrdinal) { + return nsChangeHint_ReconstructFrame; + } + return NS_STYLE_HINT_REFLOW; +} + +// -------------------- +// nsStyleColumn +// +/* static */ const uint32_t nsStyleColumn::kMaxColumnCount; + +nsStyleColumn::nsStyleColumn(StyleStructContext aContext) + : mColumnCount(NS_STYLE_COLUMN_COUNT_AUTO) + , mColumnWidth(eStyleUnit_Auto) + , mColumnGap(eStyleUnit_Normal) + , mColumnRuleColor(StyleComplexColor::CurrentColor()) + , mColumnRuleStyle(NS_STYLE_BORDER_STYLE_NONE) + , mColumnFill(NS_STYLE_COLUMN_FILL_BALANCE) + , mColumnRuleWidth((StaticPresData::Get() + ->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]) + , mTwipsPerPixel(aContext.AppUnitsPerDevPixel()) +{ + MOZ_COUNT_CTOR(nsStyleColumn); +} + +nsStyleColumn::~nsStyleColumn() +{ + MOZ_COUNT_DTOR(nsStyleColumn); +} + +nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) + : mColumnCount(aSource.mColumnCount) + , mColumnWidth(aSource.mColumnWidth) + , mColumnGap(aSource.mColumnGap) + , mColumnRuleColor(aSource.mColumnRuleColor) + , mColumnRuleStyle(aSource.mColumnRuleStyle) + , mColumnFill(aSource.mColumnFill) + , 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) { + // 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 || + mColumnGap != aNewData.mColumnGap || + 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(StyleStructContext aContext) + : 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) + , 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) + , 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) + , 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 nsSVGPathGeometryFrame::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 nsSVGPathGeometryFrames. + 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 nsSVGPathGeometryFrame::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 nsSVGPathGeometryFrame::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) { + return hint | nsChangeHint_RepaintFrame; + } + + 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; + } + NS_NOTREACHED("unexpected type"); + return eCSSKeyword_UNKNOWN; +} + +// -------------------- +// 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(StyleStructContext aContext) + : mMask(nsStyleImageLayers::LayerType::Mask) + , mStopColor(NS_RGB(0, 0, 0)) + , mFloodColor(NS_RGB(0, 0, 0)) + , mLightingColor(NS_RGB(255, 255, 255)) + , 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::Destroy(nsPresContext* aContext) +{ + this->~nsStyleSVGReset(); + aContext->PresShell()-> + FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); +} + +void +nsStyleSVGReset::FinishStyle(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPresContext->StyleSet()->IsServo()); + + mMask.ResolveImages(aPresContext); +} + +nsChangeHint +nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aNewData) const +{ + nsChangeHint hint = nsChangeHint(0); + + if (mClipPath != aNewData.mClipPath) { + hint |= nsChangeHint_UpdateEffects | + nsChangeHint_RepaintFrame; + // clip-path changes require that we update the PreEffectsBBoxProperty, + // which is done during overflow computation. + hint |= nsChangeHint_UpdateOverflow; + } + + 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 nsSVGPathGeometryFrame::mRect, and + // vector-effect affect stroke. As a result we need to reflow if + // vector-effect changes in order to have nsSVGPathGeometryFrame:: + // 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; +} + +// nsStyleSVGPaint implementation +nsStyleSVGPaint::nsStyleSVGPaint(nsStyleSVGPaintType aType) + : mType(aType) + , mFallbackColor(NS_RGB(0, 0, 0)) +{ + MOZ_ASSERT(aType == nsStyleSVGPaintType(0) || + aType == eStyleSVGPaintType_None || + aType == eStyleSVGPaintType_Color); + mPaint.mColor = NS_RGB(0, 0, 0); +} + +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 = NS_RGB(0, 0, 0); + break; + case eStyleSVGPaintType_Server: + mPaint.mPaintServer->Release(); + mPaint.mPaintServer = nullptr; + MOZ_FALLTHROUGH; + case eStyleSVGPaintType_ContextFill: + case eStyleSVGPaintType_ContextStroke: + mFallbackColor = NS_RGB(0, 0, 0); + 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.mFallbackColor); + break; + case eStyleSVGPaintType_ContextFill: + case eStyleSVGPaintType_ContextStroke: + SetContextValue(aOther.mType, aOther.mFallbackColor); + break; + } +} + +void +nsStyleSVGPaint::SetNone() +{ + Reset(); + mType = eStyleSVGPaintType_None; +} + +void +nsStyleSVGPaint::SetContextValue(nsStyleSVGPaintType aType, + nscolor aFallbackColor) +{ + MOZ_ASSERT(aType == eStyleSVGPaintType_ContextFill || + aType == eStyleSVGPaintType_ContextStroke); + Reset(); + mType = aType; + mFallbackColor = aFallbackColor; +} + +void +nsStyleSVGPaint::SetColor(nscolor aColor) +{ + Reset(); + mType = eStyleSVGPaintType_Color; + mPaint.mColor = aColor; +} + +void +nsStyleSVGPaint::SetPaintServer(css::URLValue* aPaintServer, + nscolor aFallbackColor) +{ + MOZ_ASSERT(aPaintServer); + Reset(); + mType = eStyleSVGPaintType_Server; + mPaint.mPaintServer = aPaintServer; + mPaint.mPaintServer->AddRef(); + 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) && + mFallbackColor == aOther.mFallbackColor; + case eStyleSVGPaintType_ContextFill: + case eStyleSVGPaintType_ContextStroke: + return mFallbackColor == aOther.mFallbackColor; + default: + MOZ_ASSERT(mType == eStyleSVGPaintType_None, + "Unexpected SVG paint type"); + return true; + } +} + +// -------------------- +// nsStylePosition +// +nsStylePosition::nsStylePosition(StyleStructContext aContext) + : mWidth(eStyleUnit_Auto) + , mMinWidth(eStyleUnit_Auto) + , mMaxWidth(eStyleUnit_None) + , mHeight(eStyleUnit_Auto) + , mMinHeight(eStyleUnit_Auto) + , mMaxHeight(eStyleUnit_None) + , mFlexBasis(eStyleUnit_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) + , mJustifyItems(NS_STYLE_JUSTIFY_AUTO) + , 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) + , mGridColumnGap(nscoord(0), nsStyleCoord::CoordConstructor) + , mGridRowGap(nscoord(0), nsStyleCoord::CoordConstructor) +{ + MOZ_COUNT_CTOR(nsStylePosition); + + // positioning values not inherited + + mObjectPosition.SetInitialPercentValues(0.5f); + + nsStyleCoord autoCoord(eStyleUnit_Auto); + NS_FOR_CSS_SIDES(side) { + mOffset.Set(side, autoCoord); + } + + // 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) + , 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) + , mGridTemplateColumns(aSource.mGridTemplateColumns) + , mGridTemplateRows(aSource.mGridTemplateRows) + , mGridTemplateAreas(aSource.mGridTemplateAreas) + , mGridColumnStart(aSource.mGridColumnStart) + , mGridColumnEnd(aSource.mGridColumnEnd) + , mGridRowStart(aSource.mGridRowStart) + , mGridRowEnd(aSource.mGridRowEnd) + , mGridColumnGap(aSource.mGridColumnGap) + , mGridRowGap(aSource.mGridRowGap) +{ + MOZ_COUNT_CTOR(nsStylePosition); +} + +static bool +IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2) +{ + NS_FOR_CSS_SIDES(side) { + if ((aSides1.GetUnit(side) == eStyleUnit_Auto) != + (aSides2.GetUnit(side) == eStyleUnit_Auto)) { + return false; + } + } + return true; +} + +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 (mGridTemplateColumns != aNewData.mGridTemplateColumns || + 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 || + mGridColumnGap != aNewData.mGridColumnGap || + mGridRowGap != aNewData.mGridRowGap) { + 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; + } + + // '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; + + // If aOldStyleVisibility is null, we don't need to bother with any of + // these tests, since we know that the element never had its + // nsStyleVisibility accessed, which means it couldn't have done + // layout. + // Note that we pass an nsStyleVisibility here because we don't want + // to cause a new struct to be computed during + // nsStyleContext::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. + if (aOldStyleVisibility) { + bool isVertical = WritingMode(aOldStyleVisibility).IsVertical(); + if (isVertical ? widthChanged : heightChanged) { + // Block-size changes can affect descendant intrinsic sizes due to + // replaced elements with percentage bsizes in descendants which + // also have percentage bsizes. This is handled via + // nsChangeHint_UpdateComputedBSize which clears intrinsic sizes + // for frames that have such replaced elements. + hint |= nsChangeHint_NeedReflow | + nsChangeHint_UpdateComputedBSize | + nsChangeHint_ReflowChangesSizeOrPosition; + } + + if (isVertical ? heightChanged : widthChanged) { + // None of our inline-size differences can affect descendant + // intrinsic sizes and none of them need to force children to + // reflow. + hint |= nsChangeHint_AllReflowHints & + ~(nsChangeHint_ClearDescendantIntrinsics | + nsChangeHint_NeedDirtyReflow); + } + } else { + if (widthChanged || heightChanged) { + hint |= nsChangeHint_NeutralChange; + } + } + + // 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; +} + +/* static */ bool +nsStylePosition::WidthCoordDependsOnContainer(const nsStyleCoord &aCoord) +{ + return aCoord.HasPercent() || + (aCoord.GetUnit() == eStyleUnit_Enumerated && + (aCoord.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT || + aCoord.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE)); +} + +uint8_t +nsStylePosition::UsedAlignSelf(nsStyleContext* 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::ComputedJustifyItems(nsStyleContext* aParent) const +{ + if (mJustifyItems != NS_STYLE_JUSTIFY_AUTO) { + return mJustifyItems; + } + if (MOZ_LIKELY(aParent)) { + auto inheritedJustifyItems = + aParent->StylePosition()->ComputedJustifyItems(aParent->GetParent()); + // "If the inherited value of justify-items includes the 'legacy' keyword, + // 'auto' computes to the inherited value." Otherwise, 'normal'. + if (inheritedJustifyItems & NS_STYLE_JUSTIFY_LEGACY) { + return inheritedJustifyItems; + } + } + return NS_STYLE_JUSTIFY_NORMAL; +} + +uint8_t +nsStylePosition::UsedJustifySelf(nsStyleContext* aParent) const +{ + if (mJustifySelf != NS_STYLE_JUSTIFY_AUTO) { + return mJustifySelf; + } + if (MOZ_LIKELY(aParent)) { + auto inheritedJustifyItems = aParent->StylePosition()-> + ComputedJustifyItems(aParent->GetParent()); + return inheritedJustifyItems & ~NS_STYLE_JUSTIFY_LEGACY; + } + return NS_STYLE_JUSTIFY_NORMAL; +} + +// -------------------- +// nsStyleTable +// + +nsStyleTable::nsStyleTable(StyleStructContext aContext) + : 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(StyleStructContext aContext) + : 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 +// + +nsStyleColor::nsStyleColor(StyleStructContext aContext) + : mColor(aContext.DefaultColor()) +{ + 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 || + 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) +{ +} + +bool +nsStyleGradient::IsOpaque() +{ + for (uint32_t i = 0; i < mStops.Length(); i++) { + if (NS_GET_A(mStops[i].mColor) < 255) { + 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, + * mImageValue and mImageValue 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<css::ImageValue> aImageValue, + already_AddRefed<ImageTracker> aImageTracker) + : mModeFlags(aModeFlags) + , mRequestProxy(aRequestProxy) + , mImageValue(aImageValue) + , mImageTracker(aImageTracker) + { + } + + NS_IMETHOD Run() final + { + MOZ_ASSERT(NS_IsMainThread()); + + 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(NS_IsMainThread()); } + +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<css::ImageValue> mImageValue; + RefPtr<ImageTracker> mImageTracker; +}; + +nsStyleImageRequest::nsStyleImageRequest(Mode aModeFlags, + imgRequestProxy* aRequestProxy, + css::ImageValue* aImageValue, + ImageTracker* aImageTracker) + : mRequestProxy(aRequestProxy) + , mImageValue(aImageValue) + , mImageTracker(aImageTracker) + , mModeFlags(aModeFlags) + , mResolved(true) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aRequestProxy); + MOZ_ASSERT(aImageValue); + MOZ_ASSERT(!!(aModeFlags & Mode::Track) == !!aImageTracker); + + MaybeTrackAndLock(); +} + +nsStyleImageRequest::nsStyleImageRequest( + Mode aModeFlags, + nsStringBuffer* aURLBuffer, + already_AddRefed<PtrHolder<nsIURI>> aBaseURI, + already_AddRefed<PtrHolder<nsIURI>> aReferrer, + already_AddRefed<PtrHolder<nsIPrincipal>> aPrincipal) + : mModeFlags(aModeFlags) + , mResolved(false) +{ + mImageValue = new css::ImageValue(aURLBuffer, Move(aBaseURI), + Move(aReferrer), Move(aPrincipal)); +} + +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 mImageValue, all on the main thread. + { + RefPtr<StyleImageRequestCleanupTask> task = + new StyleImageRequestCleanupTask(mModeFlags, + mRequestProxy.forget(), + mImageValue.forget(), + mImageTracker.forget()); + if (NS_IsMainThread()) { + task->Run(); + } else { + NS_DispatchToMainThread(task.forget()); + } + } + + MOZ_ASSERT(!mRequestProxy); + MOZ_ASSERT(!mImageValue); + MOZ_ASSERT(!mImageTracker); +} + +bool +nsStyleImageRequest::Resolve(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!IsResolved(), "already resolved"); + + mResolved = true; + + // For now, just have unique nsCSSValue/ImageValue objects. We should + // really store the ImageValue on the Servo specified value, so that we can + // share imgRequestProxys that come from the same rule in the same + // document. + mImageValue->Initialize(aPresContext->Document()); + + nsCSSValue value; + value.SetImageValue(mImageValue); + mRequestProxy = value.GetPossiblyStaticImageValue(aPresContext->Document(), + aPresContext); + + if (!mRequestProxy) { + // The URL resolution or image load failed. + return false; + } + + if (mModeFlags & Mode::Track) { + mImageTracker = aPresContext->Document()->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; +} + +void +CachedBorderImageData::PurgeCachedImages() +{ + 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) + , 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(aOther.mElementId); + } + + UniquePtr<nsStyleSides> cropRectCopy; + if (aOther.mCropRect) { + cropRectCopy = MakeUnique<nsStyleSides>(*aOther.mCropRect.get()); + } + SetCropRect(Move(cropRectCopy)); +} + +void +nsStyleImage::SetNull() +{ + if (mType == eStyleImageType_Gradient) { + mGradient->Release(); + } else if (mType == eStyleImageType_Image) { + NS_RELEASE(mImage); + } else if (mType == eStyleImageType_Element) { + free(mElementId); + } + + 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(const char16_t* aElementId) +{ + if (mType != eStyleImageType_Null) { + SetNull(); + } + + if (aElementId) { + mElementId = NS_strdup(aElementId); + mType = eStyleImageType_Element; + } +} + +void +nsStyleImage::SetCropRect(UniquePtr<nsStyleSides> aCropRect) +{ + mCropRect = Move(aCropRect); +} + +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: + NS_NOTREACHED("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); +} + +bool +nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect, + bool* aIsEntireImage) const +{ + if (mType != eStyleImageType_Image) { + return false; + } + + 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; +} + +nsresult +nsStyleImage::StartDecoding() const +{ + if (mType == eStyleImageType_Image) { + imgRequestProxy* req = GetImageData(); + if (!req) { + return NS_ERROR_FAILURE; + } + return req->StartDecoding(); + } + return NS_OK; +} + +bool +nsStyleImage::IsOpaque() const +{ + if (!IsComplete()) { + return false; + } + + if (mType == eStyleImageType_Gradient) { + return mGradient->IsOpaque(); + } + + if (mType == eStyleImageType_Element) { + 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; + bool rv = ComputeActualCropRect(actualCropRect); + NS_ASSERTION(rv, "ComputeActualCropRect() can not fail here"); + return rv && !actualCropRect.IsEmpty(); + } + + return false; +} + +bool +nsStyleImage::IsComplete() const +{ + switch (mType) { + case eStyleImageType_Null: + return false; + case eStyleImageType_Gradient: + case eStyleImageType_Element: + 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_SIZE_AVAILABLE) && + (status & imgIRequest::STATUS_FRAME_COMPLETE); + } + default: + NS_NOTREACHED("unexpected image type"); + return false; + } +} + +bool +nsStyleImage::IsLoaded() const +{ + switch (mType) { + case eStyleImageType_Null: + return false; + case eStyleImageType_Gradient: + case eStyleImageType_Element: + 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: + NS_NOTREACHED("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 NS_strcmp(mElementId, aOther.mElementId) == 0; + } + + 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); + } +} + +// -------------------- +// 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 +}; + +#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND +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 +}; +#endif + +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; +} + +bool +nsStyleImageLayers::HasLayerWithImage() const +{ + for (uint32_t i = 0; i < mImageCount; i++) { + // mLayers[i].mSourceURI can be nullptr if mask-image prop value is + // <element-reference> or <gradient> + // mLayers[i].mImage can be empty if mask-image prop value is a reference + // to SVG mask element. + // So we need to test both mSourceURI and mImage. + if ((mLayers[i].mSourceURI && mLayers[i].mSourceURI->GetURI()) || + !mLayers[i].mImage.IsEmpty()) { + return true; + } + } + + return false; +} + +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; +} + +bool +nsStyleImageLayers::IsInitialPositionForLayerType(Position aPosition, LayerType aType) +{ + if (aPosition.mXPosition.mPercent == 0.0f && + aPosition.mXPosition.mLength == 0 && + aPosition.mXPosition.mHasPercent && + aPosition.mYPosition.mPercent == 0.0f && + aPosition.mYPosition.mLength == 0 && + aPosition.mYPosition.mHasPercent) { + return true; + } + + return false; +} + +void +Position::SetInitialPercentValues(float aPercentVal) +{ + mXPosition.mPercent = aPercentVal; + mXPosition.mLength = 0; + mXPosition.mHasPercent = true; + mYPosition.mPercent = aPercentVal; + mYPosition.mLength = 0; + mYPosition.mHasPercent = true; +} + +void +Position::SetInitialZeroValues() +{ + mXPosition.mPercent = 0; + mXPosition.mLength = 0; + mXPosition.mHasPercent = false; + mYPosition.mPercent = 0; + mYPosition.mLength = 0; + mYPosition.mHasPercent = false; +} + +bool +nsStyleImageLayers::Size::DependsOnPositioningAreaSize(const nsStyleImage& aImage) const +{ + MOZ_ASSERT(aImage.GetType() != eStyleImageType_Null, + "caller should have handled this"); + + // If either dimension contains a non-zero percentage, rendering for that + // dimension straightforwardly depends on frame size. + if ((mWidthType == eLengthPercentage && mWidth.mPercent != 0.0f) || + (mHeightType == eLengthPercentage && mHeight.mPercent != 0.0f)) { + return true; + } + + // So too for contain and cover. + if (mWidthType == eContain || mWidthType == eCover) { + return true; + } + + // If both dimensions are fixed lengths, there's no dependency. + if (mWidthType == eLengthPercentage && mHeightType == eLengthPercentage) { + return false; + } + + MOZ_ASSERT((mWidthType == eLengthPercentage && mHeightType == eAuto) || + (mWidthType == eAuto && mHeightType == eLengthPercentage) || + (mWidthType == eAuto && mHeightType == eAuto), + "logic error"); + + 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 mWidthType == mHeightType; + } + + // Otherwise, rendering depends on frame size when the image dimensions + // and background-size don't complement each other. + return !(hasWidth && mHeightType == eLengthPercentage) && + !(hasHeight && mWidthType == eLengthPercentage); + } + } else { + NS_NOTREACHED("missed an enum value"); + } + + // Passed the gauntlet: no dependency. + return false; +} + +void +nsStyleImageLayers::Size::SetInitialValues() +{ + mWidthType = mHeightType = eAuto; +} + +bool +nsStyleImageLayers::Size::operator==(const Size& aOther) const +{ + MOZ_ASSERT(mWidthType < eDimensionType_COUNT, + "bad mWidthType for this"); + MOZ_ASSERT(mHeightType < eDimensionType_COUNT, + "bad mHeightType for this"); + MOZ_ASSERT(aOther.mWidthType < eDimensionType_COUNT, + "bad mWidthType for aOther"); + MOZ_ASSERT(aOther.mHeightType < eDimensionType_COUNT, + "bad mHeightType for aOther"); + + return mWidthType == aOther.mWidthType && + mHeightType == aOther.mHeightType && + (mWidthType != eLengthPercentage || mWidth == aOther.mWidth) && + (mHeightType != eLengthPercentage || mHeight == aOther.mHeight); +} + +nsStyleImageLayers::Layer::Layer() + : mClip(NS_STYLE_IMAGELAYER_CLIP_BORDER) + , mAttachment(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL) + , mBlendMode(NS_STYLE_BLEND_NORMAL) + , mComposite(NS_STYLE_MASK_COMPOSITE_ADD) + , mMaskMode(NS_STYLE_MASK_MODE_MATCH_SOURCE) +{ + mImage.SetNull(); + mSize.SetInitialValues(); +} + +nsStyleImageLayers::Layer::~Layer() +{ +} + +void +nsStyleImageLayers::Layer::Initialize(nsStyleImageLayers::LayerType aType) +{ + mRepeat.SetInitialValues(); + + mPosition.SetInitialPercentValues(0.0f); + + if (aType == LayerType::Background) { + mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_PADDING; + } else { + MOZ_ASSERT(aType == LayerType::Mask, "unsupported layer type."); + mOrigin = NS_STYLE_IMAGELAYER_ORIGIN_BORDER; + } +} + +bool +nsStyleImageLayers::Layer::RenderingMightDependOnPositioningAreaSizeChange() const +{ + // Do we even have an image? + if (mImage.IsEmpty()) { + return false; + } + + return mPosition.DependsOnPositioningAreaSize() || + mSize.DependsOnPositioningAreaSize(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 && + DefinitelyEqualURIs(mSourceURI, aOther.mSourceURI); +} + +nsChangeHint +nsStyleImageLayers::Layer::CalcDifference(const nsStyleImageLayers::Layer& aNewLayer) const +{ + nsChangeHint hint = nsChangeHint(0); + if (!DefinitelyEqualURIs(mSourceURI, aNewLayer.mSourceURI)) { + hint |= nsChangeHint_RepaintFrame | nsChangeHint_UpdateEffects; + + // If Layer::mSourceURI links to a SVG mask, it has a fragment. Not vice + // versa. Here are examples of URI contains a fragment, two of them link + // to a SVG mask: + // mask:url(a.svg#maskID); // The fragment of this URI is an ID of a mask + // // element in a.svg. + // mask:url(#localMaskID); // The fragment of this URI is an ID of a mask + // // element in local document. + // mask:url(b.svg#viewBoxID); // The fragment of this URI is an ID of a + // // viewbox defined in b.svg. + // That is, if mSourceURI has a fragment, it may link to a SVG mask; If + // not, it "must" not link to a SVG mask. + bool maybeSVGMask = false; + if (mSourceURI) { + if (mSourceURI->IsLocalRef()) { + maybeSVGMask = true; + } else if (mSourceURI->GetURI()) { + mSourceURI->GetURI()->GetHasRef(&maybeSVGMask); + } + } + + if (!maybeSVGMask) { + if (aNewLayer.mSourceURI) { + if (aNewLayer.mSourceURI->IsLocalRef()) { + maybeSVGMask = true; + } else if (aNewLayer.mSourceURI->GetURI()) { + aNewLayer.mSourceURI->GetURI()->GetHasRef(&maybeSVGMask); + } + } + } + + // Return nsChangeHint_UpdateOverflow if either URI might link to an SVG + // mask. + if (maybeSVGMask) { + // Mask changes require that we update the PreEffectsBBoxProperty, + // which is done during overflow computation. + hint |= nsChangeHint_UpdateOverflow; + } + } 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(StyleStructContext aContext) + : mImage(nsStyleImageLayers::LayerType::Background) + , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) +{ + 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::Destroy(nsPresContext* aContext) +{ + this->~nsStyleBackground(); + aContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStyleBackground, this); +} + +void +nsStyleBackground::FinishStyle(nsPresContext* aPresContext) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPresContext->StyleSet()->IsServo()); + + mImage.ResolveImages(aPresContext); +} + +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 == NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED && + !layer.mImage.IsEmpty() && + !nsLayoutUtils::IsTransformed(aFrame)) { + return true; + } + } + return false; +} + +bool +nsStyleBackground::IsTransparent() const +{ + return BottomLayer().mImage.IsEmpty() && + mImage.mImageCount == 1 && + NS_GET_A(mBackgroundColor) == 0; +} + +void +nsTimingFunction::AssignFromKeyword(int32_t aTimingFunctionType) +{ + switch (aTimingFunctionType) { + case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START: + mType = Type::StepStart; + mSteps = 1; + return; + default: + MOZ_FALLTHROUGH_ASSERT("aTimingFunctionType must be a keyword value"); + case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END: + mType = Type::StepEnd; + mSteps = 1; + return; + case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE: + case NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR: + case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN: + case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT: + case NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT: + mType = static_cast<Type>(aTimingFunctionType); + break; + } + + static_assert(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE == 0 && + NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR == 1 && + NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN == 2 && + NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT == 3 && + NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT == 4, + "transition timing function constants not as expected"); + + static const float timingFunctionValues[5][4] = { + { 0.25f, 0.10f, 0.25f, 1.00f }, // ease + { 0.00f, 0.00f, 1.00f, 1.00f }, // linear + { 0.42f, 0.00f, 1.00f, 1.00f }, // ease-in + { 0.00f, 0.00f, 0.58f, 1.00f }, // ease-out + { 0.42f, 0.00f, 0.58f, 1.00f } // ease-in-out + }; + + MOZ_ASSERT(0 <= aTimingFunctionType && aTimingFunctionType < 5, + "keyword out of range"); + mFunc.mX1 = timingFunctionValues[aTimingFunctionType][0]; + mFunc.mY1 = timingFunctionValues[aTimingFunctionType][1]; + mFunc.mX2 = timingFunctionValues[aTimingFunctionType][2]; + mFunc.mY2 = timingFunctionValues[aTimingFunctionType][3]; +} + +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(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE); + mDuration = 0.0; + mDelay = 0.0; + mProperty = eCSSPropertyExtra_all_properties; +} + +void +StyleTransition::SetUnknownProperty(nsCSSPropertyID aProperty, + const nsAString& aPropertyString) +{ + MOZ_ASSERT(nsCSSProps::LookupProperty(aPropertyString, + CSSEnabledState::eForAllContent) == + aProperty, + "property and property string should match"); + MOZ_ASSERT(aProperty == eCSSProperty_UNKNOWN || + aProperty == eCSSPropertyExtra_variable, + "should be either unknown or custom property"); + mProperty = aProperty; + mUnknownProperty = NS_Atomize(aPropertyString); +} + +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(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE); + mDuration = 0.0; + mDelay = 0.0; + mName = EmptyString(); + mDirection = dom::PlaybackDirection::Normal; + mFillMode = dom::FillMode::None; + mPlayState = NS_STYLE_ANIMATION_PLAY_STATE_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(StyleStructContext aContext) + : mDisplay(StyleDisplay::Inline) + , mOriginalDisplay(StyleDisplay::Inline) + , mContain(NS_STYLE_CONTAIN_NONE) + , mAppearance(NS_THEME_NONE) + , mPosition(NS_STYLE_POSITION_STATIC) + , mFloat(StyleFloat::None) + , mOriginalFloat(StyleFloat::None) + , mBreakType(StyleClear::None) + , mBreakInside(NS_STYLE_PAGE_BREAK_AUTO) + , mBreakBefore(false) + , mBreakAfter(false) + , mOverflowX(NS_STYLE_OVERFLOW_VISIBLE) + , mOverflowY(NS_STYLE_OVERFLOW_VISIBLE) + , mOverflowClipBox(NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX) + , mResize(NS_STYLE_RESIZE_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) + , mScrollSnapTypeX(NS_STYLE_SCROLL_SNAP_TYPE_NONE) + , mScrollSnapTypeY(NS_STYLE_SCROLL_SNAP_TYPE_NONE) + , mScrollSnapPointsX(eStyleUnit_None) + , mScrollSnapPointsY(eStyleUnit_None) + , mBackfaceVisibility(NS_STYLE_BACKFACE_VISIBILITY_VISIBLE) + , mTransformStyle(NS_STYLE_TRANSFORM_STYLE_FLAT) + , mTransformBox(NS_STYLE_TRANSFORM_BOX_BORDER_BOX) + , mSpecifiedTransform(nullptr) + , 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) +{ + MOZ_COUNT_CTOR(nsStyleDisplay); + + // Initial value for mScrollSnapDestination is "0px 0px" + mScrollSnapDestination.SetInitialZeroValues(); + + 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) + , mOverflowClipBox(aSource.mOverflowClipBox) + , mResize(aSource.mResize) + , mOrient(aSource.mOrient) + , mIsolation(aSource.mIsolation) + , mTopLayer(aSource.mTopLayer) + , mWillChangeBitField(aSource.mWillChangeBitField) + , mWillChange(aSource.mWillChange) + , mTouchAction(aSource.mTouchAction) + , mScrollBehavior(aSource.mScrollBehavior) + , 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) + , 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) + , mShapeOutside(aSource.mShapeOutside) +{ + MOZ_COUNT_CTOR(nsStyleDisplay); +} + +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) + || mOverflowX != aNewData.mOverflowX + || mOverflowY != aNewData.mOverflowY + || mScrollBehavior != aNewData.mScrollBehavior + || mScrollSnapTypeX != aNewData.mScrollSnapTypeX + || mScrollSnapTypeY != aNewData.mScrollSnapTypeY + || mScrollSnapPointsX != aNewData.mScrollSnapPointsX + || mScrollSnapPointsY != aNewData.mScrollSnapPointsY + || mScrollSnapDestination != aNewData.mScrollSnapDestination + || mTopLayer != aNewData.mTopLayer + || mResize != aNewData.mResize) { + hint |= nsChangeHint_ReconstructFrame; + } + + /* 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 ((mAppearance == NS_THEME_TEXTFIELD && + aNewData.mAppearance != NS_THEME_TEXTFIELD) || + (mAppearance != NS_THEME_TEXTFIELD && + aNewData.mAppearance == NS_THEME_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 (mFloat != aNewData.mFloat) { + // Changing which side we float on doesn't affect descendants directly + hint |= nsChangeHint_AllReflowHints & + ~(nsChangeHint_ClearDescendantIntrinsics | + nsChangeHint_NeedDirtyReflow); + } + + 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. + if (mBreakType != aNewData.mBreakType + || mBreakInside != aNewData.mBreakInside + || mBreakBefore != aNewData.mBreakBefore + || mBreakAfter != aNewData.mBreakAfter + || mAppearance != aNewData.mAppearance + || mOrient != aNewData.mOrient + || mOverflowClipBox != aNewData.mOverflowClipBox) { + 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()) { + // We do not need to apply nsChangeHint_UpdateTransformLayer since + // nsChangeHint_RepaintFrame will forcibly invalidate the frame area and + // ensure layers are rebuilt (or removed). + hint |= nsChangeHint_UpdateContainingBlock | + nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateOverflow | + nsChangeHint_RepaintFrame; + } 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); + + if (!mSpecifiedTransform != !aNewData.mSpecifiedTransform || + (mSpecifiedTransform && + *mSpecifiedTransform != *aNewData.mSpecifiedTransform)) { + transformHint |= nsChangeHint_UpdateTransformLayer; + + if (mSpecifiedTransform && + aNewData.mSpecifiedTransform) { + transformHint |= nsChangeHint_UpdatePostTransformOverflow; + } else { + transformHint |= nsChangeHint_UpdateOverflow; + } + } + + 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 (HasPerspectiveStyle() != aNewData.HasPerspectiveStyle()) { + // A change from/to being a containing block for position:fixed. + hint |= nsChangeHint_UpdateContainingBlock; + } + + if (mChildPerspective != aNewData.mChildPerspective || + 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; + } + } + } + + // 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) { + 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; + } + + // 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 style context change anyway. + + // Note: Likewise, for animation-*, the animation manager gets + // notified about every new style context 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 || + mShapeOutside != aNewData.mShapeOutside)) { + hint |= nsChangeHint_NeutralChange; + } + + return hint; +} + +// -------------------- +// nsStyleVisibility +// + +nsStyleVisibility::nsStyleVisibility(StyleStructContext aContext) + : mDirection(aContext.GetBidi() == 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(NS_STYLE_COLOR_ADJUST_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). + hint |= nsChangeHint_ReconstructFrame; + } else { + if ((mImageOrientation != aNewData.mImageOrientation)) { + hint |= nsChangeHint_AllReflowHints | + nsChangeHint_RepaintFrame; + } + if (mVisible != aNewData.mVisible) { + 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); + MOZ_ASSERT(!mImageTracked, + "nsStyleContentData being destroyed while still tracking image!"); + if (mType == eStyleContentType_Image) { + NS_IF_RELEASE(mContent.mImage); + } else if (mType == eStyleContentType_Counter || + mType == eStyleContentType_Counters) { + mContent.mCounters->Release(); + } else if (mContent.mString) { + free(mContent.mString); + } +} + +nsStyleContentData::nsStyleContentData(const nsStyleContentData& aOther) + : mType(aOther.mType) +#ifdef DEBUG + , mImageTracked(false) +#endif +{ + MOZ_COUNT_CTOR(nsStyleContentData); + if (mType == eStyleContentType_Image) { + mContent.mImage = aOther.mContent.mImage; + NS_IF_ADDREF(mContent.mImage); + } else if (mType == eStyleContentType_Counter || + mType == eStyleContentType_Counters) { + mContent.mCounters = aOther.mContent.mCounters; + mContent.mCounters->AddRef(); + } else if (aOther.mContent.mString) { + mContent.mString = NS_strdup(aOther.mContent.mString); + } else { + mContent.mString = nullptr; + } +} + +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 == eStyleContentType_Image) { + if (!mContent.mImage || !aOther.mContent.mImage) { + return mContent.mImage == aOther.mContent.mImage; + } + bool eq; + nsCOMPtr<nsIURI> thisURI, otherURI; + mContent.mImage->GetURI(getter_AddRefs(thisURI)); + aOther.mContent.mImage->GetURI(getter_AddRefs(otherURI)); + return thisURI == otherURI || // handles null==null + (thisURI && otherURI && + NS_SUCCEEDED(thisURI->Equals(otherURI, &eq)) && + eq); + } + if (mType == eStyleContentType_Counter || + mType == eStyleContentType_Counters) { + return *mContent.mCounters == *aOther.mContent.mCounters; + } + return safe_strcmp(mContent.mString, aOther.mContent.mString) == 0; +} + +void +nsStyleContentData::TrackImage(ImageTracker* aImageTracker) +{ + // Sanity + MOZ_ASSERT(!mImageTracked, "Already tracking image!"); + MOZ_ASSERT(mType == eStyleContentType_Image, + "Trying to do image tracking on non-image!"); + MOZ_ASSERT(mContent.mImage, + "Can't track image when there isn't one!"); + + aImageTracker->Add(mContent.mImage); + + // Mark state +#ifdef DEBUG + mImageTracked = true; +#endif +} + +void +nsStyleContentData::UntrackImage(ImageTracker* aImageTracker) +{ + // Sanity + MOZ_ASSERT(mImageTracked, "Image not tracked!"); + MOZ_ASSERT(mType == eStyleContentType_Image, + "Trying to do image tracking on non-image!"); + MOZ_ASSERT(mContent.mImage, + "Can't untrack image when there isn't one!"); + + aImageTracker->Remove(mContent.mImage); + + // Mark state +#ifdef DEBUG + mImageTracked = false; +#endif +} + + +//----------------------- +// nsStyleContent +// + +nsStyleContent::nsStyleContent(StyleStructContext aContext) +{ + MOZ_COUNT_CTOR(nsStyleContent); +} + +nsStyleContent::~nsStyleContent() +{ + MOZ_COUNT_DTOR(nsStyleContent); +} + +void +nsStyleContent::Destroy(nsPresContext* aContext) +{ + // Unregister any images we might have with the document. + for (auto& content : mContents) { + if (content.mType == eStyleContentType_Image && content.mContent.mImage) { + content.UntrackImage(aContext->Document()->ImageTracker()); + } + } + + this->~nsStyleContent(); + aContext->PresShell()->FreeByObjectID(eArenaObjectID_nsStyleContent, this); +} + +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 +{ + // In ElementRestyler::Restyle we assume that if there's no existing + // ::before or ::after and we don't have to restyle children of the + // node then we can't end up with a ::before or ::after due to the + // restyle of the node itself. That's not quite true, but the only + // exception to the above is when the 'content' property of the node + // changes and the pseudo-element inherits the changed value. Since + // the code here triggers a frame change on the node in that case, + // the optimization in ElementRestyler::Restyle is ok. But if we ever + // change this code to not reconstruct frames on changes to the + // 'content' property, then we will need to revisit the optimization + // in ElementRestyler::Restyle. + + // 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(StyleStructContext aContext) + : 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) +{ + MOZ_COUNT_CTOR(nsStyleTextReset); + *this = aSource; +} + +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(StyleStructContext aContext) + : mTextAlign(NS_STYLE_TEXT_ALIGN_START) + , mTextAlignLast(NS_STYLE_TEXT_ALIGN_AUTO) + , mTextAlignTrue(false) + , mTextAlignLastTrue(false) + , mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE) + , mWhiteSpace(NS_STYLE_WHITESPACE_NORMAL) + , mWordBreak(NS_STYLE_WORDBREAK_NORMAL) + , mOverflowWrap(NS_STYLE_OVERFLOWWRAP_NORMAL) + , mHyphens(NS_STYLE_HYPHENS_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(nsCSSParser::ControlCharVisibilityDefault()) + , mTextEmphasisStyle(NS_STYLE_TEXT_EMPHASIS_STYLE_NONE) + , mTextRendering(NS_STYLE_TEXT_RENDERING_AUTO) + , mTabSize(NS_STYLE_TABSIZE_INITIAL) + , mTextEmphasisColor(StyleComplexColor::CurrentColor()) + , mWebkitTextFillColor(StyleComplexColor::CurrentColor()) + , mWebkitTextStrokeColor(StyleComplexColor::CurrentColor()) + , mWordSpacing(0, nsStyleCoord::CoordConstructor) + , mLetterSpacing(eStyleUnit_Normal) + , mLineHeight(eStyleUnit_Normal) + , mTextIndent(0, nsStyleCoord::CoordConstructor) + , mWebkitTextStrokeWidth(0, nsStyleCoord::CoordConstructor) + , mTextShadow(nullptr) +{ + MOZ_COUNT_CTOR(nsStyleText); + nsCOMPtr<nsIAtom> language = aContext.GetContentLanguage(); + 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) + , mTextAlignTrue(false) + , mTextAlignLastTrue(false) + , 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) + , mTabSize(aSource.mTabSize) + , mTextEmphasisColor(aSource.mTextEmphasisColor) + , mWebkitTextFillColor(aSource.mWebkitTextFillColor) + , mWebkitTextStrokeColor(aSource.mWebkitTextStrokeColor) + , 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) || + (mTextAlignTrue != aNewData.mTextAlignTrue) || + (mTextAlignLastTrue != aNewData.mTextAlignLastTrue) || + (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) || + (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; +} + +//----------------------- +// nsStyleUserInterface +// + +nsCursorImage::nsCursorImage() + : mHaveHotspot(false) + , mHotspotX(0.0f) + , mHotspotY(0.0f) +{ +} + +nsCursorImage::nsCursorImage(const nsCursorImage& aOther) + : mHaveHotspot(aOther.mHaveHotspot) + , mHotspotX(aOther.mHotspotX) + , mHotspotY(aOther.mHotspotY) +{ + SetImage(aOther.GetImage()); +} + +nsCursorImage::~nsCursorImage() +{ + SetImage(nullptr); +} + +nsCursorImage& +nsCursorImage::operator=(const nsCursorImage& aOther) +{ + if (this != &aOther) { + mHaveHotspot = aOther.mHaveHotspot; + mHotspotX = aOther.mHotspotX; + mHotspotY = aOther.mHotspotY; + SetImage(aOther.GetImage()); + } + + 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 && + EqualImages(mImage, aOther.mImage); +} + +nsStyleUserInterface::nsStyleUserInterface(StyleStructContext aContext) + : mUserInput(StyleUserInput::Auto) + , mUserModify(StyleUserModify::ReadOnly) + , mUserFocus(StyleUserFocus::None) + , mPointerEvents(NS_STYLE_POINTER_EVENTS_AUTO) + , mCursor(NS_STYLE_CURSOR_AUTO) +{ + MOZ_COUNT_CTOR(nsStyleUserInterface); +} + +nsStyleUserInterface::nsStyleUserInterface(const nsStyleUserInterface& aSource) + : mUserInput(aSource.mUserInput) + , mUserModify(aSource.mUserModify) + , mUserFocus(aSource.mUserFocus) + , mPointerEvents(aSource.mPointerEvents) + , mCursor(aSource.mCursor) + , mCursorImages(aSource.mCursorImages) +{ + MOZ_COUNT_CTOR(nsStyleUserInterface); +} + +nsStyleUserInterface::~nsStyleUserInterface() +{ + MOZ_COUNT_DTOR(nsStyleUserInterface); +} + +nsChangeHint +nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& 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) { + // nsSVGPathGeometryFrame's mRect depends on stroke _and_ on the value + // of pointer-events. See nsSVGPathGeometryFrame::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; + } + + return hint; +} + +//----------------------- +// nsStyleUIReset +// + +nsStyleUIReset::nsStyleUIReset(StyleStructContext aContext) + : mUserSelect(StyleUserSelect::Auto) + , mForceBrokenImageIcon(0) + , mIMEMode(NS_STYLE_IME_MODE_AUTO) + , mWindowDragging(StyleWindowDragging::Default) + , mWindowShadow(NS_STYLE_WINDOW_SHADOW_DEFAULT) +{ + MOZ_COUNT_CTOR(nsStyleUIReset); +} + +nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource) + : mUserSelect(aSource.mUserSelect) + , mForceBrokenImageIcon(aSource.mForceBrokenImageIcon) + , mIMEMode(aSource.mIMEMode) + , mWindowDragging(aSource.mWindowDragging) + , mWindowShadow(aSource.mWindowShadow) +{ + MOZ_COUNT_CTOR(nsStyleUIReset); +} + +nsStyleUIReset::~nsStyleUIReset() +{ + MOZ_COUNT_DTOR(nsStyleUIReset); +} + +nsChangeHint +nsStyleUIReset::CalcDifference(const nsStyleUIReset& aNewData) const +{ + // ignore mIMEMode + if (mForceBrokenImageIcon != aNewData.mForceBrokenImageIcon) { + return nsChangeHint_ReconstructFrame; + } + 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. + return NS_STYLE_HINT_REFLOW; + } + if (mUserSelect != aNewData.mUserSelect) { + return NS_STYLE_HINT_VISUAL; + } + + if (mWindowDragging != aNewData.mWindowDragging) { + return nsChangeHint_SchedulePaint; + } + + return nsChangeHint(0); +} + +//----------------------- +// nsStyleVariables +// + +nsStyleVariables::nsStyleVariables(StyleStructContext aContext) +{ + MOZ_COUNT_CTOR(nsStyleVariables); +} + +nsStyleVariables::nsStyleVariables(const nsStyleVariables& aSource) + : mVariables(aSource.mVariables) +{ + MOZ_COUNT_CTOR(nsStyleVariables); +} + +nsStyleVariables::~nsStyleVariables() +{ + MOZ_COUNT_DTOR(nsStyleVariables); +} + +nsChangeHint +nsStyleVariables::CalcDifference(const nsStyleVariables& aNewData) const +{ + return nsChangeHint(0); +} + +//----------------------- +// nsStyleEffects +// + +nsStyleEffects::nsStyleEffects(StyleStructContext aContext) + : 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; +} |