/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=78: */ /* 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/. */ /* * a node in the lexicographic tree of rules that match an element, * responsible for converting the rules' information into computed style */ #include #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/DebugOnly.h" #include "mozilla/Likely.h" #include "mozilla/LookAndFeel.h" #include "nsRuleNode.h" #include "nscore.h" #include "nsIWidget.h" #include "nsIPresShell.h" #include "nsFontMetrics.h" #include "gfxFont.h" #include "nsCSSPseudoElements.h" #include "nsThemeConstants.h" #include "pldhash.h" #include "nsStyleContext.h" #include "nsStyleSet.h" #include "nsStyleStruct.h" #include "nsSize.h" #include "nsRuleData.h" #include "nsIStyleRule.h" #include "nsBidiUtils.h" #include "nsStyleStructInlines.h" #include "nsCSSProps.h" #include "nsTArray.h" #include "nsContentUtils.h" #include "CSSCalc.h" #include "nsPrintfCString.h" #include "nsRenderingContext.h" #include "nsStyleUtil.h" #include "nsIDocument.h" #include "prtime.h" #include "CSSVariableResolver.h" #include "nsCSSParser.h" #if defined(_MSC_VER) || defined(__MINGW32__) #include #ifdef _MSC_VER #define alloca _alloca #endif #endif #ifdef SOLARIS #include #endif using std::max; using std::min; using namespace mozilla; using namespace mozilla::dom; #define NS_SET_IMAGE_REQUEST(method_, context_, request_) \ if ((context_)->PresContext()->IsDynamic()) { \ method_(request_); \ } else { \ nsRefPtr req = nsContentUtils::GetStaticRequest(request_); \ method_(req); \ } #define NS_SET_IMAGE_REQUEST_WITH_DOC(method_, context_, requestgetter_) \ { \ nsIDocument* doc = (context_)->PresContext()->Document(); \ NS_SET_IMAGE_REQUEST(method_, context_, requestgetter_(doc)) \ } /* * For storage of an |nsRuleNode|'s children in a PLDHashTable. */ struct ChildrenHashEntry : public PLDHashEntryHdr { // key is |mRuleNode->GetKey()| nsRuleNode *mRuleNode; }; /* static */ PLDHashNumber nsRuleNode::ChildrenHashHashKey(PLDHashTable *aTable, const void *aKey) { const nsRuleNode::Key *key = static_cast(aKey); // Disagreement on importance and level for the same rule is extremely // rare, so hash just on the rule. return PL_DHashVoidPtrKeyStub(aTable, key->mRule); } /* static */ bool nsRuleNode::ChildrenHashMatchEntry(PLDHashTable *aTable, const PLDHashEntryHdr *aHdr, const void *aKey) { const ChildrenHashEntry *entry = static_cast(aHdr); const nsRuleNode::Key *key = static_cast(aKey); return entry->mRuleNode->GetKey() == *key; } /* static */ const PLDHashTableOps nsRuleNode::ChildrenHashOps = { // It's probably better to allocate the table itself using malloc and // free rather than the pres shell's arena because the table doesn't // grow very often and the pres shell's arena doesn't recycle very // large size allocations. PL_DHashAllocTable, PL_DHashFreeTable, ChildrenHashHashKey, ChildrenHashMatchEntry, PL_DHashMoveEntryStub, PL_DHashClearEntryStub, PL_DHashFinalizeStub, nullptr }; // EnsureBlockDisplay: // - if the display value (argument) is not a block-type // then we set it to a valid block display value // - For enforcing the floated/positioned element CSS2 rules // - We allow the behavior of "list-item" to be customized. // CSS21 says that position/float do not convert 'list-item' to 'block', // but it explicitly does not define whether 'list-item' should be // converted to block *on the root node*. To allow for flexibility // (so that we don't have to support a list-item root node), this method // lets the caller pick either behavior, using the 'aConvertListItem' arg. // Reference: http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo /* static */ void nsRuleNode::EnsureBlockDisplay(uint8_t& display, bool aConvertListItem /* = false */) { // see if the display value is already a block switch (display) { case NS_STYLE_DISPLAY_LIST_ITEM : if (aConvertListItem) { display = NS_STYLE_DISPLAY_BLOCK; break; } // else, fall through to share the 'break' for non-changing display vals case NS_STYLE_DISPLAY_NONE : // never change display:none *ever* case NS_STYLE_DISPLAY_TABLE : case NS_STYLE_DISPLAY_BLOCK : case NS_STYLE_DISPLAY_FLEX : // do not muck with these at all - already blocks // This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we // should just call that?) // This needs to match the check done in // nsCSSFrameConstructor::FindMathMLData for . break; case NS_STYLE_DISPLAY_INLINE_TABLE : // make inline tables into tables display = NS_STYLE_DISPLAY_TABLE; break; case NS_STYLE_DISPLAY_INLINE_FLEX: // make inline flex containers into flex containers display = NS_STYLE_DISPLAY_FLEX; break; default : // make it a block display = NS_STYLE_DISPLAY_BLOCK; } } static nscoord CalcLengthWith(const nsCSSValue& aValue, nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, bool aUseUserFontSet, bool& aCanStoreInRuleTree); struct CalcLengthCalcOps : public css::BasicCoordCalcOps, public css::NumbersAlreadyNormalizedOps { // All of the parameters to CalcLengthWith except aValue. const nscoord mFontSize; const nsStyleFont* const mStyleFont; nsStyleContext* const mStyleContext; nsPresContext* const mPresContext; const bool mUseProvidedRootEmSize; const bool mUseUserFontSet; bool& mCanStoreInRuleTree; CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, bool aUseUserFontSet, bool& aCanStoreInRuleTree) : mFontSize(aFontSize), mStyleFont(aStyleFont), mStyleContext(aStyleContext), mPresContext(aPresContext), mUseProvidedRootEmSize(aUseProvidedRootEmSize), mUseUserFontSet(aUseUserFontSet), mCanStoreInRuleTree(aCanStoreInRuleTree) { } result_type ComputeLeafValue(const nsCSSValue& aValue) { return CalcLengthWith(aValue, mFontSize, mStyleFont, mStyleContext, mPresContext, mUseProvidedRootEmSize, mUseUserFontSet, mCanStoreInRuleTree); } }; static inline nscoord ScaleCoord(const nsCSSValue &aValue, float factor) { return NSToCoordRoundWithClamp(aValue.GetFloatValue() * factor); } already_AddRefed GetMetricsFor(nsPresContext* aPresContext, nsStyleContext* aStyleContext, const nsStyleFont* aStyleFont, nscoord aFontSize, // overrides value from aStyleFont bool aUseUserFontSet) { nsFont font = aStyleFont->mFont; font.size = aFontSize; gfxUserFontSet *fs = nullptr; if (aUseUserFontSet) { fs = aPresContext->GetUserFontSet(); } gfxTextPerfMetrics *tp = aPresContext->GetTextPerfMetrics(); nsRefPtr fm; aPresContext->DeviceContext()->GetMetricsFor(font, aStyleFont->mLanguage, fs, tp, *getter_AddRefs(fm)); return fm.forget(); } static nsSize CalcViewportUnitsScale(nsPresContext* aPresContext) { // The caller is making use of viewport units, so notify the pres context // that it will need to rebuild the rule tree if the size of the viewport // changes. aPresContext->SetUsesViewportUnits(true); // The default (when we have 'overflow: auto' on the root element, or // trivially for 'overflow: hidden' since we never have scrollbars in that // case) is to define the scale of the viewport units without considering // scrollbars. nsSize viewportSize(aPresContext->GetVisibleArea().Size()); // Check for 'overflow: scroll' styles on the root scroll frame. If we find // any, the standard requires us to take scrollbars into account. nsIScrollableFrame* scrollFrame = aPresContext->PresShell()->GetRootScrollFrameAsScrollable(); if (scrollFrame) { ScrollbarStyles styles(scrollFrame->GetScrollbarStyles()); if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL || styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { // Gather scrollbar size information. nsRefPtr context = aPresContext->PresShell()->GetReferenceRenderingContext(); nsMargin sizes(scrollFrame->GetDesiredScrollbarSizes(aPresContext, context)); if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) { // 'overflow-x: scroll' means we must consider the horizontal scrollbar, // which affects the scale of viewport height units. viewportSize.height -= sizes.TopBottom(); } if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { // 'overflow-y: scroll' means we must consider the vertical scrollbar, // which affects the scale of viewport width units. viewportSize.width -= sizes.LeftRight(); } } } return viewportSize; } static nscoord CalcLengthWith(const nsCSSValue& aValue, nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, // aUseUserFontSet should always be true // except when called from // CalcLengthWithInitialFont. bool aUseUserFontSet, bool& aCanStoreInRuleTree) { NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(), "not a length or calc unit"); NS_ASSERTION(aStyleFont || aStyleContext, "Must have style data"); NS_ASSERTION(!aStyleFont || !aStyleContext, "Duplicate sources of data"); NS_ASSERTION(aPresContext, "Must have prescontext"); if (aValue.IsFixedLengthUnit()) { return aValue.GetFixedLength(aPresContext); } if (aValue.IsPixelLengthUnit()) { return aValue.GetPixelLength(); } if (aValue.IsCalcUnit()) { // For properties for which lengths are the *only* units accepted in // calc(), we can handle calc() here and just compute a final // result. We ensure that we don't get to this code for other // properties by not calling CalcLength in those cases: SetCoord // only calls CalcLength for a calc when it is appropriate to do so. CalcLengthCalcOps ops(aFontSize, aStyleFont, aStyleContext, aPresContext, aUseProvidedRootEmSize, aUseUserFontSet, aCanStoreInRuleTree); return css::ComputeCalc(aValue, ops); } switch (aValue.GetUnit()) { // nsPresContext::SetVisibleArea and // nsPresContext::MediaFeatureValuesChanged handle dynamic changes // of the basis for viewport units by rebuilding the rule tree and // style context tree. Not caching them in the rule tree wouldn't // be sufficient to handle these changes because we also need a way // to get rid of cached values in the style context tree without any // changes in specified style. We can either do this by not caching // in the rule tree and then throwing away the style context tree // for dynamic viewport size changes, or by allowing caching in the // rule tree and using the existing rebuild style data path that // throws away the style context and the rule tree. // Thus we do cache viewport units in the rule tree. This allows us // to benefit from the performance advantages of the rule tree // (e.g., faster dynamic changes on other things, like transforms) // and allows us not to need an additional code path, in exchange // for an increased cost to dynamic changes to the viewport size // when viewport units are in use. case eCSSUnit_ViewportWidth: { return ScaleCoord(aValue, 0.01f * CalcViewportUnitsScale(aPresContext).width); } case eCSSUnit_ViewportHeight: { return ScaleCoord(aValue, 0.01f * CalcViewportUnitsScale(aPresContext).height); } case eCSSUnit_ViewportMin: { nsSize vuScale(CalcViewportUnitsScale(aPresContext)); return ScaleCoord(aValue, 0.01f * min(vuScale.width, vuScale.height)); } case eCSSUnit_ViewportMax: { nsSize vuScale(CalcViewportUnitsScale(aPresContext)); return ScaleCoord(aValue, 0.01f * max(vuScale.width, vuScale.height)); } // While we could deal with 'rem' units correctly by simply not // caching any data that uses them in the rule tree, it's valuable // to store them in the rule tree (for faster dynamic changes of // other things). And since the font size of the root element // changes rarely, we instead handle dynamic changes to the root // element's font size by rebuilding all style data in // nsCSSFrameConstructor::RestyleElement. case eCSSUnit_RootEM: { aPresContext->SetUsesRootEMUnits(true); nscoord rootFontSize; // NOTE: Be very careful with |styleFont|, since we haven't set // aCanStoreInRuleTree to false yet, so we don't want to introduce // any dependencies on aStyleContext's data here. const nsStyleFont *styleFont = aStyleFont ? aStyleFont : aStyleContext->StyleFont(); if (aUseProvidedRootEmSize) { // We should use the provided aFontSize as the reference length to // scale. This only happens when we are calculating font-size or // an equivalent (scriptminsize or CalcLengthWithInitialFont) on // the root element, in which case aFontSize is already the // value we want. if (aFontSize == -1) { // XXX Should this be styleFont->mSize instead to avoid taking // minfontsize prefs into account? aFontSize = styleFont->mFont.size; } rootFontSize = aFontSize; } else if (aStyleContext && !aStyleContext->GetParent()) { // This is the root element (XXX we don't really know this, but // nsRuleNode::SetFont makes the same assumption!), so we should // use StyleFont on this context to get the root element's // font size. rootFontSize = styleFont->mFont.size; } else { // This is not the root element or we are calculating something other // than font size, so rem is relative to the root element's font size. nsRefPtr rootStyle; const nsStyleFont *rootStyleFont = styleFont; Element* docElement = aPresContext->Document()->GetRootElement(); if (docElement) { rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement, nullptr); rootStyleFont = rootStyle->StyleFont(); } rootFontSize = rootStyleFont->mFont.size; } return ScaleCoord(aValue, float(rootFontSize)); } default: // Fall through to the code for units that can't be stored in the // rule tree because they depend on font data. break; } // Common code for units that depend on the element's font data and // thus can't be stored in the rule tree: aCanStoreInRuleTree = false; const nsStyleFont *styleFont = aStyleFont ? aStyleFont : aStyleContext->StyleFont(); if (aFontSize == -1) { // XXX Should this be styleFont->mSize instead to avoid taking minfontsize // prefs into account? aFontSize = styleFont->mFont.size; } switch (aValue.GetUnit()) { case eCSSUnit_EM: { // CSS2.1 specifies that this unit scales to the computed font // size, not the em-width in the font metrics, despite the name. return ScaleCoord(aValue, float(aFontSize)); } case eCSSUnit_XHeight: { nsRefPtr fm = GetMetricsFor(aPresContext, aStyleContext, styleFont, aFontSize, aUseUserFontSet); return ScaleCoord(aValue, float(fm->XHeight())); } case eCSSUnit_Char: { nsRefPtr fm = GetMetricsFor(aPresContext, aStyleContext, styleFont, aFontSize, aUseUserFontSet); gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFontAt(0) ->GetMetrics().zeroOrAveCharWidth); return ScaleCoord(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth)); } default: NS_NOTREACHED("unexpected unit"); break; } return 0; } /* static */ nscoord nsRuleNode::CalcLength(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { NS_ASSERTION(aStyleContext, "Must have style data"); return CalcLengthWith(aValue, -1, nullptr, aStyleContext, aPresContext, false, true, aCanStoreInRuleTree); } /* Inline helper function to redirect requests to CalcLength. */ static inline nscoord CalcLength(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { return nsRuleNode::CalcLength(aValue, aStyleContext, aPresContext, aCanStoreInRuleTree); } /* static */ nscoord nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, const nsCSSValue& aValue) { nsStyleFont defaultFont(aPresContext); // FIXME: best language? bool canStoreInRuleTree; return CalcLengthWith(aValue, -1, &defaultFont, nullptr, aPresContext, true, false, canStoreInRuleTree); } struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps { typedef nsRuleNode::ComputedCalc result_type; LengthPercentPairCalcOps(nsStyleContext* aContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) : mContext(aContext), mPresContext(aPresContext), mCanStoreInRuleTree(aCanStoreInRuleTree), mHasPercent(false) {} nsStyleContext* mContext; nsPresContext* mPresContext; bool& mCanStoreInRuleTree; bool mHasPercent; result_type ComputeLeafValue(const nsCSSValue& aValue) { if (aValue.GetUnit() == eCSSUnit_Percent) { mHasPercent = true; return result_type(0, aValue.GetPercentValue()); } return result_type(CalcLength(aValue, mContext, mPresContext, mCanStoreInRuleTree), 0.0f); } result_type MergeAdditive(nsCSSUnit aCalcFunction, result_type aValue1, result_type aValue2) { if (aCalcFunction == eCSSUnit_Calc_Plus) { return result_type(NSCoordSaturatingAdd(aValue1.mLength, aValue2.mLength), aValue1.mPercent + aValue2.mPercent); } NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus, "min() and max() are not allowed in calc() on " "transform"); return result_type(NSCoordSaturatingSubtract(aValue1.mLength, aValue2.mLength, 0), aValue1.mPercent - aValue2.mPercent); } result_type MergeMultiplicativeL(nsCSSUnit aCalcFunction, float aValue1, result_type aValue2) { NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L, "unexpected unit"); return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1), aValue1 * aValue2.mPercent); } result_type MergeMultiplicativeR(nsCSSUnit aCalcFunction, result_type aValue1, float aValue2) { NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R || aCalcFunction == eCSSUnit_Calc_Divided, "unexpected unit"); if (aCalcFunction == eCSSUnit_Calc_Divided) { aValue2 = 1.0f / aValue2; } return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2), aValue1.mPercent * aValue2); } }; static void SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, nsStyleContext* aStyleContext, bool& aCanStoreInRuleTree) { LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); nsRuleNode::ComputedCalc vals = ComputeCalc(aValue, ops); nsStyleCoord::Calc *calcObj = new (aStyleContext->Alloc(sizeof(nsStyleCoord::Calc))) nsStyleCoord::Calc; // Because we use aStyleContext->Alloc(), we have to store the result // on the style context and not in the rule tree. aCanStoreInRuleTree = false; calcObj->mLength = vals.mLength; calcObj->mPercent = vals.mPercent; calcObj->mHasPercent = ops.mHasPercent; aCoord.SetCalcValue(calcObj); } /* static */ nsRuleNode::ComputedCalc nsRuleNode::SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { LengthPercentPairCalcOps ops(aStyleContext, aPresContext, aCanStoreInRuleTree); return ComputeCalc(aValue, ops); } // This is our public API for handling calc() expressions that involve // percentages. /* static */ nscoord nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue, nscoord aPercentageBasis) { nsStyleCoord::Calc *calc = aValue.GetCalcValue(); return calc->mLength + NSToCoordFloorClamped(aPercentageBasis * calc->mPercent); } /* static */ nscoord nsRuleNode::ComputeCoordPercentCalc(const nsStyleCoord& aCoord, nscoord aPercentageBasis) { switch (aCoord.GetUnit()) { case eStyleUnit_Coord: return aCoord.GetCoordValue(); case eStyleUnit_Percent: return NSToCoordFloorClamped(aPercentageBasis * aCoord.GetPercentValue()); case eStyleUnit_Calc: return ComputeComputedCalc(aCoord, aPercentageBasis); default: NS_ABORT_IF_FALSE(false, "unexpected unit"); return 0; } } /* Given an enumerated value that represents a box position, converts it to * a float representing the percentage of the box it corresponds to. For * example, "center" becomes 0.5f. * * @param aEnumValue The enumerated value. * @return The float percent it corresponds to. */ static float GetFloatFromBoxPosition(int32_t aEnumValue) { switch (aEnumValue) { case NS_STYLE_BG_POSITION_LEFT: case NS_STYLE_BG_POSITION_TOP: return 0.0f; case NS_STYLE_BG_POSITION_RIGHT: case NS_STYLE_BG_POSITION_BOTTOM: return 1.0f; default: NS_NOTREACHED("unexpected value"); // fall through case NS_STYLE_BG_POSITION_CENTER: return 0.5f; } } #define SETCOORD_NORMAL 0x01 // N #define SETCOORD_AUTO 0x02 // A #define SETCOORD_INHERIT 0x04 // H #define SETCOORD_PERCENT 0x08 // P #define SETCOORD_FACTOR 0x10 // F #define SETCOORD_LENGTH 0x20 // L #define SETCOORD_INTEGER 0x40 // I #define SETCOORD_ENUMERATED 0x80 // E #define SETCOORD_NONE 0x100 // O #define SETCOORD_INITIAL_ZERO 0x200 #define SETCOORD_INITIAL_AUTO 0x400 #define SETCOORD_INITIAL_NONE 0x800 #define SETCOORD_INITIAL_NORMAL 0x1000 #define SETCOORD_INITIAL_HALF 0x2000 #define SETCOORD_INITIAL_HUNDRED_PCT 0x00004000 #define SETCOORD_INITIAL_FACTOR_ONE 0x00008000 #define SETCOORD_INITIAL_FACTOR_ZERO 0x00010000 #define SETCOORD_CALC_LENGTH_ONLY 0x00020000 #define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY #define SETCOORD_STORE_CALC 0x00080000 #define SETCOORD_BOX_POSITION 0x00100000 // exclusive with _ENUMERATED #define SETCOORD_ANGLE 0x00200000 #define SETCOORD_UNSET_INHERIT 0x00400000 #define SETCOORD_UNSET_INITIAL 0x00800000 #define SETCOORD_LP (SETCOORD_LENGTH | SETCOORD_PERCENT) #define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT) #define SETCOORD_AH (SETCOORD_AUTO | SETCOORD_INHERIT) #define SETCOORD_LAH (SETCOORD_AUTO | SETCOORD_LENGTH | SETCOORD_INHERIT) #define SETCOORD_LPH (SETCOORD_LP | SETCOORD_INHERIT) #define SETCOORD_LPAH (SETCOORD_LP | SETCOORD_AH) #define SETCOORD_LPEH (SETCOORD_LP | SETCOORD_ENUMERATED | SETCOORD_INHERIT) #define SETCOORD_LPAEH (SETCOORD_LPAH | SETCOORD_ENUMERATED) #define SETCOORD_LPO (SETCOORD_LP | SETCOORD_NONE) #define SETCOORD_LPOH (SETCOORD_LPH | SETCOORD_NONE) #define SETCOORD_LPOEH (SETCOORD_LPOH | SETCOORD_ENUMERATED) #define SETCOORD_LE (SETCOORD_LENGTH | SETCOORD_ENUMERATED) #define SETCOORD_LEH (SETCOORD_LE | SETCOORD_INHERIT) #define SETCOORD_IA (SETCOORD_INTEGER | SETCOORD_AUTO) #define SETCOORD_LAE (SETCOORD_LENGTH | SETCOORD_AUTO | SETCOORD_ENUMERATED) // changes aCoord iff it returns true static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, const nsStyleCoord& aParentCoord, int32_t aMask, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { bool result = true; if (aValue.GetUnit() == eCSSUnit_Null) { result = false; } else if ((((aMask & SETCOORD_LENGTH) != 0) && aValue.IsLengthUnit()) || (((aMask & SETCOORD_CALC_LENGTH_ONLY) != 0) && aValue.IsCalcUnit())) { nscoord len = CalcLength(aValue, aStyleContext, aPresContext, aCanStoreInRuleTree); if ((aMask & SETCOORD_CALC_CLAMP_NONNEGATIVE) && len < 0) { NS_ASSERTION(aValue.IsCalcUnit(), "parser should have ensured no nonnegative lengths"); len = 0; } aCoord.SetCoordValue(len); } else if (((aMask & SETCOORD_PERCENT) != 0) && (aValue.GetUnit() == eCSSUnit_Percent)) { aCoord.SetPercentValue(aValue.GetPercentValue()); } else if (((aMask & SETCOORD_INTEGER) != 0) && (aValue.GetUnit() == eCSSUnit_Integer)) { aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Integer); } else if (((aMask & SETCOORD_ENUMERATED) != 0) && (aValue.GetUnit() == eCSSUnit_Enumerated)) { aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Enumerated); } else if (((aMask & SETCOORD_BOX_POSITION) != 0) && (aValue.GetUnit() == eCSSUnit_Enumerated)) { aCoord.SetPercentValue(GetFloatFromBoxPosition(aValue.GetIntValue())); } else if (((aMask & SETCOORD_AUTO) != 0) && (aValue.GetUnit() == eCSSUnit_Auto)) { aCoord.SetAutoValue(); } else if ((((aMask & SETCOORD_INHERIT) != 0) && aValue.GetUnit() == eCSSUnit_Inherit) || (((aMask & SETCOORD_UNSET_INHERIT) != 0) && aValue.GetUnit() == eCSSUnit_Unset)) { aCoord = aParentCoord; // just inherit value from parent aCanStoreInRuleTree = false; } else if (((aMask & SETCOORD_NORMAL) != 0) && (aValue.GetUnit() == eCSSUnit_Normal)) { aCoord.SetNormalValue(); } else if (((aMask & SETCOORD_NONE) != 0) && (aValue.GetUnit() == eCSSUnit_None)) { aCoord.SetNoneValue(); } else if (((aMask & SETCOORD_FACTOR) != 0) && (aValue.GetUnit() == eCSSUnit_Number)) { aCoord.SetFactorValue(aValue.GetFloatValue()); } else if (((aMask & SETCOORD_STORE_CALC) != 0) && (aValue.IsCalcUnit())) { SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext, aCanStoreInRuleTree); } else if (aValue.GetUnit() == eCSSUnit_Initial || (aValue.GetUnit() == eCSSUnit_Unset && ((aMask & SETCOORD_UNSET_INITIAL) != 0))) { if ((aMask & SETCOORD_INITIAL_AUTO) != 0) { aCoord.SetAutoValue(); } else if ((aMask & SETCOORD_INITIAL_ZERO) != 0) { aCoord.SetCoordValue(0); } else if ((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) { aCoord.SetFactorValue(0.0f); } else if ((aMask & SETCOORD_INITIAL_NONE) != 0) { aCoord.SetNoneValue(); } else if ((aMask & SETCOORD_INITIAL_NORMAL) != 0) { aCoord.SetNormalValue(); } else if ((aMask & SETCOORD_INITIAL_HALF) != 0) { aCoord.SetPercentValue(0.5f); } else if ((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) { aCoord.SetPercentValue(1.0f); } else if ((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) { aCoord.SetFactorValue(1.0f); } else { result = false; // didn't set anything } } else if ((aMask & SETCOORD_ANGLE) != 0 && (aValue.IsAngularUnit())) { nsStyleUnit unit; switch (aValue.GetUnit()) { case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; default: NS_NOTREACHED("unrecognized angular unit"); unit = eStyleUnit_Degree; } aCoord.SetAngleValue(aValue.GetAngleValue(), unit); } else { result = false; // didn't set anything } return result; } // This inline function offers a shortcut for SetCoord() by refusing to accept // SETCOORD_LENGTH, SETCOORD_INHERIT and SETCOORD_UNSET_* masks. static inline bool SetAbsCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, int32_t aMask) { NS_ABORT_IF_FALSE((aMask & (SETCOORD_LH | SETCOORD_UNSET_INHERIT | SETCOORD_UNSET_INITIAL)) == 0, "does not handle SETCOORD_LENGTH, SETCOORD_INHERIT and " "SETCOORD_UNSET_*"); // The values of the following variables will never be used; so it does not // matter what to set. const nsStyleCoord dummyParentCoord; nsStyleContext* dummyStyleContext = nullptr; nsPresContext* dummyPresContext = nullptr; bool dummyCanStoreInRuleTree = true; bool rv = SetCoord(aValue, aCoord, dummyParentCoord, aMask, dummyStyleContext, dummyPresContext, dummyCanStoreInRuleTree); NS_ABORT_IF_FALSE(dummyCanStoreInRuleTree, "SetCoord() should not modify dummyCanStoreInRuleTree."); return rv; } /* Given a specified value that might be a pair value, call SetCoord twice, * either using each member of the pair, or using the unpaired value twice. */ static bool SetPairCoords(const nsCSSValue& aValue, nsStyleCoord& aCoordX, nsStyleCoord& aCoordY, const nsStyleCoord& aParentX, const nsStyleCoord& aParentY, int32_t aMask, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { const nsCSSValue& valX = aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mXValue : aValue; const nsCSSValue& valY = aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mYValue : aValue; bool cX = SetCoord(valX, aCoordX, aParentX, aMask, aStyleContext, aPresContext, aCanStoreInRuleTree); mozilla::DebugOnly cY = SetCoord(valY, aCoordY, aParentY, aMask, aStyleContext, aPresContext, aCanStoreInRuleTree); NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other"); return cX; } static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor, nsPresContext* aPresContext, nsStyleContext *aContext, nscolor& aResult, bool& aCanStoreInRuleTree) { bool result = false; nsCSSUnit unit = aValue.GetUnit(); if (aValue.IsNumericColorUnit()) { aResult = aValue.GetColorValue(); result = true; } else if (eCSSUnit_Ident == unit) { nsAutoString value; aValue.GetStringValue(value); nscolor rgba; if (NS_ColorNameToRGB(value, &rgba)) { aResult = rgba; result = true; } } else if (eCSSUnit_EnumColor == unit) { int32_t intValue = aValue.GetIntValue(); if (0 <= intValue) { LookAndFeel::ColorID colorID = (LookAndFeel::ColorID) intValue; if (NS_SUCCEEDED(LookAndFeel::GetColor(colorID, &aResult))) { result = true; } } else { aResult = NS_RGB(0, 0, 0); result = false; switch (intValue) { case NS_COLOR_MOZ_HYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultLinkColor(); result = true; } break; case NS_COLOR_MOZ_VISITEDHYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultVisitedLinkColor(); result = true; } break; case NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultActiveLinkColor(); result = true; } break; case NS_COLOR_CURRENTCOLOR: // The data computed from this can't be shared in the rule tree // because they could be used on a node with a different color aCanStoreInRuleTree = false; if (aContext) { aResult = aContext->StyleColor()->mColor; result = true; } break; case NS_COLOR_MOZ_DEFAULT_COLOR: if (aPresContext) { aResult = aPresContext->DefaultColor(); result = true; } break; case NS_COLOR_MOZ_DEFAULT_BACKGROUND_COLOR: if (aPresContext) { aResult = aPresContext->DefaultBackgroundColor(); result = true; } break; default: NS_NOTREACHED("Should never have an unknown negative colorID."); break; } } } else if (eCSSUnit_Inherit == unit) { aResult = aParentColor; result = true; aCanStoreInRuleTree = false; } else if (eCSSUnit_Enumerated == unit && aValue.GetIntValue() == NS_STYLE_COLOR_INHERIT_FROM_BODY) { NS_ASSERTION(aPresContext->CompatibilityMode() == eCompatibility_NavQuirks, "Should only get this value in quirks mode"); // We just grab the color from the prescontext, and rely on the fact that // if the body color ever changes all its descendants will get new style // contexts (but NOT necessarily new rulenodes). aResult = aPresContext->BodyTextColor(); result = true; aCanStoreInRuleTree = false; } return result; } static void SetGradientCoord(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aContext, nsStyleCoord& aResult, bool& aCanStoreInRuleTree) { // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT if (!SetCoord(aValue, aResult, nsStyleCoord(), SETCOORD_LPO | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC, aContext, aPresContext, aCanStoreInRuleTree)) { NS_NOTREACHED("unexpected unit for gradient anchor point"); aResult.SetNoneValue(); } } static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aContext, nsStyleGradient& aResult, bool& aCanStoreInRuleTree) { NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Gradient, "The given data is not a gradient"); const nsCSSValueGradient* gradient = aValue.GetGradientValue(); if (gradient->mIsExplicitSize) { SetCoord(gradient->GetRadiusX(), aResult.mRadiusX, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC, aContext, aPresContext, aCanStoreInRuleTree); if (gradient->GetRadiusY().GetUnit() != eCSSUnit_None) { SetCoord(gradient->GetRadiusY(), aResult.mRadiusY, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC, aContext, aPresContext, aCanStoreInRuleTree); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; } else { aResult.mRadiusY = aResult.mRadiusX; aResult.mShape = NS_STYLE_GRADIENT_SHAPE_CIRCULAR; } aResult.mSize = NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE; } else if (gradient->mIsRadial) { if (gradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated) { aResult.mShape = gradient->GetRadialShape().GetIntValue(); } else { NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, "bad unit for radial shape"); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; } if (gradient->GetRadialSize().GetUnit() == eCSSUnit_Enumerated) { aResult.mSize = gradient->GetRadialSize().GetIntValue(); } else { NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, "bad unit for radial shape"); aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; } } else { NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, "bad unit for linear shape"); NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, "bad unit for linear size"); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_LINEAR; aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; } aResult.mLegacySyntax = gradient->mIsLegacySyntax; // bg-position SetGradientCoord(gradient->mBgPos.mXValue, aPresContext, aContext, aResult.mBgPosX, aCanStoreInRuleTree); SetGradientCoord(gradient->mBgPos.mYValue, aPresContext, aContext, aResult.mBgPosY, aCanStoreInRuleTree); aResult.mRepeating = gradient->mIsRepeating; // angle const nsStyleCoord dummyParentCoord; if (!SetCoord(gradient->mAngle, aResult.mAngle, dummyParentCoord, SETCOORD_ANGLE, aContext, aPresContext, aCanStoreInRuleTree)) { NS_ASSERTION(gradient->mAngle.GetUnit() == eCSSUnit_None, "bad unit for gradient angle"); aResult.mAngle.SetNoneValue(); } // stops for (uint32_t i = 0; i < gradient->mStops.Length(); i++) { nsStyleGradientStop stop; const nsCSSValueGradientStop &valueStop = gradient->mStops[i]; if (!SetCoord(valueStop.mLocation, stop.mLocation, nsStyleCoord(), SETCOORD_LPO | SETCOORD_STORE_CALC, aContext, aPresContext, aCanStoreInRuleTree)) { NS_NOTREACHED("unexpected unit for gradient stop location"); } // inherit is not a valid color for stops, so we pass in a dummy // parent color NS_ASSERTION(valueStop.mColor.GetUnit() != eCSSUnit_Inherit, "inherit is not a valid color for gradient stops"); SetColor(valueStop.mColor, NS_RGB(0, 0, 0), aPresContext, aContext, stop.mColor, aCanStoreInRuleTree); aResult.mStops.AppendElement(stop); } } // -moz-image-rect(, , , , ) static void SetStyleImageToImageRect(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleImage& aResult) { NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Function && aValue.EqualsFunction(eCSSKeyword__moz_image_rect), "the value is not valid -moz-image-rect()"); nsCSSValue::Array* arr = aValue.GetArrayValue(); NS_ABORT_IF_FALSE(arr && arr->Count() == 6, "invalid number of arguments"); // if (arr->Item(1).GetUnit() == eCSSUnit_Image) { NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData, aStyleContext, arr->Item(1).GetImageValue) } else { NS_WARNING("nsCSSValue::Image::Image() failed?"); } // , , , nsStyleSides cropRect; NS_FOR_CSS_SIDES(side) { nsStyleCoord coord; const nsCSSValue& val = arr->Item(2 + side); #ifdef DEBUG bool unitOk = #endif SetAbsCoord(val, coord, SETCOORD_FACTOR | SETCOORD_PERCENT); NS_ABORT_IF_FALSE(unitOk, "Incorrect data structure created by CSS parser"); cropRect.Set(side, coord); } aResult.SetCropRect(&cropRect); } static void SetStyleImage(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleImage& aResult, bool& aCanStoreInRuleTree) { if (aValue.GetUnit() == eCSSUnit_Null) { return; } aResult.SetNull(); switch (aValue.GetUnit()) { case eCSSUnit_Image: NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData, aStyleContext, aValue.GetImageValue) break; case eCSSUnit_Function: if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) { SetStyleImageToImageRect(aStyleContext, aValue, aResult); } else { NS_NOTREACHED("-moz-image-rect() is the only expected function"); } break; case eCSSUnit_Gradient: { nsStyleGradient* gradient = new nsStyleGradient(); if (gradient) { SetGradient(aValue, aStyleContext->PresContext(), aStyleContext, *gradient, aCanStoreInRuleTree); aResult.SetGradientData(gradient); } break; } case eCSSUnit_Element: aResult.SetElementId(aValue.GetStringBufferValue()); break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: break; default: // We might have eCSSUnit_URL values for if-visited style // contexts, which we can safely treat like 'none'. Otherwise // this is an unexpected unit. NS_ASSERTION(aStyleContext->IsStyleIfVisited() && aValue.GetUnit() == eCSSUnit_URL, "unexpected unit; maybe nsCSSValue::Image::Image() failed?"); break; } } // flags for SetDiscrete - align values with SETCOORD_* constants // where possible #define SETDSC_NORMAL 0x01 // N #define SETDSC_AUTO 0x02 // A #define SETDSC_INTEGER 0x40 // I #define SETDSC_ENUMERATED 0x80 // E #define SETDSC_NONE 0x100 // O #define SETDSC_SYSTEM_FONT 0x2000 #define SETDSC_UNSET_INHERIT 0x00400000 #define SETDSC_UNSET_INITIAL 0x00800000 // no caller cares whether aField was changed or not template static void SetDiscrete(const nsCSSValue& aValue, FieldT & aField, bool& aCanStoreInRuleTree, uint32_t aMask, FieldT aParentValue, T1 aInitialValue, T2 aAutoValue, T3 aNoneValue, T4 aNormalValue, T5 aSystemFontValue) { switch (aValue.GetUnit()) { case eCSSUnit_Null: return; // every caller of SetDiscrete provides inherit and initial // alternatives, so we don't require them to say so in the mask case eCSSUnit_Inherit: aCanStoreInRuleTree = false; aField = aParentValue; return; case eCSSUnit_Initial: aField = aInitialValue; return; // every caller provides one or other of these alternatives, // but they have to say which case eCSSUnit_Enumerated: if (aMask & SETDSC_ENUMERATED) { aField = aValue.GetIntValue(); return; } break; case eCSSUnit_Integer: if (aMask & SETDSC_INTEGER) { aField = aValue.GetIntValue(); return; } break; // remaining possibilities in descending order of frequency of use case eCSSUnit_Auto: if (aMask & SETDSC_AUTO) { aField = aAutoValue; return; } break; case eCSSUnit_None: if (aMask & SETDSC_NONE) { aField = aNoneValue; return; } break; case eCSSUnit_Normal: if (aMask & SETDSC_NORMAL) { aField = aNormalValue; return; } break; case eCSSUnit_System_Font: if (aMask & SETDSC_SYSTEM_FONT) { aField = aSystemFontValue; return; } break; case eCSSUnit_Unset: if (aMask & SETDSC_UNSET_INHERIT) { aCanStoreInRuleTree = false; aField = aParentValue; return; } if (aMask & SETDSC_UNSET_INITIAL) { aField = aInitialValue; return; } break; default: break; } NS_NOTREACHED("SetDiscrete: inappropriate unit"); } // flags for SetFactor #define SETFCT_POSITIVE 0x01 // assert value is >= 0.0f #define SETFCT_OPACITY 0x02 // clamp value to [0.0f .. 1.0f] #define SETFCT_NONE 0x04 // allow _None (uses aInitialValue). #define SETFCT_UNSET_INHERIT 0x00400000 #define SETFCT_UNSET_INITIAL 0x00800000 static void SetFactor(const nsCSSValue& aValue, float& aField, bool& aCanStoreInRuleTree, float aParentValue, float aInitialValue, uint32_t aFlags = 0) { switch (aValue.GetUnit()) { case eCSSUnit_Null: return; case eCSSUnit_Number: aField = aValue.GetFloatValue(); if (aFlags & SETFCT_POSITIVE) { NS_ASSERTION(aField >= 0.0f, "negative value for positive-only property"); if (aField < 0.0f) aField = 0.0f; } if (aFlags & SETFCT_OPACITY) { if (aField < 0.0f) aField = 0.0f; if (aField > 1.0f) aField = 1.0f; } return; case eCSSUnit_Inherit: aCanStoreInRuleTree = false; aField = aParentValue; return; case eCSSUnit_Initial: aField = aInitialValue; return; case eCSSUnit_None: if (aFlags & SETFCT_NONE) { aField = aInitialValue; return; } break; case eCSSUnit_Unset: if (aFlags & SETFCT_UNSET_INHERIT) { aCanStoreInRuleTree = false; aField = aParentValue; return; } if (aFlags & SETFCT_UNSET_INITIAL) { aField = aInitialValue; return; } break; default: break; } NS_NOTREACHED("SetFactor: inappropriate unit"); } // Overloaded new operator. Initializes the memory to 0 and relies on an arena // (which comes from the presShell) to perform the allocation. void* nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW { // Check the recycle list first. return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsRuleNode_id, sz); } /* static */ PLDHashOperator nsRuleNode::EnqueueRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, void *arg) { ChildrenHashEntry *entry = static_cast(hdr); nsRuleNode ***destroyQueueTail = static_cast(arg); **destroyQueueTail = entry->mRuleNode; *destroyQueueTail = &entry->mRuleNode->mNextSibling; return PL_DHASH_NEXT; } // Overridden to prevent the global delete from being called, since the memory // came out of an nsIArena instead of the global delete operator's heap. void nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) { nsRuleNode *destroyQueue, **destroyQueueTail; if (aDestroyQueueTail) { destroyQueueTail = *aDestroyQueueTail; } else { destroyQueue = nullptr; destroyQueueTail = &destroyQueue; } if (ChildrenAreHashed()) { PLDHashTable *children = ChildrenHash(); PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren, &destroyQueueTail); *destroyQueueTail = nullptr; // ensure null-termination PL_DHashTableDestroy(children); } else if (HaveChildren()) { *destroyQueueTail = ChildrenList(); do { destroyQueueTail = &(*destroyQueueTail)->mNextSibling; } while (*destroyQueueTail); } mChildren.asVoid = nullptr; if (aDestroyQueueTail) { // Our caller destroys the queue. *aDestroyQueueTail = destroyQueueTail; } else { // We have to do destroy the queue. When we destroy each node, it // will add its children to the queue. while (destroyQueue) { nsRuleNode *cur = destroyQueue; destroyQueue = destroyQueue->mNextSibling; if (!destroyQueue) { NS_ASSERTION(destroyQueueTail == &cur->mNextSibling, "mangled list"); destroyQueueTail = &destroyQueue; } cur->DestroyInternal(&destroyQueueTail); } } // Destroy ourselves. this->~nsRuleNode(); // Don't let the memory be freed, since it will be recycled // instead. Don't call the global operator delete. mPresContext->PresShell()->FreeByObjectID(nsPresArena::nsRuleNode_id, this); } nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) { return new (aPresContext) nsRuleNode(aPresContext, nullptr, nullptr, 0xff, false); } nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportant) : mPresContext(aContext), mParent(aParent), mRule(aRule), mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) | (aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)), mNoneBits(0), mRefCnt(0) { MOZ_ASSERT(aContext); NS_ABORT_IF_FALSE(IsRoot() == !aRule, "non-root rule nodes must have a rule"); mChildren.asVoid = nullptr; MOZ_COUNT_CTOR(nsRuleNode); if (mRule) { mRule->AddRef(); } NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits"); NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes"); /* If IsRoot(), then aContext->StyleSet() is typically null at this point. In any case, we don't want to treat the root rulenode as unused. */ if (!IsRoot()) { mParent->AddRef(); aContext->StyleSet()->RuleNodeUnused(); } // nsStyleSet::GetContext depends on there being only one animation // rule. NS_ABORT_IF_FALSE(IsRoot() || GetLevel() != nsStyleSet::eAnimationSheet || mParent->IsRoot() || mParent->GetLevel() != nsStyleSet::eAnimationSheet, "must be only one rule at animation level"); } nsRuleNode::~nsRuleNode() { MOZ_COUNT_DTOR(nsRuleNode); if (mStyleData.mResetData || mStyleData.mInheritedData) mStyleData.Destroy(mDependentBits, mPresContext); if (mRule) { mRule->Release(); } } nsRuleNode* nsRuleNode::Transition(nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportantRule) { nsRuleNode* next = nullptr; nsRuleNode::Key key(aRule, aLevel, aIsImportantRule); if (HaveChildren() && !ChildrenAreHashed()) { int32_t numKids = 0; nsRuleNode* curr = ChildrenList(); while (curr && curr->GetKey() != key) { curr = curr->mNextSibling; ++numKids; } if (curr) next = curr; else if (numKids >= kMaxChildrenInList) ConvertChildrenToHash(); } if (ChildrenAreHashed()) { ChildrenHashEntry *entry = static_cast (PL_DHashTableOperate(ChildrenHash(), &key, PL_DHASH_ADD)); if (!entry) { NS_WARNING("out of memory"); return this; } if (entry->mRuleNode) next = entry->mRuleNode; else { next = entry->mRuleNode = new (mPresContext) nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); if (!next) { PL_DHashTableRawRemove(ChildrenHash(), entry); NS_WARNING("out of memory"); return this; } } } else if (!next) { // Create the new entry in our list. next = new (mPresContext) nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); if (!next) { NS_WARNING("out of memory"); return this; } next->mNextSibling = ChildrenList(); SetChildrenList(next); } return next; } void nsRuleNode::SetUsedDirectly() { mDependentBits |= NS_RULE_NODE_USED_DIRECTLY; // Maintain the invariant that any rule node that is used directly has // all structs that live in the rule tree cached (which // nsRuleNode::GetStyleData depends on for speed). if (mDependentBits & NS_STYLE_INHERIT_MASK) { for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) { uint32_t bit = nsCachedStyleData::GetBitForSID(sid); if (mDependentBits & bit) { nsRuleNode *source = mParent; while ((source->mDependentBits & bit) && !source->IsUsedDirectly()) { source = source->mParent; } void *data = source->mStyleData.GetStyleData(sid); NS_ASSERTION(data, "unexpected null struct"); mStyleData.SetStyleData(sid, mPresContext, data); } } } } void nsRuleNode::ConvertChildrenToHash() { NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(), "must have a non-empty list of children"); PLDHashTable *hash = PL_NewDHashTable(&ChildrenHashOps, nullptr, sizeof(ChildrenHashEntry), kMaxChildrenInList * 4); if (!hash) return; for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) { // This will never fail because of the initial size we gave the table. ChildrenHashEntry *entry = static_cast( PL_DHashTableOperate(hash, curr->mRule, PL_DHASH_ADD)); NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list"); entry->mRuleNode = curr; } SetChildrenHash(hash); } inline void nsRuleNode::PropagateNoneBit(uint32_t aBit, nsRuleNode* aHighestNode) { nsRuleNode* curr = this; for (;;) { NS_ASSERTION(!(curr->mNoneBits & aBit), "propagating too far"); curr->mNoneBits |= aBit; if (curr == aHighestNode) break; curr = curr->mParent; } } inline void nsRuleNode::PropagateDependentBit(nsStyleStructID aSID, nsRuleNode* aHighestNode, void* aStruct) { NS_ASSERTION(aStruct, "expected struct"); uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); for (nsRuleNode* curr = this; curr != aHighestNode; curr = curr->mParent) { if (curr->mDependentBits & bit) { #ifdef DEBUG while (curr != aHighestNode) { NS_ASSERTION(curr->mDependentBits & bit, "bit not set"); curr = curr->mParent; } #endif break; } curr->mDependentBits |= bit; if (curr->IsUsedDirectly()) { curr->mStyleData.SetStyleData(aSID, mPresContext, aStruct); } } } /* * The following "Check" functions are used for determining what type of * sharing can be used for the data on this rule node. MORE HERE... */ /* * a callback function that that can revise the result of * CheckSpecifiedProperties before finishing; aResult is the current * result, and it returns the revised one. */ typedef nsRuleNode::RuleDetail (* CheckCallbackFn)(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult); /** * @param aValue the value being examined * @param aSpecifiedCount to be incremented by one if the value is specified * @param aInheritedCount to be incremented by one if the value is set to inherit * @param aUnsetCount to be incremented by one if the value is set to unset */ inline void ExamineCSSValue(const nsCSSValue& aValue, uint32_t& aSpecifiedCount, uint32_t& aInheritedCount, uint32_t& aUnsetCount) { if (aValue.GetUnit() != eCSSUnit_Null) { ++aSpecifiedCount; if (aValue.GetUnit() == eCSSUnit_Inherit) { ++aInheritedCount; } else if (aValue.GetUnit() == eCSSUnit_Unset) { ++aUnsetCount; } } } static nsRuleNode::RuleDetail CheckFontCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // em, ex, percent, 'larger', and 'smaller' values on font-size depend // on the parent context's font-size // Likewise, 'lighter' and 'bolder' values of 'font-weight', and 'wider' // and 'narrower' values of 'font-stretch' depend on the parent. const nsCSSValue& size = *aRuleData->ValueForFontSize(); const nsCSSValue& weight = *aRuleData->ValueForFontWeight(); if ((size.IsRelativeLengthUnit() && size.GetUnit() != eCSSUnit_RootEM) || size.GetUnit() == eCSSUnit_Percent || (size.GetUnit() == eCSSUnit_Enumerated && (size.GetIntValue() == NS_STYLE_FONT_SIZE_SMALLER || size.GetIntValue() == NS_STYLE_FONT_SIZE_LARGER)) || aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Integer || (weight.GetUnit() == eCSSUnit_Enumerated && (weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_BOLDER || weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_LIGHTER))) { NS_ASSERTION(aResult == nsRuleNode::eRulePartialReset || aResult == nsRuleNode::eRuleFullReset || aResult == nsRuleNode::eRulePartialMixed || aResult == nsRuleNode::eRuleFullMixed, "we know we already have a reset-counted property"); // Promote reset to mixed since we have something that depends on // the parent. But never promote to inherited since that could // cause inheritance of the exact value. if (aResult == nsRuleNode::eRulePartialReset) aResult = nsRuleNode::eRulePartialMixed; else if (aResult == nsRuleNode::eRuleFullReset) aResult = nsRuleNode::eRuleFullMixed; } return aResult; } static nsRuleNode::RuleDetail CheckColorCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // currentColor values for color require inheritance const nsCSSValue* colorValue = aRuleData->ValueForColor(); if (colorValue->GetUnit() == eCSSUnit_EnumColor && colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) { NS_ASSERTION(aResult == nsRuleNode::eRuleFullReset, "we should already be counted as full-reset"); aResult = nsRuleNode::eRuleFullInherited; } return aResult; } static nsRuleNode::RuleDetail CheckTextCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); if (textAlignValue->GetUnit() == eCSSUnit_Enumerated && textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT) { // Promote reset to mixed since we have something that depends on // the parent. if (aResult == nsRuleNode::eRulePartialReset) aResult = nsRuleNode::eRulePartialMixed; else if (aResult == nsRuleNode::eRuleFullReset) aResult = nsRuleNode::eRuleFullMixed; } return aResult; } static nsRuleNode::RuleDetail CheckVariablesCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // We don't actually have any properties on nsStyleVariables, so we do // all of the RuleDetail calculation in here. if (aRuleData->mVariables) { return nsRuleNode::eRulePartialMixed; } return nsRuleNode::eRuleNone; } #define FLAG_DATA_FOR_PROPERTY(name_, id_, method_, flags_, pref_, \ parsevariant_, kwtable_, stylestructoffset_, \ animtype_) \ flags_, // The order here must match the enums in *CheckCounter in nsCSSProps.cpp. static const uint32_t gFontFlags[] = { #define CSS_PROP_FONT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_FONT }; static const uint32_t gDisplayFlags[] = { #define CSS_PROP_DISPLAY FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_DISPLAY }; static const uint32_t gVisibilityFlags[] = { #define CSS_PROP_VISIBILITY FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_VISIBILITY }; static const uint32_t gMarginFlags[] = { #define CSS_PROP_MARGIN FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_MARGIN }; static const uint32_t gBorderFlags[] = { #define CSS_PROP_BORDER FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_BORDER }; static const uint32_t gPaddingFlags[] = { #define CSS_PROP_PADDING FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_PADDING }; static const uint32_t gOutlineFlags[] = { #define CSS_PROP_OUTLINE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_OUTLINE }; static const uint32_t gListFlags[] = { #define CSS_PROP_LIST FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_LIST }; static const uint32_t gColorFlags[] = { #define CSS_PROP_COLOR FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_COLOR }; static const uint32_t gBackgroundFlags[] = { #define CSS_PROP_BACKGROUND FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_BACKGROUND }; static const uint32_t gPositionFlags[] = { #define CSS_PROP_POSITION FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_POSITION }; static const uint32_t gTableFlags[] = { #define CSS_PROP_TABLE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TABLE }; static const uint32_t gTableBorderFlags[] = { #define CSS_PROP_TABLEBORDER FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TABLEBORDER }; static const uint32_t gContentFlags[] = { #define CSS_PROP_CONTENT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_CONTENT }; static const uint32_t gQuotesFlags[] = { #define CSS_PROP_QUOTES FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_QUOTES }; static const uint32_t gTextFlags[] = { #define CSS_PROP_TEXT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TEXT }; static const uint32_t gTextResetFlags[] = { #define CSS_PROP_TEXTRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TEXTRESET }; static const uint32_t gUserInterfaceFlags[] = { #define CSS_PROP_USERINTERFACE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_USERINTERFACE }; static const uint32_t gUIResetFlags[] = { #define CSS_PROP_UIRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_UIRESET }; static const uint32_t gXULFlags[] = { #define CSS_PROP_XUL FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_XUL }; static const uint32_t gSVGFlags[] = { #define CSS_PROP_SVG FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_SVG }; static const uint32_t gSVGResetFlags[] = { #define CSS_PROP_SVGRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_SVGRESET }; static const uint32_t gColumnFlags[] = { #define CSS_PROP_COLUMN FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_COLUMN }; // There are no properties in nsStyleVariables, but we can't have a // zero length array. static const uint32_t gVariablesFlags[] = { 0, #define CSS_PROP_VARIABLES FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_VARIABLES }; static_assert(sizeof(gVariablesFlags) == sizeof(uint32_t), "if nsStyleVariables has properties now you can remove the dummy " "gVariablesFlags entry"); #undef FLAG_DATA_FOR_PROPERTY static const uint32_t* gFlagsByStruct[] = { #define STYLE_STRUCT(name, checkdata_cb) \ g##name##Flags, #include "nsStyleStructList.h" #undef STYLE_STRUCT }; static const CheckCallbackFn gCheckCallbacks[] = { #define STYLE_STRUCT(name, checkdata_cb) \ checkdata_cb, #include "nsStyleStructList.h" #undef STYLE_STRUCT }; #ifdef DEBUG static bool AreAllMathMLPropertiesUndefined(const nsRuleData* aRuleData) { return aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForScriptSizeMultiplier()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForScriptMinSize()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForMathVariant()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForMathDisplay()->GetUnit() == eCSSUnit_Null; } #endif inline nsRuleNode::RuleDetail nsRuleNode::CheckSpecifiedProperties(const nsStyleStructID aSID, const nsRuleData* aRuleData) { // Build a count of the: uint32_t total = 0, // total number of props in the struct specified = 0, // number that were specified for this node inherited = 0, // number that were 'inherit' (and not // eCSSUnit_Inherit) for this node unset = 0; // number that were 'unset' // See comment in nsRuleData.h above mValueOffsets. NS_ABORT_IF_FALSE(aRuleData->mValueOffsets[aSID] == 0, "we assume the value offset is zero instead of adding it"); for (nsCSSValue *values = aRuleData->mValueStorage, *values_end = values + nsCSSProps::PropertyCountInStruct(aSID); values != values_end; ++values) { ++total; ExamineCSSValue(*values, specified, inherited, unset); } if (!nsCachedStyleData::IsReset(aSID)) { // For inherited properties, 'unset' means the same as 'inherit'. inherited += unset; unset = 0; } #if 0 printf("CheckSpecifiedProperties: SID=%d total=%d spec=%d inh=%d.\n", aSID, total, specified, inherited); #endif NS_ASSERTION(aSID != eStyleStruct_Font || mPresContext->Document()->GetMathMLEnabled() || AreAllMathMLPropertiesUndefined(aRuleData), "MathML style property was defined even though MathML is disabled"); /* * Return the most specific information we can: prefer None or Full * over Partial, and Reset or Inherited over Mixed, since we can * optimize based on the edge cases and not the in-between cases. */ nsRuleNode::RuleDetail result; if (inherited == total) result = eRuleFullInherited; else if (specified == total // MathML defines 5 properties in Font that will never be set when // MathML is not in use. Therefore if all but five // properties have been set, and MathML is not enabled, we can treat // this as fully specified. Code in nsMathMLElementFactory will // rebuild the rule tree and style data when MathML is first enabled // (see nsMathMLElement::BindToTree). || (aSID == eStyleStruct_Font && specified + 5 == total && !mPresContext->Document()->GetMathMLEnabled()) ) { if (inherited == 0) result = eRuleFullReset; else result = eRuleFullMixed; } else if (specified == 0) result = eRuleNone; else if (specified == inherited) result = eRulePartialInherited; else if (inherited == 0) result = eRulePartialReset; else result = eRulePartialMixed; CheckCallbackFn cb = gCheckCallbacks[aSID]; if (cb) { result = (*cb)(aRuleData, result); } return result; } // If we need to restrict which properties apply to the style context, // return the bit to check in nsCSSProp's flags table. Otherwise, // return 0. inline uint32_t GetPseudoRestriction(nsStyleContext *aContext) { // This needs to match nsStyleSet::WalkRestrictionRule. uint32_t pseudoRestriction = 0; nsIAtom *pseudoType = aContext->GetPseudo(); if (pseudoType) { if (pseudoType == nsCSSPseudoElements::firstLetter) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LETTER; } else if (pseudoType == nsCSSPseudoElements::firstLine) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LINE; } else if (pseudoType == nsCSSPseudoElements::mozPlaceholder) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_PLACEHOLDER; } } return pseudoRestriction; } static void UnsetPropertiesWithoutFlags(const nsStyleStructID aSID, nsRuleData* aRuleData, uint32_t aFlags) { NS_ASSERTION(aFlags != 0, "aFlags must be nonzero"); const uint32_t *flagData = gFlagsByStruct[aSID]; // See comment in nsRuleData.h above mValueOffsets. NS_ABORT_IF_FALSE(aRuleData->mValueOffsets[aSID] == 0, "we assume the value offset is zero instead of adding it"); nsCSSValue *values = aRuleData->mValueStorage; for (size_t i = 0, i_end = nsCSSProps::PropertyCountInStruct(aSID); i != i_end; ++i) { if ((flagData[i] & aFlags) != aFlags) values[i].Reset(); } } /** * We allocate arrays of CSS values with alloca. (These arrays are a * fixed size per style struct, but we don't want to waste the * allocation and construction/destruction costs of the big structs when * we're handling much smaller ones.) Since the lifetime of an alloca * allocation is the life of the calling function, the caller must call * alloca. However, to ensure that constructors and destructors are * balanced, we do the constructor and destructor calling from this RAII * class, AutoCSSValueArray. */ struct AutoCSSValueArray { /** * aStorage must be the result of alloca(aCount * sizeof(nsCSSValue)) */ AutoCSSValueArray(void* aStorage, size_t aCount) { NS_ABORT_IF_FALSE(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0, "bad alignment from alloca"); mCount = aCount; // Don't use placement new[], since it might store extra data // for the count (on Windows!). mArray = static_cast(aStorage); for (size_t i = 0; i < mCount; ++i) { new (mArray + i) nsCSSValue(); } } ~AutoCSSValueArray() { for (size_t i = 0; i < mCount; ++i) { mArray[i].~nsCSSValue(); } } nsCSSValue* get() { return mArray; } private: nsCSSValue *mArray; size_t mCount; }; /* static */ bool nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID, nsRuleData* aRuleData, nsStyleContext* aContext) { MOZ_ASSERT(aSID != eStyleStruct_Variables); MOZ_ASSERT(aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(aSID)); MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0); nsCSSParser parser; bool anyTokenStreams = false; // Look at each property in the nsRuleData for the given style struct. size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); for (nsCSSValue* value = aRuleData->mValueStorage, *values_end = aRuleData->mValueStorage + nprops; value != values_end; value++) { if (value->GetUnit() != eCSSUnit_TokenStream) { continue; } const CSSVariableValues* variables = &aContext->StyleVariables()->mVariables; nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue(); // XXX Should pass in sheet here (see bug 952338). parser.ParsePropertyWithVariableReferences( tokenStream->mPropertyID, tokenStream->mShorthandPropertyID, tokenStream->mTokenStream, variables, aRuleData, tokenStream->mSheetURI, tokenStream->mBaseURI, tokenStream->mSheetPrincipal, nullptr, tokenStream->mLineNumber, tokenStream->mLineOffset); aRuleData->mCanStoreInRuleTree = false; anyTokenStreams = true; } return anyTokenStreams; } const void* nsRuleNode::WalkRuleTree(const nsStyleStructID aSID, nsStyleContext* aContext) { // use placement new[] on the result of alloca() to allocate a // variable-sized stack array, including execution of constructors, // and use an RAII class to run the destructors too. size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); AutoCSSValueArray dataArray(dataStorage, nprops); nsRuleData ruleData(nsCachedStyleData::GetBitForSID(aSID), dataArray.get(), mPresContext, aContext); ruleData.mValueOffsets[aSID] = 0; // We start at the most specific rule in the tree. void* startStruct = nullptr; nsRuleNode* ruleNode = this; nsRuleNode* highestNode = nullptr; // The highest node in the rule tree // that has the same properties // specified for struct |aSID| as // |this| does. nsRuleNode* rootNode = this; // After the loop below, this will be the // highest node that we've walked without // finding cached data on the rule tree. // If we don't find any cached data, it // will be the root. (XXX misnamed) RuleDetail detail = eRuleNone; uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); while (ruleNode) { // See if this rule node has cached the fact that the remaining // nodes along this path specify no data whatsoever. if (ruleNode->mNoneBits & bit) break; // If the dependent bit is set on a rule node for this struct, that // means its rule won't have any information to add, so skip it. // NOTE: If we exit the loop because of the !IsUsedDirectly() check, // then we're guaranteed to break immediately afterwards due to a // non-null startStruct. while ((ruleNode->mDependentBits & bit) && !ruleNode->IsUsedDirectly()) { NS_ASSERTION(ruleNode->mStyleData.GetStyleData(aSID) == nullptr, "dependent bit with cached data makes no sense"); // Climb up to the next rule in the tree (a less specific rule). rootNode = ruleNode; ruleNode = ruleNode->mParent; NS_ASSERTION(!(ruleNode->mNoneBits & bit), "can't have both bits set"); } // Check for cached data after the inner loop above -- otherwise // we'll miss it. startStruct = ruleNode->mStyleData.GetStyleData(aSID); if (startStruct) break; // We found a rule with fully specified data. We don't // need to go up the tree any further, since the remainder // of this branch has already been computed. // Ask the rule to fill in the properties that it specifies. nsIStyleRule *rule = ruleNode->mRule; if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); } // Now we check to see how many properties have been specified by // the rules we've examined so far. RuleDetail oldDetail = detail; detail = CheckSpecifiedProperties(aSID, &ruleData); if (oldDetail == eRuleNone && detail != eRuleNone) highestNode = ruleNode; if (detail == eRuleFullReset || detail == eRuleFullMixed || detail == eRuleFullInherited) break; // We don't need to examine any more rules. All properties // have been fully specified. // Climb up to the next rule in the tree (a less specific rule). rootNode = ruleNode; ruleNode = ruleNode->mParent; } bool recomputeDetail = false; // If we are computing a style struct other than nsStyleVariables, and // ruleData has any properties with variable references (nsCSSValues of // type eCSSUnit_TokenStream), then we need to resolve these. if (aSID != eStyleStruct_Variables) { // A property's value might have became 'inherit' after resolving // variable references. (This happens when an inherited property // fails to parse its resolved value.) We need to recompute // |detail| in case this happened. recomputeDetail = ResolveVariableReferences(aSID, &ruleData, aContext); } // If needed, unset the properties that don't have a flag that allows // them to be set for this style context. (For example, only some // properties apply to :first-line and :first-letter.) uint32_t pseudoRestriction = GetPseudoRestriction(aContext); if (pseudoRestriction) { UnsetPropertiesWithoutFlags(aSID, &ruleData, pseudoRestriction); // We need to recompute |detail| based on the restrictions we just applied. // We can adjust |detail| arbitrarily because of the restriction // rule added in nsStyleSet::WalkRestrictionRule. recomputeDetail = true; } if (recomputeDetail) { detail = CheckSpecifiedProperties(aSID, &ruleData); } NS_ASSERTION(!startStruct || (detail != eRuleFullReset && detail != eRuleFullMixed && detail != eRuleFullInherited), "can't have start struct and be fully specified"); bool isReset = nsCachedStyleData::IsReset(aSID); if (!highestNode) highestNode = rootNode; if (!ruleData.mCanStoreInRuleTree) detail = eRulePartialMixed; // Treat as though some data is specified to avoid // the optimizations and force data computation. if (detail == eRuleNone && startStruct) { // We specified absolutely no rule information, but a parent rule in the tree // specified all the rule information. We set a bit along the branch from our // node in the tree to the node that specified the data that tells nodes on that // branch that they never need to examine their rules for this particular struct type // ever again. PropagateDependentBit(aSID, ruleNode, startStruct); return startStruct; } if ((!startStruct && !isReset && (detail == eRuleNone || detail == eRulePartialInherited)) || detail == eRuleFullInherited) { // We specified no non-inherited information and neither did any of // our parent rules. // We set a bit along the branch from the highest node (ruleNode) // down to our node (this) indicating that no non-inherited data was // specified. This bit is guaranteed to be set already on the path // from the highest node to the root node in the case where // (detail == eRuleNone), which is the most common case here. // We must check |!isReset| because the Compute*Data functions for // reset structs wouldn't handle none bits correctly. if (highestNode != this && !isReset) PropagateNoneBit(bit, highestNode); // All information must necessarily be inherited from our parent style context. // In the absence of any computed data in the rule tree and with // no rules specified that didn't have values of 'inherit', we should check our parent. nsStyleContext* parentContext = aContext->GetParent(); if (isReset) { /* Reset structs don't inherit from first-line. */ /* See similar code in COMPUTE_START_RESET */ while (parentContext && parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { parentContext = parentContext->GetParent(); } } if (parentContext) { // We have a parent, and so we should just inherit from the parent. // Set the inherit bits on our context. These bits tell the style context that // it never has to go back to the rule tree for data. Instead the style context tree // should be walked to find the data. const void* parentStruct = parentContext->StyleData(aSID); aContext->AddStyleBit(bit); // makes const_cast OK. aContext->SetStyle(aSID, const_cast(parentStruct)); return parentStruct; } else // We are the root. In the case of fonts, the default values just // come from the pres context. return SetDefaultOnRoot(aSID, aContext); } // We need to compute the data from the information that the rules specified. const void* res; #define STYLE_STRUCT_TEST aSID #define STYLE_STRUCT(name, checkdata_cb) \ res = Compute##name##Data(startStruct, &ruleData, aContext, \ highestNode, detail, ruleData.mCanStoreInRuleTree); #include "nsStyleStructList.h" #undef STYLE_STRUCT #undef STYLE_STRUCT_TEST // Now return the result. return res; } const void* nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContext) { switch (aSID) { case eStyleStruct_Font: { nsStyleFont* fontData = new (mPresContext) nsStyleFont(mPresContext); nscoord minimumFontSize = mPresContext->MinFontSize(fontData->mLanguage); if (minimumFontSize > 0 && !mPresContext->IsChrome()) { fontData->mFont.size = std::max(fontData->mSize, minimumFontSize); } else { fontData->mFont.size = fontData->mSize; } aContext->SetStyle(eStyleStruct_Font, fontData); return fontData; } case eStyleStruct_Display: { nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay(); aContext->SetStyle(eStyleStruct_Display, disp); return disp; } case eStyleStruct_Visibility: { nsStyleVisibility* vis = new (mPresContext) nsStyleVisibility(mPresContext); aContext->SetStyle(eStyleStruct_Visibility, vis); return vis; } case eStyleStruct_Text: { nsStyleText* text = new (mPresContext) nsStyleText(); aContext->SetStyle(eStyleStruct_Text, text); return text; } case eStyleStruct_TextReset: { nsStyleTextReset* text = new (mPresContext) nsStyleTextReset(); aContext->SetStyle(eStyleStruct_TextReset, text); return text; } case eStyleStruct_Color: { nsStyleColor* color = new (mPresContext) nsStyleColor(mPresContext); aContext->SetStyle(eStyleStruct_Color, color); return color; } case eStyleStruct_Background: { nsStyleBackground* bg = new (mPresContext) nsStyleBackground(); aContext->SetStyle(eStyleStruct_Background, bg); return bg; } case eStyleStruct_Margin: { nsStyleMargin* margin = new (mPresContext) nsStyleMargin(); aContext->SetStyle(eStyleStruct_Margin, margin); return margin; } case eStyleStruct_Border: { nsStyleBorder* border = new (mPresContext) nsStyleBorder(mPresContext); aContext->SetStyle(eStyleStruct_Border, border); return border; } case eStyleStruct_Padding: { nsStylePadding* padding = new (mPresContext) nsStylePadding(); aContext->SetStyle(eStyleStruct_Padding, padding); return padding; } case eStyleStruct_Outline: { nsStyleOutline* outline = new (mPresContext) nsStyleOutline(mPresContext); aContext->SetStyle(eStyleStruct_Outline, outline); return outline; } case eStyleStruct_List: { nsStyleList* list = new (mPresContext) nsStyleList(); aContext->SetStyle(eStyleStruct_List, list); return list; } case eStyleStruct_Position: { nsStylePosition* pos = new (mPresContext) nsStylePosition(); aContext->SetStyle(eStyleStruct_Position, pos); return pos; } case eStyleStruct_Table: { nsStyleTable* table = new (mPresContext) nsStyleTable(); aContext->SetStyle(eStyleStruct_Table, table); return table; } case eStyleStruct_TableBorder: { nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(mPresContext); aContext->SetStyle(eStyleStruct_TableBorder, table); return table; } case eStyleStruct_Content: { nsStyleContent* content = new (mPresContext) nsStyleContent(); aContext->SetStyle(eStyleStruct_Content, content); return content; } case eStyleStruct_Quotes: { nsStyleQuotes* quotes = new (mPresContext) nsStyleQuotes(); aContext->SetStyle(eStyleStruct_Quotes, quotes); return quotes; } case eStyleStruct_UserInterface: { nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface(); aContext->SetStyle(eStyleStruct_UserInterface, ui); return ui; } case eStyleStruct_UIReset: { nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset(); aContext->SetStyle(eStyleStruct_UIReset, ui); return ui; } case eStyleStruct_XUL: { nsStyleXUL* xul = new (mPresContext) nsStyleXUL(); aContext->SetStyle(eStyleStruct_XUL, xul); return xul; } case eStyleStruct_Column: { nsStyleColumn* column = new (mPresContext) nsStyleColumn(mPresContext); aContext->SetStyle(eStyleStruct_Column, column); return column; } case eStyleStruct_SVG: { nsStyleSVG* svg = new (mPresContext) nsStyleSVG(); aContext->SetStyle(eStyleStruct_SVG, svg); return svg; } case eStyleStruct_SVGReset: { nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset(); aContext->SetStyle(eStyleStruct_SVGReset, svgReset); return svgReset; } case eStyleStruct_Variables: { nsStyleVariables* vars = new (mPresContext) nsStyleVariables(); aContext->SetStyle(eStyleStruct_Variables, vars); return vars; } default: /* * unhandled case: nsStyleStructID_Length. * last item of nsStyleStructID, to know its length. */ NS_ABORT_IF_FALSE(false, "unexpected SID"); return nullptr; } return nullptr; } /* * This function handles cascading of *-left or *-right box properties * against *-start (which is L for LTR and R for RTL) or *-end (which is * R for LTR and L for RTL). * * Cascading these properties correctly is hard because we need to * cascade two properties as one, but which two properties depends on a * third property ('direction'). We solve this by treating each of * these properties (say, 'margin-start') as a shorthand that sets a * property containing the value of the property specified * ('margin-start-value') and sets a pair of properties * ('margin-left-ltr-source' and 'margin-right-rtl-source') saying which * of the properties we use. Thus, when we want to compute the value of * 'margin-left' when 'direction' is 'ltr', we look at the value of * 'margin-left-ltr-source', which tells us whether to use the highest * 'margin-left' in the cascade or the highest 'margin-start'. * * Finally, since we can compute the normal (*-left and *-right) * properties in a loop, this function works by modifying the data we * will use in that loop (which the caller must copy from the const * input). */ void nsRuleNode::AdjustLogicalBoxProp(nsStyleContext* aContext, const nsCSSValue& aLTRSource, const nsCSSValue& aRTLSource, const nsCSSValue& aLTRLogicalValue, const nsCSSValue& aRTLLogicalValue, mozilla::css::Side aSide, nsCSSRect& aValueRect, bool& aCanStoreInRuleTree) { bool LTRlogical = aLTRSource.GetUnit() == eCSSUnit_Enumerated && aLTRSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL; bool RTLlogical = aRTLSource.GetUnit() == eCSSUnit_Enumerated && aRTLSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL; if (LTRlogical || RTLlogical) { // We can't cache anything on the rule tree if we use any data from // the style context, since data cached in the rule tree could be // used with a style context with a different value. aCanStoreInRuleTree = false; uint8_t dir = aContext->StyleVisibility()->mDirection; if (dir == NS_STYLE_DIRECTION_LTR) { if (LTRlogical) aValueRect.*(nsCSSRect::sides[aSide]) = aLTRLogicalValue; } else { if (RTLlogical) aValueRect.*(nsCSSRect::sides[aSide]) = aRTLLogicalValue; } } else if (aLTRLogicalValue.GetUnit() == eCSSUnit_Inherit || aRTLLogicalValue.GetUnit() == eCSSUnit_Inherit) { // It actually is valid to store this in the ruletree, since // LTRlogical and RTLlogical are both false, but doing that will // trigger asserts. Silence those. aCanStoreInRuleTree = false; } } /** * Begin an nsRuleNode::Compute*Data function for an inherited struct. * * @param type_ The nsStyle* type this function computes. * @param ctorargs_ The arguments used for the default nsStyle* constructor. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ #define COMPUTE_START_INHERITED(type_, ctorargs_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ nsStyleContext* parentContext = aContext->GetParent(); \ \ nsStyle##type_* data_ = nullptr; \ mozilla::Maybe maybeFakeParentData; \ const nsStyle##type_* parentdata_ = nullptr; \ bool canStoreInRuleTree = aCanStoreInRuleTree; \ \ /* If |canStoreInRuleTree| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ if (aRuleDetail != eRuleFullReset && \ (!aStartStruct || (aRuleDetail != eRulePartialReset && \ aRuleDetail != eRuleNone))) { \ if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ maybeFakeParentData.construct ctorargs_; \ parentdata_ = maybeFakeParentData.addr(); \ } \ } \ if (aStartStruct) \ /* We only need to compute the delta between this computed data and */ \ /* our computed data. */ \ data_ = new (mPresContext) \ nsStyle##type_(*static_cast(aStartStruct)); \ else { \ if (aRuleDetail != eRuleFullMixed && aRuleDetail != eRuleFullReset) { \ /* No question. We will have to inherit. Go ahead and init */ \ /* with inherited vals from parent. */ \ canStoreInRuleTree = false; \ if (parentdata_) \ data_ = new (mPresContext) nsStyle##type_(*parentdata_); \ else \ data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ } \ else \ data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ } \ \ if (!parentdata_) \ parentdata_ = data_; /** * Begin an nsRuleNode::Compute*Data function for a reset struct. * * @param type_ The nsStyle* type this function computes. * @param ctorargs_ The arguments used for the default nsStyle* constructor. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ #define COMPUTE_START_RESET(type_, ctorargs_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ nsStyleContext* parentContext = aContext->GetParent(); \ /* Reset structs don't inherit from first-line */ \ /* See similar code in WalkRuleTree */ \ while (parentContext && \ parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { \ parentContext = parentContext->GetParent(); \ } \ \ nsStyle##type_* data_; \ if (aStartStruct) \ /* We only need to compute the delta between this computed data and */ \ /* our computed data. */ \ data_ = new (mPresContext) \ nsStyle##type_(*static_cast(aStartStruct)); \ else \ data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ \ /* If |canStoreInRuleTree| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ mozilla::Maybe maybeFakeParentData; \ const nsStyle##type_* parentdata_ = data_; \ if (aRuleDetail != eRuleFullReset && \ aRuleDetail != eRulePartialReset && \ aRuleDetail != eRuleNone) { \ if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ maybeFakeParentData.construct ctorargs_; \ parentdata_ = maybeFakeParentData.addr(); \ } \ } \ bool canStoreInRuleTree = aCanStoreInRuleTree; /** * End an nsRuleNode::Compute*Data function for an inherited struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_INHERITED(type_, data_) \ NS_POSTCONDITION(!canStoreInRuleTree || aRuleDetail == eRuleFullReset || \ (aStartStruct && aRuleDetail == eRulePartialReset), \ "canStoreInRuleTree must be false for inherited structs " \ "unless all properties have been specified with values " \ "other than inherit"); \ if (canStoreInRuleTree) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mInheritedData) { \ aHighestNode->mStyleData.mInheritedData = \ new (mPresContext) nsInheritedStyleData; \ } \ NS_ASSERTION(!aHighestNode->mStyleData.mInheritedData-> \ mStyleStructs[eStyleStruct_##type_], \ "Going to leak style data"); \ aHighestNode->mStyleData.mInheritedData-> \ mStyleStructs[eStyleStruct_##type_] = data_; \ /* Propagate the bit down. */ \ PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ /* Tell the style context that it doesn't own the data */ \ aContext-> \ AddStyleBit(nsCachedStyleData::GetBitForSID(eStyleStruct_##type_)); \ } \ /* Always cache inherited data on the style context */ \ aContext->SetStyle##type_(data_); \ \ return data_; /** * End an nsRuleNode::Compute*Data function for a reset struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_RESET(type_, data_) \ NS_POSTCONDITION(!canStoreInRuleTree || \ aRuleDetail == eRuleNone || \ aRuleDetail == eRulePartialReset || \ aRuleDetail == eRuleFullReset, \ "canStoreInRuleTree must be false for reset structs " \ "if any properties were specified as inherit"); \ if (!canStoreInRuleTree) \ /* We can't be cached in the rule node. We have to be put right */ \ /* on the style context. */ \ aContext->SetStyle(eStyleStruct_##type_, data_); \ else { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mResetData) { \ aHighestNode->mStyleData.mResetData = \ new (mPresContext) nsResetStyleData; \ } \ NS_ASSERTION(!aHighestNode->mStyleData.mResetData-> \ mStyleStructs[eStyleStruct_##type_], \ "Going to leak style data"); \ aHighestNode->mStyleData.mResetData-> \ mStyleStructs[eStyleStruct_##type_] = data_; \ /* Propagate the bit down. */ \ PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ } \ \ return data_; // This function figures out how much scaling should be suppressed to // satisfy scriptminsize. This is our attempt to implement // http://www.w3.org/TR/MathML2/chapter3.html#id.3.3.4.2.2 // This is called after mScriptLevel, mScriptMinSize and mScriptSizeMultiplier // have been set in aFont. // // Here are the invariants we enforce: // 1) A decrease in size must not reduce the size below minscriptsize. // 2) An increase in size must not increase the size above the size we would // have if minscriptsize had not been applied anywhere. // 3) The scriptlevel-induced size change must between 1.0 and the parent's // scriptsizemultiplier^(new script level - old script level), as close to the // latter as possible subject to constraints 1 and 2. static nscoord ComputeScriptLevelSize(const nsStyleFont* aFont, const nsStyleFont* aParentFont, nsPresContext* aPresContext, nscoord* aUnconstrainedSize) { int32_t scriptLevelChange = aFont->mScriptLevel - aParentFont->mScriptLevel; if (scriptLevelChange == 0) { *aUnconstrainedSize = aParentFont->mScriptUnconstrainedSize; // Constraint #3 says that we cannot change size, and #1 and #2 are always // satisfied with no change. It's important this be fast because it covers // all non-MathML content. return aParentFont->mSize; } // Compute actual value of minScriptSize nscoord minScriptSize = aParentFont->mScriptMinSize; if (aFont->mAllowZoom) { minScriptSize = nsStyleFont::ZoomText(aPresContext, minScriptSize); } double scriptLevelScale = pow(aParentFont->mScriptSizeMultiplier, scriptLevelChange); // Compute the size we would have had if minscriptsize had never been // applied, also prevent overflow (bug 413274) *aUnconstrainedSize = NSToCoordRound(std::min(aParentFont->mScriptUnconstrainedSize*scriptLevelScale, double(nscoord_MAX))); // Compute the size we could get via scriptlevel change nscoord scriptLevelSize = NSToCoordRound(std::min(aParentFont->mSize*scriptLevelScale, double(nscoord_MAX))); if (scriptLevelScale <= 1.0) { if (aParentFont->mSize <= minScriptSize) { // We can't decrease the font size at all, so just stick to no change // (authors are allowed to explicitly set the font size smaller than // minscriptsize) return aParentFont->mSize; } // We can decrease, so apply constraint #1 return std::max(minScriptSize, scriptLevelSize); } else { // scriptminsize can only make sizes larger than the unconstrained size NS_ASSERTION(*aUnconstrainedSize <= scriptLevelSize, "How can this ever happen?"); // Apply constraint #2 return std::min(scriptLevelSize, std::max(*aUnconstrainedSize, minScriptSize)); } } /* static */ nscoord nsRuleNode::CalcFontPointSize(int32_t aHTMLSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { #define sFontSizeTableMin 9 #define sFontSizeTableMax 16 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla // and eventually in WinIE5.5 will help to establish a standard rendering across // platforms and browsers. For now, it is used only in Strict mode. More can be read // in the document written by Todd Farhner at: // http://style.verso.com/font_size_intervals/altintervals.html // static int32_t sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = { { 9, 9, 9, 9, 11, 14, 18, 27}, { 9, 9, 9, 10, 12, 15, 20, 30}, { 9, 9, 10, 11, 13, 17, 22, 33}, { 9, 9, 10, 12, 14, 18, 24, 36}, { 9, 10, 12, 13, 16, 20, 26, 39}, { 9, 10, 12, 14, 17, 21, 28, 42}, { 9, 10, 13, 15, 18, 23, 30, 45}, { 9, 10, 13, 16, 18, 24, 32, 48} }; // HTML 1 2 3 4 5 6 7 // CSS xxs xs s m l xl xxl // | // user pref // //------------------------------------------------------------ // // This table gives us compatibility with WinNav4 for the default fonts only. // In WinNav4, the default fonts were: // // Times/12pt == Times/16px at 96ppi // Courier/10pt == Courier/13px at 96ppi // // The 2 lines below marked "anchored" have the exact pixel sizes used by // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px. // // All values other than the anchored values were filled in by hand, never // going below 9px, and maintaining a "diagonal" relationship. See for // example the 13s -- they follow a diagonal line through the table. // static int32_t sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = { { 9, 9, 9, 9, 11, 14, 18, 28 }, { 9, 9, 9, 10, 12, 15, 20, 31 }, { 9, 9, 9, 11, 13, 17, 22, 34 }, { 9, 9, 10, 12, 14, 18, 24, 37 }, { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13) { 9, 9, 11, 14, 17, 21, 28, 42 }, { 9, 10, 12, 15, 17, 23, 30, 45 }, { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16) }; // HTML 1 2 3 4 5 6 7 // CSS xxs xs s m l xl xxl // | // user pref #if 0 // // These are the exact pixel values used by WinIE5 at 96ppi. // { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger { ?, 16, 19, 21, 24, 32, 43, ?? } // largest // // HTML 1 2 3 4 5 6 7 // CSS ? ? ? ? ? ? ? ? // // (CSS not tested yet.) // #endif static int32_t sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 }; static int32_t sCSSColumns[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl static int32_t sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7 double dFontSize; if (aFontSizeType == eFontSize_HTML) { aHTMLSize--; // input as 1-7 } if (aHTMLSize < 0) aHTMLSize = 0; else if (aHTMLSize > 6) aHTMLSize = 6; int32_t* column; switch (aFontSizeType) { case eFontSize_HTML: column = sHTMLColumns; break; case eFontSize_CSS: column = sCSSColumns; break; } // Make special call specifically for fonts (needed PrintPreview) int32_t fontSize = nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize); if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax)) { int32_t row = fontSize - sFontSizeTableMin; if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) { dFontSize = nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable[row][column[aHTMLSize]]); } else { dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]); } } else { int32_t factor = sFontSizeFactors[column[aHTMLSize]]; dFontSize = (factor * aBasePointSize) / 100; } if (1.0 < dFontSize) { return (nscoord)dFontSize; } return (nscoord)1; } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ /* static */ nscoord nsRuleNode::FindNextSmallerFontSize(nscoord aFontSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { int32_t index; int32_t indexMin; int32_t indexMax; float relativePosition; nscoord smallerSize; nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning nscoord smallestIndexFontSize; nscoord largestIndexFontSize; nscoord smallerIndexFontSize; nscoord largerIndexFontSize; nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); if (aFontSizeType == eFontSize_HTML) { indexMin = 1; indexMax = 7; } else { indexMin = 0; indexMax = 6; } smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); if (aFontSize > smallestIndexFontSize) { if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table // find largest index smaller than current for (index = indexMax; index >= indexMin; index--) { indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); if (indexFontSize < aFontSize) break; } // set up points beyond table for interpolation purposes if (indexFontSize == smallestIndexFontSize) { smallerIndexFontSize = indexFontSize - onePx; largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } else if (indexFontSize == largestIndexFontSize) { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5); } else { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } // compute the relative position of the parent size between the two closest indexed sizes relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize); // set the new size to have the same relative position between the next smallest two indexed sizes smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize)); } else { // larger than HTML table, drop by 33% smallerSize = NSToCoordRound(float(aFontSize) / 1.5); } } else { // smaller than HTML table, drop by 1px smallerSize = std::max(aFontSize - onePx, onePx); } return smallerSize; } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ /* static */ nscoord nsRuleNode::FindNextLargerFontSize(nscoord aFontSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { int32_t index; int32_t indexMin; int32_t indexMax; float relativePosition; nscoord adjustment; nscoord largerSize; nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning nscoord smallestIndexFontSize; nscoord largestIndexFontSize; nscoord smallerIndexFontSize; nscoord largerIndexFontSize; nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); if (aFontSizeType == eFontSize_HTML) { indexMin = 1; indexMax = 7; } else { indexMin = 0; indexMax = 6; } smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); if (aFontSize > (smallestIndexFontSize - onePx)) { if (aFontSize < largestIndexFontSize) { // larger will be in HTML table // find smallest index larger than current for (index = indexMin; index <= indexMax; index++) { indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); if (indexFontSize > aFontSize) break; } // set up points beyond table for interpolation purposes if (indexFontSize == smallestIndexFontSize) { smallerIndexFontSize = indexFontSize - onePx; largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } else if (indexFontSize == largestIndexFontSize) { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = NSCoordSaturatingMultiply(largestIndexFontSize, 1.5); } else { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } // compute the relative position of the parent size between the two closest indexed sizes relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize); // set the new size to have the same relative position between the next largest two indexed sizes adjustment = NSCoordSaturatingNonnegativeMultiply(largerIndexFontSize - indexFontSize, relativePosition); largerSize = NSCoordSaturatingAdd(indexFontSize, adjustment); } else { // larger than HTML table, increase by 50% largerSize = NSCoordSaturatingMultiply(aFontSize, 1.5); } } else { // smaller than HTML table, increase by 1px largerSize = NSCoordSaturatingAdd(aFontSize, onePx); } return largerSize; } struct SetFontSizeCalcOps : public css::BasicCoordCalcOps, public css::NumbersAlreadyNormalizedOps { // The parameters beyond aValue that we need for CalcLengthWith. const nscoord mParentSize; const nsStyleFont* const mParentFont; nsPresContext* const mPresContext; const bool mAtRoot; bool& mCanStoreInRuleTree; SetFontSizeCalcOps(nscoord aParentSize, const nsStyleFont* aParentFont, nsPresContext* aPresContext, bool aAtRoot, bool& aCanStoreInRuleTree) : mParentSize(aParentSize), mParentFont(aParentFont), mPresContext(aPresContext), mAtRoot(aAtRoot), mCanStoreInRuleTree(aCanStoreInRuleTree) { } result_type ComputeLeafValue(const nsCSSValue& aValue) { nscoord size; if (aValue.IsLengthUnit()) { // Note that font-based length units use the parent's size // unadjusted for scriptlevel changes. A scriptlevel change // between us and the parent is simply ignored. size = CalcLengthWith(aValue, mParentSize, mParentFont, nullptr, mPresContext, mAtRoot, true, mCanStoreInRuleTree); if (!aValue.IsRelativeLengthUnit() && mParentFont->mAllowZoom) { size = nsStyleFont::ZoomText(mPresContext, size); } } else if (eCSSUnit_Percent == aValue.GetUnit()) { mCanStoreInRuleTree = false; // Note that % units use the parent's size unadjusted for scriptlevel // changes. A scriptlevel change between us and the parent is simply // ignored. // aValue.GetPercentValue() may be negative for, e.g., calc(-50%) size = NSCoordSaturatingMultiply(mParentSize, aValue.GetPercentValue()); } else { NS_ABORT_IF_FALSE(false, "unexpected value"); size = mParentSize; } return size; } }; /* static */ void nsRuleNode::SetFontSize(nsPresContext* aPresContext, const nsRuleData* aRuleData, const nsStyleFont* aFont, const nsStyleFont* aParentFont, nscoord* aSize, const nsFont& aSystemFont, nscoord aParentSize, nscoord aScriptLevelAdjustedParentSize, bool aUsedStartStruct, bool aAtRoot, bool& aCanStoreInRuleTree) { // If false, means that *aSize has not been zoomed. If true, means that // *aSize has been zoomed iff aParentFont->mAllowZoom is true. bool sizeIsZoomedAccordingToParent = false; int32_t baseSize = (int32_t) aPresContext-> GetDefaultFont(aFont->mGenericID, aFont->mLanguage)->size; const nsCSSValue* sizeValue = aRuleData->ValueForFontSize(); if (eCSSUnit_Enumerated == sizeValue->GetUnit()) { int32_t value = sizeValue->GetIntValue(); if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) && (value <= NS_STYLE_FONT_SIZE_XXLARGE)) { *aSize = CalcFontPointSize(value, baseSize, aPresContext, eFontSize_CSS); } else if (NS_STYLE_FONT_SIZE_XXXLARGE == value) { // is not specified in CSS, so we don't use eFontSize_CSS. *aSize = CalcFontPointSize(value, baseSize, aPresContext); } else if (NS_STYLE_FONT_SIZE_LARGER == value || NS_STYLE_FONT_SIZE_SMALLER == value) { aCanStoreInRuleTree = false; // Un-zoom so we use the tables correctly. We'll then rezoom due // to the |zoom = true| above. // Note that relative units here use the parent's size unadjusted // for scriptlevel changes. A scriptlevel change between us and the parent // is simply ignored. nscoord parentSize = aParentSize; if (aParentFont->mAllowZoom) { parentSize = nsStyleFont::UnZoomText(aPresContext, parentSize); } if (NS_STYLE_FONT_SIZE_LARGER == value) { *aSize = FindNextLargerFontSize(parentSize, baseSize, aPresContext, eFontSize_CSS); NS_ASSERTION(*aSize >= parentSize, "FindNextLargerFontSize failed"); } else { *aSize = FindNextSmallerFontSize(parentSize, baseSize, aPresContext, eFontSize_CSS); NS_ASSERTION(*aSize < parentSize || parentSize <= nsPresContext::CSSPixelsToAppUnits(1), "FindNextSmallerFontSize failed"); } } else { NS_NOTREACHED("unexpected value"); } } else if (sizeValue->IsLengthUnit() || sizeValue->GetUnit() == eCSSUnit_Percent || sizeValue->IsCalcUnit()) { SetFontSizeCalcOps ops(aParentSize, aParentFont, aPresContext, aAtRoot, aCanStoreInRuleTree); *aSize = css::ComputeCalc(*sizeValue, ops); if (*aSize < 0) { NS_ABORT_IF_FALSE(sizeValue->IsCalcUnit(), "negative lengths and percents should be rejected " "by parser"); *aSize = 0; } // The calc ops will always zoom its result according to the value // of aParentFont->mAllowZoom. sizeIsZoomedAccordingToParent = true; } else if (eCSSUnit_System_Font == sizeValue->GetUnit()) { // this becomes our cascading size *aSize = aSystemFont.size; } else if (eCSSUnit_Inherit == sizeValue->GetUnit() || eCSSUnit_Unset == sizeValue->GetUnit()) { aCanStoreInRuleTree = false; // We apply scriptlevel change for this case, because the default is // to inherit and we don't want explicit "inherit" to differ from the // default. *aSize = aScriptLevelAdjustedParentSize; sizeIsZoomedAccordingToParent = true; } else if (eCSSUnit_Initial == sizeValue->GetUnit()) { // The initial value is 'medium', which has magical sizing based on // the generic font family, so do that here too. *aSize = baseSize; } else { NS_ASSERTION(eCSSUnit_Null == sizeValue->GetUnit(), "What kind of font-size value is this?"); // if aUsedStartStruct is true, then every single property in the // font struct is being set all at once. This means scriptlevel is not // going to have any influence on the font size; there is no need to // do anything here. if (!aUsedStartStruct && aParentSize != aScriptLevelAdjustedParentSize) { // There was no rule affecting the size but the size has been // affected by the parent's size via scriptlevel change. So we cannot // store the data in the rule tree. aCanStoreInRuleTree = false; *aSize = aScriptLevelAdjustedParentSize; sizeIsZoomedAccordingToParent = true; } else { return; } } // We want to zoom the cascaded size so that em-based measurements, // line-heights, etc., work. bool currentlyZoomed = sizeIsZoomedAccordingToParent && aParentFont->mAllowZoom; if (!currentlyZoomed && aFont->mAllowZoom) { *aSize = nsStyleFont::ZoomText(aPresContext, *aSize); } else if (currentlyZoomed && !aFont->mAllowZoom) { *aSize = nsStyleFont::UnZoomText(aPresContext, *aSize); } } static int8_t ClampTo8Bit(int32_t aValue) { if (aValue < -128) return -128; if (aValue > 127) return 127; return int8_t(aValue); } /* static */ void nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, uint8_t aGenericFontID, const nsRuleData* aRuleData, const nsStyleFont* aParentFont, nsStyleFont* aFont, bool aUsedStartStruct, bool& aCanStoreInRuleTree) { bool atRoot = !aContext->GetParent(); // -x-text-zoom: none, inherit, initial bool allowZoom; const nsCSSValue* textZoomValue = aRuleData->ValueForTextZoom(); if (eCSSUnit_Null != textZoomValue->GetUnit()) { if (eCSSUnit_Inherit == textZoomValue->GetUnit()) { allowZoom = aParentFont->mAllowZoom; } else if (eCSSUnit_None == textZoomValue->GetUnit()) { allowZoom = false; } else { MOZ_ASSERT(eCSSUnit_Initial == textZoomValue->GetUnit(), "unexpected unit"); allowZoom = true; } aFont->EnableZoom(aPresContext, allowZoom); } // mLanguage must be set before before any of the CalcLengthWith calls // (direct calls or calls via SetFontSize) for the cases where |aParentFont| // is the same as |aFont|. // // -x-lang: string, inherit // This is not a real CSS property, it is an HTML attribute mapped to CSS. const nsCSSValue* langValue = aRuleData->ValueForLang(); if (eCSSUnit_Ident == langValue->GetUnit()) { nsAutoString lang; langValue->GetStringValue(lang); nsContentUtils::ASCIIToLower(lang); aFont->mLanguage = do_GetAtom(lang); aFont->mExplicitLanguage = true; } const nsFont* defaultVariableFont = aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID, aFont->mLanguage); // -moz-system-font: enum (never inherit!) static_assert( NS_STYLE_FONT_CAPTION == LookAndFeel::eFont_Caption && NS_STYLE_FONT_ICON == LookAndFeel::eFont_Icon && NS_STYLE_FONT_MENU == LookAndFeel::eFont_Menu && NS_STYLE_FONT_MESSAGE_BOX == LookAndFeel::eFont_MessageBox && NS_STYLE_FONT_SMALL_CAPTION == LookAndFeel::eFont_SmallCaption && NS_STYLE_FONT_STATUS_BAR == LookAndFeel::eFont_StatusBar && NS_STYLE_FONT_WINDOW == LookAndFeel::eFont_Window && NS_STYLE_FONT_DOCUMENT == LookAndFeel::eFont_Document && NS_STYLE_FONT_WORKSPACE == LookAndFeel::eFont_Workspace && NS_STYLE_FONT_DESKTOP == LookAndFeel::eFont_Desktop && NS_STYLE_FONT_INFO == LookAndFeel::eFont_Info && NS_STYLE_FONT_DIALOG == LookAndFeel::eFont_Dialog && NS_STYLE_FONT_BUTTON == LookAndFeel::eFont_Button && NS_STYLE_FONT_PULL_DOWN_MENU == LookAndFeel::eFont_PullDownMenu && NS_STYLE_FONT_LIST == LookAndFeel::eFont_List && NS_STYLE_FONT_FIELD == LookAndFeel::eFont_Field, "LookAndFeel.h system-font constants out of sync with nsStyleConsts.h"); // Fall back to defaultVariableFont. nsFont systemFont = *defaultVariableFont; const nsCSSValue* systemFontValue = aRuleData->ValueForSystemFont(); if (eCSSUnit_Enumerated == systemFontValue->GetUnit()) { gfxFontStyle fontStyle; LookAndFeel::FontID fontID = (LookAndFeel::FontID)systemFontValue->GetIntValue(); float devPerCSS = (float)nsPresContext::AppUnitsPerCSSPixel() / aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); if (LookAndFeel::GetFont(fontID, systemFont.name, fontStyle, devPerCSS)) { systemFont.style = fontStyle.style; systemFont.systemFont = fontStyle.systemFont; systemFont.variant = NS_FONT_VARIANT_NORMAL; systemFont.weight = fontStyle.weight; systemFont.stretch = fontStyle.stretch; systemFont.decorations = NS_FONT_DECORATION_NONE; systemFont.size = NSFloatPixelsToAppUnits(fontStyle.size, aPresContext->DeviceContext()-> UnscaledAppUnitsPerDevPixel()); //systemFont.langGroup = fontStyle.langGroup; systemFont.sizeAdjust = fontStyle.sizeAdjust; #ifdef XP_WIN // XXXldb This platform-specific stuff should be in the // LookAndFeel implementation, not here. // XXXzw Should we even still *have* this code? It looks to be making // old, probably obsolete assumptions. if (fontID == LookAndFeel::eFont_Field || fontID == LookAndFeel::eFont_Button || fontID == LookAndFeel::eFont_List) { // As far as I can tell the system default fonts and sizes // on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are // all pre-determined and cannot be changed by either the control panel // or programmatically. // Fields (text fields) // Button and Selects (listboxes/comboboxes) // We use whatever font is defined by the system. Which it appears // (and the assumption is) it is always a proportional font. Then we // always use 2 points smaller than what the browser has defined as // the default proportional font. // Assumption: system defined font is proportional systemFont.size = std::max(defaultVariableFont->size - nsPresContext::CSSPointsToAppUnits(2), 0); } #endif } } // font-family: string list, enum, inherit const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); NS_ASSERTION(eCSSUnit_Enumerated != familyValue->GetUnit(), "system fonts should not be in mFamily anymore"); if (eCSSUnit_Families == familyValue->GetUnit()) { // set the correct font if we are using DocumentFonts OR we are overriding for XUL // MJA: bug 31816 if (aGenericFontID == kGenericFont_NONE) { // only bother appending fallback fonts if this isn't a fallback generic font itself if (!aFont->mFont.name.IsEmpty()) aFont->mFont.name.Append((char16_t)','); // defaultVariableFont.name should always be "serif" or "sans-serif". aFont->mFont.name.Append(defaultVariableFont->name); } aFont->mFont.systemFont = false; // Technically this is redundant with the code below, but it's good // to have since we'll still want it once we get rid of // SetGenericFont (bug 380915). aFont->mGenericID = aGenericFontID; } else if (eCSSUnit_System_Font == familyValue->GetUnit()) { aFont->mFont.name = systemFont.name; aFont->mFont.systemFont = true; aFont->mGenericID = kGenericFont_NONE; } else if (eCSSUnit_Inherit == familyValue->GetUnit() || eCSSUnit_Unset == familyValue->GetUnit()) { aCanStoreInRuleTree = false; aFont->mFont.name = aParentFont->mFont.name; aFont->mFont.systemFont = aParentFont->mFont.systemFont; aFont->mGenericID = aParentFont->mGenericID; } else if (eCSSUnit_Initial == familyValue->GetUnit()) { aFont->mFont.name = defaultVariableFont->name; aFont->mFont.systemFont = defaultVariableFont->systemFont; aFont->mGenericID = kGenericFont_NONE; } // When we're in the loop in SetGenericFont, we must ensure that we // always keep aFont->mFlags set to the correct generic. But we have // to be careful not to touch it when we're called directly from // ComputeFontData, because we could have a start struct. if (aGenericFontID != kGenericFont_NONE) { aFont->mGenericID = aGenericFontID; } // -moz-math-variant: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMathVariant(), aFont->mMathVariant, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, aParentFont->mMathVariant, NS_MATHML_MATHVARIANT_NONE, 0, 0, 0, 0); // -moz-math-display: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMathDisplay(), aFont->mMathDisplay, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, aParentFont->mMathDisplay, NS_MATHML_DISPLAYSTYLE_INLINE, 0, 0, 0, 0); // font-smoothing: enum, inherit, initial SetDiscrete(*aRuleData->ValueForOSXFontSmoothing(), aFont->mFont.smoothing, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, aParentFont->mFont.smoothing, defaultVariableFont->smoothing, 0, 0, 0, 0); // font-style: enum, inherit, initial, -moz-system-font if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { // -moz-math-variant overrides font-style aFont->mFont.style = NS_FONT_STYLE_NORMAL; } else { SetDiscrete(*aRuleData->ValueForFontStyle(), aFont->mFont.style, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.style, defaultVariableFont->style, 0, 0, 0, systemFont.style); } // font-variant: enum, inherit, initial, -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariant(), aFont->mFont.variant, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variant, defaultVariableFont->variant, 0, 0, 0, systemFont.variant); // font-weight: int, enum, inherit, initial, -moz-system-font // special handling for enum const nsCSSValue* weightValue = aRuleData->ValueForFontWeight(); if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { // -moz-math-variant overrides font-weight aFont->mFont.weight = NS_FONT_WEIGHT_NORMAL; } else if (eCSSUnit_Enumerated == weightValue->GetUnit()) { int32_t value = weightValue->GetIntValue(); switch (value) { case NS_STYLE_FONT_WEIGHT_NORMAL: case NS_STYLE_FONT_WEIGHT_BOLD: aFont->mFont.weight = value; break; case NS_STYLE_FONT_WEIGHT_BOLDER: { aCanStoreInRuleTree = false; int32_t inheritedValue = aParentFont->mFont.weight; if (inheritedValue <= 300) { aFont->mFont.weight = 400; } else if (inheritedValue <= 500) { aFont->mFont.weight = 700; } else { aFont->mFont.weight = 900; } break; } case NS_STYLE_FONT_WEIGHT_LIGHTER: { aCanStoreInRuleTree = false; int32_t inheritedValue = aParentFont->mFont.weight; if (inheritedValue < 600) { aFont->mFont.weight = 100; } else if (inheritedValue < 800) { aFont->mFont.weight = 400; } else { aFont->mFont.weight = 700; } break; } } } else SetDiscrete(*weightValue, aFont->mFont.weight, aCanStoreInRuleTree, SETDSC_INTEGER | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.weight, defaultVariableFont->weight, 0, 0, 0, systemFont.weight); // font-stretch: enum, inherit, initial, -moz-system-font SetDiscrete(*aRuleData->ValueForFontStretch(), aFont->mFont.stretch, aCanStoreInRuleTree, SETDSC_SYSTEM_FONT | SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, aParentFont->mFont.stretch, defaultVariableFont->stretch, 0, 0, 0, systemFont.stretch); // Compute scriptlevel, scriptminsize and scriptsizemultiplier now so // they're available for font-size computation. // -moz-script-min-size: length const nsCSSValue* scriptMinSizeValue = aRuleData->ValueForScriptMinSize(); if (scriptMinSizeValue->IsLengthUnit()) { // scriptminsize in font units (em, ex) has to be interpreted relative // to the parent font, or the size definitions are circular and we // aFont->mScriptMinSize = CalcLengthWith(*scriptMinSizeValue, aParentFont->mSize, aParentFont, nullptr, aPresContext, atRoot, true, aCanStoreInRuleTree); } // -moz-script-size-multiplier: factor, inherit, initial SetFactor(*aRuleData->ValueForScriptSizeMultiplier(), aFont->mScriptSizeMultiplier, aCanStoreInRuleTree, aParentFont->mScriptSizeMultiplier, NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER, SETFCT_POSITIVE | SETFCT_UNSET_INHERIT); // -moz-script-level: integer, number, inherit const nsCSSValue* scriptLevelValue = aRuleData->ValueForScriptLevel(); if (eCSSUnit_Integer == scriptLevelValue->GetUnit()) { // "relative" aCanStoreInRuleTree = false; aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + scriptLevelValue->GetIntValue()); } else if (eCSSUnit_Number == scriptLevelValue->GetUnit()) { // "absolute" aFont->mScriptLevel = ClampTo8Bit(int32_t(scriptLevelValue->GetFloatValue())); } else if (eCSSUnit_Auto == scriptLevelValue->GetUnit()) { // auto aCanStoreInRuleTree = false; aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + (aParentFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE ? 1 : 0)); } else if (eCSSUnit_Inherit == scriptLevelValue->GetUnit() || eCSSUnit_Unset == scriptLevelValue->GetUnit()) { aCanStoreInRuleTree = false; aFont->mScriptLevel = aParentFont->mScriptLevel; } else if (eCSSUnit_Initial == scriptLevelValue->GetUnit()) { aFont->mScriptLevel = 0; } // font-kerning: none, enum, inherit, initial, -moz-system-font SetDiscrete(*aRuleData->ValueForFontKerning(), aFont->mFont.kerning, aCanStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.kerning, defaultVariableFont->kerning, 0, 0, 0, systemFont.kerning); // font-synthesis: none, enum (bit field), inherit, initial, -moz-system-font SetDiscrete(*aRuleData->ValueForFontSynthesis(), aFont->mFont.synthesis, aCanStoreInRuleTree, SETDSC_NONE | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.synthesis, defaultVariableFont->synthesis, 0, 0, 0, systemFont.synthesis); // font-variant-alternates: normal, enum (bit field) + functions, inherit, // initial, -moz-system-font const nsCSSValue* variantAlternatesValue = aRuleData->ValueForFontVariantAlternates(); int32_t variantAlternates = 0; switch (variantAlternatesValue->GetUnit()) { case eCSSUnit_Inherit: case eCSSUnit_Unset: aFont->mFont.CopyAlternates(aParentFont->mFont); aCanStoreInRuleTree = false; break; case eCSSUnit_Initial: case eCSSUnit_Normal: aFont->mFont.variantAlternates = 0; aFont->mFont.alternateValues.Clear(); aFont->mFont.featureValueLookup = nullptr; break; case eCSSUnit_Pair: NS_ASSERTION(variantAlternatesValue->GetPairValue().mXValue.GetUnit() == eCSSUnit_Enumerated, "strange unit for variantAlternates"); variantAlternates = variantAlternatesValue->GetPairValue().mXValue.GetIntValue(); aFont->mFont.variantAlternates = variantAlternates; if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { // fetch the feature lookup object from the styleset aFont->mFont.featureValueLookup = aPresContext->StyleSet()->GetFontFeatureValuesLookup(); NS_ASSERTION(variantAlternatesValue->GetPairValue().mYValue.GetUnit() == eCSSUnit_List, "function list not a list value"); nsStyleUtil::ComputeFunctionalAlternates( variantAlternatesValue->GetPairValue().mYValue.GetListValue(), aFont->mFont.alternateValues); } break; default: break; } // font-variant-caps: normal, enum, inherit, initial, -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariantCaps(), aFont->mFont.variantCaps, aCanStoreInRuleTree, SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variantCaps, defaultVariableFont->variantCaps, 0, 0, 0, systemFont.variantCaps); // font-variant-east-asian: normal, enum (bit field), inherit, initial, // -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariantEastAsian(), aFont->mFont.variantEastAsian, aCanStoreInRuleTree, SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variantEastAsian, defaultVariableFont->variantEastAsian, 0, 0, 0, systemFont.variantEastAsian); // font-variant-ligatures: normal, enum (bit field), inherit, initial, // -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariantLigatures(), aFont->mFont.variantLigatures, aCanStoreInRuleTree, SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variantLigatures, defaultVariableFont->variantLigatures, 0, 0, 0, systemFont.variantLigatures); // font-variant-numeric: normal, enum (bit field), inherit, initial, // -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariantNumeric(), aFont->mFont.variantNumeric, aCanStoreInRuleTree, SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variantNumeric, defaultVariableFont->variantNumeric, 0, 0, 0, systemFont.variantNumeric); // font-variant-position: normal, enum, inherit, initial, // -moz-system-font SetDiscrete(*aRuleData->ValueForFontVariantPosition(), aFont->mFont.variantPosition, aCanStoreInRuleTree, SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, aParentFont->mFont.variantPosition, defaultVariableFont->variantPosition, 0, 0, 0, systemFont.variantPosition); // font-feature-settings const nsCSSValue* featureSettingsValue = aRuleData->ValueForFontFeatureSettings(); switch (featureSettingsValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Normal: case eCSSUnit_Initial: aFont->mFont.fontFeatureSettings.Clear(); break; case eCSSUnit_Inherit: case eCSSUnit_Unset: aCanStoreInRuleTree = false; aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings; break; case eCSSUnit_System_Font: aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings; break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: ComputeFontFeatures(featureSettingsValue->GetPairListValue(), aFont->mFont.fontFeatureSettings); break; default: NS_ABORT_IF_FALSE(false, "unexpected value unit"); break; } // font-language-override const nsCSSValue* languageOverrideValue = aRuleData->ValueForFontLanguageOverride(); if (eCSSUnit_Inherit == languageOverrideValue->GetUnit() || eCSSUnit_Unset == languageOverrideValue->GetUnit()) { aCanStoreInRuleTree = false; aFont->mFont.languageOverride = aParentFont->mFont.languageOverride; } else if (eCSSUnit_Normal == languageOverrideValue->GetUnit() || eCSSUnit_Initial == languageOverrideValue->GetUnit()) { aFont->mFont.languageOverride.Truncate(); } else if (eCSSUnit_System_Font == languageOverrideValue->GetUnit()) { aFont->mFont.languageOverride = systemFont.languageOverride; } else if (eCSSUnit_String == languageOverrideValue->GetUnit()) { languageOverrideValue->GetStringValue(aFont->mFont.languageOverride); } // font-size: enum, length, percent, inherit nscoord scriptLevelAdjustedParentSize = aParentFont->mSize; nscoord scriptLevelAdjustedUnconstrainedParentSize; scriptLevelAdjustedParentSize = ComputeScriptLevelSize(aFont, aParentFont, aPresContext, &scriptLevelAdjustedUnconstrainedParentSize); NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize, "If we have a start struct, we should have reset everything coming in here"); SetFontSize(aPresContext, aRuleData, aFont, aParentFont, &aFont->mSize, systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize, aUsedStartStruct, atRoot, aCanStoreInRuleTree); if (aParentFont->mSize == aParentFont->mScriptUnconstrainedSize && scriptLevelAdjustedParentSize == scriptLevelAdjustedUnconstrainedParentSize) { // Fast path: we have not been affected by scriptminsize so we don't // need to call SetFontSize again to compute the // scriptminsize-unconstrained size. This is OK even if we have a // start struct, because if we have a start struct then 'font-size' // was specified and so scriptminsize has no effect. aFont->mScriptUnconstrainedSize = aFont->mSize; } else { SetFontSize(aPresContext, aRuleData, aFont, aParentFont, &aFont->mScriptUnconstrainedSize, systemFont, aParentFont->mScriptUnconstrainedSize, scriptLevelAdjustedUnconstrainedParentSize, aUsedStartStruct, atRoot, aCanStoreInRuleTree); } NS_ASSERTION(aFont->mScriptUnconstrainedSize <= aFont->mSize, "scriptminsize should never be making things bigger"); nscoord fontSize = aFont->mSize; // enforce the user' specified minimum font-size on the value that we expose // (but don't change font-size:0, since that would unhide hidden text) if (fontSize > 0) { nscoord minFontSize = aPresContext->MinFontSize(aFont->mLanguage); if (minFontSize < 0) { minFontSize = 0; } if (fontSize < minFontSize && !aPresContext->IsChrome()) { // override the minimum font-size constraint fontSize = minFontSize; } } aFont->mFont.size = fontSize; // font-size-adjust: number, none, inherit, initial, -moz-system-font const nsCSSValue* sizeAdjustValue = aRuleData->ValueForFontSizeAdjust(); if (eCSSUnit_System_Font == sizeAdjustValue->GetUnit()) { aFont->mFont.sizeAdjust = systemFont.sizeAdjust; } else SetFactor(*sizeAdjustValue, aFont->mFont.sizeAdjust, aCanStoreInRuleTree, aParentFont->mFont.sizeAdjust, 0.0f, SETFCT_NONE | SETFCT_UNSET_INHERIT); } /* static */ void nsRuleNode::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList, nsTArray& aFeatureSettings) { aFeatureSettings.Clear(); for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) { gfxFontFeature feat = {0, 0}; NS_ABORT_IF_FALSE(aFeaturesList->mXValue.GetUnit() == eCSSUnit_String, "unexpected value unit"); // tag is a 4-byte ASCII sequence nsAutoString tag; p->mXValue.GetStringValue(tag); if (tag.Length() != 4) { continue; } // parsing validates that these are ASCII chars // tags are always big-endian feat.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3]; // value NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Integer, "should have found an integer unit"); feat.mValue = p->mYValue.GetIntValue(); aFeatureSettings.AppendElement(feat); } } // This should die (bug 380915). // // SetGenericFont: // - backtrack to an ancestor with the same generic font name (possibly // up to the root where default values come from the presentation context) // - re-apply cascading rules from there without caching intermediate values /* static */ void nsRuleNode::SetGenericFont(nsPresContext* aPresContext, nsStyleContext* aContext, uint8_t aGenericFontID, nsStyleFont* aFont) { // walk up the contexts until a context with the desired generic font nsAutoTArray contextPath; contextPath.AppendElement(aContext); nsStyleContext* higherContext = aContext->GetParent(); while (higherContext) { if (higherContext->StyleFont()->mGenericID == aGenericFontID) { // done walking up the higher contexts break; } contextPath.AppendElement(higherContext); higherContext = higherContext->GetParent(); } // re-apply the cascading rules, starting from the higher context // If we stopped earlier because we reached the root of the style tree, // we will start with the default generic font from the presentation // context. Otherwise we start with the higher context. const nsFont* defaultFont = aPresContext->GetDefaultFont(aGenericFontID, aFont->mLanguage); nsStyleFont parentFont(*defaultFont, aPresContext); if (higherContext) { const nsStyleFont* tmpFont = higherContext->StyleFont(); parentFont = *tmpFont; } *aFont = parentFont; bool dummy; uint32_t fontBit = nsCachedStyleData::GetBitForSID(eStyleStruct_Font); // use placement new[] on the result of alloca() to allocate a // variable-sized stack array, including execution of constructors, // and use an RAII class to run the destructors too. size_t nprops = nsCSSProps::PropertyCountInStruct(eStyleStruct_Font); void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); for (int32_t i = contextPath.Length() - 1; i >= 0; --i) { nsStyleContext* context = contextPath[i]; AutoCSSValueArray dataArray(dataStorage, nprops); nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Font), dataArray.get(), aPresContext, context); ruleData.mValueOffsets[eStyleStruct_Font] = 0; // Trimmed down version of ::WalkRuleTree() to re-apply the style rules // Note that we *do* need to do this for our own data, since what is // in |fontData| in ComputeFontData is only for the rules below // aStartStruct. for (nsRuleNode* ruleNode = context->RuleNode(); ruleNode; ruleNode = ruleNode->GetParent()) { if (ruleNode->mNoneBits & fontBit) // no more font rules on this branch, get out break; nsIStyleRule *rule = ruleNode->GetRule(); if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); } } // Compute the delta from the information that the rules specified // Avoid unnecessary operations in SetFont(). But we care if it's // the final value that we're computing. if (i != 0) ruleData.ValueForFontFamily()->Reset(); ResolveVariableReferences(eStyleStruct_Font, &ruleData, aContext); nsRuleNode::SetFont(aPresContext, context, aGenericFontID, &ruleData, &parentFont, aFont, false, dummy); parentFont = *aFont; } } static bool ExtractGeneric(const nsString& aFamily, bool aGeneric, void *aData) { nsAutoString *data = static_cast(aData); if (aGeneric) { *data = aFamily; return false; // stop enumeration } return true; } const void* nsRuleNode::ComputeFontData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Font, (mPresContext), font, parentFont) // NOTE: The |aRuleDetail| passed in is a little bit conservative due // to the -moz-system-font property. We really don't need to consider // it here in determining whether to cache in the rule tree. However, // we do need to consider it in WalkRuleTree when deciding whether to // walk further up the tree. So this means that when the font struct // is fully specified using *longhand* properties (excluding // -moz-system-font), we won't cache in the rule tree even though we // could. However, it's pretty unlikely authors will do that // (although there is a pretty good chance they'll fully specify it // using the 'font' shorthand). bool useDocumentFonts = mPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts); // See if we are in the chrome // We only need to know this to determine if we have to use the // document fonts (overriding the useDocumentFonts flag). if (!useDocumentFonts && mPresContext->IsChrome()) { // if we are not using document fonts, but this is a XUL document, // then we use the document fonts anyway useDocumentFonts = true; } // Figure out if we are a generic font uint8_t generic = kGenericFont_NONE; // XXXldb What if we would have had a string if we hadn't been doing // the optimization with a non-null aStartStruct? const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); if (eCSSUnit_Families == familyValue->GetUnit()) { familyValue->GetStringValue(font->mFont.name); // XXXldb Do we want to extract the generic for this if it's not only a // generic? nsFont::GetGenericID(font->mFont.name, &generic); // If we aren't allowed to use document fonts, then we are only entitled // to use the user's default variable-width font and fixed-width font if (!useDocumentFonts) { // Extract the generic from the specified font family... nsAutoString genericName; if (!font->mFont.EnumerateFamilies(ExtractGeneric, &genericName)) { // The specified font had a generic family. font->mFont.name = genericName; nsFont::GetGenericID(genericName, &generic); // ... and only use it if it's -moz-fixed or monospace if (generic != kGenericFont_moz_fixed && generic != kGenericFont_monospace) { font->mFont.name.Truncate(); generic = kGenericFont_NONE; } } else { // The specified font did not have a generic family. font->mFont.name.Truncate(); generic = kGenericFont_NONE; } } } // Now compute our font struct if (generic == kGenericFont_NONE) { // continue the normal processing nsRuleNode::SetFont(mPresContext, aContext, generic, aRuleData, parentFont, font, aStartStruct != nullptr, canStoreInRuleTree); } else { // re-calculate the font as a generic font canStoreInRuleTree = false; nsRuleNode::SetGenericFont(mPresContext, aContext, generic, font); } COMPUTE_END_INHERITED(Font, font) } template inline uint32_t ListLength(const T* aList) { uint32_t len = 0; while (aList) { len++; aList = aList->mNext; } return len; } already_AddRefed nsRuleNode::GetShadowData(const nsCSSValueList* aList, nsStyleContext* aContext, bool aIsBoxShadow, bool& aCanStoreInRuleTree) { uint32_t arrayLength = ListLength(aList); NS_ABORT_IF_FALSE(arrayLength > 0, "Non-null text-shadow list, yet we counted 0 items."); nsRefPtr shadowList = new(arrayLength) nsCSSShadowArray(arrayLength); if (!shadowList) return nullptr; nsStyleCoord tempCoord; DebugOnly unitOK; for (nsCSSShadowItem* item = shadowList->ShadowAt(0); aList; aList = aList->mNext, ++item) { NS_ABORT_IF_FALSE(aList->mValue.GetUnit() == eCSSUnit_Array, "expecting a plain array value"); nsCSSValue::Array *arr = aList->mValue.GetArrayValue(); // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT unitOK = SetCoord(arr->Item(0), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mXOffset = tempCoord.GetCoordValue(); unitOK = SetCoord(arr->Item(1), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mYOffset = tempCoord.GetCoordValue(); // Blur radius is optional in the current box-shadow spec if (arr->Item(2).GetUnit() != eCSSUnit_Null) { unitOK = SetCoord(arr->Item(2), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE, aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mRadius = tempCoord.GetCoordValue(); } else { item->mRadius = 0; } // Find the spread radius if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) { unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); item->mSpread = tempCoord.GetCoordValue(); } else { item->mSpread = 0; } if (arr->Item(4).GetUnit() != eCSSUnit_Null) { item->mHasColor = true; // 2nd argument can be bogus since inherit is not a valid color unitOK = SetColor(arr->Item(4), 0, mPresContext, aContext, item->mColor, aCanStoreInRuleTree); NS_ASSERTION(unitOK, "unexpected unit"); } if (aIsBoxShadow && arr->Item(5).GetUnit() == eCSSUnit_Enumerated) { NS_ASSERTION(arr->Item(5).GetIntValue() == NS_STYLE_BOX_SHADOW_INSET, "invalid keyword type for box shadow"); item->mInset = true; } else { item->mInset = false; } } return shadowList.forget(); } const void* nsRuleNode::ComputeTextData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Text, (), text, parentText) // tab-size: integer, inherit SetDiscrete(*aRuleData->ValueForTabSize(), text->mTabSize, canStoreInRuleTree, SETDSC_INTEGER | SETDSC_UNSET_INHERIT, parentText->mTabSize, NS_STYLE_TABSIZE_INITIAL, 0, 0, 0, 0); // letter-spacing: normal, length, inherit SetCoord(*aRuleData->ValueForLetterSpacing(), text->mLetterSpacing, parentText->mLetterSpacing, SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); // text-shadow: none, list, inherit, initial const nsCSSValue* textShadowValue = aRuleData->ValueForTextShadow(); if (textShadowValue->GetUnit() != eCSSUnit_Null) { text->mTextShadow = nullptr; // Don't need to handle none/initial explicitly: The above assignment // takes care of that if (textShadowValue->GetUnit() == eCSSUnit_Inherit || textShadowValue->GetUnit() == eCSSUnit_Unset) { canStoreInRuleTree = false; text->mTextShadow = parentText->mTextShadow; } else if (textShadowValue->GetUnit() == eCSSUnit_List || textShadowValue->GetUnit() == eCSSUnit_ListDep) { // List of arrays text->mTextShadow = GetShadowData(textShadowValue->GetListValue(), aContext, false, canStoreInRuleTree); } } // line-height: normal, number, length, percent, inherit const nsCSSValue* lineHeightValue = aRuleData->ValueForLineHeight(); if (eCSSUnit_Percent == lineHeightValue->GetUnit()) { canStoreInRuleTree = false; // Use |mFont.size| to pick up minimum font size. text->mLineHeight.SetCoordValue( NSToCoordRound(float(aContext->StyleFont()->mFont.size) * lineHeightValue->GetPercentValue())); } else if (eCSSUnit_Initial == lineHeightValue->GetUnit() || eCSSUnit_System_Font == lineHeightValue->GetUnit()) { text->mLineHeight.SetNormalValue(); } else { SetCoord(*lineHeightValue, text->mLineHeight, parentText->mLineHeight, SETCOORD_LEH | SETCOORD_FACTOR | SETCOORD_NORMAL | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); if (lineHeightValue->IsLengthUnit() && !lineHeightValue->IsRelativeLengthUnit()) { nscoord lh = nsStyleFont::ZoomText(mPresContext, text->mLineHeight.GetCoordValue()); canStoreInRuleTree = false; const nsStyleFont *font = aContext->StyleFont(); nscoord minimumFontSize = mPresContext->MinFontSize(font->mLanguage); if (minimumFontSize > 0 && !mPresContext->IsChrome()) { if (font->mSize != 0) { lh = nscoord(float(lh) * float(font->mFont.size) / float(font->mSize)); } else { lh = minimumFontSize; } } text->mLineHeight.SetCoordValue(lh); } } // text-align: enum, string, pair(enum|string), inherit, initial // NOTE: string is not implemented yet. const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); text->mTextAlignTrue = false; if (eCSSUnit_String == textAlignValue->GetUnit()) { NS_NOTYETIMPLEMENTED("align string"); } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() && NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT == textAlignValue->GetIntValue()) { canStoreInRuleTree = false; uint8_t parentAlign = parentText->mTextAlign; text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ? NS_STYLE_TEXT_ALIGN_CENTER : parentAlign; } else { if (eCSSUnit_Pair == textAlignValue->GetUnit()) { // Two values were specified, one must be 'true'. text->mTextAlignTrue = true; const nsCSSValuePair& textAlignValuePair = textAlignValue->GetPairValue(); textAlignValue = &textAlignValuePair.mXValue; if (eCSSUnit_Enumerated == textAlignValue->GetUnit()) { if (textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { textAlignValue = &textAlignValuePair.mYValue; } } else if (eCSSUnit_String == textAlignValue->GetUnit()) { NS_NOTYETIMPLEMENTED("align string"); } } else if (eCSSUnit_Inherit == textAlignValue->GetUnit() || eCSSUnit_Unset == textAlignValue->GetUnit()) { text->mTextAlignTrue = parentText->mTextAlignTrue; } SetDiscrete(*textAlignValue, text->mTextAlign, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mTextAlign, NS_STYLE_TEXT_ALIGN_DEFAULT, 0, 0, 0, 0); } // text-align-last: enum, pair(enum), inherit, initial const nsCSSValue* textAlignLastValue = aRuleData->ValueForTextAlignLast(); text->mTextAlignLastTrue = false; if (eCSSUnit_Pair == textAlignLastValue->GetUnit()) { // Two values were specified, one must be 'true'. text->mTextAlignLastTrue = true; const nsCSSValuePair& textAlignLastValuePair = textAlignLastValue->GetPairValue(); textAlignLastValue = &textAlignLastValuePair.mXValue; if (eCSSUnit_Enumerated == textAlignLastValue->GetUnit()) { if (textAlignLastValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { textAlignLastValue = &textAlignLastValuePair.mYValue; } } } else if (eCSSUnit_Inherit == textAlignLastValue->GetUnit() || eCSSUnit_Unset == textAlignLastValue->GetUnit()) { text->mTextAlignLastTrue = parentText->mTextAlignLastTrue; } SetDiscrete(*textAlignLastValue, text->mTextAlignLast, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mTextAlignLast, NS_STYLE_TEXT_ALIGN_AUTO, 0, 0, 0, 0); // text-indent: length, percent, calc, inherit, initial SetCoord(*aRuleData->ValueForTextIndent(), text->mTextIndent, parentText->mTextIndent, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); // text-transform: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTextTransform(), text->mTextTransform, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mTextTransform, NS_STYLE_TEXT_TRANSFORM_NONE, 0, 0, 0, 0); // white-space: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWhiteSpace(), text->mWhiteSpace, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mWhiteSpace, NS_STYLE_WHITESPACE_NORMAL, 0, 0, 0, 0); // word-break: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWordBreak(), text->mWordBreak, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mWordBreak, NS_STYLE_WORDBREAK_NORMAL, 0, 0, 0, 0); // word-spacing: normal, length, inherit nsStyleCoord tempCoord; const nsCSSValue* wordSpacingValue = aRuleData->ValueForWordSpacing(); if (SetCoord(*wordSpacingValue, tempCoord, nsStyleCoord(parentText->mWordSpacing, nsStyleCoord::CoordConstructor), SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree)) { if (tempCoord.GetUnit() == eStyleUnit_Coord) { text->mWordSpacing = tempCoord.GetCoordValue(); } else if (tempCoord.GetUnit() == eStyleUnit_Normal) { text->mWordSpacing = 0; } else { NS_NOTREACHED("unexpected unit"); } } else { NS_ASSERTION(wordSpacingValue->GetUnit() == eCSSUnit_Null, "unexpected unit"); } // word-wrap: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWordWrap(), text->mWordWrap, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mWordWrap, NS_STYLE_WORDWRAP_NORMAL, 0, 0, 0, 0); // hyphens: enum, inherit, initial SetDiscrete(*aRuleData->ValueForHyphens(), text->mHyphens, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mHyphens, NS_STYLE_HYPHENS_MANUAL, 0, 0, 0, 0); // text-size-adjust: none, auto, inherit, initial SetDiscrete(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust, canStoreInRuleTree, SETDSC_NONE | SETDSC_AUTO | SETDSC_UNSET_INHERIT, parentText->mTextSizeAdjust, NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // initial value NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // auto value NS_STYLE_TEXT_SIZE_ADJUST_NONE, // none value 0, 0); // -moz-text-discard: enum, inherit, initial SetDiscrete(*aRuleData->ValueForControlCharacterVisibility(), text->mControlCharacterVisibility, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mControlCharacterVisibility, NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN, 0, 0, 0, 0); // text-orientation: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTextOrientation(), text->mTextOrientation, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mTextOrientation, NS_STYLE_TEXT_ORIENTATION_AUTO, 0, 0, 0, 0); // text-combine-horizontal: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTextCombineHorizontal(), text->mTextCombineHorizontal, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mTextCombineHorizontal, NS_STYLE_TEXT_COMBINE_HORIZ_NONE, 0, 0, 0, 0); COMPUTE_END_INHERITED(Text, text) } const void* nsRuleNode::ComputeTextResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(TextReset, (), text, parentText) // vertical-align: enum, length, percent, calc, inherit const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign(); if (!SetCoord(*verticalAlignValue, text->mVerticalAlign, parentText->mVerticalAlign, SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC, aContext, mPresContext, canStoreInRuleTree)) { if (eCSSUnit_Initial == verticalAlignValue->GetUnit() || eCSSUnit_Unset == verticalAlignValue->GetUnit()) { text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated); } } // text-decoration-line: enum (bit field), inherit, initial const nsCSSValue* decorationLineValue = aRuleData->ValueForTextDecorationLine(); if (eCSSUnit_Enumerated == decorationLineValue->GetUnit()) { int32_t td = decorationLineValue->GetIntValue(); text->mTextDecorationLine = td; if (td & NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) { bool underlineLinks = mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks); if (underlineLinks) { text->mTextDecorationLine |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; } else { text->mTextDecorationLine &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; } } } else if (eCSSUnit_Inherit == decorationLineValue->GetUnit()) { canStoreInRuleTree = false; text->mTextDecorationLine = parentText->mTextDecorationLine; } else if (eCSSUnit_Initial == decorationLineValue->GetUnit() || eCSSUnit_Unset == decorationLineValue->GetUnit()) { text->mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE; } // text-decoration-color: color, string, enum, inherit, initial const nsCSSValue* decorationColorValue = aRuleData->ValueForTextDecorationColor(); nscolor decorationColor; if (eCSSUnit_Inherit == decorationColorValue->GetUnit()) { canStoreInRuleTree = false; if (parentContext) { bool isForeground; parentText->GetDecorationColor(decorationColor, isForeground); if (isForeground) { text->SetDecorationColor(parentContext->StyleColor()->mColor); } else { text->SetDecorationColor(decorationColor); } } else { text->SetDecorationColorToForeground(); } } else if (eCSSUnit_EnumColor == decorationColorValue->GetUnit() && decorationColorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) { text->SetDecorationColorToForeground(); } else if (SetColor(*decorationColorValue, 0, mPresContext, aContext, decorationColor, canStoreInRuleTree)) { text->SetDecorationColor(decorationColor); } else if (eCSSUnit_Initial == decorationColorValue->GetUnit() || eCSSUnit_Unset == decorationColorValue->GetUnit() || eCSSUnit_Enumerated == decorationColorValue->GetUnit()) { NS_ABORT_IF_FALSE(eCSSUnit_Enumerated != decorationColorValue->GetUnit() || decorationColorValue->GetIntValue() == NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, "unexpected enumerated value"); text->SetDecorationColorToForeground(); } // text-decoration-style: enum, inherit, initial const nsCSSValue* decorationStyleValue = aRuleData->ValueForTextDecorationStyle(); if (eCSSUnit_Enumerated == decorationStyleValue->GetUnit()) { text->SetDecorationStyle(decorationStyleValue->GetIntValue()); } else if (eCSSUnit_Inherit == decorationStyleValue->GetUnit()) { text->SetDecorationStyle(parentText->GetDecorationStyle()); canStoreInRuleTree = false; } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit() || eCSSUnit_Unset == decorationStyleValue->GetUnit()) { text->SetDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID); } // text-overflow: enum, string, pair(enum|string), inherit, initial const nsCSSValue* textOverflowValue = aRuleData->ValueForTextOverflow(); if (eCSSUnit_Initial == textOverflowValue->GetUnit() || eCSSUnit_Unset == textOverflowValue->GetUnit()) { text->mTextOverflow = nsStyleTextOverflow(); } else if (eCSSUnit_Inherit == textOverflowValue->GetUnit()) { canStoreInRuleTree = false; text->mTextOverflow = parentText->mTextOverflow; } else if (eCSSUnit_Enumerated == textOverflowValue->GetUnit()) { // A single enumerated value. SetDiscrete(*textOverflowValue, text->mTextOverflow.mRight.mType, canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextOverflow.mRight.mType, NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); text->mTextOverflow.mRight.mString.Truncate(); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; text->mTextOverflow.mLeft.mString.Truncate(); text->mTextOverflow.mLogicalDirections = true; } else if (eCSSUnit_String == textOverflowValue->GetUnit()) { // A single string value. text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; textOverflowValue->GetStringValue(text->mTextOverflow.mRight.mString); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; text->mTextOverflow.mLeft.mString.Truncate(); text->mTextOverflow.mLogicalDirections = true; } else if (eCSSUnit_Pair == textOverflowValue->GetUnit()) { // Two values were specified. text->mTextOverflow.mLogicalDirections = false; const nsCSSValuePair& textOverflowValuePair = textOverflowValue->GetPairValue(); const nsCSSValue *textOverflowLeftValue = &textOverflowValuePair.mXValue; if (eCSSUnit_Enumerated == textOverflowLeftValue->GetUnit()) { SetDiscrete(*textOverflowLeftValue, text->mTextOverflow.mLeft.mType, canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextOverflow.mLeft.mType, NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); text->mTextOverflow.mLeft.mString.Truncate(); } else if (eCSSUnit_String == textOverflowLeftValue->GetUnit()) { textOverflowLeftValue->GetStringValue(text->mTextOverflow.mLeft.mString); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_STRING; } const nsCSSValue *textOverflowRightValue = &textOverflowValuePair.mYValue; if (eCSSUnit_Enumerated == textOverflowRightValue->GetUnit()) { SetDiscrete(*textOverflowRightValue, text->mTextOverflow.mRight.mType, canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextOverflow.mRight.mType, NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); text->mTextOverflow.mRight.mString.Truncate(); } else if (eCSSUnit_String == textOverflowRightValue->GetUnit()) { textOverflowRightValue->GetStringValue(text->mTextOverflow.mRight.mString); text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; } } // unicode-bidi: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentText->mUnicodeBidi, NS_STYLE_UNICODE_BIDI_NORMAL, 0, 0, 0, 0); COMPUTE_END_RESET(TextReset, text) } const void* nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(UserInterface, (), ui, parentUI) // cursor: enum, url, inherit const nsCSSValue* cursorValue = aRuleData->ValueForCursor(); nsCSSUnit cursorUnit = cursorValue->GetUnit(); if (cursorUnit != eCSSUnit_Null) { delete [] ui->mCursorArray; ui->mCursorArray = nullptr; ui->mCursorArrayLength = 0; if (cursorUnit == eCSSUnit_Inherit || cursorUnit == eCSSUnit_Unset) { canStoreInRuleTree = false; ui->mCursor = parentUI->mCursor; ui->CopyCursorArrayFrom(*parentUI); } else if (cursorUnit == eCSSUnit_Initial) { ui->mCursor = NS_STYLE_CURSOR_AUTO; } else { // The parser will never create a list that is *all* URL values -- // that's invalid. NS_ABORT_IF_FALSE(cursorUnit == eCSSUnit_List || cursorUnit == eCSSUnit_ListDep, nsPrintfCString("unrecognized cursor unit %d", cursorUnit).get()); const nsCSSValueList* list = cursorValue->GetListValue(); const nsCSSValueList* list2 = list; nsIDocument* doc = aContext->PresContext()->Document(); uint32_t arrayLength = 0; for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext) if (list->mValue.GetArrayValue()->Item(0).GetImageValue(doc)) ++arrayLength; if (arrayLength != 0) { ui->mCursorArray = new nsCursorImage[arrayLength]; if (ui->mCursorArray) { ui->mCursorArrayLength = arrayLength; for (nsCursorImage *item = ui->mCursorArray; list2->mValue.GetUnit() == eCSSUnit_Array; list2 = list2->mNext) { nsCSSValue::Array *arr = list2->mValue.GetArrayValue(); imgIRequest *req = arr->Item(0).GetImageValue(doc); if (req) { item->SetImage(req); if (arr->Item(1).GetUnit() != eCSSUnit_Null) { item->mHaveHotspot = true; item->mHotspotX = arr->Item(1).GetFloatValue(), item->mHotspotY = arr->Item(2).GetFloatValue(); } ++item; } } } } NS_ASSERTION(list, "Must have non-array value at the end"); NS_ASSERTION(list->mValue.GetUnit() == eCSSUnit_Enumerated, "Unexpected fallback value at end of cursor list"); ui->mCursor = list->mValue.GetIntValue(); } } // user-input: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUserInput(), ui->mUserInput, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentUI->mUserInput, NS_STYLE_USER_INPUT_AUTO, 0, 0, 0, 0); // user-modify: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUserModify(), ui->mUserModify, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentUI->mUserModify, NS_STYLE_USER_MODIFY_READ_ONLY, 0, 0, 0, 0); // user-focus: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUserFocus(), ui->mUserFocus, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentUI->mUserFocus, NS_STYLE_USER_FOCUS_NONE, 0, 0, 0, 0); COMPUTE_END_INHERITED(UserInterface, ui) } const void* nsRuleNode::ComputeUIResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(UIReset, (), ui, parentUI) // user-select: enum, inherit, initial SetDiscrete(*aRuleData->ValueForUserSelect(), ui->mUserSelect, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentUI->mUserSelect, NS_STYLE_USER_SELECT_AUTO, 0, 0, 0, 0); // ime-mode: enum, inherit, initial SetDiscrete(*aRuleData->ValueForImeMode(), ui->mIMEMode, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentUI->mIMEMode, NS_STYLE_IME_MODE_AUTO, 0, 0, 0, 0); // force-broken-image-icons: integer, inherit, initial SetDiscrete(*aRuleData->ValueForForceBrokenImageIcon(), ui->mForceBrokenImageIcon, canStoreInRuleTree, SETDSC_INTEGER | SETDSC_UNSET_INITIAL, parentUI->mForceBrokenImageIcon, 0, 0, 0, 0, 0); // -moz-window-shadow: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWindowShadow(), ui->mWindowShadow, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentUI->mWindowShadow, NS_STYLE_WINDOW_SHADOW_DEFAULT, 0, 0, 0, 0); COMPUTE_END_RESET(UIReset, ui) } // Information about each transition or animation property that is // constant. struct TransitionPropInfo { nsCSSProperty property; // Location of the count of the property's computed value. uint32_t nsStyleDisplay::* sdCount; }; // Each property's index in this array must match its index in the // mutable array |transitionPropData| below. static const TransitionPropInfo transitionPropInfo[4] = { { eCSSProperty_transition_delay, &nsStyleDisplay::mTransitionDelayCount }, { eCSSProperty_transition_duration, &nsStyleDisplay::mTransitionDurationCount }, { eCSSProperty_transition_property, &nsStyleDisplay::mTransitionPropertyCount }, { eCSSProperty_transition_timing_function, &nsStyleDisplay::mTransitionTimingFunctionCount }, }; // Each property's index in this array must match its index in the // mutable array |animationPropData| below. static const TransitionPropInfo animationPropInfo[8] = { { eCSSProperty_animation_delay, &nsStyleDisplay::mAnimationDelayCount }, { eCSSProperty_animation_duration, &nsStyleDisplay::mAnimationDurationCount }, { eCSSProperty_animation_name, &nsStyleDisplay::mAnimationNameCount }, { eCSSProperty_animation_timing_function, &nsStyleDisplay::mAnimationTimingFunctionCount }, { eCSSProperty_animation_direction, &nsStyleDisplay::mAnimationDirectionCount }, { eCSSProperty_animation_fill_mode, &nsStyleDisplay::mAnimationFillModeCount }, { eCSSProperty_animation_play_state, &nsStyleDisplay::mAnimationPlayStateCount }, { eCSSProperty_animation_iteration_count, &nsStyleDisplay::mAnimationIterationCountCount }, }; // Information about each transition or animation property that changes // during ComputeDisplayData. struct TransitionPropData { const nsCSSValueList *list; nsCSSUnit unit; uint32_t num; }; static uint32_t CountTransitionProps(const TransitionPropInfo* aInfo, TransitionPropData* aData, size_t aLength, nsStyleDisplay* aDisplay, const nsStyleDisplay* aParentDisplay, const nsRuleData* aRuleData, bool& aCanStoreInRuleTree) { // The four transition properties or eight animation properties are // stored in nsCSSDisplay in a single array for all properties. The // number of transitions is equal to the number of items in the // longest property's value. Properties that have fewer values than // the longest are filled in by repeating the list. However, this // repetition does not extend the computed value of that particular // property (for purposes of inheritance, or, in our code, for when // other properties are overridden by a more specific rule). // But actually, since the spec isn't clear yet, we'll fully compute // all of them (so we can switch easily later), but only care about // the ones up to the number of items for 'transition-property', per // http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html . // Transitions are difficult to handle correctly because of this. For // example, we need to handle scenarios such as: // * a more general rule specifies transition-property: a, b, c; // * a more specific rule overrides as transition-property: d; // // If only the general rule applied, we would fill in the extra // properties (duration, delay, etc) with initial values to create 3 // fully-specified transitions. But when the more specific rule // applies, we should only create a single transition. In order to do // this we need to remember which properties were explicitly specified // and which ones were just filled in with initial values to get a // fully-specified transition, which we do by remembering the number // of values for each property. uint32_t numTransitions = 0; for (size_t i = 0; i < aLength; ++i) { const TransitionPropInfo& info = aInfo[i]; TransitionPropData& data = aData[i]; // cache whether any of the properties are specified as 'inherit' so // we can use it below const nsCSSValue& value = *aRuleData->ValueFor(info.property); data.unit = value.GetUnit(); data.list = (value.GetUnit() == eCSSUnit_List || value.GetUnit() == eCSSUnit_ListDep) ? value.GetListValue() : nullptr; // General algorithm to determine how many total transitions we need // to build. For each property: // - if there is no value specified in for the property in // displayData, use the values from the start struct, but only if // they were explicitly specified // - if there is a value specified for the property in displayData: // - if the value is 'inherit', count the number of values for // that property are specified by the parent, but only those // that were explicitly specified // - otherwise, count the number of values specified in displayData // calculate number of elements if (data.unit == eCSSUnit_Inherit) { data.num = aParentDisplay->*(info.sdCount); aCanStoreInRuleTree = false; } else if (data.list) { data.num = ListLength(data.list); } else { data.num = aDisplay->*(info.sdCount); } if (data.num > numTransitions) numTransitions = data.num; } return numTransitions; } static void ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult) { switch (aValue.GetUnit()) { case eCSSUnit_Enumerated: aResult = nsTimingFunction(aValue.GetIntValue()); break; case eCSSUnit_Cubic_Bezier: { nsCSSValue::Array* array = aValue.GetArrayValue(); NS_ASSERTION(array && array->Count() == 4, "Need 4 control points"); aResult = nsTimingFunction(array->Item(0).GetFloatValue(), array->Item(1).GetFloatValue(), array->Item(2).GetFloatValue(), array->Item(3).GetFloatValue()); } break; case eCSSUnit_Steps: { nsCSSValue::Array* array = aValue.GetArrayValue(); NS_ASSERTION(array && array->Count() == 2, "Need 2 items"); NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer, "unexpected first value"); NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Enumerated && (array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START || array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END), "unexpected second value"); nsTimingFunction::Type type = (array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END) ? nsTimingFunction::StepEnd : nsTimingFunction::StepStart; aResult = nsTimingFunction(type, array->Item(0).GetIntValue()); } break; default: NS_NOTREACHED("Invalid transition property unit"); } } const void* nsRuleNode::ComputeDisplayData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Display, (), display, parentDisplay) // We may have ended up with aStartStruct's values of mDisplay and // mFloats, but those may not be correct if our style data overrides // its position or float properties. Reset to mOriginalDisplay and // mOriginalFloats; it if turns out we still need the display/floats // adjustments we'll do them below. display->mDisplay = display->mOriginalDisplay; display->mFloats = display->mOriginalFloats; // Each property's index in this array must match its index in the // const array |transitionPropInfo| above. TransitionPropData transitionPropData[4]; TransitionPropData& delay = transitionPropData[0]; TransitionPropData& duration = transitionPropData[1]; TransitionPropData& property = transitionPropData[2]; TransitionPropData& timingFunction = transitionPropData[3]; #define FOR_ALL_TRANSITION_PROPS(var_) \ for (uint32_t var_ = 0; var_ < 4; ++var_) // CSS Transitions uint32_t numTransitions = CountTransitionProps(transitionPropInfo, transitionPropData, ArrayLength(transitionPropData), display, parentDisplay, aRuleData, canStoreInRuleTree); display->mTransitions.SetLength(numTransitions); FOR_ALL_TRANSITION_PROPS(p) { const TransitionPropInfo& i = transitionPropInfo[p]; TransitionPropData& d = transitionPropData[p]; display->*(i.sdCount) = d.num; } // Fill in the transitions we just allocated with the appropriate values. for (uint32_t i = 0; i < numTransitions; ++i) { nsTransition *transition = &display->mTransitions[i]; if (i >= delay.num) { transition->SetDelay(display->mTransitions[i % delay.num].GetDelay()); } else if (delay.unit == eCSSUnit_Inherit) { // FIXME (Bug 522599) (for all transition properties): write a test that // detects when this was wrong for i >= delay.num if parent had // count for this property not equal to length NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDelayCount, "delay.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); transition->SetDelay(parentDisplay->mTransitions[i].GetDelay()); } else if (delay.unit == eCSSUnit_Initial || delay.unit == eCSSUnit_Unset) { transition->SetDelay(0.0); } else if (delay.list) { switch (delay.list->mValue.GetUnit()) { case eCSSUnit_Seconds: transition->SetDelay(PR_MSEC_PER_SEC * delay.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: transition->SetDelay(delay.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid delay unit"); } } if (i >= duration.num) { transition->SetDuration( display->mTransitions[i % duration.num].GetDuration()); } else if (duration.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDurationCount, "duration.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); transition->SetDuration(parentDisplay->mTransitions[i].GetDuration()); } else if (duration.unit == eCSSUnit_Initial || duration.unit == eCSSUnit_Unset) { transition->SetDuration(0.0); } else if (duration.list) { switch (duration.list->mValue.GetUnit()) { case eCSSUnit_Seconds: transition->SetDuration(PR_MSEC_PER_SEC * duration.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: transition->SetDuration(duration.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid duration unit"); } } if (i >= property.num) { transition->CopyPropertyFrom(display->mTransitions[i % property.num]); } else if (property.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionPropertyCount, "property.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); transition->CopyPropertyFrom(parentDisplay->mTransitions[i]); } else if (property.unit == eCSSUnit_Initial || property.unit == eCSSUnit_Unset) { transition->SetProperty(eCSSPropertyExtra_all_properties); } else if (property.unit == eCSSUnit_None) { transition->SetProperty(eCSSPropertyExtra_no_properties); } else if (property.list) { const nsCSSValue &val = property.list->mValue; if (val.GetUnit() == eCSSUnit_Ident) { nsDependentString propertyStr(property.list->mValue.GetStringBufferValue()); nsCSSProperty prop = nsCSSProps::LookupProperty(propertyStr, nsCSSProps::eEnabled); if (prop == eCSSProperty_UNKNOWN) { transition->SetUnknownProperty(propertyStr); } else { transition->SetProperty(prop); } } else { NS_ABORT_IF_FALSE(val.GetUnit() == eCSSUnit_All, nsPrintfCString("Invalid transition property unit %d", val.GetUnit()).get()); transition->SetProperty(eCSSPropertyExtra_all_properties); } } if (i >= timingFunction.num) { transition->SetTimingFunction( display->mTransitions[i % timingFunction.num].GetTimingFunction()); } else if (timingFunction.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionTimingFunctionCount, "timingFunction.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); transition->SetTimingFunction( parentDisplay->mTransitions[i].GetTimingFunction()); } else if (timingFunction.unit == eCSSUnit_Initial || timingFunction.unit == eCSSUnit_Unset) { transition->SetTimingFunction( nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); } else if (timingFunction.list) { ComputeTimingFunction(timingFunction.list->mValue, transition->TimingFunctionSlot()); } FOR_ALL_TRANSITION_PROPS(p) { const TransitionPropInfo& info = transitionPropInfo[p]; TransitionPropData& d = transitionPropData[p]; // if we're at the end of the list, start at the beginning and repeat // until we're out of transitions to populate if (d.list) { d.list = d.list->mNext ? d.list->mNext : aRuleData->ValueFor(info.property)->GetListValue(); } } } // Each property's index in this array must match its index in the // const array |animationPropInfo| above. TransitionPropData animationPropData[8]; TransitionPropData& animDelay = animationPropData[0]; TransitionPropData& animDuration = animationPropData[1]; TransitionPropData& animName = animationPropData[2]; TransitionPropData& animTimingFunction = animationPropData[3]; TransitionPropData& animDirection = animationPropData[4]; TransitionPropData& animFillMode = animationPropData[5]; TransitionPropData& animPlayState = animationPropData[6]; TransitionPropData& animIterationCount = animationPropData[7]; #define FOR_ALL_ANIMATION_PROPS(var_) \ for (uint32_t var_ = 0; var_ < 8; ++var_) // CSS Animations. uint32_t numAnimations = CountTransitionProps(animationPropInfo, animationPropData, ArrayLength(animationPropData), display, parentDisplay, aRuleData, canStoreInRuleTree); display->mAnimations.SetLength(numAnimations); FOR_ALL_ANIMATION_PROPS(p) { const TransitionPropInfo& i = animationPropInfo[p]; TransitionPropData& d = animationPropData[p]; display->*(i.sdCount) = d.num; } // Fill in the animations we just allocated with the appropriate values. for (uint32_t i = 0; i < numAnimations; ++i) { nsAnimation *animation = &display->mAnimations[i]; if (i >= animDelay.num) { animation->SetDelay(display->mAnimations[i % animDelay.num].GetDelay()); } else if (animDelay.unit == eCSSUnit_Inherit) { // FIXME (Bug 522599) (for all animation properties): write a test that // detects when this was wrong for i >= animDelay.num if parent had // count for this property not equal to length NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDelayCount, "animDelay.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetDelay(parentDisplay->mAnimations[i].GetDelay()); } else if (animDelay.unit == eCSSUnit_Initial || animDelay.unit == eCSSUnit_Unset) { animation->SetDelay(0.0); } else if (animDelay.list) { switch (animDelay.list->mValue.GetUnit()) { case eCSSUnit_Seconds: animation->SetDelay(PR_MSEC_PER_SEC * animDelay.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: animation->SetDelay(animDelay.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid delay unit"); } } if (i >= animDuration.num) { animation->SetDuration( display->mAnimations[i % animDuration.num].GetDuration()); } else if (animDuration.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDurationCount, "animDuration.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetDuration(parentDisplay->mAnimations[i].GetDuration()); } else if (animDuration.unit == eCSSUnit_Initial || animDuration.unit == eCSSUnit_Unset) { animation->SetDuration(0.0); } else if (animDuration.list) { switch (animDuration.list->mValue.GetUnit()) { case eCSSUnit_Seconds: animation->SetDuration(PR_MSEC_PER_SEC * animDuration.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: animation->SetDuration(animDuration.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid duration unit"); } } if (i >= animName.num) { animation->SetName(display->mAnimations[i % animName.num].GetName()); } else if (animName.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationNameCount, "animName.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetName(parentDisplay->mAnimations[i].GetName()); } else if (animName.unit == eCSSUnit_Initial || animName.unit == eCSSUnit_Unset) { animation->SetName(EmptyString()); } else if (animName.list) { switch (animName.list->mValue.GetUnit()) { case eCSSUnit_Ident: { nsDependentString nameStr(animName.list->mValue.GetStringBufferValue()); animation->SetName(nameStr); break; } case eCSSUnit_None: { animation->SetName(EmptyString()); break; } default: NS_ABORT_IF_FALSE(false, nsPrintfCString("Invalid animation-name unit %d", animName.list->mValue.GetUnit()).get()); } } if (i >= animTimingFunction.num) { animation->SetTimingFunction( display->mAnimations[i % animTimingFunction.num].GetTimingFunction()); } else if (animTimingFunction.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationTimingFunctionCount, "animTimingFunction.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetTimingFunction( parentDisplay->mAnimations[i].GetTimingFunction()); } else if (animTimingFunction.unit == eCSSUnit_Initial || animTimingFunction.unit == eCSSUnit_Unset) { animation->SetTimingFunction( nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); } else if (animTimingFunction.list) { ComputeTimingFunction(animTimingFunction.list->mValue, animation->TimingFunctionSlot()); } if (i >= animDirection.num) { animation->SetDirection(display->mAnimations[i % animDirection.num].GetDirection()); } else if (animDirection.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDirectionCount, "animDirection.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetDirection(parentDisplay->mAnimations[i].GetDirection()); } else if (animDirection.unit == eCSSUnit_Initial || animDirection.unit == eCSSUnit_Unset) { animation->SetDirection(NS_STYLE_ANIMATION_DIRECTION_NORMAL); } else if (animDirection.list) { NS_ABORT_IF_FALSE(animDirection.list->mValue.GetUnit() == eCSSUnit_Enumerated, nsPrintfCString("Invalid animation-direction unit %d", animDirection.list->mValue.GetUnit()).get()); animation->SetDirection(animDirection.list->mValue.GetIntValue()); } if (i >= animFillMode.num) { animation->SetFillMode(display->mAnimations[i % animFillMode.num].GetFillMode()); } else if (animFillMode.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationFillModeCount, "animFillMode.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetFillMode(parentDisplay->mAnimations[i].GetFillMode()); } else if (animFillMode.unit == eCSSUnit_Initial || animFillMode.unit == eCSSUnit_Unset) { animation->SetFillMode(NS_STYLE_ANIMATION_FILL_MODE_NONE); } else if (animFillMode.list) { NS_ABORT_IF_FALSE(animFillMode.list->mValue.GetUnit() == eCSSUnit_Enumerated, nsPrintfCString("Invalid animation-fill-mode unit %d", animFillMode.list->mValue.GetUnit()).get()); animation->SetFillMode(animFillMode.list->mValue.GetIntValue()); } if (i >= animPlayState.num) { animation->SetPlayState(display->mAnimations[i % animPlayState.num].GetPlayState()); } else if (animPlayState.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationPlayStateCount, "animPlayState.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetPlayState(parentDisplay->mAnimations[i].GetPlayState()); } else if (animPlayState.unit == eCSSUnit_Initial || animPlayState.unit == eCSSUnit_Unset) { animation->SetPlayState(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING); } else if (animPlayState.list) { NS_ABORT_IF_FALSE(animPlayState.list->mValue.GetUnit() == eCSSUnit_Enumerated, nsPrintfCString("Invalid animation-play-state unit %d", animPlayState.list->mValue.GetUnit()).get()); animation->SetPlayState(animPlayState.list->mValue.GetIntValue()); } if (i >= animIterationCount.num) { animation->SetIterationCount(display->mAnimations[i % animIterationCount.num].GetIterationCount()); } else if (animIterationCount.unit == eCSSUnit_Inherit) { NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationIterationCountCount, "animIterationCount.num computed incorrectly"); NS_ABORT_IF_FALSE(!canStoreInRuleTree, "should have made canStoreInRuleTree false above"); animation->SetIterationCount(parentDisplay->mAnimations[i].GetIterationCount()); } else if (animIterationCount.unit == eCSSUnit_Initial || animIterationCount.unit == eCSSUnit_Unset) { animation->SetIterationCount(1.0f); } else if (animIterationCount.list) { switch(animIterationCount.list->mValue.GetUnit()) { case eCSSUnit_Enumerated: NS_ABORT_IF_FALSE(animIterationCount.list->mValue.GetIntValue() == NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE, "unexpected value"); animation->SetIterationCount(NS_IEEEPositiveInfinity()); break; case eCSSUnit_Number: animation->SetIterationCount( animIterationCount.list->mValue.GetFloatValue()); break; default: NS_ABORT_IF_FALSE(false, "unexpected animation-iteration-count unit"); } } FOR_ALL_ANIMATION_PROPS(p) { const TransitionPropInfo& info = animationPropInfo[p]; TransitionPropData& d = animationPropData[p]; // if we're at the end of the list, start at the beginning and repeat // until we're out of animations to populate if (d.list) { d.list = d.list->mNext ? d.list->mNext : aRuleData->ValueFor(info.property)->GetListValue(); } } } // opacity: factor, inherit, initial SetFactor(*aRuleData->ValueForOpacity(), display->mOpacity, canStoreInRuleTree, parentDisplay->mOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // display: enum, inherit, initial SetDiscrete(*aRuleData->ValueForDisplay(), display->mDisplay, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mDisplay, NS_STYLE_DISPLAY_INLINE, 0, 0, 0, 0); // mix-blend-mode: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMixBlendMode(), display->mMixBlendMode, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mMixBlendMode, NS_STYLE_BLEND_NORMAL, 0, 0, 0, 0); // Backup original display value for calculation of a hypothetical // box (CSS2 10.6.4/10.6.5), in addition to getting our style data right later. // See nsHTMLReflowState::CalculateHypotheticalBox display->mOriginalDisplay = display->mDisplay; // appearance: enum, inherit, initial SetDiscrete(*aRuleData->ValueForAppearance(), display->mAppearance, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mAppearance, NS_THEME_NONE, 0, 0, 0, 0); // binding: url, none, inherit const nsCSSValue* bindingValue = aRuleData->ValueForBinding(); if (eCSSUnit_URL == bindingValue->GetUnit()) { mozilla::css::URLValue* url = bindingValue->GetURLStructValue(); NS_ASSERTION(url, "What's going on here?"); if (MOZ_LIKELY(url->GetURI())) { display->mBinding = url; } else { display->mBinding = nullptr; } } else if (eCSSUnit_None == bindingValue->GetUnit() || eCSSUnit_Initial == bindingValue->GetUnit() || eCSSUnit_Unset == bindingValue->GetUnit()) { display->mBinding = nullptr; } else if (eCSSUnit_Inherit == bindingValue->GetUnit()) { canStoreInRuleTree = false; display->mBinding = parentDisplay->mBinding; } // position: enum, inherit, initial SetDiscrete(*aRuleData->ValueForPosition(), display->mPosition, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mPosition, NS_STYLE_POSITION_STATIC, 0, 0, 0, 0); // clear: enum, inherit, initial SetDiscrete(*aRuleData->ValueForClear(), display->mBreakType, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mBreakType, NS_STYLE_CLEAR_NONE, 0, 0, 0, 0); // temp fix for bug 24000 // Map 'auto' and 'avoid' to false, and 'always', 'left', and // 'right' to true. // "A conforming user agent may interpret the values 'left' and // 'right' as 'always'." - CSS2.1, section 13.3.1 const nsCSSValue* breakBeforeValue = aRuleData->ValueForPageBreakBefore(); if (eCSSUnit_Enumerated == breakBeforeValue->GetUnit()) { display->mBreakBefore = (NS_STYLE_PAGE_BREAK_AVOID != breakBeforeValue->GetIntValue() && NS_STYLE_PAGE_BREAK_AUTO != breakBeforeValue->GetIntValue()); } else if (eCSSUnit_Initial == breakBeforeValue->GetUnit() || eCSSUnit_Unset == breakBeforeValue->GetUnit()) { display->mBreakBefore = false; } else if (eCSSUnit_Inherit == breakBeforeValue->GetUnit()) { canStoreInRuleTree = false; display->mBreakBefore = parentDisplay->mBreakBefore; } const nsCSSValue* breakAfterValue = aRuleData->ValueForPageBreakAfter(); if (eCSSUnit_Enumerated == breakAfterValue->GetUnit()) { display->mBreakAfter = (NS_STYLE_PAGE_BREAK_AVOID != breakAfterValue->GetIntValue() && NS_STYLE_PAGE_BREAK_AUTO != breakAfterValue->GetIntValue()); } else if (eCSSUnit_Initial == breakAfterValue->GetUnit() || eCSSUnit_Unset == breakAfterValue->GetUnit()) { display->mBreakAfter = false; } else if (eCSSUnit_Inherit == breakAfterValue->GetUnit()) { canStoreInRuleTree = false; display->mBreakAfter = parentDisplay->mBreakAfter; } // end temp fix // page-break-inside: enum, inherit, initial SetDiscrete(*aRuleData->ValueForPageBreakInside(), display->mBreakInside, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mBreakInside, NS_STYLE_PAGE_BREAK_AUTO, 0, 0, 0, 0); // touch-action: none, auto, enum, inherit, initial SetDiscrete(*aRuleData->ValueForTouchAction(), display->mTouchAction, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_AUTO | SETDSC_NONE | SETDSC_UNSET_INITIAL, parentDisplay->mTouchAction, NS_STYLE_TOUCH_ACTION_AUTO, NS_STYLE_TOUCH_ACTION_AUTO, NS_STYLE_TOUCH_ACTION_NONE, 0, 0); // float: enum, inherit, initial SetDiscrete(*aRuleData->ValueForFloat(), display->mFloats, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mFloats, NS_STYLE_FLOAT_NONE, 0, 0, 0, 0); // Save mFloats in mOriginalFloats in case we need it later display->mOriginalFloats = display->mFloats; // overflow-x: enum, inherit, initial SetDiscrete(*aRuleData->ValueForOverflowX(), display->mOverflowX, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mOverflowX, NS_STYLE_OVERFLOW_VISIBLE, 0, 0, 0, 0); // overflow-y: enum, inherit, initial SetDiscrete(*aRuleData->ValueForOverflowY(), display->mOverflowY, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mOverflowY, NS_STYLE_OVERFLOW_VISIBLE, 0, 0, 0, 0); // CSS3 overflow-x and overflow-y require some fixup as well in some // cases. NS_STYLE_OVERFLOW_VISIBLE and NS_STYLE_OVERFLOW_CLIP are // meaningful only when used in both dimensions. if (display->mOverflowX != display->mOverflowY && (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE || display->mOverflowX == NS_STYLE_OVERFLOW_CLIP || display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE || display->mOverflowY == NS_STYLE_OVERFLOW_CLIP)) { // We can't store in the rule tree since a more specific rule might // change these conditions. canStoreInRuleTree = false; // NS_STYLE_OVERFLOW_CLIP is a deprecated value, so if it's specified // in only one dimension, convert it to NS_STYLE_OVERFLOW_HIDDEN. if (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP) display->mOverflowX = NS_STYLE_OVERFLOW_HIDDEN; if (display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) display->mOverflowY = NS_STYLE_OVERFLOW_HIDDEN; // If 'visible' is specified but doesn't match the other dimension, it // turns into 'auto'. if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) display->mOverflowX = NS_STYLE_OVERFLOW_AUTO; if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) display->mOverflowY = NS_STYLE_OVERFLOW_AUTO; } SetDiscrete(*aRuleData->ValueForResize(), display->mResize, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mResize, NS_STYLE_RESIZE_NONE, 0, 0, 0, 0); // clip property: length, auto, inherit const nsCSSValue* clipValue = aRuleData->ValueForClip(); switch (clipValue->GetUnit()) { case eCSSUnit_Inherit: canStoreInRuleTree = false; display->mClipFlags = parentDisplay->mClipFlags; display->mClip = parentDisplay->mClip; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_Auto: display->mClipFlags = NS_STYLE_CLIP_AUTO; display->mClip.SetRect(0,0,0,0); break; case eCSSUnit_Null: break; case eCSSUnit_Rect: { const nsCSSRect& clipRect = clipValue->GetRectValue(); display->mClipFlags = NS_STYLE_CLIP_RECT; if (clipRect.mTop.GetUnit() == eCSSUnit_Auto) { display->mClip.y = 0; display->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO; } else if (clipRect.mTop.IsLengthUnit()) { display->mClip.y = CalcLength(clipRect.mTop, aContext, mPresContext, canStoreInRuleTree); } if (clipRect.mBottom.GetUnit() == eCSSUnit_Auto) { // Setting to NS_MAXSIZE for the 'auto' case ensures that // the clip rect is nonempty. It is important that mClip be // nonempty if the actual clip rect could be nonempty. display->mClip.height = NS_MAXSIZE; display->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO; } else if (clipRect.mBottom.IsLengthUnit()) { display->mClip.height = CalcLength(clipRect.mBottom, aContext, mPresContext, canStoreInRuleTree) - display->mClip.y; } if (clipRect.mLeft.GetUnit() == eCSSUnit_Auto) { display->mClip.x = 0; display->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO; } else if (clipRect.mLeft.IsLengthUnit()) { display->mClip.x = CalcLength(clipRect.mLeft, aContext, mPresContext, canStoreInRuleTree); } if (clipRect.mRight.GetUnit() == eCSSUnit_Auto) { // Setting to NS_MAXSIZE for the 'auto' case ensures that // the clip rect is nonempty. It is important that mClip be // nonempty if the actual clip rect could be nonempty. display->mClip.width = NS_MAXSIZE; display->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO; } else if (clipRect.mRight.IsLengthUnit()) { display->mClip.width = CalcLength(clipRect.mRight, aContext, mPresContext, canStoreInRuleTree) - display->mClip.x; } break; } default: NS_ABORT_IF_FALSE(false, "unrecognized clip unit"); } if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { // CSS2 9.7 specifies display type corrections dealing with 'float' // and 'position'. Since generated content can't be floated or // positioned, we can deal with it here. if (nsCSSPseudoElements::firstLetter == aContext->GetPseudo()) { // a non-floating first-letter must be inline // XXX this fix can go away once bug 103189 is fixed correctly // Note that we reset mOriginalDisplay to enforce the invariant that it equals mDisplay if we're not positioned or floating. display->mOriginalDisplay = display->mDisplay = NS_STYLE_DISPLAY_INLINE; // We can't cache the data in the rule tree since if a more specific // rule has 'float: left' we'll end up with the wrong 'display' // property. canStoreInRuleTree = false; } if (display->IsAbsolutelyPositionedStyle()) { // 1) if position is 'absolute' or 'fixed' then display must be // block-level and float must be 'none' EnsureBlockDisplay(display->mDisplay); display->mFloats = NS_STYLE_FLOAT_NONE; // Note that it's OK to cache this struct in the ruletree // because it's fine as-is for any style context that points to // it directly, and any use of it as aStartStruct (e.g. if a // more specific rule sets "position: static") will use // mOriginalDisplay and mOriginalFloats, which we have carefully // not changed. } else if (display->mFloats != NS_STYLE_FLOAT_NONE) { // 2) if float is not none, and display is not none, then we must // set a block-level 'display' type per CSS2.1 section 9.7. EnsureBlockDisplay(display->mDisplay); // Note that it's OK to cache this struct in the ruletree // because it's fine as-is for any style context that points to // it directly, and any use of it as aStartStruct (e.g. if a // more specific rule sets "float: none") will use // mOriginalDisplay, which we have carefully not changed. } } /* Convert the nsCSSValueList into an nsTArray. */ const nsCSSValue* transformValue = aRuleData->ValueForTransform(); switch (transformValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: display->mSpecifiedTransform = nullptr; break; case eCSSUnit_Inherit: display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform; canStoreInRuleTree = false; break; case eCSSUnit_SharedList: { nsCSSValueSharedList* list = transformValue->GetSharedListValue(); nsCSSValueList* head = list->mHead; MOZ_ASSERT(head, "transform list must have at least one item"); // can get a _None in here from transform animation if (head->mValue.GetUnit() == eCSSUnit_None) { NS_ABORT_IF_FALSE(head->mNext == nullptr, "none must be alone"); display->mSpecifiedTransform = nullptr; } else { display->mSpecifiedTransform = list; } break; } default: NS_ABORT_IF_FALSE(false, "unrecognized transform unit"); } /* Convert the nsCSSValueList into a will-change bitfield for fast lookup */ const nsCSSValue* willChangeValue = aRuleData->ValueForWillChange(); switch (willChangeValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_List: case eCSSUnit_ListDep: { display->mWillChange.Clear(); display->mWillChangeBitField = 0; for (const nsCSSValueList* item = willChangeValue->GetListValue(); item; item = item->mNext) { if (item->mValue.UnitHasStringValue()) { nsAutoString buffer; item->mValue.GetStringValue(buffer); if (buffer.EqualsLiteral("transform")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM; } if (buffer.EqualsLiteral("opacity")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY; } if (buffer.EqualsLiteral("scroll-position")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL; } display->mWillChange.AppendElement(buffer); } } break; } case eCSSUnit_Inherit: display->mWillChange = parentDisplay->mWillChange; display->mWillChangeBitField = parentDisplay->mWillChangeBitField; canStoreInRuleTree = false; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_Auto: display->mWillChange.Clear(); display->mWillChangeBitField = 0; break; default: MOZ_ASSERT(false, "unrecognized will-change unit"); } /* Convert -moz-transform-origin. */ const nsCSSValue* transformOriginValue = aRuleData->ValueForTransformOrigin(); if (transformOriginValue->GetUnit() != eCSSUnit_Null) { const nsCSSValue& valX = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mXValue : *transformOriginValue; const nsCSSValue& valY = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mYValue : *transformOriginValue; const nsCSSValue& valZ = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mZValue : *transformOriginValue; mozilla::DebugOnly cX = SetCoord(valX, display->mTransformOrigin[0], parentDisplay->mTransformOrigin[0], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); mozilla::DebugOnly cY = SetCoord(valY, display->mTransformOrigin[1], parentDisplay->mTransformOrigin[1], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); if (valZ.GetUnit() == eCSSUnit_Null) { // Null for the z component means a 0 translation, not // unspecified, as we have already checked the triplet // value for Null. display->mTransformOrigin[2].SetCoordValue(0); } else { mozilla::DebugOnly cZ = SetCoord(valZ, display->mTransformOrigin[2], parentDisplay->mTransformOrigin[2], SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); NS_ABORT_IF_FALSE(cY == cZ, "changed one but not the other"); } NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other"); NS_ASSERTION(cX, "Malformed -moz-transform-origin parse!"); } const nsCSSValue* perspectiveOriginValue = aRuleData->ValueForPerspectiveOrigin(); if (perspectiveOriginValue->GetUnit() != eCSSUnit_Null) { mozilla::DebugOnly result = SetPairCoords(*perspectiveOriginValue, display->mPerspectiveOrigin[0], display->mPerspectiveOrigin[1], parentDisplay->mPerspectiveOrigin[0], parentDisplay->mPerspectiveOrigin[1], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); NS_ASSERTION(result, "Malformed -moz-perspective-origin parse!"); } SetCoord(*aRuleData->ValueForPerspective(), display->mChildPerspective, parentDisplay->mChildPerspective, SETCOORD_LAH | SETCOORD_INITIAL_ZERO | SETCOORD_NONE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetDiscrete(*aRuleData->ValueForBackfaceVisibility(), display->mBackfaceVisibility, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mBackfaceVisibility, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE, 0, 0, 0, 0); // transform-style: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTransformStyle(), display->mTransformStyle, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mTransformStyle, NS_STYLE_TRANSFORM_STYLE_FLAT, 0, 0, 0, 0); // orient: enum, inherit, initial SetDiscrete(*aRuleData->ValueForOrient(), display->mOrient, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mOrient, NS_STYLE_ORIENT_AUTO, 0, 0, 0, 0); COMPUTE_END_RESET(Display, display) } const void* nsRuleNode::ComputeVisibilityData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Visibility, (mPresContext), visibility, parentVisibility) // IMPORTANT: No properties in this struct have lengths in them. We // depend on this since CalcLengthWith can call StyleVisibility() // to get the language for resolving fonts! // direction: enum, inherit, initial SetDiscrete(*aRuleData->ValueForDirection(), visibility->mDirection, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentVisibility->mDirection, (GET_BIDI_OPTION_DIRECTION(mPresContext->GetBidi()) == IBMBIDI_TEXTDIRECTION_RTL) ? NS_STYLE_DIRECTION_RTL : NS_STYLE_DIRECTION_LTR, 0, 0, 0, 0); // visibility: enum, inherit, initial SetDiscrete(*aRuleData->ValueForVisibility(), visibility->mVisible, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentVisibility->mVisible, NS_STYLE_VISIBILITY_VISIBLE, 0, 0, 0, 0); // pointer-events: enum, inherit, initial SetDiscrete(*aRuleData->ValueForPointerEvents(), visibility->mPointerEvents, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentVisibility->mPointerEvents, NS_STYLE_POINTER_EVENTS_AUTO, 0, 0, 0, 0); // writing-mode: enum, inherit, initial SetDiscrete(*aRuleData->ValueForWritingMode(), visibility->mWritingMode, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentVisibility->mWritingMode, NS_STYLE_WRITING_MODE_HORIZONTAL_TB, 0, 0, 0, 0); // image-orientation: enum, inherit, initial const nsCSSValue* orientation = aRuleData->ValueForImageOrientation(); if (orientation->GetUnit() == eCSSUnit_Inherit || orientation->GetUnit() == eCSSUnit_Unset) { canStoreInRuleTree = false; visibility->mImageOrientation = parentVisibility->mImageOrientation; } else if (orientation->GetUnit() == eCSSUnit_Initial) { visibility->mImageOrientation = nsStyleImageOrientation(); } else if (orientation->IsAngularUnit()) { double angle = orientation->GetAngleValueInRadians(); visibility->mImageOrientation = nsStyleImageOrientation::CreateAsAngleAndFlip(angle, false); } else if (orientation->GetUnit() == eCSSUnit_Array) { const nsCSSValue::Array* array = orientation->GetArrayValue(); MOZ_ASSERT(array->Item(0).IsAngularUnit(), "First image-orientation value is not an angle"); MOZ_ASSERT(array->Item(1).GetUnit() == eCSSUnit_Enumerated && array->Item(1).GetIntValue() == NS_STYLE_IMAGE_ORIENTATION_FLIP, "Second image-orientation value is not 'flip'"); double angle = array->Item(0).GetAngleValueInRadians(); visibility->mImageOrientation = nsStyleImageOrientation::CreateAsAngleAndFlip(angle, true); } else if (orientation->GetUnit() == eCSSUnit_Enumerated) { switch (orientation->GetIntValue()) { case NS_STYLE_IMAGE_ORIENTATION_FLIP: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFlip(); break; case NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFromImage(); break; default: NS_NOTREACHED("Invalid image-orientation enumerated value"); } } else { MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); } COMPUTE_END_INHERITED(Visibility, visibility) } const void* nsRuleNode::ComputeColorData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Color, (mPresContext), color, parentColor) // color: color, string, inherit // Special case for currentColor. According to CSS3, setting color to 'currentColor' // should behave as if it is inherited const nsCSSValue* colorValue = aRuleData->ValueForColor(); if ((colorValue->GetUnit() == eCSSUnit_EnumColor && colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) || colorValue->GetUnit() == eCSSUnit_Unset) { color->mColor = parentColor->mColor; canStoreInRuleTree = false; } else if (colorValue->GetUnit() == eCSSUnit_Initial) { color->mColor = mPresContext->DefaultColor(); } else { SetColor(*colorValue, parentColor->mColor, mPresContext, aContext, color->mColor, canStoreInRuleTree); } COMPUTE_END_INHERITED(Color, color) } // information about how to compute values for background-* properties template struct BackgroundItemComputer { }; template <> struct BackgroundItemComputer { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, uint8_t& aComputedValue, bool& aCanStoreInRuleTree) { SetDiscrete(aSpecifiedValue->mValue, aComputedValue, aCanStoreInRuleTree, SETDSC_ENUMERATED, uint8_t(0), 0, 0, 0, 0, 0); } }; template <> struct BackgroundItemComputer { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValuePairList* aSpecifiedValue, nsStyleBackground::Repeat& aComputedValue, bool& aCanStoreInRuleTree) { NS_ASSERTION(aSpecifiedValue->mXValue.GetUnit() == eCSSUnit_Enumerated && (aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Enumerated || aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null), "Invalid unit"); bool hasContraction = true; uint8_t value = aSpecifiedValue->mXValue.GetIntValue(); switch (value) { case NS_STYLE_BG_REPEAT_REPEAT_X: aComputedValue.mXRepeat = NS_STYLE_BG_REPEAT_REPEAT; aComputedValue.mYRepeat = NS_STYLE_BG_REPEAT_NO_REPEAT; break; case NS_STYLE_BG_REPEAT_REPEAT_Y: aComputedValue.mXRepeat = NS_STYLE_BG_REPEAT_NO_REPEAT; aComputedValue.mYRepeat = NS_STYLE_BG_REPEAT_REPEAT; break; default: aComputedValue.mXRepeat = value; hasContraction = false; break; } if (hasContraction) { NS_ASSERTION(aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null, "Invalid unit."); return; } switch (aSpecifiedValue->mYValue.GetUnit()) { case eCSSUnit_Null: aComputedValue.mYRepeat = aComputedValue.mXRepeat; break; case eCSSUnit_Enumerated: value = aSpecifiedValue->mYValue.GetIntValue(); NS_ASSERTION(value == NS_STYLE_BG_REPEAT_NO_REPEAT || value == NS_STYLE_BG_REPEAT_REPEAT, "Unexpected value"); aComputedValue.mYRepeat = value; break; default: NS_NOTREACHED("Unexpected CSS value"); break; } } }; template <> struct BackgroundItemComputer { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, nsStyleImage& aComputedValue, bool& aCanStoreInRuleTree) { SetStyleImage(aStyleContext, aSpecifiedValue->mValue, aComputedValue, aCanStoreInRuleTree); } }; /* Helper function for * BackgroundItemComputer * It computes a single PositionCoord from an nsCSSValue object * (contained in a list). */ typedef nsStyleBackground::Position::PositionCoord PositionCoord; static void ComputeBackgroundPositionCoord(nsStyleContext* aStyleContext, const nsCSSValue& aEdge, const nsCSSValue& aOffset, PositionCoord* aResult, bool& aCanStoreInRuleTree) { if (eCSSUnit_Percent == aOffset.GetUnit()) { aResult->mLength = 0; aResult->mPercent = aOffset.GetPercentValue(); aResult->mHasPercent = true; } else if (aOffset.IsLengthUnit()) { aResult->mLength = CalcLength(aOffset, aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); aResult->mPercent = 0.0f; aResult->mHasPercent = false; } else if (aOffset.IsCalcUnit()) { LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); nsRuleNode::ComputedCalc vals = ComputeCalc(aOffset, ops); aResult->mLength = vals.mLength; aResult->mPercent = vals.mPercent; aResult->mHasPercent = ops.mHasPercent; } else { aResult->mLength = 0; aResult->mPercent = 0.0f; aResult->mHasPercent = false; NS_ASSERTION(aOffset.GetUnit() == eCSSUnit_Null, "unexpected unit"); } if (eCSSUnit_Enumerated == aEdge.GetUnit()) { int sign; if (aEdge.GetIntValue() & (NS_STYLE_BG_POSITION_BOTTOM | NS_STYLE_BG_POSITION_RIGHT)) { sign = -1; } else { sign = 1; } aResult->mPercent = GetFloatFromBoxPosition(aEdge.GetIntValue()) + sign * aResult->mPercent; aResult->mLength = sign * aResult->mLength; aResult->mHasPercent = true; } else { NS_ASSERTION(eCSSUnit_Null == aEdge.GetUnit(), "unexpected unit"); } } template <> struct BackgroundItemComputer { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, nsStyleBackground::Position& aComputedValue, bool& aCanStoreInRuleTree) { NS_ASSERTION(aSpecifiedValue->mValue.GetUnit() == eCSSUnit_Array, "bg-position not an array"); nsRefPtr bgPositionArray = aSpecifiedValue->mValue.GetArrayValue(); const nsCSSValue &xEdge = bgPositionArray->Item(0); const nsCSSValue &xOffset = bgPositionArray->Item(1); const nsCSSValue &yEdge = bgPositionArray->Item(2); const nsCSSValue &yOffset = bgPositionArray->Item(3); NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || eCSSUnit_Null == xEdge.GetUnit()) && (eCSSUnit_Enumerated == yEdge.GetUnit() || eCSSUnit_Null == yEdge.GetUnit()) && eCSSUnit_Enumerated != xOffset.GetUnit() && eCSSUnit_Enumerated != yOffset.GetUnit(), "Invalid background position"); ComputeBackgroundPositionCoord(aStyleContext, xEdge, xOffset, &aComputedValue.mXPosition, aCanStoreInRuleTree); ComputeBackgroundPositionCoord(aStyleContext, yEdge, yOffset, &aComputedValue.mYPosition, aCanStoreInRuleTree); } }; struct BackgroundSizeAxis { nsCSSValue nsCSSValuePairList::* specified; nsStyleBackground::Size::Dimension nsStyleBackground::Size::* result; uint8_t nsStyleBackground::Size::* type; }; static const BackgroundSizeAxis gBGSizeAxes[] = { { &nsCSSValuePairList::mXValue, &nsStyleBackground::Size::mWidth, &nsStyleBackground::Size::mWidthType }, { &nsCSSValuePairList::mYValue, &nsStyleBackground::Size::mHeight, &nsStyleBackground::Size::mHeightType } }; template <> struct BackgroundItemComputer { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValuePairList* aSpecifiedValue, nsStyleBackground::Size& aComputedValue, bool& aCanStoreInRuleTree) { nsStyleBackground::Size &size = aComputedValue; for (const BackgroundSizeAxis *axis = gBGSizeAxes, *axis_end = ArrayEnd(gBGSizeAxes); axis < axis_end; ++axis) { const nsCSSValue &specified = aSpecifiedValue->*(axis->specified); if (eCSSUnit_Auto == specified.GetUnit()) { size.*(axis->type) = nsStyleBackground::Size::eAuto; } else if (eCSSUnit_Enumerated == specified.GetUnit()) { static_assert(nsStyleBackground::Size::eContain == NS_STYLE_BG_SIZE_CONTAIN && nsStyleBackground::Size::eCover == NS_STYLE_BG_SIZE_COVER, "background size constants out of sync"); NS_ABORT_IF_FALSE(specified.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || specified.GetIntValue() == NS_STYLE_BG_SIZE_COVER, "invalid enumerated value for size coordinate"); size.*(axis->type) = specified.GetIntValue(); } else if (eCSSUnit_Null == specified.GetUnit()) { NS_ABORT_IF_FALSE(axis == gBGSizeAxes + 1, "null allowed only as height value, and only " "for contain/cover/initial/inherit"); #ifdef DEBUG { const nsCSSValue &widthValue = aSpecifiedValue->mXValue; NS_ABORT_IF_FALSE(widthValue.GetUnit() != eCSSUnit_Inherit && widthValue.GetUnit() != eCSSUnit_Initial && widthValue.GetUnit() != eCSSUnit_Unset, "initial/inherit/unset should already have been handled"); NS_ABORT_IF_FALSE(widthValue.GetUnit() == eCSSUnit_Enumerated && (widthValue.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || widthValue.GetIntValue() == NS_STYLE_BG_SIZE_COVER), "null height value not corresponding to allowable " "non-null width value"); } #endif size.*(axis->type) = size.mWidthType; } else if (eCSSUnit_Percent == specified.GetUnit()) { (size.*(axis->result)).mLength = 0; (size.*(axis->result)).mPercent = specified.GetPercentValue(); (size.*(axis->result)).mHasPercent = true; size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; } else if (specified.IsLengthUnit()) { (size.*(axis->result)).mLength = CalcLength(specified, aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); (size.*(axis->result)).mPercent = 0.0f; (size.*(axis->result)).mHasPercent = false; size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; } else { NS_ABORT_IF_FALSE(specified.IsCalcUnit(), "unexpected unit"); LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aCanStoreInRuleTree); nsRuleNode::ComputedCalc vals = ComputeCalc(specified, ops); (size.*(axis->result)).mLength = vals.mLength; (size.*(axis->result)).mPercent = vals.mPercent; (size.*(axis->result)).mHasPercent = ops.mHasPercent; size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; } } NS_ABORT_IF_FALSE(size.mWidthType < nsStyleBackground::Size::eDimensionType_COUNT, "bad width type"); NS_ABORT_IF_FALSE(size.mHeightType < nsStyleBackground::Size::eDimensionType_COUNT, "bad height type"); NS_ABORT_IF_FALSE((size.mWidthType != nsStyleBackground::Size::eContain && size.mWidthType != nsStyleBackground::Size::eCover) || size.mWidthType == size.mHeightType, "contain/cover apply to both dimensions or to neither"); } }; template static void SetBackgroundList(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, const nsAutoTArray &aParentLayers, ComputedValueItem nsStyleBackground::Layer::* aResultLocation, ComputedValueItem aInitialValue, uint32_t aParentItemCount, uint32_t& aItemCount, uint32_t& aMaxItemCount, bool& aRebuild, bool& aCanStoreInRuleTree) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aRebuild = true; aCanStoreInRuleTree = false; aLayers.EnsureLengthAtLeast(aParentItemCount); aItemCount = aParentItemCount; for (uint32_t i = 0; i < aParentItemCount; ++i) { aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; } break; case eCSSUnit_Initial: case eCSSUnit_Unset: aRebuild = true; aItemCount = 1; aLayers[0].*aResultLocation = aInitialValue; break; case eCSSUnit_List: case eCSSUnit_ListDep: { aRebuild = true; aItemCount = 0; const nsCSSValueList* item = aValue.GetListValue(); do { NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null && item->mValue.GetUnit() != eCSSUnit_Inherit && item->mValue.GetUnit() != eCSSUnit_Initial && item->mValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); ++aItemCount; aLayers.EnsureLengthAtLeast(aItemCount); BackgroundItemComputer ::ComputeValue(aStyleContext, item, aLayers[aItemCount-1].*aResultLocation, aCanStoreInRuleTree); item = item->mNext; } while (item); break; } default: NS_ABORT_IF_FALSE(false, nsPrintfCString("unexpected unit %d", aValue.GetUnit()).get()); } if (aItemCount > aMaxItemCount) aMaxItemCount = aItemCount; } template static void SetBackgroundPairList(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, const nsAutoTArray &aParentLayers, ComputedValueItem nsStyleBackground::Layer::* aResultLocation, ComputedValueItem aInitialValue, uint32_t aParentItemCount, uint32_t& aItemCount, uint32_t& aMaxItemCount, bool& aRebuild, bool& aCanStoreInRuleTree) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aRebuild = true; aCanStoreInRuleTree = false; aLayers.EnsureLengthAtLeast(aParentItemCount); aItemCount = aParentItemCount; for (uint32_t i = 0; i < aParentItemCount; ++i) { aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; } break; case eCSSUnit_Initial: case eCSSUnit_Unset: aRebuild = true; aItemCount = 1; aLayers[0].*aResultLocation = aInitialValue; break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { aRebuild = true; aItemCount = 0; const nsCSSValuePairList* item = aValue.GetPairListValue(); do { NS_ASSERTION(item->mXValue.GetUnit() != eCSSUnit_Inherit && item->mXValue.GetUnit() != eCSSUnit_Initial && item->mXValue.GetUnit() != eCSSUnit_Unset && item->mYValue.GetUnit() != eCSSUnit_Inherit && item->mYValue.GetUnit() != eCSSUnit_Initial && item->mYValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); ++aItemCount; aLayers.EnsureLengthAtLeast(aItemCount); BackgroundItemComputer ::ComputeValue(aStyleContext, item, aLayers[aItemCount-1].*aResultLocation, aCanStoreInRuleTree); item = item->mNext; } while (item); break; } default: NS_ABORT_IF_FALSE(false, nsPrintfCString("unexpected unit %d", aValue.GetUnit()).get()); } if (aItemCount > aMaxItemCount) aMaxItemCount = aItemCount; } template static void FillBackgroundList(nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, ComputedValueItem nsStyleBackground::Layer::* aResultLocation, uint32_t aItemCount, uint32_t aFillCount) { NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length"); for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount; ++sourceLayer, ++destLayer) { aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation; } } const void* nsRuleNode::ComputeBackgroundData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Background, (), bg, parentBG) // background-color: color, string, inherit const nsCSSValue* backColorValue = aRuleData->ValueForBackgroundColor(); if (eCSSUnit_Initial == backColorValue->GetUnit() || eCSSUnit_Unset == backColorValue->GetUnit()) { bg->mBackgroundColor = NS_RGBA(0, 0, 0, 0); } else if (!SetColor(*backColorValue, parentBG->mBackgroundColor, mPresContext, aContext, bg->mBackgroundColor, canStoreInRuleTree)) { NS_ASSERTION(eCSSUnit_Null == backColorValue->GetUnit(), "unexpected color unit"); } uint32_t maxItemCount = 1; bool rebuild = false; // background-image: url (stored as image), none, inherit [list] nsStyleImage initialImage; SetBackgroundList(aContext, *aRuleData->ValueForBackgroundImage(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mImage, initialImage, parentBG->mImageCount, bg->mImageCount, maxItemCount, rebuild, canStoreInRuleTree); // background-repeat: enum, inherit, initial [pair list] nsStyleBackground::Repeat initialRepeat; initialRepeat.SetInitialValues(); SetBackgroundPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mRepeat, initialRepeat, parentBG->mRepeatCount, bg->mRepeatCount, maxItemCount, rebuild, canStoreInRuleTree); // background-attachment: enum, inherit, initial [list] SetBackgroundList(aContext, *aRuleData->ValueForBackgroundAttachment(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mAttachment, uint8_t(NS_STYLE_BG_ATTACHMENT_SCROLL), parentBG->mAttachmentCount, bg->mAttachmentCount, maxItemCount, rebuild, canStoreInRuleTree); // background-clip: enum, inherit, initial [list] SetBackgroundList(aContext, *aRuleData->ValueForBackgroundClip(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mClip, uint8_t(NS_STYLE_BG_CLIP_BORDER), parentBG->mClipCount, bg->mClipCount, maxItemCount, rebuild, canStoreInRuleTree); // background-inline-policy: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBackgroundInlinePolicy(), bg->mBackgroundInlinePolicy, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentBG->mBackgroundInlinePolicy, NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, 0, 0, 0, 0); // background-blend-mode: enum, inherit, initial [list] SetBackgroundList(aContext, *aRuleData->ValueForBackgroundBlendMode(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mBlendMode, uint8_t(NS_STYLE_BLEND_NORMAL), parentBG->mBlendModeCount, bg->mBlendModeCount, maxItemCount, rebuild, canStoreInRuleTree); // background-origin: enum, inherit, initial [list] SetBackgroundList(aContext, *aRuleData->ValueForBackgroundOrigin(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mOrigin, uint8_t(NS_STYLE_BG_ORIGIN_PADDING), parentBG->mOriginCount, bg->mOriginCount, maxItemCount, rebuild, canStoreInRuleTree); // background-position: enum, length, percent (flags), inherit [pair list] nsStyleBackground::Position initialPosition; initialPosition.SetInitialValues(); SetBackgroundList(aContext, *aRuleData->ValueForBackgroundPosition(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mPosition, initialPosition, parentBG->mPositionCount, bg->mPositionCount, maxItemCount, rebuild, canStoreInRuleTree); // background-size: enum, length, auto, inherit, initial [pair list] nsStyleBackground::Size initialSize; initialSize.SetInitialValues(); SetBackgroundPairList(aContext, *aRuleData->ValueForBackgroundSize(), bg->mLayers, parentBG->mLayers, &nsStyleBackground::Layer::mSize, initialSize, parentBG->mSizeCount, bg->mSizeCount, maxItemCount, rebuild, canStoreInRuleTree); if (rebuild) { // Delete any extra items. We need to keep layers in which any // property was specified. bg->mLayers.TruncateLength(maxItemCount); uint32_t fillCount = bg->mImageCount; FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mImage, bg->mImageCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mRepeat, bg->mRepeatCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mAttachment, bg->mAttachmentCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mClip, bg->mClipCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mBlendMode, bg->mBlendModeCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mOrigin, bg->mOriginCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mPosition, bg->mPositionCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mSize, bg->mSizeCount, fillCount); } // Now that the dust has settled, register the images with the document for (uint32_t i = 0; i < bg->mImageCount; ++i) bg->mLayers[i].TrackImages(aContext->PresContext()); COMPUTE_END_RESET(Background, bg) } const void* nsRuleNode::ComputeMarginData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Margin, (), margin, parentMargin) // margin: length, percent, auto, inherit nsStyleCoord coord; nsCSSRect ourMargin; ourMargin.mTop = *aRuleData->ValueForMarginTop(); ourMargin.mRight = *aRuleData->ValueForMarginRightValue(); ourMargin.mBottom = *aRuleData->ValueForMarginBottom(); ourMargin.mLeft = *aRuleData->ValueForMarginLeftValue(); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForMarginLeftLTRSource(), *aRuleData->ValueForMarginLeftRTLSource(), *aRuleData->ValueForMarginStartValue(), *aRuleData->ValueForMarginEndValue(), NS_SIDE_LEFT, ourMargin, canStoreInRuleTree); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForMarginRightLTRSource(), *aRuleData->ValueForMarginRightRTLSource(), *aRuleData->ValueForMarginEndValue(), *aRuleData->ValueForMarginStartValue(), NS_SIDE_RIGHT, ourMargin, canStoreInRuleTree); NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentMargin->mMargin.Get(side); if (SetCoord(ourMargin.*(nsCSSRect::sides[side]), coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { margin->mMargin.Set(side, coord); } } margin->RecalcData(); COMPUTE_END_RESET(Margin, margin) } static void SetBorderImageRect(const nsCSSValue& aValue, /** outparam */ nsCSSRect& aRect) { switch (aValue.GetUnit()) { case eCSSUnit_Null: aRect.Reset(); break; case eCSSUnit_Rect: aRect = aValue.GetRectValue(); break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aRect.SetAllSidesTo(aValue); break; default: NS_ASSERTION(false, "Unexpected border image value for rect."); } } static void SetBorderImagePair(const nsCSSValue& aValue, /** outparam */ nsCSSValuePair& aPair) { switch (aValue.GetUnit()) { case eCSSUnit_Null: aPair.Reset(); break; case eCSSUnit_Pair: aPair = aValue.GetPairValue(); break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aPair.SetBothValuesTo(aValue); break; default: NS_ASSERTION(false, "Unexpected border image value for pair."); } } static void SetBorderImageSlice(const nsCSSValue& aValue, /** outparam */ nsCSSValue& aSlice, /** outparam */ nsCSSValue& aFill) { const nsCSSValueList* valueList; switch (aValue.GetUnit()) { case eCSSUnit_Null: aSlice.Reset(); aFill.Reset(); break; case eCSSUnit_List: // Get slice dimensions. valueList = aValue.GetListValue(); aSlice = valueList->mValue; // Get "fill" keyword. valueList = valueList->mNext; if (valueList) { aFill = valueList->mValue; } else { aFill.SetInitialValue(); } break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aSlice = aValue; aFill = aValue; break; default: NS_ASSERTION(false, "Unexpected border image value for pair."); } } const void* nsRuleNode::ComputeBorderData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Border, (mPresContext), border, parentBorder) // box-shadow: none, list, inherit, initial const nsCSSValue* boxShadowValue = aRuleData->ValueForBoxShadow(); switch (boxShadowValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: border->mBoxShadow = nullptr; break; case eCSSUnit_Inherit: border->mBoxShadow = parentBorder->mBoxShadow; canStoreInRuleTree = false; break; case eCSSUnit_List: case eCSSUnit_ListDep: border->mBoxShadow = GetShadowData(boxShadowValue->GetListValue(), aContext, true, canStoreInRuleTree); break; default: NS_ABORT_IF_FALSE(false, nsPrintfCString("unrecognized shadow unit %d", boxShadowValue->GetUnit()).get()); } // border-width, border-*-width: length, enum, inherit nsStyleCoord coord; nsCSSRect ourBorderWidth; ourBorderWidth.mTop = *aRuleData->ValueForBorderTopWidth(); ourBorderWidth.mRight = *aRuleData->ValueForBorderRightWidthValue(); ourBorderWidth.mBottom = *aRuleData->ValueForBorderBottomWidth(); ourBorderWidth.mLeft = *aRuleData->ValueForBorderLeftWidthValue(); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderLeftWidthLTRSource(), *aRuleData->ValueForBorderLeftWidthRTLSource(), *aRuleData->ValueForBorderStartWidthValue(), *aRuleData->ValueForBorderEndWidthValue(), NS_SIDE_LEFT, ourBorderWidth, canStoreInRuleTree); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderRightWidthLTRSource(), *aRuleData->ValueForBorderRightWidthRTLSource(), *aRuleData->ValueForBorderEndWidthValue(), *aRuleData->ValueForBorderStartWidthValue(), NS_SIDE_RIGHT, ourBorderWidth, canStoreInRuleTree); { // scope for compilers with broken |for| loop scoping NS_FOR_CSS_SIDES(side) { const nsCSSValue &value = ourBorderWidth.*(nsCSSRect::sides[side]); NS_ASSERTION(eCSSUnit_Percent != value.GetUnit(), "Percentage borders not implemented yet " "If implementing, make sure to fix all consumers of " "nsStyleBorder, the IsPercentageAwareChild method, " "the nsAbsoluteContainingBlock::FrameDependsOnContainer " "method, the " "nsLineLayout::IsPercentageAwareReplacedElement method " "and probably some other places"); if (eCSSUnit_Enumerated == value.GetUnit()) { NS_ASSERTION(value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || value.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, "Unexpected enum value"); border->SetBorderWidth(side, (mPresContext->GetBorderWidthTable())[value.GetIntValue()]); } // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT else if (SetCoord(value, coord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, canStoreInRuleTree)) { NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); // clamp negative calc() to 0. border->SetBorderWidth(side, std::max(coord.GetCoordValue(), 0)); } else if (eCSSUnit_Inherit == value.GetUnit()) { canStoreInRuleTree = false; border->SetBorderWidth(side, parentBorder->GetComputedBorder().Side(side)); } else if (eCSSUnit_Initial == value.GetUnit() || eCSSUnit_Unset == value.GetUnit()) { border->SetBorderWidth(side, (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); } else { NS_ASSERTION(eCSSUnit_Null == value.GetUnit(), "missing case handling border width"); } } } // border-style, border-*-style: enum, inherit nsCSSRect ourBorderStyle; ourBorderStyle.mTop = *aRuleData->ValueForBorderTopStyle(); ourBorderStyle.mRight = *aRuleData->ValueForBorderRightStyleValue(); ourBorderStyle.mBottom = *aRuleData->ValueForBorderBottomStyle(); ourBorderStyle.mLeft = *aRuleData->ValueForBorderLeftStyleValue(); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderLeftStyleLTRSource(), *aRuleData->ValueForBorderLeftStyleRTLSource(), *aRuleData->ValueForBorderStartStyleValue(), *aRuleData->ValueForBorderEndStyleValue(), NS_SIDE_LEFT, ourBorderStyle, canStoreInRuleTree); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderRightStyleLTRSource(), *aRuleData->ValueForBorderRightStyleRTLSource(), *aRuleData->ValueForBorderEndStyleValue(), *aRuleData->ValueForBorderStartStyleValue(), NS_SIDE_RIGHT, ourBorderStyle, canStoreInRuleTree); { // scope for compilers with broken |for| loop scoping NS_FOR_CSS_SIDES(side) { const nsCSSValue &value = ourBorderStyle.*(nsCSSRect::sides[side]); nsCSSUnit unit = value.GetUnit(); NS_ABORT_IF_FALSE(eCSSUnit_None != unit, "'none' should be handled as enumerated value"); if (eCSSUnit_Enumerated == unit) { border->SetBorderStyle(side, value.GetIntValue()); } else if (eCSSUnit_Initial == unit || eCSSUnit_Unset == unit) { border->SetBorderStyle(side, NS_STYLE_BORDER_STYLE_NONE); } else if (eCSSUnit_Inherit == unit) { canStoreInRuleTree = false; border->SetBorderStyle(side, parentBorder->GetBorderStyle(side)); } } } // -moz-border-*-colors: color, string, enum, none, inherit/initial nscolor borderColor; nscolor unused = NS_RGB(0,0,0); static const nsCSSProperty borderColorsProps[] = { eCSSProperty_border_top_colors, eCSSProperty_border_right_colors, eCSSProperty_border_bottom_colors, eCSSProperty_border_left_colors }; NS_FOR_CSS_SIDES(side) { const nsCSSValue& value = *aRuleData->ValueFor(borderColorsProps[side]); switch (value.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: border->ClearBorderColors(side); break; case eCSSUnit_Inherit: { canStoreInRuleTree = false; border->ClearBorderColors(side); if (parentContext) { nsBorderColors *parentColors; parentBorder->GetCompositeColors(side, &parentColors); if (parentColors) { border->EnsureBorderColors(); border->mBorderColors[side] = parentColors->Clone(); } } break; } case eCSSUnit_List: case eCSSUnit_ListDep: { // Some composite border color information has been specified for this // border side. border->EnsureBorderColors(); border->ClearBorderColors(side); const nsCSSValueList* list = value.GetListValue(); while (list) { if (SetColor(list->mValue, unused, mPresContext, aContext, borderColor, canStoreInRuleTree)) border->AppendBorderColor(side, borderColor); else { NS_NOTREACHED("unexpected item in -moz-border-*-colors list"); } list = list->mNext; } break; } default: NS_ABORT_IF_FALSE(false, "unrecognized border color unit"); } } // border-color, border-*-color: color, string, enum, inherit bool foreground; nsCSSRect ourBorderColor; ourBorderColor.mTop = *aRuleData->ValueForBorderTopColor(); ourBorderColor.mRight = *aRuleData->ValueForBorderRightColorValue(); ourBorderColor.mBottom = *aRuleData->ValueForBorderBottomColor(); ourBorderColor.mLeft = *aRuleData->ValueForBorderLeftColorValue(); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderLeftColorLTRSource(), *aRuleData->ValueForBorderLeftColorRTLSource(), *aRuleData->ValueForBorderStartColorValue(), *aRuleData->ValueForBorderEndColorValue(), NS_SIDE_LEFT, ourBorderColor, canStoreInRuleTree); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForBorderRightColorLTRSource(), *aRuleData->ValueForBorderRightColorRTLSource(), *aRuleData->ValueForBorderEndColorValue(), *aRuleData->ValueForBorderStartColorValue(), NS_SIDE_RIGHT, ourBorderColor, canStoreInRuleTree); { // scope for compilers with broken |for| loop scoping NS_FOR_CSS_SIDES(side) { const nsCSSValue &value = ourBorderColor.*(nsCSSRect::sides[side]); if (eCSSUnit_Inherit == value.GetUnit()) { canStoreInRuleTree = false; if (parentContext) { parentBorder->GetBorderColor(side, borderColor, foreground); if (foreground) { // We want to inherit the color from the parent, not use the // color on the element where this chunk of style data will be // used. We can ensure that the data for the parent are fully // computed (unlike for the element where this will be used, for // which the color could be specified on a more specific rule). border->SetBorderColor(side, parentContext->StyleColor()->mColor); } else border->SetBorderColor(side, borderColor); } else { // We're the root border->SetBorderToForeground(side); } } else if (SetColor(value, unused, mPresContext, aContext, borderColor, canStoreInRuleTree)) { border->SetBorderColor(side, borderColor); } else if (eCSSUnit_Enumerated == value.GetUnit()) { switch (value.GetIntValue()) { case NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR: border->SetBorderToForeground(side); break; default: NS_NOTREACHED("Unexpected enumerated color"); break; } } else if (eCSSUnit_Initial == value.GetUnit() || eCSSUnit_Unset == value.GetUnit()) { border->SetBorderToForeground(side); } } } // border-radius: length, percent, inherit { const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_radius); NS_FOR_CSS_FULL_CORNERS(corner) { int cx = NS_FULL_TO_HALF_CORNER(corner, false); int cy = NS_FULL_TO_HALF_CORNER(corner, true); const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); nsStyleCoord parentX = parentBorder->mBorderRadius.Get(cx); nsStyleCoord parentY = parentBorder->mBorderRadius.Get(cy); nsStyleCoord coordX, coordY; if (SetPairCoords(radius, coordX, coordY, parentX, parentY, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { border->mBorderRadius.Set(cx, coordX); border->mBorderRadius.Set(cy, coordY); } } } // float-edge: enum, inherit, initial SetDiscrete(*aRuleData->ValueForFloatEdge(), border->mFloatEdge, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentBorder->mFloatEdge, NS_STYLE_FLOAT_EDGE_CONTENT, 0, 0, 0, 0); // border-image-source const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource(); if (borderImageSource->GetUnit() == eCSSUnit_Inherit) { canStoreInRuleTree = false; border->mBorderImageSource = parentBorder->mBorderImageSource; } else { SetStyleImage(aContext, *borderImageSource, border->mBorderImageSource, canStoreInRuleTree); } nsCSSValue borderImageSliceValue; nsCSSValue borderImageSliceFill; SetBorderImageSlice(*aRuleData->ValueForBorderImageSlice(), borderImageSliceValue, borderImageSliceFill); // border-image-slice: fill SetDiscrete(borderImageSliceFill, border->mBorderImageFill, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentBorder->mBorderImageFill, NS_STYLE_BORDER_IMAGE_SLICE_NOFILL, 0, 0, 0, 0); nsCSSRect borderImageSlice; SetBorderImageRect(borderImageSliceValue, borderImageSlice); nsCSSRect borderImageWidth; SetBorderImageRect(*aRuleData->ValueForBorderImageWidth(), borderImageWidth); nsCSSRect borderImageOutset; SetBorderImageRect(*aRuleData->ValueForBorderImageOutset(), borderImageOutset); NS_FOR_CSS_SIDES (side) { // border-image-slice if (SetCoord(borderImageSlice.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageSlice.Get(side), SETCOORD_FACTOR | SETCOORD_PERCENT | SETCOORD_INHERIT | SETCOORD_INITIAL_HUNDRED_PCT | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { border->mBorderImageSlice.Set(side, coord); } // border-image-width // 'auto' here means "same as slice" if (SetCoord(borderImageWidth.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageWidth.Get(side), SETCOORD_LPAH | SETCOORD_FACTOR | SETCOORD_INITIAL_FACTOR_ONE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { border->mBorderImageWidth.Set(side, coord); } // border-image-outset if (SetCoord(borderImageOutset.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageOutset.Get(side), SETCOORD_LENGTH | SETCOORD_FACTOR | SETCOORD_INHERIT | SETCOORD_INITIAL_FACTOR_ZERO | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { border->mBorderImageOutset.Set(side, coord); } } // border-image-repeat nsCSSValuePair borderImageRepeat; SetBorderImagePair(*aRuleData->ValueForBorderImageRepeat(), borderImageRepeat); SetDiscrete(borderImageRepeat.mXValue, border->mBorderImageRepeatH, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentBorder->mBorderImageRepeatH, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); SetDiscrete(borderImageRepeat.mYValue, border->mBorderImageRepeatV, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentBorder->mBorderImageRepeatV, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); border->TrackImage(aContext->PresContext()); COMPUTE_END_RESET(Border, border) } const void* nsRuleNode::ComputePaddingData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Padding, (), padding, parentPadding) // padding: length, percent, inherit nsStyleCoord coord; nsCSSRect ourPadding; ourPadding.mTop = *aRuleData->ValueForPaddingTop(); ourPadding.mRight = *aRuleData->ValueForPaddingRightValue(); ourPadding.mBottom = *aRuleData->ValueForPaddingBottom(); ourPadding.mLeft = *aRuleData->ValueForPaddingLeftValue(); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForPaddingLeftLTRSource(), *aRuleData->ValueForPaddingLeftRTLSource(), *aRuleData->ValueForPaddingStartValue(), *aRuleData->ValueForPaddingEndValue(), NS_SIDE_LEFT, ourPadding, canStoreInRuleTree); AdjustLogicalBoxProp(aContext, *aRuleData->ValueForPaddingRightLTRSource(), *aRuleData->ValueForPaddingRightRTLSource(), *aRuleData->ValueForPaddingEndValue(), *aRuleData->ValueForPaddingStartValue(), NS_SIDE_RIGHT, ourPadding, canStoreInRuleTree); NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentPadding->mPadding.Get(side); if (SetCoord(ourPadding.*(nsCSSRect::sides[side]), coord, parentCoord, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { padding->mPadding.Set(side, coord); } } padding->RecalcData(); COMPUTE_END_RESET(Padding, padding) } const void* nsRuleNode::ComputeOutlineData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Outline, (mPresContext), outline, parentOutline) // outline-width: length, enum, inherit const nsCSSValue* outlineWidthValue = aRuleData->ValueForOutlineWidth(); if (eCSSUnit_Initial == outlineWidthValue->GetUnit() || eCSSUnit_Unset == outlineWidthValue->GetUnit()) { outline->mOutlineWidth = nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated); } else { SetCoord(*outlineWidthValue, outline->mOutlineWidth, parentOutline->mOutlineWidth, SETCOORD_LEH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, canStoreInRuleTree); } // outline-offset: length, inherit nsStyleCoord tempCoord; const nsCSSValue* outlineOffsetValue = aRuleData->ValueForOutlineOffset(); if (SetCoord(*outlineOffsetValue, tempCoord, nsStyleCoord(parentOutline->mOutlineOffset, nsStyleCoord::CoordConstructor), SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { outline->mOutlineOffset = tempCoord.GetCoordValue(); } else { NS_ASSERTION(outlineOffsetValue->GetUnit() == eCSSUnit_Null, "unexpected unit"); } // outline-color: color, string, enum, inherit nscolor outlineColor; nscolor unused = NS_RGB(0,0,0); const nsCSSValue* outlineColorValue = aRuleData->ValueForOutlineColor(); if (eCSSUnit_Inherit == outlineColorValue->GetUnit()) { canStoreInRuleTree = false; if (parentContext) { if (parentOutline->GetOutlineColor(outlineColor)) outline->SetOutlineColor(outlineColor); else { // We want to inherit the color from the parent, not use the // color on the element where this chunk of style data will be // used. We can ensure that the data for the parent are fully // computed (unlike for the element where this will be used, for // which the color could be specified on a more specific rule). outline->SetOutlineColor(parentContext->StyleColor()->mColor); } } else { outline->SetOutlineInitialColor(); } } else if (SetColor(*outlineColorValue, unused, mPresContext, aContext, outlineColor, canStoreInRuleTree)) outline->SetOutlineColor(outlineColor); else if (eCSSUnit_Enumerated == outlineColorValue->GetUnit() || eCSSUnit_Initial == outlineColorValue->GetUnit() || eCSSUnit_Unset == outlineColorValue->GetUnit()) { outline->SetOutlineInitialColor(); } // -moz-outline-radius: length, percent, inherit { const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty__moz_outline_radius); NS_FOR_CSS_FULL_CORNERS(corner) { int cx = NS_FULL_TO_HALF_CORNER(corner, false); int cy = NS_FULL_TO_HALF_CORNER(corner, true); const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); nsStyleCoord parentX = parentOutline->mOutlineRadius.Get(cx); nsStyleCoord parentY = parentOutline->mOutlineRadius.Get(cy); nsStyleCoord coordX, coordY; if (SetPairCoords(radius, coordX, coordY, parentX, parentY, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { outline->mOutlineRadius.Set(cx, coordX); outline->mOutlineRadius.Set(cy, coordY); } } } // outline-style: enum, inherit, initial // cannot use SetDiscrete because of SetOutlineStyle const nsCSSValue* outlineStyleValue = aRuleData->ValueForOutlineStyle(); nsCSSUnit unit = outlineStyleValue->GetUnit(); NS_ABORT_IF_FALSE(eCSSUnit_None != unit && eCSSUnit_Auto != unit, "'none' and 'auto' should be handled as enumerated values"); if (eCSSUnit_Enumerated == unit) { outline->SetOutlineStyle(outlineStyleValue->GetIntValue()); } else if (eCSSUnit_Initial == unit || eCSSUnit_Unset == unit) { outline->SetOutlineStyle(NS_STYLE_BORDER_STYLE_NONE); } else if (eCSSUnit_Inherit == unit) { canStoreInRuleTree = false; outline->SetOutlineStyle(parentOutline->GetOutlineStyle()); } outline->RecalcData(mPresContext); COMPUTE_END_RESET(Outline, outline) } const void* nsRuleNode::ComputeListData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(List, (), list, parentList) // list-style-type: enum, inherit, initial SetDiscrete(*aRuleData->ValueForListStyleType(), list->mListStyleType, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentList->mListStyleType, NS_STYLE_LIST_STYLE_DISC, 0, 0, 0, 0); // list-style-image: url, none, inherit const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage(); if (eCSSUnit_Image == imageValue->GetUnit()) { NS_SET_IMAGE_REQUEST_WITH_DOC(list->SetListStyleImage, aContext, imageValue->GetImageValue) } else if (eCSSUnit_None == imageValue->GetUnit() || eCSSUnit_Initial == imageValue->GetUnit()) { list->SetListStyleImage(nullptr); } else if (eCSSUnit_Inherit == imageValue->GetUnit() || eCSSUnit_Unset == imageValue->GetUnit()) { canStoreInRuleTree = false; NS_SET_IMAGE_REQUEST(list->SetListStyleImage, aContext, parentList->GetListStyleImage()) } // list-style-position: enum, inherit, initial SetDiscrete(*aRuleData->ValueForListStylePosition(), list->mListStylePosition, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentList->mListStylePosition, NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, 0, 0, 0, 0); // image region property: length, auto, inherit const nsCSSValue* imageRegionValue = aRuleData->ValueForImageRegion(); switch (imageRegionValue->GetUnit()) { case eCSSUnit_Inherit: case eCSSUnit_Unset: canStoreInRuleTree = false; list->mImageRegion = parentList->mImageRegion; break; case eCSSUnit_Initial: case eCSSUnit_Auto: list->mImageRegion.SetRect(0,0,0,0); break; case eCSSUnit_Null: break; case eCSSUnit_Rect: { const nsCSSRect& rgnRect = imageRegionValue->GetRectValue(); if (rgnRect.mTop.GetUnit() == eCSSUnit_Auto) list->mImageRegion.y = 0; else if (rgnRect.mTop.IsLengthUnit()) list->mImageRegion.y = CalcLength(rgnRect.mTop, aContext, mPresContext, canStoreInRuleTree); if (rgnRect.mBottom.GetUnit() == eCSSUnit_Auto) list->mImageRegion.height = 0; else if (rgnRect.mBottom.IsLengthUnit()) list->mImageRegion.height = CalcLength(rgnRect.mBottom, aContext, mPresContext, canStoreInRuleTree) - list->mImageRegion.y; if (rgnRect.mLeft.GetUnit() == eCSSUnit_Auto) list->mImageRegion.x = 0; else if (rgnRect.mLeft.IsLengthUnit()) list->mImageRegion.x = CalcLength(rgnRect.mLeft, aContext, mPresContext, canStoreInRuleTree); if (rgnRect.mRight.GetUnit() == eCSSUnit_Auto) list->mImageRegion.width = 0; else if (rgnRect.mRight.IsLengthUnit()) list->mImageRegion.width = CalcLength(rgnRect.mRight, aContext, mPresContext, canStoreInRuleTree) - list->mImageRegion.x; break; } default: NS_ABORT_IF_FALSE(false, "unrecognized image-region unit"); } COMPUTE_END_INHERITED(List, list) } const void* nsRuleNode::ComputePositionData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Position, (), pos, parentPos) // box offsets: length, percent, calc, auto, inherit static const nsCSSProperty offsetProps[] = { eCSSProperty_top, eCSSProperty_right, eCSSProperty_bottom, eCSSProperty_left }; nsStyleCoord coord; NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentPos->mOffset.Get(side); if (SetCoord(*aRuleData->ValueFor(offsetProps[side]), coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree)) { pos->mOffset.Set(side, coord); } } SetCoord(*aRuleData->ValueForWidth(), pos->mWidth, parentPos->mWidth, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetCoord(*aRuleData->ValueForMinWidth(), pos->mMinWidth, parentPos->mMinWidth, SETCOORD_LPEH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetCoord(*aRuleData->ValueForMaxWidth(), pos->mMaxWidth, parentPos->mMaxWidth, SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetCoord(*aRuleData->ValueForHeight(), pos->mHeight, parentPos->mHeight, SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetCoord(*aRuleData->ValueForMinHeight(), pos->mMinHeight, parentPos->mMinHeight, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); SetCoord(*aRuleData->ValueForMaxHeight(), pos->mMaxHeight, parentPos->mMaxHeight, SETCOORD_LPOH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); // box-sizing: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxSizing(), pos->mBoxSizing, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mBoxSizing, NS_STYLE_BOX_SIZING_CONTENT, 0, 0, 0, 0); // align-content: enum, inherit, initial SetDiscrete(*aRuleData->ValueForAlignContent(), pos->mAlignContent, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mAlignContent, NS_STYLE_ALIGN_CONTENT_STRETCH, 0, 0, 0, 0); // align-items: enum, inherit, initial SetDiscrete(*aRuleData->ValueForAlignItems(), pos->mAlignItems, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mAlignItems, NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE, 0, 0, 0, 0); // align-self: enum, inherit, initial // NOTE: align-self's initial value is the special keyword "auto", which is // supposed to compute to our parent's computed value of "align-items". So // technically, "auto" itself is never a valid computed value for align-self, // since it always computes to something else. Despite that, we do actually // store "auto" in nsStylePosition::mAlignSelf, as NS_STYLE_ALIGN_SELF_AUTO // (and then resolve it as-necessary). We do this because "auto" is the // initial value for this property, so if we were to actually resolve it in // nsStylePosition, we'd never be able to share any nsStylePosition structs // in the rule tree, since their mAlignSelf values would depend on the parent // style, by default. if (aRuleData->ValueForAlignSelf()->GetUnit() == eCSSUnit_Inherit) { // Special handling for "align-self: inherit", in case we're inheriting // "align-self: auto", in which case we need to resolve the parent's "auto" // and inherit that resolved value. uint8_t inheritedAlignSelf = parentPos->mAlignSelf; if (inheritedAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) { if (!parentContext) { // We're the root node. Nothing to inherit from --> just use default // value. inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE; } else { // Our parent's "auto" value should resolve to our grandparent's value // for "align-items". So, that's what we're supposed to inherit. nsStyleContext* grandparentContext = parentContext->GetParent(); if (!grandparentContext) { // No grandparent --> our parent is the root node, so its // "align-self: auto" computes to the default "align-items" value: inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE; } else { // Normal case -- we have a grandparent. // Its "align-items" value is what we should end up inheriting. const nsStylePosition* grandparentPos = grandparentContext->StylePosition(); inheritedAlignSelf = grandparentPos->mAlignItems; } } } pos->mAlignSelf = inheritedAlignSelf; canStoreInRuleTree = false; } else { SetDiscrete(*aRuleData->ValueForAlignSelf(), pos->mAlignSelf, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mAlignSelf, // (unused -- we handled inherit above) NS_STYLE_ALIGN_SELF_AUTO, // initial == auto 0, 0, 0, 0); } // flex-basis: auto, length, percent, enum, calc, inherit, initial // (Note: The flags here should match those used for 'width' property above.) SetCoord(*aRuleData->ValueForFlexBasis(), pos->mFlexBasis, parentPos->mFlexBasis, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); // flex-direction: enum, inherit, initial SetDiscrete(*aRuleData->ValueForFlexDirection(), pos->mFlexDirection, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mFlexDirection, NS_STYLE_FLEX_DIRECTION_ROW, 0, 0, 0, 0); // flex-grow: float, inherit, initial SetFactor(*aRuleData->ValueForFlexGrow(), pos->mFlexGrow, canStoreInRuleTree, parentPos->mFlexGrow, 0.0f, SETFCT_UNSET_INITIAL); // flex-shrink: float, inherit, initial SetFactor(*aRuleData->ValueForFlexShrink(), pos->mFlexShrink, canStoreInRuleTree, parentPos->mFlexShrink, 1.0f, SETFCT_UNSET_INITIAL); // flex-wrap: enum, inherit, initial SetDiscrete(*aRuleData->ValueForFlexWrap(), pos->mFlexWrap, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mFlexWrap, NS_STYLE_FLEX_WRAP_NOWRAP, 0, 0, 0, 0); // order: integer, inherit, initial SetDiscrete(*aRuleData->ValueForOrder(), pos->mOrder, canStoreInRuleTree, SETDSC_INTEGER | SETDSC_UNSET_INITIAL, parentPos->mOrder, NS_STYLE_ORDER_INITIAL, 0, 0, 0, 0); // justify-content: enum, inherit, initial SetDiscrete(*aRuleData->ValueForJustifyContent(), pos->mJustifyContent, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentPos->mJustifyContent, NS_STYLE_JUSTIFY_CONTENT_FLEX_START, 0, 0, 0, 0); // z-index const nsCSSValue* zIndexValue = aRuleData->ValueForZIndex(); if (! SetCoord(*zIndexValue, pos->mZIndex, parentPos->mZIndex, SETCOORD_IA | SETCOORD_INITIAL_AUTO | SETCOORD_UNSET_INITIAL, aContext, nullptr, canStoreInRuleTree)) { if (eCSSUnit_Inherit == zIndexValue->GetUnit()) { // handle inherit, because it's ok to inherit 'auto' here canStoreInRuleTree = false; pos->mZIndex = parentPos->mZIndex; } } COMPUTE_END_RESET(Position, pos) } const void* nsRuleNode::ComputeTableData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Table, (), table, parentTable) // table-layout: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTableLayout(), table->mLayoutStrategy, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentTable->mLayoutStrategy, NS_STYLE_TABLE_LAYOUT_AUTO, 0, 0, 0, 0); // span: pixels (not a real CSS prop) const nsCSSValue* spanValue = aRuleData->ValueForSpan(); if (eCSSUnit_Enumerated == spanValue->GetUnit() || eCSSUnit_Integer == spanValue->GetUnit()) table->mSpan = spanValue->GetIntValue(); COMPUTE_END_RESET(Table, table) } const void* nsRuleNode::ComputeTableBorderData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(TableBorder, (mPresContext), table, parentTable) // border-collapse: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBorderCollapse(), table->mBorderCollapse, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentTable->mBorderCollapse, NS_STYLE_BORDER_SEPARATE, 0, 0, 0, 0); const nsCSSValue* borderSpacingValue = aRuleData->ValueForBorderSpacing(); if (borderSpacingValue->GetUnit() != eCSSUnit_Null) { // border-spacing-x/y: length, inherit nsStyleCoord parentX(parentTable->mBorderSpacingX, nsStyleCoord::CoordConstructor); nsStyleCoord parentY(parentTable->mBorderSpacingY, nsStyleCoord::CoordConstructor); nsStyleCoord coordX, coordY; #ifdef DEBUG bool result = #endif SetPairCoords(*borderSpacingValue, coordX, coordY, parentX, parentY, SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); NS_ASSERTION(result, "malformed table border value"); table->mBorderSpacingX = coordX.GetCoordValue(); table->mBorderSpacingY = coordY.GetCoordValue(); } // caption-side: enum, inherit, initial SetDiscrete(*aRuleData->ValueForCaptionSide(), table->mCaptionSide, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentTable->mCaptionSide, NS_STYLE_CAPTION_SIDE_TOP, 0, 0, 0, 0); // empty-cells: enum, inherit, initial SetDiscrete(*aRuleData->ValueForEmptyCells(), table->mEmptyCells, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentTable->mEmptyCells, (mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) ? NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND : NS_STYLE_TABLE_EMPTY_CELLS_SHOW, 0, 0, 0, 0); COMPUTE_END_INHERITED(TableBorder, table) } const void* nsRuleNode::ComputeContentData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { uint32_t count; nsAutoString buffer; COMPUTE_START_RESET(Content, (), content, parentContent) // content: [string, url, counter, attr, enum]+, normal, none, inherit const nsCSSValue* contentValue = aRuleData->ValueForContent(); switch (contentValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Normal: case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: // "normal", "none", "initial" and "unset" all mean no content content->AllocateContents(0); break; case eCSSUnit_Inherit: canStoreInRuleTree = false; count = parentContent->ContentCount(); if (NS_SUCCEEDED(content->AllocateContents(count))) { while (0 < count--) { content->ContentAt(count) = parentContent->ContentAt(count); } } break; case eCSSUnit_Enumerated: { NS_ABORT_IF_FALSE(contentValue->GetIntValue() == NS_STYLE_CONTENT_ALT_CONTENT, "unrecognized solitary content keyword"); content->AllocateContents(1); nsStyleContentData& data = content->ContentAt(0); data.mType = eStyleContentType_AltContent; data.mContent.mString = nullptr; break; } case eCSSUnit_List: case eCSSUnit_ListDep: { const nsCSSValueList* contentValueList = contentValue->GetListValue(); count = 0; while (contentValueList) { count++; contentValueList = contentValueList->mNext; } if (NS_SUCCEEDED(content->AllocateContents(count))) { const nsAutoString nullStr; count = 0; contentValueList = contentValue->GetListValue(); while (contentValueList) { const nsCSSValue& value = contentValueList->mValue; nsCSSUnit unit = value.GetUnit(); nsStyleContentType type; nsStyleContentData &data = content->ContentAt(count++); switch (unit) { case eCSSUnit_String: type = eStyleContentType_String; break; case eCSSUnit_Image: type = eStyleContentType_Image; break; case eCSSUnit_Attr: type = eStyleContentType_Attr; break; case eCSSUnit_Counter: type = eStyleContentType_Counter; break; case eCSSUnit_Counters: type = eStyleContentType_Counters; break; case eCSSUnit_Enumerated: switch (value.GetIntValue()) { case NS_STYLE_CONTENT_OPEN_QUOTE: type = eStyleContentType_OpenQuote; break; case NS_STYLE_CONTENT_CLOSE_QUOTE: type = eStyleContentType_CloseQuote; break; case NS_STYLE_CONTENT_NO_OPEN_QUOTE: type = eStyleContentType_NoOpenQuote; break; case NS_STYLE_CONTENT_NO_CLOSE_QUOTE: type = eStyleContentType_NoCloseQuote; break; default: NS_ERROR("bad content value"); type = eStyleContentType_Uninitialized; } break; default: NS_ERROR("bad content type"); type = eStyleContentType_Uninitialized; } data.mType = type; if (type == eStyleContentType_Image) { NS_SET_IMAGE_REQUEST_WITH_DOC(data.SetImage, aContext, value.GetImageValue); } else if (type <= eStyleContentType_Attr) { value.GetStringValue(buffer); data.mContent.mString = NS_strdup(buffer.get()); } else if (type <= eStyleContentType_Counters) { data.mContent.mCounters = value.GetArrayValue(); data.mContent.mCounters->AddRef(); } else { data.mContent.mString = nullptr; } contentValueList = contentValueList->mNext; } } break; } default: NS_ABORT_IF_FALSE(false, nsPrintfCString("unrecognized content unit %d", contentValue->GetUnit()).get()); } // counter-increment: [string [int]]+, none, inherit const nsCSSValue* counterIncrementValue = aRuleData->ValueForCounterIncrement(); switch (counterIncrementValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: content->AllocateCounterIncrements(0); break; case eCSSUnit_Inherit: canStoreInRuleTree = false; count = parentContent->CounterIncrementCount(); if (NS_SUCCEEDED(content->AllocateCounterIncrements(count))) { while (0 < count--) { const nsStyleCounterData *data = parentContent->GetCounterIncrementAt(count); content->SetCounterIncrementAt(count, data->mCounter, data->mValue); } } break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourIncrement = counterIncrementValue->GetPairListValue(); NS_ABORT_IF_FALSE(ourIncrement->mXValue.GetUnit() == eCSSUnit_Ident, "unexpected value unit"); count = ListLength(ourIncrement); if (NS_FAILED(content->AllocateCounterIncrements(count))) { break; } count = 0; for (const nsCSSValuePairList* p = ourIncrement; p; p = p->mNext, count++) { int32_t increment; if (p->mYValue.GetUnit() == eCSSUnit_Integer) { increment = p->mYValue.GetIntValue(); } else { increment = 1; } p->mXValue.GetStringValue(buffer); content->SetCounterIncrementAt(count, buffer, increment); } break; } default: NS_ABORT_IF_FALSE(false, "unexpected value unit"); } // counter-reset: [string [int]]+, none, inherit const nsCSSValue* counterResetValue = aRuleData->ValueForCounterReset(); switch (counterResetValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: content->AllocateCounterResets(0); break; case eCSSUnit_Inherit: canStoreInRuleTree = false; count = parentContent->CounterResetCount(); if (NS_SUCCEEDED(content->AllocateCounterResets(count))) { while (0 < count--) { const nsStyleCounterData *data = parentContent->GetCounterResetAt(count); content->SetCounterResetAt(count, data->mCounter, data->mValue); } } break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourReset = counterResetValue->GetPairListValue(); NS_ABORT_IF_FALSE(ourReset->mXValue.GetUnit() == eCSSUnit_Ident, "unexpected value unit"); count = ListLength(ourReset); if (NS_FAILED(content->AllocateCounterResets(count))) { break; } count = 0; for (const nsCSSValuePairList* p = ourReset; p; p = p->mNext, count++) { int32_t reset; if (p->mYValue.GetUnit() == eCSSUnit_Integer) { reset = p->mYValue.GetIntValue(); } else { reset = 0; } p->mXValue.GetStringValue(buffer); content->SetCounterResetAt(count, buffer, reset); } break; } default: NS_ABORT_IF_FALSE(false, "unexpected value unit"); } // marker-offset: length, auto, inherit SetCoord(*aRuleData->ValueForMarkerOffset(), content->mMarkerOffset, parentContent->mMarkerOffset, SETCOORD_LH | SETCOORD_AUTO | SETCOORD_INITIAL_AUTO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); // If we ended up with an image, track it. for (uint32_t i = 0; i < content->ContentCount(); ++i) { if ((content->ContentAt(i).mType == eStyleContentType_Image) && content->ContentAt(i).mContent.mImage) { content->ContentAt(i).TrackImage(aContext->PresContext()); } } COMPUTE_END_RESET(Content, content) } const void* nsRuleNode::ComputeQuotesData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Quotes, (), quotes, parentQuotes) // quotes: inherit, initial, none, [string string]+ const nsCSSValue* quotesValue = aRuleData->ValueForQuotes(); switch (quotesValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: case eCSSUnit_Unset: canStoreInRuleTree = false; quotes->CopyFrom(*parentQuotes); break; case eCSSUnit_Initial: quotes->SetInitial(); break; case eCSSUnit_None: quotes->AllocateQuotes(0); break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourQuotes = quotesValue->GetPairListValue(); nsAutoString buffer; nsAutoString closeBuffer; uint32_t count = ListLength(ourQuotes); if (NS_FAILED(quotes->AllocateQuotes(count))) { break; } count = 0; while (ourQuotes) { NS_ABORT_IF_FALSE(ourQuotes->mXValue.GetUnit() == eCSSUnit_String && ourQuotes->mYValue.GetUnit() == eCSSUnit_String, "improper list contents for quotes"); ourQuotes->mXValue.GetStringValue(buffer); ourQuotes->mYValue.GetStringValue(closeBuffer); quotes->SetQuotesAt(count++, buffer, closeBuffer); ourQuotes = ourQuotes->mNext; } break; } default: NS_ABORT_IF_FALSE(false, "unexpected value unit"); } COMPUTE_END_INHERITED(Quotes, quotes) } const void* nsRuleNode::ComputeXULData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(XUL, (), xul, parentXUL) // box-align: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxAlign(), xul->mBoxAlign, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentXUL->mBoxAlign, NS_STYLE_BOX_ALIGN_STRETCH, 0, 0, 0, 0); // box-direction: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxDirection(), xul->mBoxDirection, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentXUL->mBoxDirection, NS_STYLE_BOX_DIRECTION_NORMAL, 0, 0, 0, 0); // box-flex: factor, inherit SetFactor(*aRuleData->ValueForBoxFlex(), xul->mBoxFlex, canStoreInRuleTree, parentXUL->mBoxFlex, 0.0f, SETFCT_UNSET_INITIAL); // box-orient: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxOrient(), xul->mBoxOrient, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentXUL->mBoxOrient, NS_STYLE_BOX_ORIENT_HORIZONTAL, 0, 0, 0, 0); // box-pack: enum, inherit, initial SetDiscrete(*aRuleData->ValueForBoxPack(), xul->mBoxPack, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentXUL->mBoxPack, NS_STYLE_BOX_PACK_START, 0, 0, 0, 0); // box-ordinal-group: integer, inherit, initial SetDiscrete(*aRuleData->ValueForBoxOrdinalGroup(), xul->mBoxOrdinal, canStoreInRuleTree, SETDSC_INTEGER | SETDSC_UNSET_INITIAL, parentXUL->mBoxOrdinal, 1, 0, 0, 0, 0); const nsCSSValue* stackSizingValue = aRuleData->ValueForStackSizing(); if (eCSSUnit_Inherit == stackSizingValue->GetUnit()) { canStoreInRuleTree = false; xul->mStretchStack = parentXUL->mStretchStack; } else if (eCSSUnit_Initial == stackSizingValue->GetUnit() || eCSSUnit_Unset == stackSizingValue->GetUnit()) { xul->mStretchStack = true; } else if (eCSSUnit_Enumerated == stackSizingValue->GetUnit()) { xul->mStretchStack = stackSizingValue->GetIntValue() == NS_STYLE_STACK_SIZING_STRETCH_TO_FIT; } COMPUTE_END_RESET(XUL, xul) } const void* nsRuleNode::ComputeColumnData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(Column, (mPresContext), column, parent) // column-width: length, auto, inherit SetCoord(*aRuleData->ValueForColumnWidth(), column->mColumnWidth, parent->mColumnWidth, SETCOORD_LAH | SETCOORD_INITIAL_AUTO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); // column-gap: length, inherit, normal SetCoord(*aRuleData->ValueForColumnGap(), column->mColumnGap, parent->mColumnGap, SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, aContext, mPresContext, canStoreInRuleTree); // clamp negative calc() to 0 if (column->mColumnGap.GetUnit() == eStyleUnit_Coord) { column->mColumnGap.SetCoordValue( std::max(column->mColumnGap.GetCoordValue(), 0)); } // column-count: auto, integer, inherit const nsCSSValue* columnCountValue = aRuleData->ValueForColumnCount(); if (eCSSUnit_Auto == columnCountValue->GetUnit() || eCSSUnit_Initial == columnCountValue->GetUnit() || eCSSUnit_Unset == columnCountValue->GetUnit()) { column->mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO; } else if (eCSSUnit_Integer == columnCountValue->GetUnit()) { column->mColumnCount = columnCountValue->GetIntValue(); // Max kMaxColumnCount columns - wallpaper for bug 345583. column->mColumnCount = std::min(column->mColumnCount, nsStyleColumn::kMaxColumnCount); } else if (eCSSUnit_Inherit == columnCountValue->GetUnit()) { canStoreInRuleTree = false; column->mColumnCount = parent->mColumnCount; } // column-rule-width: length, enum, inherit const nsCSSValue& widthValue = *aRuleData->ValueForColumnRuleWidth(); if (eCSSUnit_Initial == widthValue.GetUnit() || eCSSUnit_Unset == widthValue.GetUnit()) { column->SetColumnRuleWidth( (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); } else if (eCSSUnit_Enumerated == widthValue.GetUnit()) { NS_ASSERTION(widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, "Unexpected enum value"); column->SetColumnRuleWidth( (mPresContext->GetBorderWidthTable())[widthValue.GetIntValue()]); } else if (eCSSUnit_Inherit == widthValue.GetUnit()) { column->SetColumnRuleWidth(parent->GetComputedColumnRuleWidth()); canStoreInRuleTree = false; } else if (widthValue.IsLengthUnit() || widthValue.IsCalcUnit()) { nscoord len = CalcLength(widthValue, aContext, mPresContext, canStoreInRuleTree); if (len < 0) { // FIXME: This is untested (by test_value_storage.html) for // column-rule-width since it gets covered up by the border // rounding code. NS_ASSERTION(widthValue.IsCalcUnit(), "parser should have rejected negative length"); len = 0; } column->SetColumnRuleWidth(len); } // column-rule-style: enum, inherit const nsCSSValue& styleValue = *aRuleData->ValueForColumnRuleStyle(); NS_ABORT_IF_FALSE(eCSSUnit_None != styleValue.GetUnit(), "'none' should be handled as enumerated value"); if (eCSSUnit_Enumerated == styleValue.GetUnit()) { column->mColumnRuleStyle = styleValue.GetIntValue(); } else if (eCSSUnit_Initial == styleValue.GetUnit() || eCSSUnit_Unset == styleValue.GetUnit()) { column->mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE; } else if (eCSSUnit_Inherit == styleValue.GetUnit()) { canStoreInRuleTree = false; column->mColumnRuleStyle = parent->mColumnRuleStyle; } // column-rule-color: color, inherit const nsCSSValue& colorValue = *aRuleData->ValueForColumnRuleColor(); if (eCSSUnit_Inherit == colorValue.GetUnit()) { canStoreInRuleTree = false; column->mColumnRuleColorIsForeground = false; if (parent->mColumnRuleColorIsForeground) { if (parentContext) { column->mColumnRuleColor = parentContext->StyleColor()->mColor; } else { nsStyleColor defaultColumnRuleColor(mPresContext); column->mColumnRuleColor = defaultColumnRuleColor.mColor; } } else { column->mColumnRuleColor = parent->mColumnRuleColor; } } else if (eCSSUnit_Initial == colorValue.GetUnit() || eCSSUnit_Unset == colorValue.GetUnit() || eCSSUnit_Enumerated == colorValue.GetUnit()) { column->mColumnRuleColorIsForeground = true; } else if (SetColor(colorValue, 0, mPresContext, aContext, column->mColumnRuleColor, canStoreInRuleTree)) { column->mColumnRuleColorIsForeground = false; } // column-fill: enum SetDiscrete(*aRuleData->ValueForColumnFill(), column->mColumnFill, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parent->mColumnFill, NS_STYLE_COLUMN_FILL_BALANCE, 0, 0, 0, 0); COMPUTE_END_RESET(Column, column) } static void SetSVGPaint(const nsCSSValue& aValue, const nsStyleSVGPaint& parentPaint, nsPresContext* aPresContext, nsStyleContext *aContext, nsStyleSVGPaint& aResult, nsStyleSVGPaintType aInitialPaintType, bool& aCanStoreInRuleTree) { nscolor color; if (aValue.GetUnit() == eCSSUnit_Inherit || aValue.GetUnit() == eCSSUnit_Unset) { aResult = parentPaint; aCanStoreInRuleTree = false; } else if (aValue.GetUnit() == eCSSUnit_None) { aResult.SetType(eStyleSVGPaintType_None); } else if (aValue.GetUnit() == eCSSUnit_Initial) { aResult.SetType(aInitialPaintType); aResult.mPaint.mColor = NS_RGB(0, 0, 0); aResult.mFallbackColor = NS_RGB(0, 0, 0); } else if (SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aContext, color, aCanStoreInRuleTree)) { aResult.SetType(eStyleSVGPaintType_Color); aResult.mPaint.mColor = color; } else if (aValue.GetUnit() == eCSSUnit_Pair) { const nsCSSValuePair& pair = aValue.GetPairValue(); if (pair.mXValue.GetUnit() == eCSSUnit_URL) { aResult.SetType(eStyleSVGPaintType_Server); aResult.mPaint.mPaintServer = pair.mXValue.GetURLValue(); NS_IF_ADDREF(aResult.mPaint.mPaintServer); } else if (pair.mXValue.GetUnit() == eCSSUnit_Enumerated) { switch (pair.mXValue.GetIntValue()) { case NS_COLOR_CONTEXT_FILL: aResult.SetType(eStyleSVGPaintType_ContextFill); break; case NS_COLOR_CONTEXT_STROKE: aResult.SetType(eStyleSVGPaintType_ContextStroke); break; default: NS_NOTREACHED("unknown keyword as paint server value"); } } else { NS_NOTREACHED("malformed paint server value"); } if (pair.mYValue.GetUnit() == eCSSUnit_None) { aResult.mFallbackColor = NS_RGBA(0, 0, 0, 0); } else { NS_ABORT_IF_FALSE(pair.mYValue.GetUnit() != eCSSUnit_Inherit, "cannot inherit fallback colour"); SetColor(pair.mYValue, NS_RGB(0, 0, 0), aPresContext, aContext, aResult.mFallbackColor, aCanStoreInRuleTree); } } else { NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Null, "malformed paint server value"); } } static void SetSVGOpacity(const nsCSSValue& aValue, float& aOpacityField, nsStyleSVGOpacitySource& aOpacityTypeField, bool& aCanStoreInRuleTree, float aParentOpacity, nsStyleSVGOpacitySource aParentOpacityType) { if (eCSSUnit_Enumerated == aValue.GetUnit()) { switch (aValue.GetIntValue()) { case NS_STYLE_CONTEXT_FILL_OPACITY: aOpacityTypeField = eStyleSVGOpacitySource_ContextFillOpacity; break; case NS_STYLE_CONTEXT_STROKE_OPACITY: aOpacityTypeField = eStyleSVGOpacitySource_ContextStrokeOpacity; break; default: NS_NOTREACHED("SetSVGOpacity: Unknown keyword"); } // Fall back on fully opaque aOpacityField = 1.0f; } else if (eCSSUnit_Inherit == aValue.GetUnit() || eCSSUnit_Unset == aValue.GetUnit()) { aCanStoreInRuleTree = false; aOpacityField = aParentOpacity; aOpacityTypeField = aParentOpacityType; } else if (eCSSUnit_Null != aValue.GetUnit()) { SetFactor(aValue, aOpacityField, aCanStoreInRuleTree, aParentOpacity, 1.0f, SETFCT_OPACITY); aOpacityTypeField = eStyleSVGOpacitySource_Normal; } } const void* nsRuleNode::ComputeSVGData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(SVG, (), svg, parentSVG) // clip-rule: enum, inherit, initial SetDiscrete(*aRuleData->ValueForClipRule(), svg->mClipRule, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mClipRule, NS_STYLE_FILL_RULE_NONZERO, 0, 0, 0, 0); // color-interpolation: enum, inherit, initial SetDiscrete(*aRuleData->ValueForColorInterpolation(), svg->mColorInterpolation, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mColorInterpolation, NS_STYLE_COLOR_INTERPOLATION_SRGB, 0, 0, 0, 0); // color-interpolation-filters: enum, inherit, initial SetDiscrete(*aRuleData->ValueForColorInterpolationFilters(), svg->mColorInterpolationFilters, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mColorInterpolationFilters, NS_STYLE_COLOR_INTERPOLATION_LINEARRGB, 0, 0, 0, 0); // fill: SetSVGPaint(*aRuleData->ValueForFill(), parentSVG->mFill, mPresContext, aContext, svg->mFill, eStyleSVGPaintType_Color, canStoreInRuleTree); // fill-opacity: factor, inherit, initial, // context-fill-opacity, context-stroke-opacity nsStyleSVGOpacitySource contextFillOpacity = svg->mFillOpacitySource; SetSVGOpacity(*aRuleData->ValueForFillOpacity(), svg->mFillOpacity, contextFillOpacity, canStoreInRuleTree, parentSVG->mFillOpacity, parentSVG->mFillOpacitySource); svg->mFillOpacitySource = contextFillOpacity; // fill-rule: enum, inherit, initial SetDiscrete(*aRuleData->ValueForFillRule(), svg->mFillRule, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mFillRule, NS_STYLE_FILL_RULE_NONZERO, 0, 0, 0, 0); // image-rendering: enum, inherit SetDiscrete(*aRuleData->ValueForImageRendering(), svg->mImageRendering, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mImageRendering, NS_STYLE_IMAGE_RENDERING_AUTO, 0, 0, 0, 0); // marker-end: url, none, inherit const nsCSSValue* markerEndValue = aRuleData->ValueForMarkerEnd(); if (eCSSUnit_URL == markerEndValue->GetUnit()) { svg->mMarkerEnd = markerEndValue->GetURLValue(); } else if (eCSSUnit_None == markerEndValue->GetUnit() || eCSSUnit_Initial == markerEndValue->GetUnit()) { svg->mMarkerEnd = nullptr; } else if (eCSSUnit_Inherit == markerEndValue->GetUnit() || eCSSUnit_Unset == markerEndValue->GetUnit()) { canStoreInRuleTree = false; svg->mMarkerEnd = parentSVG->mMarkerEnd; } // marker-mid: url, none, inherit const nsCSSValue* markerMidValue = aRuleData->ValueForMarkerMid(); if (eCSSUnit_URL == markerMidValue->GetUnit()) { svg->mMarkerMid = markerMidValue->GetURLValue(); } else if (eCSSUnit_None == markerMidValue->GetUnit() || eCSSUnit_Initial == markerMidValue->GetUnit()) { svg->mMarkerMid = nullptr; } else if (eCSSUnit_Inherit == markerMidValue->GetUnit() || eCSSUnit_Unset == markerMidValue->GetUnit()) { canStoreInRuleTree = false; svg->mMarkerMid = parentSVG->mMarkerMid; } // marker-start: url, none, inherit const nsCSSValue* markerStartValue = aRuleData->ValueForMarkerStart(); if (eCSSUnit_URL == markerStartValue->GetUnit()) { svg->mMarkerStart = markerStartValue->GetURLValue(); } else if (eCSSUnit_None == markerStartValue->GetUnit() || eCSSUnit_Initial == markerStartValue->GetUnit()) { svg->mMarkerStart = nullptr; } else if (eCSSUnit_Inherit == markerStartValue->GetUnit() || eCSSUnit_Unset == markerStartValue->GetUnit()) { canStoreInRuleTree = false; svg->mMarkerStart = parentSVG->mMarkerStart; } // paint-order: enum (bit field), inherit, initial const nsCSSValue* paintOrderValue = aRuleData->ValueForPaintOrder(); switch (paintOrderValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Enumerated: static_assert (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8, "SVGStyleStruct::mPaintOrder not big enough"); svg->mPaintOrder = static_cast(paintOrderValue->GetIntValue()); break; case eCSSUnit_Inherit: case eCSSUnit_Unset: canStoreInRuleTree = false; svg->mPaintOrder = parentSVG->mPaintOrder; break; case eCSSUnit_Initial: svg->mPaintOrder = NS_STYLE_PAINT_ORDER_NORMAL; break; default: NS_NOTREACHED("unexpected unit"); } // shape-rendering: enum, inherit SetDiscrete(*aRuleData->ValueForShapeRendering(), svg->mShapeRendering, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mShapeRendering, NS_STYLE_SHAPE_RENDERING_AUTO, 0, 0, 0, 0); // stroke: SetSVGPaint(*aRuleData->ValueForStroke(), parentSVG->mStroke, mPresContext, aContext, svg->mStroke, eStyleSVGPaintType_None, canStoreInRuleTree); // stroke-dasharray: , none, inherit, context-value const nsCSSValue* strokeDasharrayValue = aRuleData->ValueForStrokeDasharray(); switch (strokeDasharrayValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: case eCSSUnit_Unset: canStoreInRuleTree = false; svg->mStrokeDasharrayFromObject = parentSVG->mStrokeDasharrayFromObject; // only do the copy if weren't already set up by the copy constructor // FIXME Bug 389408: This is broken when aStartStruct is non-null! if (!svg->mStrokeDasharray) { svg->mStrokeDasharrayLength = parentSVG->mStrokeDasharrayLength; if (svg->mStrokeDasharrayLength) { svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength]; if (svg->mStrokeDasharray) memcpy(svg->mStrokeDasharray, parentSVG->mStrokeDasharray, svg->mStrokeDasharrayLength * sizeof(nsStyleCoord)); else svg->mStrokeDasharrayLength = 0; } } break; case eCSSUnit_Enumerated: NS_ABORT_IF_FALSE(strokeDasharrayValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE, "Unknown keyword for stroke-dasharray"); svg->mStrokeDasharrayFromObject = true; delete [] svg->mStrokeDasharray; svg->mStrokeDasharray = nullptr; svg->mStrokeDasharrayLength = 0; break; case eCSSUnit_Initial: case eCSSUnit_None: svg->mStrokeDasharrayFromObject = false; delete [] svg->mStrokeDasharray; svg->mStrokeDasharray = nullptr; svg->mStrokeDasharrayLength = 0; break; case eCSSUnit_List: case eCSSUnit_ListDep: { svg->mStrokeDasharrayFromObject = false; delete [] svg->mStrokeDasharray; svg->mStrokeDasharray = nullptr; svg->mStrokeDasharrayLength = 0; // count number of values const nsCSSValueList *value = strokeDasharrayValue->GetListValue(); svg->mStrokeDasharrayLength = ListLength(value); NS_ASSERTION(svg->mStrokeDasharrayLength != 0, "no dasharray items"); svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength]; if (svg->mStrokeDasharray) { uint32_t i = 0; while (nullptr != value) { SetCoord(value->mValue, svg->mStrokeDasharray[i++], nsStyleCoord(), SETCOORD_LP | SETCOORD_FACTOR, aContext, mPresContext, canStoreInRuleTree); value = value->mNext; } } else { svg->mStrokeDasharrayLength = 0; } break; } default: NS_ABORT_IF_FALSE(false, "unrecognized dasharray unit"); } // stroke-dashoffset: , inherit const nsCSSValue *strokeDashoffsetValue = aRuleData->ValueForStrokeDashoffset(); svg->mStrokeDashoffsetFromObject = strokeDashoffsetValue->GetUnit() == eCSSUnit_Enumerated && strokeDashoffsetValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE; if (svg->mStrokeDashoffsetFromObject) { svg->mStrokeDashoffset.SetCoordValue(0); } else { SetCoord(*aRuleData->ValueForStrokeDashoffset(), svg->mStrokeDashoffset, parentSVG->mStrokeDashoffset, SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_INITIAL_ZERO | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); } // stroke-linecap: enum, inherit, initial SetDiscrete(*aRuleData->ValueForStrokeLinecap(), svg->mStrokeLinecap, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mStrokeLinecap, NS_STYLE_STROKE_LINECAP_BUTT, 0, 0, 0, 0); // stroke-linejoin: enum, inherit, initial SetDiscrete(*aRuleData->ValueForStrokeLinejoin(), svg->mStrokeLinejoin, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mStrokeLinejoin, NS_STYLE_STROKE_LINEJOIN_MITER, 0, 0, 0, 0); // stroke-miterlimit: , inherit SetFactor(*aRuleData->ValueForStrokeMiterlimit(), svg->mStrokeMiterlimit, canStoreInRuleTree, parentSVG->mStrokeMiterlimit, 4.0f, SETFCT_UNSET_INHERIT); // stroke-opacity: nsStyleSVGOpacitySource contextStrokeOpacity = svg->mStrokeOpacitySource; SetSVGOpacity(*aRuleData->ValueForStrokeOpacity(), svg->mStrokeOpacity, contextStrokeOpacity, canStoreInRuleTree, parentSVG->mStrokeOpacity, parentSVG->mStrokeOpacitySource); svg->mStrokeOpacitySource = contextStrokeOpacity; // stroke-width: const nsCSSValue* strokeWidthValue = aRuleData->ValueForStrokeWidth(); switch (strokeWidthValue->GetUnit()) { case eCSSUnit_Enumerated: NS_ABORT_IF_FALSE(strokeWidthValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE, "Unrecognized keyword for stroke-width"); svg->mStrokeWidthFromObject = true; svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); break; case eCSSUnit_Initial: svg->mStrokeWidthFromObject = false; svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); break; default: svg->mStrokeWidthFromObject = false; SetCoord(*strokeWidthValue, svg->mStrokeWidth, parentSVG->mStrokeWidth, SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_UNSET_INHERIT, aContext, mPresContext, canStoreInRuleTree); } // text-anchor: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTextAnchor(), svg->mTextAnchor, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mTextAnchor, NS_STYLE_TEXT_ANCHOR_START, 0, 0, 0, 0); // text-rendering: enum, inherit, initial SetDiscrete(*aRuleData->ValueForTextRendering(), svg->mTextRendering, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentSVG->mTextRendering, NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0); COMPUTE_END_INHERITED(SVG, svg) } // Returns true if the nsStyleFilter was successfully set using the nsCSSValue. bool nsRuleNode::SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool& aCanStoreInRuleTree) { nsCSSUnit unit = aValue.GetUnit(); if (unit == eCSSUnit_URL) { nsIURI* url = aValue.GetURLValue(); if (!url) return false; aStyleFilter->SetURL(url); return true; } NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function"); nsCSSValue::Array* filterFunction = aValue.GetArrayValue(); nsCSSKeyword functionName = (nsCSSKeyword)filterFunction->Item(0).GetIntValue(); int32_t type; DebugOnly foundKeyword = nsCSSProps::FindKeyword(functionName, nsCSSProps::kFilterFunctionKTable, type); NS_ABORT_IF_FALSE(foundKeyword, "unknown filter type"); if (type == NS_STYLE_FILTER_DROP_SHADOW) { nsRefPtr shadowArray = GetShadowData( filterFunction->Item(1).GetListValue(), aStyleContext, false, aCanStoreInRuleTree); aStyleFilter->SetDropShadow(shadowArray); return true; } int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR; if (type == NS_STYLE_FILTER_BLUR) { mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC; } else if (type == NS_STYLE_FILTER_HUE_ROTATE) { mask = SETCOORD_ANGLE; } NS_ABORT_IF_FALSE(filterFunction->Count() == 2, "all filter functions should have " "exactly one argument"); nsCSSValue& arg = filterFunction->Item(1); nsStyleCoord filterParameter; DebugOnly didSetCoord = SetCoord(arg, filterParameter, nsStyleCoord(), mask, aStyleContext, aPresContext, aCanStoreInRuleTree); aStyleFilter->SetFilterParameter(filterParameter, type); NS_ABORT_IF_FALSE(didSetCoord, "unexpected unit"); return true; } const void* nsRuleNode::ComputeSVGResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_RESET(SVGReset, (), svgReset, parentSVGReset) // stop-color: const nsCSSValue* stopColorValue = aRuleData->ValueForStopColor(); if (eCSSUnit_Initial == stopColorValue->GetUnit() || eCSSUnit_Unset == stopColorValue->GetUnit()) { svgReset->mStopColor = NS_RGB(0, 0, 0); } else { SetColor(*stopColorValue, parentSVGReset->mStopColor, mPresContext, aContext, svgReset->mStopColor, canStoreInRuleTree); } // flood-color: const nsCSSValue* floodColorValue = aRuleData->ValueForFloodColor(); if (eCSSUnit_Initial == floodColorValue->GetUnit() || eCSSUnit_Unset == floodColorValue->GetUnit()) { svgReset->mFloodColor = NS_RGB(0, 0, 0); } else { SetColor(*floodColorValue, parentSVGReset->mFloodColor, mPresContext, aContext, svgReset->mFloodColor, canStoreInRuleTree); } // lighting-color: const nsCSSValue* lightingColorValue = aRuleData->ValueForLightingColor(); if (eCSSUnit_Initial == lightingColorValue->GetUnit() || eCSSUnit_Unset == lightingColorValue->GetUnit()) { svgReset->mLightingColor = NS_RGB(255, 255, 255); } else { SetColor(*lightingColorValue, parentSVGReset->mLightingColor, mPresContext, aContext, svgReset->mLightingColor, canStoreInRuleTree); } // clip-path: url, none, inherit const nsCSSValue* clipPathValue = aRuleData->ValueForClipPath(); if (eCSSUnit_URL == clipPathValue->GetUnit()) { svgReset->mClipPath = clipPathValue->GetURLValue(); } else if (eCSSUnit_None == clipPathValue->GetUnit() || eCSSUnit_Initial == clipPathValue->GetUnit() || eCSSUnit_Unset == clipPathValue->GetUnit()) { svgReset->mClipPath = nullptr; } else if (eCSSUnit_Inherit == clipPathValue->GetUnit()) { canStoreInRuleTree = false; svgReset->mClipPath = parentSVGReset->mClipPath; } // stop-opacity: SetFactor(*aRuleData->ValueForStopOpacity(), svgReset->mStopOpacity, canStoreInRuleTree, parentSVGReset->mStopOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // flood-opacity: SetFactor(*aRuleData->ValueForFloodOpacity(), svgReset->mFloodOpacity, canStoreInRuleTree, parentSVGReset->mFloodOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // dominant-baseline: enum, inherit, initial SetDiscrete(*aRuleData->ValueForDominantBaseline(), svgReset->mDominantBaseline, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentSVGReset->mDominantBaseline, NS_STYLE_DOMINANT_BASELINE_AUTO, 0, 0, 0, 0); // vector-effect: enum, inherit, initial SetDiscrete(*aRuleData->ValueForVectorEffect(), svgReset->mVectorEffect, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentSVGReset->mVectorEffect, NS_STYLE_VECTOR_EFFECT_NONE, 0, 0, 0, 0); // filter: url, none, inherit const nsCSSValue* filterValue = aRuleData->ValueForFilter(); switch (filterValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: svgReset->mFilters.Clear(); break; case eCSSUnit_Inherit: canStoreInRuleTree = false; svgReset->mFilters = parentSVGReset->mFilters; break; case eCSSUnit_List: case eCSSUnit_ListDep: { svgReset->mFilters.Clear(); const nsCSSValueList* cur = filterValue->GetListValue(); while(cur) { nsStyleFilter styleFilter; if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, mPresContext, canStoreInRuleTree)) { svgReset->mFilters.Clear(); break; } NS_ABORT_IF_FALSE(styleFilter.GetType() != NS_STYLE_FILTER_NONE, "filter should be set"); svgReset->mFilters.AppendElement(styleFilter); cur = cur->mNext; } break; } default: NS_NOTREACHED("unexpected unit"); } // mask: url, none, inherit const nsCSSValue* maskValue = aRuleData->ValueForMask(); if (eCSSUnit_URL == maskValue->GetUnit()) { svgReset->mMask = maskValue->GetURLValue(); } else if (eCSSUnit_None == maskValue->GetUnit() || eCSSUnit_Initial == maskValue->GetUnit() || eCSSUnit_Unset == maskValue->GetUnit()) { svgReset->mMask = nullptr; } else if (eCSSUnit_Inherit == maskValue->GetUnit()) { canStoreInRuleTree = false; svgReset->mMask = parentSVGReset->mMask; } // mask-type: enum, inherit, initial SetDiscrete(*aRuleData->ValueForMaskType(), svgReset->mMaskType, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentSVGReset->mMaskType, NS_STYLE_MASK_TYPE_LUMINANCE, 0, 0, 0, 0); COMPUTE_END_RESET(SVGReset, svgReset) } const void* nsRuleNode::ComputeVariablesData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const bool aCanStoreInRuleTree) { COMPUTE_START_INHERITED(Variables, (), variables, parentVariables) MOZ_ASSERT(aRuleData->mVariables, "shouldn't be in ComputeVariablesData if there were no variable " "declarations specified"); CSSVariableResolver resolver(&variables->mVariables); resolver.Resolve(&parentVariables->mVariables, aRuleData->mVariables); canStoreInRuleTree = false; COMPUTE_END_INHERITED(Variables, variables) } const void* nsRuleNode::GetStyleData(nsStyleStructID aSID, nsStyleContext* aContext, bool aComputeData) { NS_ASSERTION(IsUsedDirectly(), "if we ever call this on rule nodes that aren't used " "directly, we should adjust handling of mDependentBits " "in some way."); const void *data; data = mStyleData.GetStyleData(aSID); if (MOZ_LIKELY(data != nullptr)) return data; // We have a fully specified struct. Just return it. if (MOZ_UNLIKELY(!aComputeData)) return nullptr; // Nothing is cached. We'll have to delve further and examine our rules. data = WalkRuleTree(aSID, aContext); NS_ABORT_IF_FALSE(data, "should have aborted on out-of-memory"); return data; } // See comments above in GetStyleData for an explanation of what the // code below does. #define STYLE_STRUCT(name_, checkdata_cb_) \ const nsStyle##name_* \ nsRuleNode::GetStyle##name_(nsStyleContext* aContext, bool aComputeData) \ { \ NS_ASSERTION(IsUsedDirectly(), \ "if we ever call this on rule nodes that aren't used " \ "directly, we should adjust handling of mDependentBits " \ "in some way."); \ \ const nsStyle##name_ *data; \ data = mStyleData.GetStyle##name_(); \ if (MOZ_LIKELY(data != nullptr)) \ return data; \ \ if (MOZ_UNLIKELY(!aComputeData)) \ return nullptr; \ \ data = static_cast \ (WalkRuleTree(eStyleStruct_##name_, aContext)); \ \ NS_ABORT_IF_FALSE(data, "should have aborted on out-of-memory"); \ return data; \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT void nsRuleNode::Mark() { for (nsRuleNode *node = this; node && !(node->mDependentBits & NS_RULE_NODE_GC_MARK); node = node->mParent) node->mDependentBits |= NS_RULE_NODE_GC_MARK; } static PLDHashOperator SweepRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number, void *arg) { ChildrenHashEntry *entry = static_cast(hdr); if (entry->mRuleNode->Sweep()) return PL_DHASH_REMOVE; // implies NEXT, unless |ed with STOP return PL_DHASH_NEXT; } bool nsRuleNode::Sweep() { // If we're not marked, then we have to delete ourself. // However, we never allow the root node to GC itself, because nsStyleSet // wants to hold onto the root node and not worry about re-creating a // rule walker if the root node is deleted. if (!(mDependentBits & NS_RULE_NODE_GC_MARK) && // Skip this only if we're the *current* root and not an old one. !(IsRoot() && mPresContext->StyleSet()->GetRuleTree() == this)) { Destroy(); return true; } // Clear our mark, for the next time around. mDependentBits &= ~NS_RULE_NODE_GC_MARK; // Call sweep on the children, since some may not be marked, and // remove any deleted children from the child lists. if (HaveChildren()) { uint32_t childrenDestroyed; if (ChildrenAreHashed()) { PLDHashTable *children = ChildrenHash(); uint32_t oldChildCount = children->entryCount; PL_DHashTableEnumerate(children, SweepRuleNodeChildren, nullptr); childrenDestroyed = children->entryCount - oldChildCount; } else { childrenDestroyed = 0; for (nsRuleNode **children = ChildrenListPtr(); *children; ) { nsRuleNode *next = (*children)->mNextSibling; if ((*children)->Sweep()) { // This rule node was destroyed, so implicitly advance by // making *children point to the next entry. *children = next; ++childrenDestroyed; } else { // Advance. children = &(*children)->mNextSibling; } } } mRefCnt -= childrenDestroyed; NS_POSTCONDITION(IsRoot() || mRefCnt > 0, "We didn't get swept, so we'd better have style contexts " "pointing to us or to one of our descendants, which means " "we'd better have a nonzero mRefCnt here!"); } return false; } /* static */ bool nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, uint32_t ruleTypeMask, bool aAuthorColorsAllowed) { uint32_t inheritBits = 0; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) inheritBits |= NS_STYLE_INHERIT_BIT(Background); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) inheritBits |= NS_STYLE_INHERIT_BIT(Border); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) inheritBits |= NS_STYLE_INHERIT_BIT(Padding); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) inheritBits |= NS_STYLE_INHERIT_BIT(Text); // properties in the SIDS, whether or not we care about them size_t nprops = 0, backgroundOffset, borderOffset, paddingOffset, textShadowOffset; // We put the reset properties the start of the nsCSSValue array.... if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { backgroundOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Background); } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { borderOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Border); } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { paddingOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Padding); } // ...and the inherited properties at the end of the array. size_t inheritedOffset = nprops; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { textShadowOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Text); } void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); AutoCSSValueArray dataArray(dataStorage, nprops); /* We're relying on the use of |aStyleContext| not mutating it! */ nsRuleData ruleData(inheritBits, dataArray.get(), aStyleContext->PresContext(), aStyleContext); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { ruleData.mValueOffsets[eStyleStruct_Background] = backgroundOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { ruleData.mValueOffsets[eStyleStruct_Border] = borderOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { ruleData.mValueOffsets[eStyleStruct_Padding] = paddingOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { ruleData.mValueOffsets[eStyleStruct_Text] = textShadowOffset; } static const nsCSSProperty backgroundValues[] = { eCSSProperty_background_color, eCSSProperty_background_image, }; static const nsCSSProperty borderValues[] = { eCSSProperty_border_top_color, eCSSProperty_border_top_style, eCSSProperty_border_top_width, eCSSProperty_border_right_color_value, eCSSProperty_border_right_style_value, eCSSProperty_border_right_width_value, eCSSProperty_border_bottom_color, eCSSProperty_border_bottom_style, eCSSProperty_border_bottom_width, eCSSProperty_border_left_color_value, eCSSProperty_border_left_style_value, eCSSProperty_border_left_width_value, eCSSProperty_border_start_color_value, eCSSProperty_border_start_style_value, eCSSProperty_border_start_width_value, eCSSProperty_border_end_color_value, eCSSProperty_border_end_style_value, eCSSProperty_border_end_width_value, eCSSProperty_border_top_left_radius, eCSSProperty_border_top_right_radius, eCSSProperty_border_bottom_right_radius, eCSSProperty_border_bottom_left_radius, }; static const nsCSSProperty paddingValues[] = { eCSSProperty_padding_top, eCSSProperty_padding_right_value, eCSSProperty_padding_bottom, eCSSProperty_padding_left_value, eCSSProperty_padding_start_value, eCSSProperty_padding_end_value, }; static const nsCSSProperty textShadowValues[] = { eCSSProperty_text_shadow }; // Number of properties we care about size_t nValues = 0; nsCSSValue* values[MOZ_ARRAY_LENGTH(backgroundValues) + MOZ_ARRAY_LENGTH(borderValues) + MOZ_ARRAY_LENGTH(paddingValues) + MOZ_ARRAY_LENGTH(textShadowValues)]; nsCSSProperty properties[MOZ_ARRAY_LENGTH(backgroundValues) + MOZ_ARRAY_LENGTH(borderValues) + MOZ_ARRAY_LENGTH(paddingValues) + MOZ_ARRAY_LENGTH(textShadowValues)]; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { for (uint32_t i = 0, i_end = ArrayLength(backgroundValues); i < i_end; ++i) { properties[nValues] = backgroundValues[i]; values[nValues++] = ruleData.ValueFor(backgroundValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { for (uint32_t i = 0, i_end = ArrayLength(borderValues); i < i_end; ++i) { properties[nValues] = borderValues[i]; values[nValues++] = ruleData.ValueFor(borderValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { for (uint32_t i = 0, i_end = ArrayLength(paddingValues); i < i_end; ++i) { properties[nValues] = paddingValues[i]; values[nValues++] = ruleData.ValueFor(paddingValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { for (uint32_t i = 0, i_end = ArrayLength(textShadowValues); i < i_end; ++i) { properties[nValues] = textShadowValues[i]; values[nValues++] = ruleData.ValueFor(textShadowValues[i]); } } nsStyleContext* styleContext = aStyleContext; // We need to be careful not to count styles covered up by user-important or // UA-important declarations. But we do want to catch explicit inherit // styling in those and check our parent style context to see whether we have // user styling for those properties. Note that we don't care here about // inheritance due to lack of a specified value, since all the properties we // care about are reset properties. bool haveExplicitUAInherit; do { haveExplicitUAInherit = false; for (nsRuleNode* ruleNode = styleContext->RuleNode(); ruleNode; ruleNode = ruleNode->GetParent()) { nsIStyleRule *rule = ruleNode->GetRule(); if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); if (ruleData.mLevel == nsStyleSet::eAgentSheet || ruleData.mLevel == nsStyleSet::eUserSheet) { // This is a rule whose effect we want to ignore, so if any of // the properties we care about were set, set them to the dummy // value that they'll never otherwise get. for (uint32_t i = 0; i < nValues; ++i) { nsCSSUnit unit = values[i]->GetUnit(); if (unit != eCSSUnit_Null && unit != eCSSUnit_Dummy && unit != eCSSUnit_DummyInherit) { if (unit == eCSSUnit_Inherit || (i >= inheritedOffset && unit == eCSSUnit_Unset)) { haveExplicitUAInherit = true; values[i]->SetDummyInheritValue(); } else { values[i]->SetDummyValue(); } } } } else { // If any of the values we care about was set by the above rule, // we have author style. for (uint32_t i = 0; i < nValues; ++i) { if (values[i]->GetUnit() != eCSSUnit_Null && values[i]->GetUnit() != eCSSUnit_Dummy && // see above values[i]->GetUnit() != eCSSUnit_DummyInherit) { // If author colors are not allowed, only claim to have // author-specified rules if we're looking at a non-color // property or if we're looking at the background color and it's // set to transparent. Anything else should get set to a dummy // value instead. if (aAuthorColorsAllowed || !nsCSSProps::PropHasFlags(properties[i], CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) || (properties[i] == eCSSProperty_background_color && !values[i]->IsNonTransparentColor())) { return true; } values[i]->SetDummyValue(); } } } } } if (haveExplicitUAInherit) { // reset all the eCSSUnit_Null values to eCSSUnit_Dummy (since they're // not styled by the author, or by anyone else), and then reset all the // eCSSUnit_DummyInherit values to eCSSUnit_Null (so we will be able to // detect them being styled by the author) and move up to our parent // style context. for (uint32_t i = 0; i < nValues; ++i) if (values[i]->GetUnit() == eCSSUnit_Null) values[i]->SetDummyValue(); for (uint32_t i = 0; i < nValues; ++i) if (values[i]->GetUnit() == eCSSUnit_DummyInherit) values[i]->Reset(); styleContext = styleContext->GetParent(); } } while (haveExplicitUAInherit && styleContext); return false; } /* static */ bool nsRuleNode::ComputeColor(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aStyleContext, nscolor& aResult) { MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Inherit, "aValue shouldn't have eCSSUnit_Inherit"); MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Initial, "aValue shouldn't have eCSSUnit_Initial"); MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Unset, "aValue shouldn't have eCSSUnit_Unset"); bool canStoreInRuleTree; bool ok = SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aStyleContext, aResult, canStoreInRuleTree); MOZ_ASSERT(ok || !(aPresContext && aStyleContext)); return ok; }