/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * David Hyatt (hyatt@netscape.com) * Mats Palmgren * Michael Ventnor * Jonathon Jongsma , Collabora Ltd. * L. David Baron , Mozilla Corporation * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * 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 "nsIWidget.h" #include "nsIStyleRule.h" #include "nsCRT.h" #include "nsCSSProps.h" #include "nsCOMPtr.h" #include "nsIFrame.h" #include "nsHTMLReflowState.h" #include "prenv.h" #include "nsSVGUtils.h" #include "nsBidiUtils.h" #include "imgIRequest.h" #include "imgIContainer.h" #include "prlog.h" // Make sure we have enough bits in NS_STYLE_INHERIT_MASK. PR_STATIC_ASSERT((((1 << nsStyleStructID_Length) - 1) & ~(NS_STYLE_INHERIT_MASK)) == 0); inline PRBool IsFixedUnit(const nsStyleCoord& aCoord, PRBool aEnumOK) { return aCoord.ConvertsToLength() || (aEnumOK && aCoord.GetUnit() == eStyleUnit_Enumerated); } static PRBool EqualURIs(nsIURI *aURI1, nsIURI *aURI2) { PRBool eq; return aURI1 == aURI2 || // handle null==null, and optimize (aURI1 && aURI2 && NS_SUCCEEDED(aURI1->Equals(aURI2, &eq)) && // not equal on fail eq); } static PRBool EqualURIs(nsCSSValue::URL *aURI1, nsCSSValue::URL *aURI2) { return aURI1 == aURI2 || // handle null==null, and optimize (aURI1 && aURI2 && aURI1->URIEquals(*aURI2)); } static PRBool EqualImages(imgIRequest *aImage1, imgIRequest* aImage2) { if (aImage1 == aImage2) { return PR_TRUE; } if (!aImage1 || !aImage2) { return PR_FALSE; } nsCOMPtr uri1, uri2; aImage1->GetURI(getter_AddRefs(uri1)); aImage2->GetURI(getter_AddRefs(uri2)); return EqualURIs(uri1, uri2); } static nsChangeHint CalcShadowDifference(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs); // -------------------- // nsStyleFont // nsStyleFont::nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext) : mFont(aFont), mGenericID(kGenericFont_NONE) { MOZ_COUNT_CTOR(nsStyleFont); mSize = mFont.size = nsStyleFont::ZoomText(aPresContext, mFont.size); #ifdef MOZ_MATHML mScriptUnconstrainedSize = mSize; mScriptMinSize = aPresContext->CSSTwipsToAppUnits( NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT)); mScriptLevel = 0; mScriptSizeMultiplier = NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER; #endif } nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) : mFont(aSrc.mFont) , mSize(aSrc.mSize) , mGenericID(aSrc.mGenericID) #ifdef MOZ_MATHML , mScriptLevel(aSrc.mScriptLevel) , mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize) , mScriptMinSize(aSrc.mScriptMinSize) , mScriptSizeMultiplier(aSrc.mScriptSizeMultiplier) #endif { MOZ_COUNT_CTOR(nsStyleFont); } nsStyleFont::nsStyleFont(nsPresContext* aPresContext) : mFont(*(aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID))), mGenericID(kGenericFont_NONE) { MOZ_COUNT_CTOR(nsStyleFont); mSize = mFont.size = nsStyleFont::ZoomText(aPresContext, mFont.size); #ifdef MOZ_MATHML mScriptUnconstrainedSize = mSize; mScriptMinSize = aPresContext->CSSTwipsToAppUnits( NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT)); mScriptLevel = 0; mScriptSizeMultiplier = NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER; #endif } void* nsStyleFont::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); if (result) memset(result, 0, sz); return result; } void nsStyleFont::Destroy(nsPresContext* aContext) { this->~nsStyleFont(); aContext->FreeToShell(sizeof(nsStyleFont), this); } nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const { if (mSize == aOther.mSize) { return CalcFontDifference(mFont, aOther.mFont); } return NS_STYLE_HINT_REFLOW; } #ifdef DEBUG /* static */ nsChangeHint nsStyleFont::MaxDifference() { return NS_STYLE_HINT_REFLOW; } #endif /* static */ nscoord nsStyleFont::ZoomText(nsPresContext *aPresContext, nscoord aSize) { return nscoord(float(aSize) * aPresContext->TextZoom()); } /* static */ nscoord nsStyleFont::UnZoomText(nsPresContext *aPresContext, nscoord aSize) { return nscoord(float(aSize) / aPresContext->TextZoom()); } nsChangeHint nsStyleFont::CalcFontDifference(const nsFont& aFont1, const nsFont& aFont2) { if ((aFont1.size == aFont2.size) && (aFont1.sizeAdjust == aFont2.sizeAdjust) && (aFont1.style == aFont2.style) && (aFont1.variant == aFont2.variant) && (aFont1.weight == aFont2.weight) && (aFont1.stretch == aFont2.stretch) && (aFont1.name == aFont2.name) && (aFont1.featureSettings == aFont2.featureSettings) && (aFont1.languageOverride == aFont2.languageOverride)) { if ((aFont1.decorations == aFont2.decorations)) { return NS_STYLE_HINT_NONE; } return NS_STYLE_HINT_VISUAL; } return NS_STYLE_HINT_REFLOW; } static PRBool IsFixedData(const nsStyleSides& aSides, PRBool aEnumOK) { NS_FOR_CSS_SIDES(side) { if (!IsFixedUnit(aSides.Get(side), aEnumOK)) return PR_FALSE; } return PR_TRUE; } static nscoord CalcCoord(const nsStyleCoord& aCoord, const nscoord* aEnumTable, PRInt32 aNumEnums) { if (aCoord.GetUnit() == eStyleUnit_Enumerated) { NS_ABORT_IF_FALSE(aEnumTable, "must have enum table"); PRInt32 value = aCoord.GetIntValue(); if (0 <= value && value < aNumEnums) { return aEnumTable[aCoord.GetIntValue()]; } NS_NOTREACHED("unexpected enum value"); return 0; } NS_ABORT_IF_FALSE(aCoord.ConvertsToLength(), "unexpected unit"); return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0); } nsStyleMargin::nsStyleMargin() { MOZ_COUNT_CTOR(nsStyleMargin); nsStyleCoord zero(0, nsStyleCoord::CoordConstructor); NS_FOR_CSS_SIDES(side) { mMargin.Set(side, zero); } mHasCachedMargin = PR_FALSE; } nsStyleMargin::nsStyleMargin(const nsStyleMargin& aSrc) { MOZ_COUNT_CTOR(nsStyleMargin); mMargin = aSrc.mMargin; mHasCachedMargin = PR_FALSE; } void* nsStyleMargin::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); if (result) memset(result, 0, sz); return result; } void nsStyleMargin::Destroy(nsPresContext* aContext) { this->~nsStyleMargin(); aContext->FreeToShell(sizeof(nsStyleMargin), this); } void nsStyleMargin::RecalcData() { if (IsFixedData(mMargin, PR_FALSE)) { NS_FOR_CSS_SIDES(side) { mCachedMargin.Side(side) = CalcCoord(mMargin.Get(side), nsnull, 0); } mHasCachedMargin = PR_TRUE; } else mHasCachedMargin = PR_FALSE; } nsChangeHint nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const { if (mMargin == aOther.mMargin) { return NS_STYLE_HINT_NONE; } // Margin differences can't affect descendant intrinsic sizes and // don't need to force children to reflow. return NS_SubtractHint(NS_STYLE_HINT_REFLOW, NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, nsChangeHint_NeedDirtyReflow)); } #ifdef DEBUG /* static */ nsChangeHint nsStyleMargin::MaxDifference() { return NS_SubtractHint(NS_STYLE_HINT_REFLOW, NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, nsChangeHint_NeedDirtyReflow)); } #endif nsStylePadding::nsStylePadding() { MOZ_COUNT_CTOR(nsStylePadding); nsStyleCoord zero(0, nsStyleCoord::CoordConstructor); NS_FOR_CSS_SIDES(side) { mPadding.Set(side, zero); } mHasCachedPadding = PR_FALSE; } nsStylePadding::nsStylePadding(const nsStylePadding& aSrc) { MOZ_COUNT_CTOR(nsStylePadding); mPadding = aSrc.mPadding; mHasCachedPadding = PR_FALSE; } void* nsStylePadding::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); if (result) memset(result, 0, sz); return result; } void nsStylePadding::Destroy(nsPresContext* aContext) { this->~nsStylePadding(); aContext->FreeToShell(sizeof(nsStylePadding), this); } void nsStylePadding::RecalcData() { if (IsFixedData(mPadding, PR_FALSE)) { NS_FOR_CSS_SIDES(side) { // Clamp negative calc() to 0. mCachedPadding.Side(side) = NS_MAX(CalcCoord(mPadding.Get(side), nsnull, 0), 0); } mHasCachedPadding = PR_TRUE; } else mHasCachedPadding = PR_FALSE; } nsChangeHint nsStylePadding::CalcDifference(const nsStylePadding& aOther) const { if (mPadding == aOther.mPadding) { return NS_STYLE_HINT_NONE; } // 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. return NS_SubtractHint(NS_STYLE_HINT_REFLOW, nsChangeHint_ClearDescendantIntrinsics); } #ifdef DEBUG /* static */ nsChangeHint nsStylePadding::MaxDifference() { return NS_SubtractHint(NS_STYLE_HINT_REFLOW, nsChangeHint_ClearDescendantIntrinsics); } #endif nsStyleBorder::nsStyleBorder(nsPresContext* aPresContext) : mHaveBorderImageWidth(PR_FALSE) #ifdef DEBUG , mImageTracked(false) #endif , mComputedBorder(0, 0, 0, 0) , mBorderImage(nsnull) { MOZ_COUNT_CTOR(nsStyleBorder); nscoord medium = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; NS_FOR_CSS_SIDES(side) { mBorder.Side(side) = medium; mBorderStyle[side] = NS_STYLE_BORDER_STYLE_NONE | BORDER_COLOR_FOREGROUND; mBorderColor[side] = NS_RGB(0, 0, 0); } NS_FOR_CSS_HALF_CORNERS(corner) { mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor)); } mBorderColors = nsnull; mBoxShadow = nsnull; mFloatEdge = NS_STYLE_FLOAT_EDGE_CONTENT; mTwipsPerPixel = aPresContext->DevPixelsToAppUnits(1); } nsBorderColors::~nsBorderColors() { NS_CSS_DELETE_LIST_MEMBER(nsBorderColors, this, mNext); } nsBorderColors* nsBorderColors::Clone(PRBool aDeep) const { nsBorderColors* result = new nsBorderColors(mColor); if (NS_UNLIKELY(!result)) return result; if (aDeep) NS_CSS_CLONE_LIST_MEMBER(nsBorderColors, this, mNext, result, (PR_FALSE)); return result; } nsStyleBorder::nsStyleBorder(const nsStyleBorder& aSrc) : mBorderRadius(aSrc.mBorderRadius), mBorderImageSplit(aSrc.mBorderImageSplit), mFloatEdge(aSrc.mFloatEdge), mBorderImageHFill(aSrc.mBorderImageHFill), mBorderImageVFill(aSrc.mBorderImageVFill), mBorderColors(nsnull), mBoxShadow(aSrc.mBoxShadow), mHaveBorderImageWidth(aSrc.mHaveBorderImageWidth), mBorderImageWidth(aSrc.mBorderImageWidth), mComputedBorder(aSrc.mComputedBorder), mBorder(aSrc.mBorder), mBorderImage(aSrc.mBorderImage), mTwipsPerPixel(aSrc.mTwipsPerPixel) { MOZ_COUNT_CTOR(nsStyleBorder); if (aSrc.mBorderColors) { EnsureBorderColors(); for (PRInt32 i = 0; i < 4; i++) if (aSrc.mBorderColors[i]) mBorderColors[i] = aSrc.mBorderColors[i]->Clone(); else mBorderColors[i] = nsnull; } NS_FOR_CSS_SIDES(side) { mBorderStyle[side] = aSrc.mBorderStyle[side]; mBorderColor[side] = aSrc.mBorderColor[side]; } NS_FOR_CSS_HALF_CORNERS(corner) { mBorderRadius.Set(corner, aSrc.mBorderRadius.Get(corner)); } } nsStyleBorder::~nsStyleBorder() { NS_ABORT_IF_FALSE(!mImageTracked, "nsStyleBorder being destroyed while still tracking image!"); MOZ_COUNT_DTOR(nsStyleBorder); if (mBorderColors) { for (PRInt32 i = 0; i < 4; i++) delete mBorderColors[i]; delete [] mBorderColors; } } void* nsStyleBorder::operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { void* result = aContext->AllocateFromShell(sz); if (result) memset(result, 0, sz); return result; } void nsStyleBorder::Destroy(nsPresContext* aContext) { if (mBorderImage) UntrackImage(aContext); this->~nsStyleBorder(); aContext->FreeToShell(sizeof(nsStyleBorder), this); } nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const { nsChangeHint shadowDifference = CalcShadowDifference(mBoxShadow, aOther.mBoxShadow); // Note that differences in mBorder don't affect rendering (which should only // use mComputedBorder), so don't need to be tested for here. // XXXbz we should be able to return a more specific change hint for // at least GetActualBorder() differences... if (mTwipsPerPixel != aOther.mTwipsPerPixel || GetActualBorder() != aOther.GetActualBorder() || mFloatEdge != aOther.mFloatEdge || (shadowDifference & nsChangeHint_ReflowFrame)) return NS_STYLE_HINT_REFLOW; // 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 VISUAL 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] != aOther.mBorderStyle[ix] || mBorderColor[ix] != aOther.mBorderColor[ix]) return NS_STYLE_HINT_VISUAL; } if (mBorderRadius != aOther.mBorderRadius || !mBorderColors != !aOther.mBorderColors) return NS_STYLE_HINT_VISUAL; if (IsBorderImageLoaded() || aOther.IsBorderImageLoaded()) { if (mBorderImage != aOther.mBorderImage || mBorderImageHFill != aOther.mBorderImageHFill || mBorderImageVFill != aOther.mBorderImageVFill || mBorderImageSplit != aOther.mBorderImageSplit) return NS_STYLE_HINT_VISUAL; // The call to GetActualBorder above already considered // mBorderImageWidth and mHaveBorderImageWidth. } // Note that at this point if mBorderColors is non-null so is // aOther.mBorderColors if (mBorderColors) { NS_FOR_CSS_SIDES(ix) { if (!nsBorderColors::Equal(mBorderColors[ix], aOther.mBorderColors[ix])) return NS_STYLE_HINT_VISUAL; } } return shadowDifference; } #ifdef DEBUG /* static */ nsChangeHint nsStyleBorder::MaxDifference() { return NS_STYLE_HINT_REFLOW; } #endif PRBool nsStyleBorder::ImageBorderDiffers() const { return mComputedBorder != (mHaveBorderImageWidth ? mBorderImageWidth : mBorder); } const nsMargin& nsStyleBorder::GetActualBorder() const { if (IsBorderImageLoaded()) if (mHaveBorderImageWidth) return mBorderImageWidth; else return mBorder; else return mComputedBorder; } void nsStyleBorder::TrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(!mImageTracked, "Already tracking image!"); NS_ABORT_IF_FALSE(mBorderImage, "Can't track null image!"); // Register the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->AddImage(mBorderImage); // Mark state #ifdef DEBUG mImageTracked = true; #endif } void nsStyleBorder::UntrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(mImageTracked, "Image not tracked!"); NS_ABORT_IF_FALSE(mBorderImage, "Can't track null image!"); // Unregister the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->RemoveImage(mBorderImage); // Mark state #ifdef DEBUG mImageTracked = false; #endif } nsStyleOutline::nsStyleOutline(nsPresContext* aPresContext) { MOZ_COUNT_CTOR(nsStyleOutline); // spacing values not inherited nsStyleCoord zero(0, nsStyleCoord::CoordConstructor); NS_FOR_CSS_HALF_CORNERS(corner) { mOutlineRadius.Set(corner, zero); } mOutlineOffset = 0; mOutlineWidth = nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated); mOutlineStyle = NS_STYLE_BORDER_STYLE_NONE; mOutlineColor = NS_RGB(0, 0, 0); mHasCachedOutline = PR_FALSE; mTwipsPerPixel = aPresContext->DevPixelsToAppUnits(1); } nsStyleOutline::nsStyleOutline(const nsStyleOutline& aSrc) { MOZ_COUNT_CTOR(nsStyleOutline); memcpy((nsStyleOutline*)this, &aSrc, sizeof(nsStyleOutline)); } void nsStyleOutline::RecalcData(nsPresContext* aContext) { if (NS_STYLE_BORDER_STYLE_NONE == GetOutlineStyle()) { mCachedOutlineWidth = 0; mHasCachedOutline = PR_TRUE; } else if (IsFixedUnit(mOutlineWidth, PR_TRUE)) { // Clamp negative calc() to 0. mCachedOutlineWidth = NS_MAX(CalcCoord(mOutlineWidth, aContext->GetBorderWidthTable(), 3), 0); mCachedOutlineWidth = NS_ROUND_BORDER_TO_PIXELS(mCachedOutlineWidth, mTwipsPerPixel); mHasCachedOutline = PR_TRUE; } else mHasCachedOutline = PR_FALSE; } nsChangeHint nsStyleOutline::CalcDifference(const nsStyleOutline& aOther) const { PRBool outlineWasVisible = mCachedOutlineWidth > 0 && mOutlineStyle != NS_STYLE_BORDER_STYLE_NONE; PRBool outlineIsVisible = aOther.mCachedOutlineWidth > 0 && aOther.mOutlineStyle != NS_STYLE_BORDER_STYLE_NONE; if (outlineWasVisible != outlineIsVisible || (outlineIsVisible && (mOutlineOffset != aOther.mOutlineOffset || mOutlineWidth != aOther.mOutlineWidth || mTwipsPerPixel != aOther.mTwipsPerPixel))) { return NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame); } if ((mOutlineStyle != aOther.mOutlineStyle) || (mOutlineColor != aOther.mOutlineColor) || (mOutlineRadius != aOther.mOutlineRadius)) { return nsChangeHint_RepaintFrame; } return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleOutline::MaxDifference() { return NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame); } #endif // -------------------- // nsStyleList // nsStyleList::nsStyleList() : mListStyleType(NS_STYLE_LIST_STYLE_DISC), mListStylePosition(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE) { MOZ_COUNT_CTOR(nsStyleList); } nsStyleList::~nsStyleList() { MOZ_COUNT_DTOR(nsStyleList); } nsStyleList::nsStyleList(const nsStyleList& aSource) : mListStyleType(aSource.mListStyleType), mListStylePosition(aSource.mListStylePosition), mImageRegion(aSource.mImageRegion) { SetListStyleImage(aSource.GetListStyleImage()); MOZ_COUNT_CTOR(nsStyleList); } nsChangeHint nsStyleList::CalcDifference(const nsStyleList& aOther) const { if (mListStylePosition != aOther.mListStylePosition) return NS_STYLE_HINT_FRAMECHANGE; if (EqualImages(mListStyleImage, aOther.mListStyleImage) && mListStyleType == aOther.mListStyleType) { if (mImageRegion.IsEqualInterior(aOther.mImageRegion)) return NS_STYLE_HINT_NONE; if (mImageRegion.width == aOther.mImageRegion.width && mImageRegion.height == aOther.mImageRegion.height) return NS_STYLE_HINT_VISUAL; } return NS_STYLE_HINT_REFLOW; } #ifdef DEBUG /* static */ nsChangeHint nsStyleList::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // -------------------- // nsStyleXUL // nsStyleXUL::nsStyleXUL() { MOZ_COUNT_CTOR(nsStyleXUL); mBoxAlign = NS_STYLE_BOX_ALIGN_STRETCH; mBoxDirection = NS_STYLE_BOX_DIRECTION_NORMAL; mBoxFlex = 0.0f; mBoxOrient = NS_STYLE_BOX_ORIENT_HORIZONTAL; mBoxPack = NS_STYLE_BOX_PACK_START; mBoxOrdinal = 1; mStretchStack = PR_TRUE; } nsStyleXUL::~nsStyleXUL() { MOZ_COUNT_DTOR(nsStyleXUL); } nsStyleXUL::nsStyleXUL(const nsStyleXUL& aSource) { MOZ_COUNT_CTOR(nsStyleXUL); memcpy((nsStyleXUL*)this, &aSource, sizeof(nsStyleXUL)); } nsChangeHint nsStyleXUL::CalcDifference(const nsStyleXUL& aOther) const { if (mBoxAlign == aOther.mBoxAlign && mBoxDirection == aOther.mBoxDirection && mBoxFlex == aOther.mBoxFlex && mBoxOrient == aOther.mBoxOrient && mBoxPack == aOther.mBoxPack && mBoxOrdinal == aOther.mBoxOrdinal) return NS_STYLE_HINT_NONE; if (mBoxOrdinal != aOther.mBoxOrdinal) return NS_STYLE_HINT_FRAMECHANGE; return NS_STYLE_HINT_REFLOW; } #ifdef DEBUG /* static */ nsChangeHint nsStyleXUL::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // -------------------- // nsStyleColumn // nsStyleColumn::nsStyleColumn(nsPresContext* aPresContext) { MOZ_COUNT_CTOR(nsStyleColumn); mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO; mColumnWidth.SetAutoValue(); mColumnGap.SetNormalValue(); mColumnRuleWidth = (aPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]; mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE; mColumnRuleColor = NS_RGB(0, 0, 0); mColumnRuleColorIsForeground = PR_TRUE; mTwipsPerPixel = aPresContext->AppUnitsPerDevPixel(); } nsStyleColumn::~nsStyleColumn() { MOZ_COUNT_DTOR(nsStyleColumn); } nsStyleColumn::nsStyleColumn(const nsStyleColumn& aSource) { MOZ_COUNT_CTOR(nsStyleColumn); memcpy((nsStyleColumn*)this, &aSource, sizeof(nsStyleColumn)); } nsChangeHint nsStyleColumn::CalcDifference(const nsStyleColumn& aOther) const { if ((mColumnWidth.GetUnit() == eStyleUnit_Auto) != (aOther.mColumnWidth.GetUnit() == eStyleUnit_Auto) || mColumnCount != aOther.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 NS_STYLE_HINT_FRAMECHANGE; if (mColumnWidth != aOther.mColumnWidth || mColumnGap != aOther.mColumnGap) return NS_STYLE_HINT_REFLOW; if (GetComputedColumnRuleWidth() != aOther.GetComputedColumnRuleWidth() || mColumnRuleStyle != aOther.mColumnRuleStyle || mColumnRuleColor != aOther.mColumnRuleColor || mColumnRuleColorIsForeground != aOther.mColumnRuleColorIsForeground) return NS_STYLE_HINT_VISUAL; return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleColumn::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // -------------------- // nsStyleSVG // nsStyleSVG::nsStyleSVG() { MOZ_COUNT_CTOR(nsStyleSVG); mFill.mType = eStyleSVGPaintType_Color; mFill.mPaint.mColor = NS_RGB(0,0,0); mFill.mFallbackColor = NS_RGB(0,0,0); mStroke.mType = eStyleSVGPaintType_None; mStroke.mPaint.mColor = NS_RGB(0,0,0); mStroke.mFallbackColor = NS_RGB(0,0,0); mStrokeDasharray = nsnull; mStrokeDashoffset.SetCoordValue(0); mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); mFillOpacity = 1.0f; mStrokeMiterlimit = 4.0f; mStrokeOpacity = 1.0f; mStrokeDasharrayLength = 0; mClipRule = NS_STYLE_FILL_RULE_NONZERO; mColorInterpolation = NS_STYLE_COLOR_INTERPOLATION_SRGB; mColorInterpolationFilters = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB; mFillRule = NS_STYLE_FILL_RULE_NONZERO; mImageRendering = NS_STYLE_IMAGE_RENDERING_AUTO; mShapeRendering = NS_STYLE_SHAPE_RENDERING_AUTO; mStrokeLinecap = NS_STYLE_STROKE_LINECAP_BUTT; mStrokeLinejoin = NS_STYLE_STROKE_LINEJOIN_MITER; mTextAnchor = NS_STYLE_TEXT_ANCHOR_START; mTextRendering = NS_STYLE_TEXT_RENDERING_AUTO; } nsStyleSVG::~nsStyleSVG() { MOZ_COUNT_DTOR(nsStyleSVG); delete [] mStrokeDasharray; } nsStyleSVG::nsStyleSVG(const nsStyleSVG& aSource) { MOZ_COUNT_CTOR(nsStyleSVG); mFill = aSource.mFill; mStroke = aSource.mStroke; mMarkerEnd = aSource.mMarkerEnd; mMarkerMid = aSource.mMarkerMid; mMarkerStart = aSource.mMarkerStart; mStrokeDasharrayLength = aSource.mStrokeDasharrayLength; if (aSource.mStrokeDasharray) { mStrokeDasharray = new nsStyleCoord[mStrokeDasharrayLength]; if (mStrokeDasharray) memcpy(mStrokeDasharray, aSource.mStrokeDasharray, mStrokeDasharrayLength * sizeof(nsStyleCoord)); else mStrokeDasharrayLength = 0; } else { mStrokeDasharray = nsnull; } 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; mImageRendering = aSource.mImageRendering; mShapeRendering = aSource.mShapeRendering; mStrokeLinecap = aSource.mStrokeLinecap; mStrokeLinejoin = aSource.mStrokeLinejoin; mTextAnchor = aSource.mTextAnchor; mTextRendering = aSource.mTextRendering; } static PRBool PaintURIChanged(const nsStyleSVGPaint& aPaint1, const nsStyleSVGPaint& aPaint2) { if (aPaint1.mType != aPaint2.mType) { return aPaint1.mType == eStyleSVGPaintType_Server || aPaint2.mType == eStyleSVGPaintType_Server; } return aPaint1.mType == eStyleSVGPaintType_Server && !EqualURIs(aPaint1.mPaint.mPaintServer, aPaint2.mPaint.mPaintServer); } nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mTextRendering != aOther.mTextRendering) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); // May be needed for non-svg frames NS_UpdateHint(hint, nsChangeHint_ReflowFrame); } if (!EqualURIs(mMarkerEnd, aOther.mMarkerEnd) || !EqualURIs(mMarkerMid, aOther.mMarkerMid) || !EqualURIs(mMarkerStart, aOther.mMarkerStart)) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); NS_UpdateHint(hint, nsChangeHint_UpdateEffects); return hint; } if (mFill != aOther.mFill || mStroke != aOther.mStroke) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); if (PaintURIChanged(mFill, aOther.mFill) || PaintURIChanged(mStroke, aOther.mStroke)) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); } // Nothing more to do, below we can only set "repaint" return hint; } if ( mStrokeDashoffset != aOther.mStrokeDashoffset || mStrokeWidth != aOther.mStrokeWidth || mFillOpacity != aOther.mFillOpacity || mStrokeMiterlimit != aOther.mStrokeMiterlimit || mStrokeOpacity != aOther.mStrokeOpacity || mClipRule != aOther.mClipRule || mColorInterpolation != aOther.mColorInterpolation || mColorInterpolationFilters != aOther.mColorInterpolationFilters || mFillRule != aOther.mFillRule || mImageRendering != aOther.mImageRendering || mShapeRendering != aOther.mShapeRendering || mStrokeDasharrayLength != aOther.mStrokeDasharrayLength || mStrokeLinecap != aOther.mStrokeLinecap || mStrokeLinejoin != aOther.mStrokeLinejoin || mTextAnchor != aOther.mTextAnchor) { NS_UpdateHint(hint, nsChangeHint_RepaintFrame); return hint; } // length of stroke dasharrays are the same (tested above) - check entries for (PRUint32 i=0; i~nsStyleSVGPaint(); new (this) nsStyleSVGPaint(); } mType = aType; } nsStyleSVGPaint& nsStyleSVGPaint::operator=(const nsStyleSVGPaint& aOther) { if (this == &aOther) return *this; SetType(aOther.mType); mFallbackColor = aOther.mFallbackColor; if (mType == eStyleSVGPaintType_Server) { mPaint.mPaintServer = aOther.mPaint.mPaintServer; NS_IF_ADDREF(mPaint.mPaintServer); } else { mPaint.mColor = aOther.mPaint.mColor; } return *this; } PRBool nsStyleSVGPaint::operator==(const nsStyleSVGPaint& aOther) const { if (mType != aOther.mType) return PR_FALSE; if (mType == eStyleSVGPaintType_Server) return EqualURIs(mPaint.mPaintServer, aOther.mPaint.mPaintServer) && mFallbackColor == aOther.mFallbackColor; if (mType == eStyleSVGPaintType_None) return PR_TRUE; return mPaint.mColor == aOther.mPaint.mColor; } // -------------------- // nsStylePosition // nsStylePosition::nsStylePosition(void) { MOZ_COUNT_CTOR(nsStylePosition); // positioning values not inherited nsStyleCoord autoCoord(eStyleUnit_Auto); mOffset.SetLeft(autoCoord); mOffset.SetTop(autoCoord); mOffset.SetRight(autoCoord); mOffset.SetBottom(autoCoord); mWidth.SetAutoValue(); mMinWidth.SetCoordValue(0); mMaxWidth.SetNoneValue(); mHeight.SetAutoValue(); mMinHeight.SetCoordValue(0); mMaxHeight.SetNoneValue(); mBoxSizing = NS_STYLE_BOX_SIZING_CONTENT; mZIndex.SetAutoValue(); } nsStylePosition::~nsStylePosition(void) { MOZ_COUNT_DTOR(nsStylePosition); } nsStylePosition::nsStylePosition(const nsStylePosition& aSource) { MOZ_COUNT_CTOR(nsStylePosition); memcpy((nsStylePosition*)this, &aSource, sizeof(nsStylePosition)); } nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) const { nsChangeHint hint = (mZIndex == aOther.mZIndex) ? NS_STYLE_HINT_NONE : nsChangeHint_RepaintFrame; if (mBoxSizing != aOther.mBoxSizing) { // Can affect both widths and heights; just a bad scene. return NS_CombineHint(hint, nsChangeHint_ReflowFrame); } if (mHeight != aOther.mHeight || mMinHeight != aOther.mMinHeight || mMaxHeight != aOther.mMaxHeight) { // Height changes can affect descendant intrinsic sizes due to replaced // elements with percentage heights in descendants which also have // percentage heights. And due to our not-so-great computation of mVResize // in nsHTMLReflowState, they do need to force reflow of the whole subtree. // XXXbz due to XUL caching heights as well, height changes also need to // clear ancestor intrinsics! return NS_CombineHint(hint, nsChangeHint_ReflowFrame); } if ((mWidth == aOther.mWidth) && (mMinWidth == aOther.mMinWidth) && (mMaxWidth == aOther.mMaxWidth)) { if (mOffset == aOther.mOffset) { return hint; } else { // Offset changes only affect positioned content, and can't affect any // intrinsic widths. They also don't need to force reflow of // descendants. return NS_CombineHint(hint, nsChangeHint_NeedReflow); } } // None of our width differences can affect descendant intrinsic // sizes and none of them need to force children to reflow. return NS_CombineHint(hint, NS_SubtractHint(nsChangeHint_ReflowFrame, NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, nsChangeHint_NeedDirtyReflow))); } #ifdef DEBUG /* static */ nsChangeHint nsStylePosition::MaxDifference() { return NS_STYLE_HINT_REFLOW; } #endif /* static */ PRBool nsStylePosition::WidthCoordDependsOnContainer(const nsStyleCoord &aCoord) { return aCoord.GetUnit() == eStyleUnit_Auto || aCoord.HasPercent() || (aCoord.GetUnit() == eStyleUnit_Enumerated && (aCoord.GetIntValue() == NS_STYLE_WIDTH_FIT_CONTENT || aCoord.GetIntValue() == NS_STYLE_WIDTH_AVAILABLE)); } // -------------------- // nsStyleTable // nsStyleTable::nsStyleTable() { MOZ_COUNT_CTOR(nsStyleTable); // values not inherited mLayoutStrategy = NS_STYLE_TABLE_LAYOUT_AUTO; mCols = NS_STYLE_TABLE_COLS_NONE; mFrame = NS_STYLE_TABLE_FRAME_NONE; mRules = NS_STYLE_TABLE_RULES_NONE; mSpan = 1; } nsStyleTable::~nsStyleTable(void) { MOZ_COUNT_DTOR(nsStyleTable); } nsStyleTable::nsStyleTable(const nsStyleTable& aSource) { MOZ_COUNT_CTOR(nsStyleTable); memcpy((nsStyleTable*)this, &aSource, sizeof(nsStyleTable)); } nsChangeHint nsStyleTable::CalcDifference(const nsStyleTable& aOther) const { // Changes in mRules may require reframing (if border-collapse stuff changes, for example). if (mRules != aOther.mRules || mSpan != aOther.mSpan || mLayoutStrategy != aOther.mLayoutStrategy) return NS_STYLE_HINT_FRAMECHANGE; if (mFrame != aOther.mFrame || mCols != aOther.mCols) return NS_STYLE_HINT_REFLOW; return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleTable::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // ----------------------- // nsStyleTableBorder nsStyleTableBorder::nsStyleTableBorder(nsPresContext* aPresContext) { MOZ_COUNT_CTOR(nsStyleTableBorder); mBorderCollapse = NS_STYLE_BORDER_SEPARATE; nsCompatibility compatMode = eCompatibility_FullStandards; if (aPresContext) compatMode = aPresContext->CompatibilityMode(); mEmptyCells = (compatMode == eCompatibility_NavQuirks) ? NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND : NS_STYLE_TABLE_EMPTY_CELLS_SHOW; mCaptionSide = NS_STYLE_CAPTION_SIDE_TOP; mBorderSpacingX = 0; mBorderSpacingY = 0; } nsStyleTableBorder::~nsStyleTableBorder(void) { MOZ_COUNT_DTOR(nsStyleTableBorder); } nsStyleTableBorder::nsStyleTableBorder(const nsStyleTableBorder& aSource) { MOZ_COUNT_CTOR(nsStyleTableBorder); memcpy((nsStyleTableBorder*)this, &aSource, sizeof(nsStyleTableBorder)); } nsChangeHint nsStyleTableBorder::CalcDifference(const nsStyleTableBorder& aOther) 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 != aOther.mBorderCollapse) { return NS_STYLE_HINT_FRAMECHANGE; } if ((mCaptionSide == aOther.mCaptionSide) && (mBorderSpacingX == aOther.mBorderSpacingX) && (mBorderSpacingY == aOther.mBorderSpacingY)) { if (mEmptyCells == aOther.mEmptyCells) return NS_STYLE_HINT_NONE; return NS_STYLE_HINT_VISUAL; } else return NS_STYLE_HINT_REFLOW; } #ifdef DEBUG /* static */ nsChangeHint nsStyleTableBorder::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // -------------------- // nsStyleColor // nsStyleColor::nsStyleColor(nsPresContext* aPresContext) { MOZ_COUNT_CTOR(nsStyleColor); mColor = aPresContext->DefaultColor(); } nsStyleColor::nsStyleColor(const nsStyleColor& aSource) { MOZ_COUNT_CTOR(nsStyleColor); mColor = aSource.mColor; } nsChangeHint nsStyleColor::CalcDifference(const nsStyleColor& aOther) const { if (mColor == aOther.mColor) return NS_STYLE_HINT_NONE; return NS_STYLE_HINT_VISUAL; } #ifdef DEBUG /* static */ nsChangeHint nsStyleColor::MaxDifference() { return NS_STYLE_HINT_VISUAL; } #endif // -------------------- // nsStyleGradient // PRBool nsStyleGradient::operator==(const nsStyleGradient& aOther) const { NS_ABORT_IF_FALSE(mSize == NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER || mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR, "incorrect combination of shape and size"); NS_ABORT_IF_FALSE(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 || mBgPosX != aOther.mBgPosX || mBgPosY != aOther.mBgPosY || mAngle != aOther.mAngle) return PR_FALSE; if (mStops.Length() != aOther.mStops.Length()) return PR_FALSE; for (PRUint32 i = 0; i < mStops.Length(); i++) { if (mStops[i].mLocation != aOther.mStops[i].mLocation || mStops[i].mColor != aOther.mStops[i].mColor) return PR_FALSE; } return PR_TRUE; } nsStyleGradient::nsStyleGradient(void) : mShape(NS_STYLE_GRADIENT_SHAPE_LINEAR) , mSize(NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER) , mRepeating(PR_FALSE) { } PRBool nsStyleGradient::IsOpaque() { for (PRUint32 i = 0; i < mStops.Length(); i++) { if (NS_GET_A(mStops[i].mColor) < 255) return PR_FALSE; } return PR_TRUE; } // -------------------- // nsStyleImage // nsStyleImage::nsStyleImage() : mType(eStyleImageType_Null) , mCropRect(nsnull) #ifdef DEBUG , mImageTracked(false) #endif { MOZ_COUNT_CTOR(nsStyleImage); } nsStyleImage::~nsStyleImage() { MOZ_COUNT_DTOR(nsStyleImage); if (mType != eStyleImageType_Null) SetNull(); } nsStyleImage::nsStyleImage(const nsStyleImage& aOther) : mType(eStyleImageType_Null) , mCropRect(nsnull) #ifdef DEBUG , mImageTracked(false) #endif { // 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) SetImageData(aOther.mImage); else if (aOther.mType == eStyleImageType_Gradient) SetGradientData(aOther.mGradient); else if (aOther.mType == eStyleImageType_Element) SetElementId(aOther.mElementId); SetCropRect(aOther.mCropRect); } void nsStyleImage::SetNull() { NS_ABORT_IF_FALSE(!mImageTracked, "Calling SetNull() with image tracked!"); if (mType == eStyleImageType_Gradient) mGradient->Release(); else if (mType == eStyleImageType_Image) NS_RELEASE(mImage); else if (mType == eStyleImageType_Element) nsCRT::free(mElementId); mType = eStyleImageType_Null; mCropRect = nsnull; } void nsStyleImage::SetImageData(imgIRequest* aImage) { NS_ABORT_IF_FALSE(!mImageTracked, "Setting a new image without untracking the old one!"); NS_IF_ADDREF(aImage); if (mType != eStyleImageType_Null) SetNull(); if (aImage) { mImage = aImage; mType = eStyleImageType_Image; } } void nsStyleImage::TrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(!mImageTracked, "Already tracking image!"); NS_ABORT_IF_FALSE(mType == eStyleImageType_Image, "Can't track image when there isn't one!"); // Register the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->AddImage(mImage); // Mark state #ifdef DEBUG mImageTracked = true; #endif } void nsStyleImage::UntrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(mImageTracked, "Image not tracked!"); NS_ABORT_IF_FALSE(mType == eStyleImageType_Image, "Can't untrack image when there isn't one!"); // Unregister the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->RemoveImage(mImage); // Mark state #ifdef DEBUG mImageTracked = false; #endif } 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 PRUnichar* aElementId) { if (mType != eStyleImageType_Null) SetNull(); if (aElementId) { mElementId = nsCRT::strdup(aElementId); mType = eStyleImageType_Element; } } void nsStyleImage::SetCropRect(nsStyleSides* aCropRect) { if (aCropRect) { mCropRect = new nsStyleSides(*aCropRect); // There is really not much we can do if 'new' fails } else { mCropRect = nsnull; } } static PRInt32 ConvertToPixelCoord(const nsStyleCoord& aCoord, PRInt32 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; } NS_ABORT_IF_FALSE(pixelValue >= 0, "we ensured non-negative while parsing"); pixelValue = NS_MIN(pixelValue, double(PR_INT32_MAX)); // avoid overflow return NS_lround(pixelValue); } PRBool nsStyleImage::ComputeActualCropRect(nsIntRect& aActualCropRect, PRBool* aIsEntireImage) const { if (mType != eStyleImageType_Image) return PR_FALSE; nsCOMPtr imageContainer; mImage->GetImage(getter_AddRefs(imageContainer)); if (!imageContainer) return PR_FALSE; nsIntSize imageSize; imageContainer->GetWidth(&imageSize.width); imageContainer->GetHeight(&imageSize.height); if (imageSize.width <= 0 || imageSize.height <= 0) return PR_FALSE; PRInt32 left = ConvertToPixelCoord(mCropRect->GetLeft(), imageSize.width); PRInt32 top = ConvertToPixelCoord(mCropRect->GetTop(), imageSize.height); PRInt32 right = ConvertToPixelCoord(mCropRect->GetRight(), imageSize.width); PRInt32 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 PR_TRUE; } nsresult nsStyleImage::RequestDecode() const { if ((mType == eStyleImageType_Image) && mImage) return mImage->RequestDecode(); return NS_OK; } PRBool nsStyleImage::IsOpaque() const { if (!IsComplete()) return PR_FALSE; if (mType == eStyleImageType_Gradient) return mGradient->IsOpaque(); if (mType == eStyleImageType_Element) return PR_FALSE; NS_ABORT_IF_FALSE(mType == eStyleImageType_Image, "unexpected image type"); nsCOMPtr imageContainer; mImage->GetImage(getter_AddRefs(imageContainer)); NS_ABORT_IF_FALSE(imageContainer, "IsComplete() said image container is ready"); // Check if the crop region of the current image frame is opaque PRBool isOpaque; if (NS_SUCCEEDED(imageContainer->GetCurrentFrameIsOpaque(&isOpaque)) && isOpaque) { if (!mCropRect) return PR_TRUE; // Must make sure if mCropRect contains at least a pixel. // XXX Is this optimization worth it? Maybe I should just return PR_FALSE. nsIntRect actualCropRect; PRBool rv = ComputeActualCropRect(actualCropRect); NS_ASSERTION(rv, "ComputeActualCropRect() can not fail here"); return rv && !actualCropRect.IsEmpty(); } return PR_FALSE; } PRBool nsStyleImage::IsComplete() const { switch (mType) { case eStyleImageType_Null: return PR_FALSE; case eStyleImageType_Gradient: case eStyleImageType_Element: return PR_TRUE; case eStyleImageType_Image: { PRUint32 status = imgIRequest::STATUS_ERROR; return NS_SUCCEEDED(mImage->GetImageStatus(&status)) && (status & imgIRequest::STATUS_SIZE_AVAILABLE) && (status & imgIRequest::STATUS_FRAME_COMPLETE); } default: NS_NOTREACHED("unexpected image type"); return PR_FALSE; } } static inline PRBool EqualRects(const nsStyleSides* aRect1, const nsStyleSides* aRect2) { return aRect1 == aRect2 || /* handles null== null, and optimize */ (aRect1 && aRect2 && *aRect1 == *aRect2); } PRBool nsStyleImage::operator==(const nsStyleImage& aOther) const { if (mType != aOther.mType) return PR_FALSE; if (!EqualRects(mCropRect, aOther.mCropRect)) return PR_FALSE; if (mType == eStyleImageType_Image) return EqualImages(mImage, aOther.mImage); if (mType == eStyleImageType_Gradient) return *mGradient == *aOther.mGradient; if (mType == eStyleImageType_Element) return nsCRT::strcmp(mElementId, aOther.mElementId) == 0; return PR_TRUE; } // -------------------- // nsStyleBackground // nsStyleBackground::nsStyleBackground() : mAttachmentCount(1) , mClipCount(1) , mOriginCount(1) , mRepeatCount(1) , mPositionCount(1) , mImageCount(1) , mSizeCount(1) , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) , mBackgroundInlinePolicy(NS_STYLE_BG_INLINE_POLICY_CONTINUOUS) { MOZ_COUNT_CTOR(nsStyleBackground); Layer *onlyLayer = mLayers.AppendElement(); NS_ASSERTION(onlyLayer, "auto array must have room for 1 element"); onlyLayer->SetInitialValues(); } nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) : mAttachmentCount(aSource.mAttachmentCount) , mClipCount(aSource.mClipCount) , mOriginCount(aSource.mOriginCount) , mRepeatCount(aSource.mRepeatCount) , mPositionCount(aSource.mPositionCount) , mImageCount(aSource.mImageCount) , mSizeCount(aSource.mSizeCount) , mLayers(aSource.mLayers) // deep copy , mBackgroundColor(aSource.mBackgroundColor) , mBackgroundInlinePolicy(aSource.mBackgroundInlinePolicy) { MOZ_COUNT_CTOR(nsStyleBackground); // If the deep copy of mLayers failed, truncate the counts. PRUint32 count = mLayers.Length(); if (count != aSource.mLayers.Length()) { NS_WARNING("truncating counts due to out-of-memory"); mAttachmentCount = NS_MAX(mAttachmentCount, count); mClipCount = NS_MAX(mClipCount, count); mOriginCount = NS_MAX(mOriginCount, count); mRepeatCount = NS_MAX(mRepeatCount, count); mPositionCount = NS_MAX(mPositionCount, count); mImageCount = NS_MAX(mImageCount, count); mSizeCount = NS_MAX(mSizeCount, count); } } nsStyleBackground::~nsStyleBackground() { MOZ_COUNT_DTOR(nsStyleBackground); } void nsStyleBackground::Destroy(nsPresContext* aContext) { // Untrack all the images stored in our layers for (PRUint32 i = 0; i < mImageCount; ++i) mLayers[i].UntrackImages(aContext); this->~nsStyleBackground(); aContext->FreeToShell(sizeof(nsStyleBackground), this); } nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const { const nsStyleBackground* moreLayers = mImageCount > aOther.mImageCount ? this : &aOther; const nsStyleBackground* lessLayers = mImageCount > aOther.mImageCount ? &aOther : this; bool hasVisualDifference = false; NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, moreLayers) { if (i < lessLayers->mImageCount) { if (moreLayers->mLayers[i] != lessLayers->mLayers[i]) { if ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) || (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element)) return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL); hasVisualDifference = true; } } else { if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL); hasVisualDifference = true; } } if (hasVisualDifference || mBackgroundColor != aOther.mBackgroundColor || mBackgroundInlinePolicy != aOther.mBackgroundInlinePolicy) return NS_STYLE_HINT_VISUAL; return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleBackground::MaxDifference() { return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL); } #endif PRBool nsStyleBackground::HasFixedBackground() const { NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, this) { const Layer &layer = mLayers[i]; if (layer.mAttachment == NS_STYLE_BG_ATTACHMENT_FIXED && !layer.mImage.IsEmpty()) { return PR_TRUE; } } return PR_FALSE; } PRBool nsStyleBackground::IsTransparent() const { return BottomLayer().mImage.IsEmpty() && mImageCount == 1 && NS_GET_A(mBackgroundColor) == 0; } void nsStyleBackground::Position::SetInitialValues() { mXPosition.mPercent = 0.0f; mXPosition.mLength = 0; mYPosition.mPercent = 0.0f; mYPosition.mLength = 0; } void nsStyleBackground::Size::SetInitialValues() { mWidthType = mHeightType = eAuto; } bool nsStyleBackground::Size::operator==(const Size& aOther) const { NS_ABORT_IF_FALSE(mWidthType < eDimensionType_COUNT, "bad mWidthType for this"); NS_ABORT_IF_FALSE(mHeightType < eDimensionType_COUNT, "bad mHeightType for this"); NS_ABORT_IF_FALSE(aOther.mWidthType < eDimensionType_COUNT, "bad mWidthType for aOther"); NS_ABORT_IF_FALSE(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); } nsStyleBackground::Layer::Layer() { } nsStyleBackground::Layer::~Layer() { } void nsStyleBackground::Layer::SetInitialValues() { mAttachment = NS_STYLE_BG_ATTACHMENT_SCROLL; mClip = NS_STYLE_BG_CLIP_BORDER; mOrigin = NS_STYLE_BG_ORIGIN_PADDING; mRepeat = NS_STYLE_BG_REPEAT_XY; mPosition.SetInitialValues(); mSize.SetInitialValues(); mImage.SetNull(); } PRBool nsStyleBackground::Layer::RenderingMightDependOnFrameSize() const { // Do we even have an image? if (mImage.IsEmpty()) { return PR_FALSE; } // Does our position or size depend on frame size? if (mPosition.DependsOnFrameSize() || mSize.DependsOnFrameSize(mImage.GetType())) { return PR_TRUE; } // Are we an SVG image with a viewBox attribute? if (mImage.GetType() == eStyleImageType_Image) { nsCOMPtr imageContainer; mImage.GetImageData()->GetImage(getter_AddRefs(imageContainer)); if (imageContainer && imageContainer->GetType() == imgIContainer::TYPE_VECTOR) { nsIFrame* rootFrame = imageContainer->GetRootLayoutFrame(); if (rootFrame && nsSVGUtils::RootSVGElementHasViewbox(rootFrame->GetContent())) { return PR_TRUE; } } } return PR_FALSE; } PRBool nsStyleBackground::Layer::operator==(const Layer& aOther) const { return mAttachment == aOther.mAttachment && mClip == aOther.mClip && mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat && mPosition == aOther.mPosition && mSize == aOther.mSize && mImage == aOther.mImage; } // -------------------- // nsStyleDisplay // void nsTimingFunction::AssignFromKeyword(PRInt32 aTimingFunctionType) { switch (aTimingFunctionType) { case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START: mType = StepStart; mSteps = 1; return; case NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END: mType = StepEnd; mSteps = 1; return; default: mType = Function; break; } PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE == 0); PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR == 1); PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN == 2); PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_OUT == 3); PR_STATIC_ASSERT(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT == 4); static const float timingFunctionValues[5][4] = { { 0.25, 0.10, 0.25, 1.00 }, // ease { 0.00, 0.00, 1.00, 1.00 }, // linear { 0.42, 0.00, 1.00, 1.00 }, // ease-in { 0.00, 0.00, 0.58, 1.00 }, // ease-out { 0.42, 0.00, 0.58, 1.00 } // ease-in-out }; NS_ABORT_IF_FALSE(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]; } nsTransition::nsTransition(const nsTransition& aCopy) : mTimingFunction(aCopy.mTimingFunction) , mDuration(aCopy.mDuration) , mDelay(aCopy.mDelay) , mProperty(aCopy.mProperty) , mUnknownProperty(aCopy.mUnknownProperty) { } void nsTransition::SetInitialValues() { mTimingFunction = nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE); mDuration = 0.0; mDelay = 0.0; mProperty = eCSSPropertyExtra_all_properties; } void nsTransition::SetUnknownProperty(const nsAString& aUnknownProperty) { NS_ASSERTION(nsCSSProps::LookupProperty(aUnknownProperty) == eCSSProperty_UNKNOWN, "should be unknown property"); mProperty = eCSSProperty_UNKNOWN; mUnknownProperty = do_GetAtom(aUnknownProperty); } #ifdef MOZ_CSS_ANIMATIONS nsAnimation::nsAnimation(const nsAnimation& 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 nsAnimation::SetInitialValues() { mTimingFunction = nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE); mDuration = 0.0; mDelay = 0.0; mName = EmptyString(); mDirection = NS_STYLE_ANIMATION_DIRECTION_NORMAL; mFillMode = NS_STYLE_ANIMATION_FILL_MODE_NONE; mPlayState = NS_STYLE_ANIMATION_PLAY_STATE_RUNNING; mIterationCount = 1.0f; } #endif nsStyleDisplay::nsStyleDisplay() { MOZ_COUNT_CTOR(nsStyleDisplay); mAppearance = NS_THEME_NONE; mDisplay = NS_STYLE_DISPLAY_INLINE; mOriginalDisplay = NS_STYLE_DISPLAY_NONE; mPosition = NS_STYLE_POSITION_STATIC; mFloats = NS_STYLE_FLOAT_NONE; mBreakType = NS_STYLE_CLEAR_NONE; mBreakBefore = PR_FALSE; mBreakAfter = PR_FALSE; mOverflowX = NS_STYLE_OVERFLOW_VISIBLE; mOverflowY = NS_STYLE_OVERFLOW_VISIBLE; mResize = NS_STYLE_RESIZE_NONE; mClipFlags = NS_STYLE_CLIP_AUTO; mClip.SetRect(0,0,0,0); mOpacity = 1.0f; mSpecifiedTransform = nsnull; mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin mTransformOrigin[1].SetPercentValue(0.5f); mTransitions.AppendElement(); NS_ABORT_IF_FALSE(mTransitions.Length() == 1, "appending within auto buffer should never fail"); mTransitions[0].SetInitialValues(); mTransitionTimingFunctionCount = 1; mTransitionDurationCount = 1; mTransitionDelayCount = 1; mTransitionPropertyCount = 1; #ifdef MOZ_CSS_ANIMATIONS mAnimations.AppendElement(); NS_ABORT_IF_FALSE(mAnimations.Length() == 1, "appending within auto buffer should never fail"); mAnimations[0].SetInitialValues(); mAnimationTimingFunctionCount = 1; mAnimationDurationCount = 1; mAnimationDelayCount = 1; mAnimationNameCount = 1; mAnimationDirectionCount = 1; mAnimationFillModeCount = 1; mAnimationPlayStateCount = 1; mAnimationIterationCountCount = 1; #endif } nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) : mTransitions(aSource.mTransitions) , mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount) , mTransitionDurationCount(aSource.mTransitionDurationCount) , mTransitionDelayCount(aSource.mTransitionDelayCount) , mTransitionPropertyCount(aSource.mTransitionPropertyCount) #ifdef MOZ_CSS_ANIMATIONS , 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) #endif { MOZ_COUNT_CTOR(nsStyleDisplay); mAppearance = aSource.mAppearance; mDisplay = aSource.mDisplay; mOriginalDisplay = aSource.mOriginalDisplay; mBinding = aSource.mBinding; mPosition = aSource.mPosition; mFloats = aSource.mFloats; mBreakType = aSource.mBreakType; mBreakBefore = aSource.mBreakBefore; mBreakAfter = aSource.mBreakAfter; mOverflowX = aSource.mOverflowX; mOverflowY = aSource.mOverflowY; mResize = aSource.mResize; mClipFlags = aSource.mClipFlags; mClip = aSource.mClip; mOpacity = aSource.mOpacity; /* Copy over the transformation information. */ mSpecifiedTransform = aSource.mSpecifiedTransform; if (mSpecifiedTransform) mTransform = aSource.mTransform; /* Copy over transform origin. */ mTransformOrigin[0] = aSource.mTransformOrigin[0]; mTransformOrigin[1] = aSource.mTransformOrigin[1]; } nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const { nsChangeHint hint = nsChangeHint(0); if (!EqualURIs(mBinding, aOther.mBinding) || mPosition != aOther.mPosition || mDisplay != aOther.mDisplay || (mFloats == NS_STYLE_FLOAT_NONE) != (aOther.mFloats == NS_STYLE_FLOAT_NONE) || (mOverflowX != aOther.mOverflowX && mDisplay != NS_STYLE_DISPLAY_INLINE) || (mOverflowY != aOther.mOverflowY && mDisplay != NS_STYLE_DISPLAY_INLINE) || mResize != aOther.mResize) NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); if (mFloats != aOther.mFloats) { // Changing which side we float on doesn't affect descendants directly NS_UpdateHint(hint, NS_SubtractHint(nsChangeHint_ReflowFrame, NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, nsChangeHint_NeedDirtyReflow))); } // XXX the following is conservative, for now: changing float breaking shouldn't // necessarily require a repaint, reflow should suffice. if (mBreakType != aOther.mBreakType || mBreakBefore != aOther.mBreakBefore || mBreakAfter != aOther.mBreakAfter || mAppearance != aOther.mAppearance || mClipFlags != aOther.mClipFlags || !mClip.IsEqualInterior(aOther.mClip)) NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame)); if (mOpacity != aOther.mOpacity) { NS_UpdateHint(hint, nsChangeHint_UpdateOpacityLayer); } /* 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 (HasTransform() != aOther.HasTransform()) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); } else if (HasTransform()) { /* 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 do a reflow and a repaint. The reflow is to recompute * the overflow rect (which probably changed if the transform changed) * and to redraw within the bounds of that new overflow rect. */ if (mTransform != aOther.mTransform) NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_UpdateTransformLayer)); for (PRUint8 index = 0; index < 2; ++index) if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) { NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame, nsChangeHint_RepaintFrame)); break; } } // 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. return hint; } #ifdef DEBUG /* static */ nsChangeHint nsStyleDisplay::MaxDifference() { // All the parts of FRAMECHANGE are present above in CalcDifference. return nsChangeHint(NS_STYLE_HINT_FRAMECHANGE | nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer); } #endif // -------------------- // nsStyleVisibility // nsStyleVisibility::nsStyleVisibility(nsPresContext* aPresContext) { MOZ_COUNT_CTOR(nsStyleVisibility); PRUint32 bidiOptions = aPresContext->GetBidi(); if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL) mDirection = NS_STYLE_DIRECTION_RTL; else mDirection = NS_STYLE_DIRECTION_LTR; nsAutoString language; aPresContext->Document()->GetContentLanguage(language); language.StripWhitespace(); // Content-Language may be a comma-separated list of language codes, // in which case the HTML5 spec says to treat it as unknown if (!language.IsEmpty() && language.FindChar(PRUnichar(',')) == kNotFound) { mLanguage = do_GetAtom(language); } else { // we didn't find a (usable) Content-Language, so we fall back // to whatever the presContext guessed from the charset mLanguage = aPresContext->GetLanguageFromCharset(); } mVisible = NS_STYLE_VISIBILITY_VISIBLE; mPointerEvents = NS_STYLE_POINTER_EVENTS_AUTO; } nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) { MOZ_COUNT_CTOR(nsStyleVisibility); mDirection = aSource.mDirection; mVisible = aSource.mVisible; mLanguage = aSource.mLanguage; mPointerEvents = aSource.mPointerEvents; } nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mDirection != aOther.mDirection) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); } else if (mLanguage == aOther.mLanguage) { if (mVisible != aOther.mVisible) { if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); } else { NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); } } } else { NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); } return hint; } #ifdef DEBUG /* static */ nsChangeHint nsStyleVisibility::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif nsStyleContentData::~nsStyleContentData() { NS_ABORT_IF_FALSE(!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) { NS_Free(mContent.mString); } } nsStyleContentData& nsStyleContentData::operator=(const nsStyleContentData& aOther) { if (this == &aOther) return *this; this->~nsStyleContentData(); new (this) nsStyleContentData(); mType = aOther.mType; 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 = nsnull; } return *this; } PRBool nsStyleContentData::operator==(const nsStyleContentData& aOther) const { if (mType != aOther.mType) return PR_FALSE; if (mType == eStyleContentType_Image) { if (!mContent.mImage || !aOther.mContent.mImage) return mContent.mImage == aOther.mContent.mImage; PRBool eq; nsCOMPtr 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 nsCRT::strcmp(mContent.mString, aOther.mContent.mString) == 0; } void nsStyleContentData::TrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(!mImageTracked, "Already tracking image!"); NS_ABORT_IF_FALSE(mType == eStyleContentType_Image, "Tryingto do image tracking on non-image!"); NS_ABORT_IF_FALSE(mContent.mImage, "Can't track image when there isn't one!"); // Register the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->AddImage(mContent.mImage); // Mark state #ifdef DEBUG mImageTracked = true; #endif } void nsStyleContentData::UntrackImage(nsPresContext* aContext) { // Sanity NS_ABORT_IF_FALSE(mImageTracked, "Image not tracked!"); NS_ABORT_IF_FALSE(mType == eStyleContentType_Image, "Trying to do image tracking on non-image!"); NS_ABORT_IF_FALSE(mContent.mImage, "Can't untrack image when there isn't one!"); // Unregister the image with the document nsIDocument* doc = aContext->Document(); if (doc) doc->RemoveImage(mContent.mImage); // Mark state #ifdef DEBUG mImageTracked = false; #endif } //----------------------- // nsStyleContent // nsStyleContent::nsStyleContent(void) : mMarkerOffset(), mContents(nsnull), mIncrements(nsnull), mResets(nsnull), mContentCount(0), mIncrementCount(0), mResetCount(0) { MOZ_COUNT_CTOR(nsStyleContent); mMarkerOffset.SetAutoValue(); } nsStyleContent::~nsStyleContent(void) { MOZ_COUNT_DTOR(nsStyleContent); DELETE_ARRAY_IF(mContents); DELETE_ARRAY_IF(mIncrements); DELETE_ARRAY_IF(mResets); } void nsStyleContent::Destroy(nsPresContext* aContext) { // Unregister any images we might have with the document. for (PRUint32 i = 0; i < mContentCount; ++i) { if ((mContents[i].mType == eStyleContentType_Image) && mContents[i].mContent.mImage) { mContents[i].UntrackImage(aContext); } } this->~nsStyleContent(); aContext->FreeToShell(sizeof(nsStyleContent), this); } nsStyleContent::nsStyleContent(const nsStyleContent& aSource) :mMarkerOffset(), mContents(nsnull), mIncrements(nsnull), mResets(nsnull), mContentCount(0), mIncrementCount(0), mResetCount(0) { MOZ_COUNT_CTOR(nsStyleContent); mMarkerOffset = aSource.mMarkerOffset; PRUint32 index; if (NS_SUCCEEDED(AllocateContents(aSource.ContentCount()))) { for (index = 0; index < mContentCount; index++) { ContentAt(index) = aSource.ContentAt(index); } } if (NS_SUCCEEDED(AllocateCounterIncrements(aSource.CounterIncrementCount()))) { for (index = 0; index < mIncrementCount; index++) { const nsStyleCounterData *data = aSource.GetCounterIncrementAt(index); mIncrements[index].mCounter = data->mCounter; mIncrements[index].mValue = data->mValue; } } if (NS_SUCCEEDED(AllocateCounterResets(aSource.CounterResetCount()))) { for (index = 0; index < mResetCount; index++) { const nsStyleCounterData *data = aSource.GetCounterResetAt(index); mResets[index].mCounter = data->mCounter; mResets[index].mValue = data->mValue; } } } nsChangeHint nsStyleContent::CalcDifference(const nsStyleContent& aOther) const { // In ReResolveStyleContext 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 ReResolveStyleContext 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 ReResolveStyleContext. if (mContentCount != aOther.mContentCount || mIncrementCount != aOther.mIncrementCount || mResetCount != aOther.mResetCount) { return NS_STYLE_HINT_FRAMECHANGE; } PRUint32 ix = mContentCount; while (0 < ix--) { if (mContents[ix] != aOther.mContents[ix]) { // Unfortunately we need to reframe here; a simple reflow // will not pick up different text or different image URLs, // since we set all that up in the CSSFrameConstructor return NS_STYLE_HINT_FRAMECHANGE; } } ix = mIncrementCount; while (0 < ix--) { if ((mIncrements[ix].mValue != aOther.mIncrements[ix].mValue) || (mIncrements[ix].mCounter != aOther.mIncrements[ix].mCounter)) { return NS_STYLE_HINT_FRAMECHANGE; } } ix = mResetCount; while (0 < ix--) { if ((mResets[ix].mValue != aOther.mResets[ix].mValue) || (mResets[ix].mCounter != aOther.mResets[ix].mCounter)) { return NS_STYLE_HINT_FRAMECHANGE; } } if (mMarkerOffset != aOther.mMarkerOffset) { return NS_STYLE_HINT_REFLOW; } return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleContent::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif nsresult nsStyleContent::AllocateContents(PRUint32 aCount) { // We need to run the destructors of the elements of mContents, so we // delete and reallocate even if aCount == mContentCount. (If // nsStyleContentData had its members private and managed their // ownership on setting, we wouldn't need this, but that seems // unnecessary at this point.) DELETE_ARRAY_IF(mContents); if (aCount) { mContents = new nsStyleContentData[aCount]; if (! mContents) { mContentCount = 0; return NS_ERROR_OUT_OF_MEMORY; } } mContentCount = aCount; return NS_OK; } // --------------------- // nsStyleQuotes // nsStyleQuotes::nsStyleQuotes(void) : mQuotesCount(0), mQuotes(nsnull) { MOZ_COUNT_CTOR(nsStyleQuotes); SetInitial(); } nsStyleQuotes::~nsStyleQuotes(void) { MOZ_COUNT_DTOR(nsStyleQuotes); DELETE_ARRAY_IF(mQuotes); } nsStyleQuotes::nsStyleQuotes(const nsStyleQuotes& aSource) : mQuotesCount(0), mQuotes(nsnull) { MOZ_COUNT_CTOR(nsStyleQuotes); CopyFrom(aSource); } void nsStyleQuotes::SetInitial() { // 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 PRUnichar initialQuotes[8] = { 0x201C, 0, 0x201D, 0, 0x2018, 0, 0x2019, 0 }; if (NS_SUCCEEDED(AllocateQuotes(2))) { SetQuotesAt(0, nsDependentString(&initialQuotes[0], 1), nsDependentString(&initialQuotes[2], 1)); SetQuotesAt(1, nsDependentString(&initialQuotes[4], 1), nsDependentString(&initialQuotes[6], 1)); } } void nsStyleQuotes::CopyFrom(const nsStyleQuotes& aSource) { if (NS_SUCCEEDED(AllocateQuotes(aSource.QuotesCount()))) { PRUint32 count = (mQuotesCount * 2); for (PRUint32 index = 0; index < count; index += 2) { aSource.GetQuotesAt(index, mQuotes[index], mQuotes[index + 1]); } } } nsChangeHint nsStyleQuotes::CalcDifference(const nsStyleQuotes& aOther) 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 (mQuotesCount == aOther.mQuotesCount) { PRUint32 ix = (mQuotesCount * 2); while (0 < ix--) { if (mQuotes[ix] != aOther.mQuotes[ix]) { return NS_STYLE_HINT_FRAMECHANGE; } } return NS_STYLE_HINT_NONE; } return NS_STYLE_HINT_FRAMECHANGE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleQuotes::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif // -------------------- // nsStyleTextReset // nsStyleTextReset::nsStyleTextReset(void) { MOZ_COUNT_CTOR(nsStyleTextReset); mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated); mTextBlink = NS_STYLE_TEXT_BLINK_NONE; mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE; mTextDecorationColor = NS_RGB(0,0,0); mTextDecorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID | BORDER_COLOR_FOREGROUND; mUnicodeBidi = NS_STYLE_UNICODE_BIDI_NORMAL; } nsStyleTextReset::nsStyleTextReset(const nsStyleTextReset& aSource) { MOZ_COUNT_CTOR(nsStyleTextReset); memcpy((nsStyleTextReset*)this, &aSource, sizeof(nsStyleTextReset)); } nsStyleTextReset::~nsStyleTextReset(void) { MOZ_COUNT_DTOR(nsStyleTextReset); } nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) const { if (mVerticalAlign == aOther.mVerticalAlign && mUnicodeBidi == aOther.mUnicodeBidi) { // Reflow for blink changes if (mTextBlink != aOther.mTextBlink) { return NS_STYLE_HINT_REFLOW; } PRUint8 lineStyle = GetDecorationStyle(); PRUint8 otherLineStyle = aOther.GetDecorationStyle(); if (mTextDecorationLine != aOther.mTextDecorationLine || lineStyle != otherLineStyle) { // Reflow for decoration line style changes only to or from double or // wave because that may cause overflow area changes if (lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE || lineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY || otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE || otherLineStyle == NS_STYLE_TEXT_DECORATION_STYLE_WAVY) { return NS_STYLE_HINT_REFLOW; } // Repaint for other style decoration lines because they must be in // default overflow rect return NS_STYLE_HINT_VISUAL; } // Repaint for decoration color changes nscolor decColor, otherDecColor; PRBool isFG, otherIsFG; GetDecorationColor(decColor, isFG); aOther.GetDecorationColor(otherDecColor, otherIsFG); if (isFG != otherIsFG || (!isFG && decColor != otherDecColor)) { return NS_STYLE_HINT_VISUAL; } return NS_STYLE_HINT_NONE; } return NS_STYLE_HINT_REFLOW; } #ifdef DEBUG /* static */ nsChangeHint nsStyleTextReset::MaxDifference() { return NS_STYLE_HINT_REFLOW; } #endif // Allowed to return one of NS_STYLE_HINT_NONE, NS_STYLE_HINT_REFLOW // or NS_STYLE_HINT_VISUAL. Currently we just return NONE or REFLOW, though. // XXXbz can this not return a more specific hint? If that's ever // changed, nsStyleBorder::CalcDifference will need changing too. static nsChangeHint CalcShadowDifference(nsCSSShadowArray* lhs, nsCSSShadowArray* rhs) { if (lhs == rhs) return NS_STYLE_HINT_NONE; if (!lhs || !rhs || lhs->Length() != rhs->Length()) return NS_STYLE_HINT_REFLOW; for (PRUint32 i = 0; i < lhs->Length(); ++i) { if (*lhs->ShadowAt(i) != *rhs->ShadowAt(i)) return NS_STYLE_HINT_REFLOW; } return NS_STYLE_HINT_NONE; } // -------------------- // nsStyleText // nsStyleText::nsStyleText(void) { MOZ_COUNT_CTOR(nsStyleText); mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE; mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL; mWordWrap = NS_STYLE_WORDWRAP_NORMAL; mHyphens = NS_STYLE_HYPHENS_MANUAL; mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); mTextIndent.SetCoordValue(0); mWordSpacing = 0; mTextShadow = nsnull; mTabSize = NS_STYLE_TABSIZE_INITIAL; } nsStyleText::nsStyleText(const nsStyleText& aSource) : mTextAlign(aSource.mTextAlign), mTextTransform(aSource.mTextTransform), mWhiteSpace(aSource.mWhiteSpace), mWordWrap(aSource.mWordWrap), mHyphens(aSource.mHyphens), mTabSize(aSource.mTabSize), mLetterSpacing(aSource.mLetterSpacing), mLineHeight(aSource.mLineHeight), mTextIndent(aSource.mTextIndent), mWordSpacing(aSource.mWordSpacing), mTextShadow(aSource.mTextShadow) { MOZ_COUNT_CTOR(nsStyleText); } nsStyleText::~nsStyleText(void) { MOZ_COUNT_DTOR(nsStyleText); } nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const { if (NewlineIsSignificant() != aOther.NewlineIsSignificant()) { // This may require construction of suppressed text frames return NS_STYLE_HINT_FRAMECHANGE; } if ((mTextAlign != aOther.mTextAlign) || (mTextTransform != aOther.mTextTransform) || (mWhiteSpace != aOther.mWhiteSpace) || (mWordWrap != aOther.mWordWrap) || (mHyphens != aOther.mHyphens) || (mLetterSpacing != aOther.mLetterSpacing) || (mLineHeight != aOther.mLineHeight) || (mTextIndent != aOther.mTextIndent) || (mWordSpacing != aOther.mWordSpacing) || (mTabSize != aOther.mTabSize)) return NS_STYLE_HINT_REFLOW; return CalcShadowDifference(mTextShadow, aOther.mTextShadow); } #ifdef DEBUG /* static */ nsChangeHint nsStyleText::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif //----------------------- // nsStyleUserInterface // nsCursorImage::nsCursorImage() : mHaveHotspot(PR_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(nsnull); } nsCursorImage& nsCursorImage::operator=(const nsCursorImage& aOther) { if (this != &aOther) { mHaveHotspot = aOther.mHaveHotspot; mHotspotX = aOther.mHotspotX; mHotspotY = aOther.mHotspotY; SetImage(aOther.GetImage()); } return *this; } nsStyleUserInterface::nsStyleUserInterface(void) { MOZ_COUNT_CTOR(nsStyleUserInterface); mUserInput = NS_STYLE_USER_INPUT_AUTO; mUserModify = NS_STYLE_USER_MODIFY_READ_ONLY; mUserFocus = NS_STYLE_USER_FOCUS_NONE; mCursor = NS_STYLE_CURSOR_AUTO; // fix for bugzilla bug 51113 mCursorArrayLength = 0; mCursorArray = nsnull; } nsStyleUserInterface::nsStyleUserInterface(const nsStyleUserInterface& aSource) : mUserInput(aSource.mUserInput), mUserModify(aSource.mUserModify), mUserFocus(aSource.mUserFocus), mCursor(aSource.mCursor) { MOZ_COUNT_CTOR(nsStyleUserInterface); CopyCursorArrayFrom(aSource); } nsStyleUserInterface::~nsStyleUserInterface(void) { MOZ_COUNT_DTOR(nsStyleUserInterface); delete [] mCursorArray; } nsChangeHint nsStyleUserInterface::CalcDifference(const nsStyleUserInterface& aOther) const { nsChangeHint hint = nsChangeHint(0); if (mCursor != aOther.mCursor) NS_UpdateHint(hint, nsChangeHint_UpdateCursor); // We could do better. But it wouldn't be worth it, URL-specified cursors are // rare. if (mCursorArrayLength > 0 || aOther.mCursorArrayLength > 0) NS_UpdateHint(hint, nsChangeHint_UpdateCursor); if (mUserModify != aOther.mUserModify) NS_UpdateHint(hint, NS_STYLE_HINT_VISUAL); if ((mUserInput != aOther.mUserInput) && ((NS_STYLE_USER_INPUT_NONE == mUserInput) || (NS_STYLE_USER_INPUT_NONE == aOther.mUserInput))) { NS_UpdateHint(hint, NS_STYLE_HINT_FRAMECHANGE); } // ignore mUserFocus return hint; } #ifdef DEBUG /* static */ nsChangeHint nsStyleUserInterface::MaxDifference() { return nsChangeHint(nsChangeHint_UpdateCursor | NS_STYLE_HINT_FRAMECHANGE); } #endif void nsStyleUserInterface::CopyCursorArrayFrom(const nsStyleUserInterface& aSource) { mCursorArray = nsnull; mCursorArrayLength = 0; if (aSource.mCursorArrayLength) { mCursorArray = new nsCursorImage[aSource.mCursorArrayLength]; if (mCursorArray) { mCursorArrayLength = aSource.mCursorArrayLength; for (PRUint32 i = 0; i < mCursorArrayLength; ++i) mCursorArray[i] = aSource.mCursorArray[i]; } } } //----------------------- // nsStyleUIReset // nsStyleUIReset::nsStyleUIReset(void) { MOZ_COUNT_CTOR(nsStyleUIReset); mUserSelect = NS_STYLE_USER_SELECT_AUTO; mForceBrokenImageIcon = 0; mIMEMode = NS_STYLE_IME_MODE_AUTO; mWindowShadow = NS_STYLE_WINDOW_SHADOW_DEFAULT; } nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource) { MOZ_COUNT_CTOR(nsStyleUIReset); mUserSelect = aSource.mUserSelect; mForceBrokenImageIcon = aSource.mForceBrokenImageIcon; mIMEMode = aSource.mIMEMode; mWindowShadow = aSource.mWindowShadow; } nsStyleUIReset::~nsStyleUIReset(void) { MOZ_COUNT_DTOR(nsStyleUIReset); } nsChangeHint nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const { // ignore mIMEMode if (mForceBrokenImageIcon != aOther.mForceBrokenImageIcon) return NS_STYLE_HINT_FRAMECHANGE; if (mWindowShadow != aOther.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 != aOther.mUserSelect) return NS_STYLE_HINT_VISUAL; return NS_STYLE_HINT_NONE; } #ifdef DEBUG /* static */ nsChangeHint nsStyleUIReset::MaxDifference() { return NS_STYLE_HINT_FRAMECHANGE; } #endif