summaryrefslogtreecommitdiffstats
path: root/layout/style/nsStyleStruct.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/style/nsStyleStruct.cpp')
-rw-r--r--layout/style/nsStyleStruct.cpp4261
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;
+}