michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * a node in the lexicographic tree of rules that match an element, michael@0: * responsible for converting the rules' information into computed style michael@0: */ michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/Likely.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: michael@0: #include "nsRuleNode.h" michael@0: #include "nscore.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsFontMetrics.h" michael@0: #include "gfxFont.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsThemeConstants.h" michael@0: #include "pldhash.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsStyleStruct.h" michael@0: #include "nsSize.h" michael@0: #include "nsRuleData.h" michael@0: #include "gfxUserFontSet.h" michael@0: #include "nsIStyleRule.h" michael@0: #include "nsBidiUtils.h" michael@0: #include "nsStyleStructInlines.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsTArray.h" michael@0: #include "nsContentUtils.h" michael@0: #include "CSSCalc.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsStyleUtil.h" michael@0: #include "nsIDocument.h" michael@0: #include "prtime.h" michael@0: #include "CSSVariableResolver.h" michael@0: #include "nsCSSParser.h" michael@0: michael@0: #if defined(_MSC_VER) || defined(__MINGW32__) michael@0: #include michael@0: #ifdef _MSC_VER michael@0: #define alloca _alloca michael@0: #endif michael@0: #endif michael@0: #ifdef SOLARIS michael@0: #include michael@0: #endif michael@0: michael@0: using std::max; michael@0: using std::min; michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: #define NS_SET_IMAGE_REQUEST(method_, context_, request_) \ michael@0: if ((context_)->PresContext()->IsDynamic()) { \ michael@0: method_(request_); \ michael@0: } else { \ michael@0: nsRefPtr req = nsContentUtils::GetStaticRequest(request_); \ michael@0: method_(req); \ michael@0: } michael@0: michael@0: #define NS_SET_IMAGE_REQUEST_WITH_DOC(method_, context_, requestgetter_) \ michael@0: { \ michael@0: nsIDocument* doc = (context_)->PresContext()->Document(); \ michael@0: NS_SET_IMAGE_REQUEST(method_, context_, requestgetter_(doc)) \ michael@0: } michael@0: michael@0: /* michael@0: * For storage of an |nsRuleNode|'s children in a PLDHashTable. michael@0: */ michael@0: michael@0: struct ChildrenHashEntry : public PLDHashEntryHdr { michael@0: // key is |mRuleNode->GetKey()| michael@0: nsRuleNode *mRuleNode; michael@0: }; michael@0: michael@0: /* static */ PLDHashNumber michael@0: nsRuleNode::ChildrenHashHashKey(PLDHashTable *aTable, const void *aKey) michael@0: { michael@0: const nsRuleNode::Key *key = michael@0: static_cast(aKey); michael@0: // Disagreement on importance and level for the same rule is extremely michael@0: // rare, so hash just on the rule. michael@0: return PL_DHashVoidPtrKeyStub(aTable, key->mRule); michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsRuleNode::ChildrenHashMatchEntry(PLDHashTable *aTable, michael@0: const PLDHashEntryHdr *aHdr, michael@0: const void *aKey) michael@0: { michael@0: const ChildrenHashEntry *entry = michael@0: static_cast(aHdr); michael@0: const nsRuleNode::Key *key = michael@0: static_cast(aKey); michael@0: return entry->mRuleNode->GetKey() == *key; michael@0: } michael@0: michael@0: /* static */ const PLDHashTableOps michael@0: nsRuleNode::ChildrenHashOps = { michael@0: // It's probably better to allocate the table itself using malloc and michael@0: // free rather than the pres shell's arena because the table doesn't michael@0: // grow very often and the pres shell's arena doesn't recycle very michael@0: // large size allocations. michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: ChildrenHashHashKey, michael@0: ChildrenHashMatchEntry, michael@0: PL_DHashMoveEntryStub, michael@0: PL_DHashClearEntryStub, michael@0: PL_DHashFinalizeStub, michael@0: nullptr michael@0: }; michael@0: michael@0: michael@0: // EnsureBlockDisplay: michael@0: // - if the display value (argument) is not a block-type michael@0: // then we set it to a valid block display value michael@0: // - For enforcing the floated/positioned element CSS2 rules michael@0: // - We allow the behavior of "list-item" to be customized. michael@0: // CSS21 says that position/float do not convert 'list-item' to 'block', michael@0: // but it explicitly does not define whether 'list-item' should be michael@0: // converted to block *on the root node*. To allow for flexibility michael@0: // (so that we don't have to support a list-item root node), this method michael@0: // lets the caller pick either behavior, using the 'aConvertListItem' arg. michael@0: // Reference: http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo michael@0: /* static */ michael@0: void michael@0: nsRuleNode::EnsureBlockDisplay(uint8_t& display, michael@0: bool aConvertListItem /* = false */) michael@0: { michael@0: // see if the display value is already a block michael@0: switch (display) { michael@0: case NS_STYLE_DISPLAY_LIST_ITEM : michael@0: if (aConvertListItem) { michael@0: display = NS_STYLE_DISPLAY_BLOCK; michael@0: break; michael@0: } // else, fall through to share the 'break' for non-changing display vals michael@0: case NS_STYLE_DISPLAY_NONE : michael@0: // never change display:none *ever* michael@0: case NS_STYLE_DISPLAY_TABLE : michael@0: case NS_STYLE_DISPLAY_BLOCK : michael@0: case NS_STYLE_DISPLAY_FLEX : michael@0: case NS_STYLE_DISPLAY_GRID : michael@0: // do not muck with these at all - already blocks michael@0: // This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we michael@0: // should just call that?) michael@0: // This needs to match the check done in michael@0: // nsCSSFrameConstructor::FindMathMLData for . michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_INLINE_TABLE : michael@0: // make inline tables into tables michael@0: display = NS_STYLE_DISPLAY_TABLE; michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_INLINE_FLEX: michael@0: // make inline flex containers into flex containers michael@0: display = NS_STYLE_DISPLAY_FLEX; michael@0: break; michael@0: michael@0: case NS_STYLE_DISPLAY_INLINE_GRID: michael@0: // make inline grid containers into grid containers michael@0: display = NS_STYLE_DISPLAY_GRID; michael@0: break; michael@0: michael@0: default : michael@0: // make it a block michael@0: display = NS_STYLE_DISPLAY_BLOCK; michael@0: } michael@0: } michael@0: michael@0: static nscoord CalcLengthWith(const nsCSSValue& aValue, michael@0: nscoord aFontSize, michael@0: const nsStyleFont* aStyleFont, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool aUseProvidedRootEmSize, michael@0: bool aUseUserFontSet, michael@0: bool& aCanStoreInRuleTree); michael@0: michael@0: struct CalcLengthCalcOps : public css::BasicCoordCalcOps, michael@0: public css::NumbersAlreadyNormalizedOps michael@0: { michael@0: // All of the parameters to CalcLengthWith except aValue. michael@0: const nscoord mFontSize; michael@0: const nsStyleFont* const mStyleFont; michael@0: nsStyleContext* const mStyleContext; michael@0: nsPresContext* const mPresContext; michael@0: const bool mUseProvidedRootEmSize; michael@0: const bool mUseUserFontSet; michael@0: bool& mCanStoreInRuleTree; michael@0: michael@0: CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont, michael@0: nsStyleContext* aStyleContext, nsPresContext* aPresContext, michael@0: bool aUseProvidedRootEmSize, bool aUseUserFontSet, michael@0: bool& aCanStoreInRuleTree) michael@0: : mFontSize(aFontSize), michael@0: mStyleFont(aStyleFont), michael@0: mStyleContext(aStyleContext), michael@0: mPresContext(aPresContext), michael@0: mUseProvidedRootEmSize(aUseProvidedRootEmSize), michael@0: mUseUserFontSet(aUseUserFontSet), michael@0: mCanStoreInRuleTree(aCanStoreInRuleTree) michael@0: { michael@0: } michael@0: michael@0: result_type ComputeLeafValue(const nsCSSValue& aValue) michael@0: { michael@0: return CalcLengthWith(aValue, mFontSize, mStyleFont, michael@0: mStyleContext, mPresContext, mUseProvidedRootEmSize, michael@0: mUseUserFontSet, mCanStoreInRuleTree); michael@0: } michael@0: }; michael@0: michael@0: static inline nscoord ScaleCoord(const nsCSSValue &aValue, float factor) michael@0: { michael@0: return NSToCoordRoundWithClamp(aValue.GetFloatValue() * factor); michael@0: } michael@0: michael@0: already_AddRefed michael@0: GetMetricsFor(nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext, michael@0: const nsStyleFont* aStyleFont, michael@0: nscoord aFontSize, // overrides value from aStyleFont michael@0: bool aUseUserFontSet) michael@0: { michael@0: nsFont font = aStyleFont->mFont; michael@0: font.size = aFontSize; michael@0: gfxUserFontSet *fs = nullptr; michael@0: if (aUseUserFontSet) { michael@0: fs = aPresContext->GetUserFontSet(); michael@0: } michael@0: gfxTextPerfMetrics *tp = aPresContext->GetTextPerfMetrics(); michael@0: nsRefPtr fm; michael@0: aPresContext->DeviceContext()->GetMetricsFor(font, michael@0: aStyleFont->mLanguage, michael@0: fs, tp, *getter_AddRefs(fm)); michael@0: return fm.forget(); michael@0: } michael@0: michael@0: michael@0: static nsSize CalcViewportUnitsScale(nsPresContext* aPresContext) michael@0: { michael@0: // The caller is making use of viewport units, so notify the pres context michael@0: // that it will need to rebuild the rule tree if the size of the viewport michael@0: // changes. michael@0: aPresContext->SetUsesViewportUnits(true); michael@0: michael@0: // The default (when we have 'overflow: auto' on the root element, or michael@0: // trivially for 'overflow: hidden' since we never have scrollbars in that michael@0: // case) is to define the scale of the viewport units without considering michael@0: // scrollbars. michael@0: nsSize viewportSize(aPresContext->GetVisibleArea().Size()); michael@0: michael@0: // Check for 'overflow: scroll' styles on the root scroll frame. If we find michael@0: // any, the standard requires us to take scrollbars into account. michael@0: nsIScrollableFrame* scrollFrame = michael@0: aPresContext->PresShell()->GetRootScrollFrameAsScrollable(); michael@0: if (scrollFrame) { michael@0: ScrollbarStyles styles(scrollFrame->GetScrollbarStyles()); michael@0: michael@0: if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL || michael@0: styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { michael@0: // Gather scrollbar size information. michael@0: nsRefPtr context = michael@0: aPresContext->PresShell()->CreateReferenceRenderingContext(); michael@0: nsMargin sizes(scrollFrame->GetDesiredScrollbarSizes(aPresContext, context)); michael@0: michael@0: if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) { michael@0: // 'overflow-x: scroll' means we must consider the horizontal scrollbar, michael@0: // which affects the scale of viewport height units. michael@0: viewportSize.height -= sizes.TopBottom(); michael@0: } michael@0: michael@0: if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { michael@0: // 'overflow-y: scroll' means we must consider the vertical scrollbar, michael@0: // which affects the scale of viewport width units. michael@0: viewportSize.width -= sizes.LeftRight(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return viewportSize; michael@0: } michael@0: michael@0: static nscoord CalcLengthWith(const nsCSSValue& aValue, michael@0: nscoord aFontSize, michael@0: const nsStyleFont* aStyleFont, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool aUseProvidedRootEmSize, michael@0: // aUseUserFontSet should always be true michael@0: // except when called from michael@0: // CalcLengthWithInitialFont. michael@0: bool aUseUserFontSet, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(), michael@0: "not a length or calc unit"); michael@0: NS_ASSERTION(aStyleFont || aStyleContext, michael@0: "Must have style data"); michael@0: NS_ASSERTION(!aStyleFont || !aStyleContext, michael@0: "Duplicate sources of data"); michael@0: NS_ASSERTION(aPresContext, "Must have prescontext"); michael@0: michael@0: if (aValue.IsFixedLengthUnit()) { michael@0: return aValue.GetFixedLength(aPresContext); michael@0: } michael@0: if (aValue.IsPixelLengthUnit()) { michael@0: return aValue.GetPixelLength(); michael@0: } michael@0: if (aValue.IsCalcUnit()) { michael@0: // For properties for which lengths are the *only* units accepted in michael@0: // calc(), we can handle calc() here and just compute a final michael@0: // result. We ensure that we don't get to this code for other michael@0: // properties by not calling CalcLength in those cases: SetCoord michael@0: // only calls CalcLength for a calc when it is appropriate to do so. michael@0: CalcLengthCalcOps ops(aFontSize, aStyleFont, michael@0: aStyleContext, aPresContext, michael@0: aUseProvidedRootEmSize, aUseUserFontSet, michael@0: aCanStoreInRuleTree); michael@0: return css::ComputeCalc(aValue, ops); michael@0: } michael@0: switch (aValue.GetUnit()) { michael@0: // nsPresContext::SetVisibleArea and michael@0: // nsPresContext::MediaFeatureValuesChanged handle dynamic changes michael@0: // of the basis for viewport units by rebuilding the rule tree and michael@0: // style context tree. Not caching them in the rule tree wouldn't michael@0: // be sufficient to handle these changes because we also need a way michael@0: // to get rid of cached values in the style context tree without any michael@0: // changes in specified style. We can either do this by not caching michael@0: // in the rule tree and then throwing away the style context tree michael@0: // for dynamic viewport size changes, or by allowing caching in the michael@0: // rule tree and using the existing rebuild style data path that michael@0: // throws away the style context and the rule tree. michael@0: // Thus we do cache viewport units in the rule tree. This allows us michael@0: // to benefit from the performance advantages of the rule tree michael@0: // (e.g., faster dynamic changes on other things, like transforms) michael@0: // and allows us not to need an additional code path, in exchange michael@0: // for an increased cost to dynamic changes to the viewport size michael@0: // when viewport units are in use. michael@0: case eCSSUnit_ViewportWidth: { michael@0: return ScaleCoord(aValue, 0.01f * CalcViewportUnitsScale(aPresContext).width); michael@0: } michael@0: case eCSSUnit_ViewportHeight: { michael@0: return ScaleCoord(aValue, 0.01f * CalcViewportUnitsScale(aPresContext).height); michael@0: } michael@0: case eCSSUnit_ViewportMin: { michael@0: nsSize vuScale(CalcViewportUnitsScale(aPresContext)); michael@0: return ScaleCoord(aValue, 0.01f * min(vuScale.width, vuScale.height)); michael@0: } michael@0: case eCSSUnit_ViewportMax: { michael@0: nsSize vuScale(CalcViewportUnitsScale(aPresContext)); michael@0: return ScaleCoord(aValue, 0.01f * max(vuScale.width, vuScale.height)); michael@0: } michael@0: // While we could deal with 'rem' units correctly by simply not michael@0: // caching any data that uses them in the rule tree, it's valuable michael@0: // to store them in the rule tree (for faster dynamic changes of michael@0: // other things). And since the font size of the root element michael@0: // changes rarely, we instead handle dynamic changes to the root michael@0: // element's font size by rebuilding all style data in michael@0: // nsCSSFrameConstructor::RestyleElement. michael@0: case eCSSUnit_RootEM: { michael@0: aPresContext->SetUsesRootEMUnits(true); michael@0: nscoord rootFontSize; michael@0: michael@0: // NOTE: Be very careful with |styleFont|, since we haven't set michael@0: // aCanStoreInRuleTree to false yet, so we don't want to introduce michael@0: // any dependencies on aStyleContext's data here. michael@0: const nsStyleFont *styleFont = michael@0: aStyleFont ? aStyleFont : aStyleContext->StyleFont(); michael@0: michael@0: if (aUseProvidedRootEmSize) { michael@0: // We should use the provided aFontSize as the reference length to michael@0: // scale. This only happens when we are calculating font-size or michael@0: // an equivalent (scriptminsize or CalcLengthWithInitialFont) on michael@0: // the root element, in which case aFontSize is already the michael@0: // value we want. michael@0: if (aFontSize == -1) { michael@0: // XXX Should this be styleFont->mSize instead to avoid taking michael@0: // minfontsize prefs into account? michael@0: aFontSize = styleFont->mFont.size; michael@0: } michael@0: rootFontSize = aFontSize; michael@0: } else if (aStyleContext && !aStyleContext->GetParent()) { michael@0: // This is the root element (XXX we don't really know this, but michael@0: // nsRuleNode::SetFont makes the same assumption!), so we should michael@0: // use StyleFont on this context to get the root element's michael@0: // font size. michael@0: rootFontSize = styleFont->mFont.size; michael@0: } else { michael@0: // This is not the root element or we are calculating something other michael@0: // than font size, so rem is relative to the root element's font size. michael@0: nsRefPtr rootStyle; michael@0: const nsStyleFont *rootStyleFont = styleFont; michael@0: Element* docElement = aPresContext->Document()->GetRootElement(); michael@0: michael@0: if (docElement) { michael@0: rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement, michael@0: nullptr); michael@0: rootStyleFont = rootStyle->StyleFont(); michael@0: } michael@0: michael@0: rootFontSize = rootStyleFont->mFont.size; michael@0: } michael@0: michael@0: return ScaleCoord(aValue, float(rootFontSize)); michael@0: } michael@0: default: michael@0: // Fall through to the code for units that can't be stored in the michael@0: // rule tree because they depend on font data. michael@0: break; michael@0: } michael@0: // Common code for units that depend on the element's font data and michael@0: // thus can't be stored in the rule tree: michael@0: aCanStoreInRuleTree = false; michael@0: const nsStyleFont *styleFont = michael@0: aStyleFont ? aStyleFont : aStyleContext->StyleFont(); michael@0: if (aFontSize == -1) { michael@0: // XXX Should this be styleFont->mSize instead to avoid taking minfontsize michael@0: // prefs into account? michael@0: aFontSize = styleFont->mFont.size; michael@0: } michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_EM: { michael@0: // CSS2.1 specifies that this unit scales to the computed font michael@0: // size, not the em-width in the font metrics, despite the name. michael@0: return ScaleCoord(aValue, float(aFontSize)); michael@0: } michael@0: case eCSSUnit_XHeight: { michael@0: nsRefPtr fm = michael@0: GetMetricsFor(aPresContext, aStyleContext, styleFont, michael@0: aFontSize, aUseUserFontSet); michael@0: return ScaleCoord(aValue, float(fm->XHeight())); michael@0: } michael@0: case eCSSUnit_Char: { michael@0: nsRefPtr fm = michael@0: GetMetricsFor(aPresContext, aStyleContext, styleFont, michael@0: aFontSize, aUseUserFontSet); michael@0: gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFontAt(0) michael@0: ->GetMetrics().zeroOrAveCharWidth); michael@0: michael@0: return ScaleCoord(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * michael@0: zeroWidth)); michael@0: } michael@0: default: michael@0: NS_NOTREACHED("unexpected unit"); michael@0: break; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::CalcLength(const nsCSSValue& aValue, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: NS_ASSERTION(aStyleContext, "Must have style data"); michael@0: michael@0: return CalcLengthWith(aValue, -1, nullptr, michael@0: aStyleContext, aPresContext, michael@0: false, true, aCanStoreInRuleTree); michael@0: } michael@0: michael@0: /* Inline helper function to redirect requests to CalcLength. */ michael@0: static inline nscoord CalcLength(const nsCSSValue& aValue, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: return nsRuleNode::CalcLength(aValue, aStyleContext, michael@0: aPresContext, aCanStoreInRuleTree); michael@0: } michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, michael@0: const nsCSSValue& aValue) michael@0: { michael@0: nsStyleFont defaultFont(aPresContext); // FIXME: best language? michael@0: bool canStoreInRuleTree; michael@0: return CalcLengthWith(aValue, -1, &defaultFont, michael@0: nullptr, aPresContext, michael@0: true, false, canStoreInRuleTree); michael@0: } michael@0: michael@0: struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps michael@0: { michael@0: typedef nsRuleNode::ComputedCalc result_type; michael@0: michael@0: LengthPercentPairCalcOps(nsStyleContext* aContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: : mContext(aContext), michael@0: mPresContext(aPresContext), michael@0: mCanStoreInRuleTree(aCanStoreInRuleTree), michael@0: mHasPercent(false) {} michael@0: michael@0: nsStyleContext* mContext; michael@0: nsPresContext* mPresContext; michael@0: bool& mCanStoreInRuleTree; michael@0: bool mHasPercent; michael@0: michael@0: result_type ComputeLeafValue(const nsCSSValue& aValue) michael@0: { michael@0: if (aValue.GetUnit() == eCSSUnit_Percent) { michael@0: mHasPercent = true; michael@0: return result_type(0, aValue.GetPercentValue()); michael@0: } michael@0: return result_type(CalcLength(aValue, mContext, mPresContext, michael@0: mCanStoreInRuleTree), michael@0: 0.0f); michael@0: } michael@0: michael@0: result_type michael@0: MergeAdditive(nsCSSUnit aCalcFunction, michael@0: result_type aValue1, result_type aValue2) michael@0: { michael@0: if (aCalcFunction == eCSSUnit_Calc_Plus) { michael@0: return result_type(NSCoordSaturatingAdd(aValue1.mLength, michael@0: aValue2.mLength), michael@0: aValue1.mPercent + aValue2.mPercent); michael@0: } michael@0: NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Minus, michael@0: "min() and max() are not allowed in calc() on " michael@0: "transform"); michael@0: return result_type(NSCoordSaturatingSubtract(aValue1.mLength, michael@0: aValue2.mLength, 0), michael@0: aValue1.mPercent - aValue2.mPercent); michael@0: } michael@0: michael@0: result_type michael@0: MergeMultiplicativeL(nsCSSUnit aCalcFunction, michael@0: float aValue1, result_type aValue2) michael@0: { michael@0: NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_L, michael@0: "unexpected unit"); michael@0: return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1), michael@0: aValue1 * aValue2.mPercent); michael@0: } michael@0: michael@0: result_type michael@0: MergeMultiplicativeR(nsCSSUnit aCalcFunction, michael@0: result_type aValue1, float aValue2) michael@0: { michael@0: NS_ABORT_IF_FALSE(aCalcFunction == eCSSUnit_Calc_Times_R || michael@0: aCalcFunction == eCSSUnit_Calc_Divided, michael@0: "unexpected unit"); michael@0: if (aCalcFunction == eCSSUnit_Calc_Divided) { michael@0: aValue2 = 1.0f / aValue2; michael@0: } michael@0: return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2), michael@0: aValue1.mPercent * aValue2); michael@0: } michael@0: michael@0: }; michael@0: michael@0: static void michael@0: SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, michael@0: nsStyleContext* aStyleContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), michael@0: aCanStoreInRuleTree); michael@0: nsRuleNode::ComputedCalc vals = ComputeCalc(aValue, ops); michael@0: michael@0: nsStyleCoord::Calc *calcObj = michael@0: new (aStyleContext->Alloc(sizeof(nsStyleCoord::Calc))) nsStyleCoord::Calc; michael@0: // Because we use aStyleContext->Alloc(), we have to store the result michael@0: // on the style context and not in the rule tree. michael@0: aCanStoreInRuleTree = false; michael@0: michael@0: calcObj->mLength = vals.mLength; michael@0: calcObj->mPercent = vals.mPercent; michael@0: calcObj->mHasPercent = ops.mHasPercent; michael@0: michael@0: aCoord.SetCalcValue(calcObj); michael@0: } michael@0: michael@0: /* static */ nsRuleNode::ComputedCalc michael@0: nsRuleNode::SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: LengthPercentPairCalcOps ops(aStyleContext, aPresContext, michael@0: aCanStoreInRuleTree); michael@0: return ComputeCalc(aValue, ops); michael@0: } michael@0: michael@0: // This is our public API for handling calc() expressions that involve michael@0: // percentages. michael@0: /* static */ nscoord michael@0: nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue, michael@0: nscoord aPercentageBasis) michael@0: { michael@0: nsStyleCoord::Calc *calc = aValue.GetCalcValue(); michael@0: return calc->mLength + michael@0: NSToCoordFloorClamped(aPercentageBasis * calc->mPercent); michael@0: } michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::ComputeCoordPercentCalc(const nsStyleCoord& aCoord, michael@0: nscoord aPercentageBasis) michael@0: { michael@0: switch (aCoord.GetUnit()) { michael@0: case eStyleUnit_Coord: michael@0: return aCoord.GetCoordValue(); michael@0: case eStyleUnit_Percent: michael@0: return NSToCoordFloorClamped(aPercentageBasis * aCoord.GetPercentValue()); michael@0: case eStyleUnit_Calc: michael@0: return ComputeComputedCalc(aCoord, aPercentageBasis); michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected unit"); michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: /* Given an enumerated value that represents a box position, converts it to michael@0: * a float representing the percentage of the box it corresponds to. For michael@0: * example, "center" becomes 0.5f. michael@0: * michael@0: * @param aEnumValue The enumerated value. michael@0: * @return The float percent it corresponds to. michael@0: */ michael@0: static float michael@0: GetFloatFromBoxPosition(int32_t aEnumValue) michael@0: { michael@0: switch (aEnumValue) { michael@0: case NS_STYLE_BG_POSITION_LEFT: michael@0: case NS_STYLE_BG_POSITION_TOP: michael@0: return 0.0f; michael@0: case NS_STYLE_BG_POSITION_RIGHT: michael@0: case NS_STYLE_BG_POSITION_BOTTOM: michael@0: return 1.0f; michael@0: default: michael@0: NS_NOTREACHED("unexpected value"); michael@0: // fall through michael@0: case NS_STYLE_BG_POSITION_CENTER: michael@0: return 0.5f; michael@0: } michael@0: } michael@0: michael@0: #define SETCOORD_NORMAL 0x01 // N michael@0: #define SETCOORD_AUTO 0x02 // A michael@0: #define SETCOORD_INHERIT 0x04 // H michael@0: #define SETCOORD_PERCENT 0x08 // P michael@0: #define SETCOORD_FACTOR 0x10 // F michael@0: #define SETCOORD_LENGTH 0x20 // L michael@0: #define SETCOORD_INTEGER 0x40 // I michael@0: #define SETCOORD_ENUMERATED 0x80 // E michael@0: #define SETCOORD_NONE 0x100 // O michael@0: #define SETCOORD_INITIAL_ZERO 0x200 michael@0: #define SETCOORD_INITIAL_AUTO 0x400 michael@0: #define SETCOORD_INITIAL_NONE 0x800 michael@0: #define SETCOORD_INITIAL_NORMAL 0x1000 michael@0: #define SETCOORD_INITIAL_HALF 0x2000 michael@0: #define SETCOORD_INITIAL_HUNDRED_PCT 0x00004000 michael@0: #define SETCOORD_INITIAL_FACTOR_ONE 0x00008000 michael@0: #define SETCOORD_INITIAL_FACTOR_ZERO 0x00010000 michael@0: #define SETCOORD_CALC_LENGTH_ONLY 0x00020000 michael@0: #define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY michael@0: #define SETCOORD_STORE_CALC 0x00080000 michael@0: #define SETCOORD_BOX_POSITION 0x00100000 // exclusive with _ENUMERATED michael@0: #define SETCOORD_ANGLE 0x00200000 michael@0: #define SETCOORD_UNSET_INHERIT 0x00400000 michael@0: #define SETCOORD_UNSET_INITIAL 0x00800000 michael@0: michael@0: #define SETCOORD_LP (SETCOORD_LENGTH | SETCOORD_PERCENT) michael@0: #define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT) michael@0: #define SETCOORD_AH (SETCOORD_AUTO | SETCOORD_INHERIT) michael@0: #define SETCOORD_LAH (SETCOORD_AUTO | SETCOORD_LENGTH | SETCOORD_INHERIT) michael@0: #define SETCOORD_LPH (SETCOORD_LP | SETCOORD_INHERIT) michael@0: #define SETCOORD_LPAH (SETCOORD_LP | SETCOORD_AH) michael@0: #define SETCOORD_LPE (SETCOORD_LP | SETCOORD_ENUMERATED) michael@0: #define SETCOORD_LPEH (SETCOORD_LPE | SETCOORD_INHERIT) michael@0: #define SETCOORD_LPAEH (SETCOORD_LPAH | SETCOORD_ENUMERATED) michael@0: #define SETCOORD_LPO (SETCOORD_LP | SETCOORD_NONE) michael@0: #define SETCOORD_LPOH (SETCOORD_LPH | SETCOORD_NONE) michael@0: #define SETCOORD_LPOEH (SETCOORD_LPOH | SETCOORD_ENUMERATED) michael@0: #define SETCOORD_LE (SETCOORD_LENGTH | SETCOORD_ENUMERATED) michael@0: #define SETCOORD_LEH (SETCOORD_LE | SETCOORD_INHERIT) michael@0: #define SETCOORD_IA (SETCOORD_INTEGER | SETCOORD_AUTO) michael@0: #define SETCOORD_LAE (SETCOORD_LENGTH | SETCOORD_AUTO | SETCOORD_ENUMERATED) michael@0: michael@0: // changes aCoord iff it returns true michael@0: static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, michael@0: const nsStyleCoord& aParentCoord, michael@0: int32_t aMask, nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: bool result = true; michael@0: if (aValue.GetUnit() == eCSSUnit_Null) { michael@0: result = false; michael@0: } michael@0: else if ((((aMask & SETCOORD_LENGTH) != 0) && michael@0: aValue.IsLengthUnit()) || michael@0: (((aMask & SETCOORD_CALC_LENGTH_ONLY) != 0) && michael@0: aValue.IsCalcUnit())) { michael@0: nscoord len = CalcLength(aValue, aStyleContext, aPresContext, michael@0: aCanStoreInRuleTree); michael@0: if ((aMask & SETCOORD_CALC_CLAMP_NONNEGATIVE) && len < 0) { michael@0: NS_ASSERTION(aValue.IsCalcUnit(), michael@0: "parser should have ensured no nonnegative lengths"); michael@0: len = 0; michael@0: } michael@0: aCoord.SetCoordValue(len); michael@0: } michael@0: else if (((aMask & SETCOORD_PERCENT) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Percent)) { michael@0: aCoord.SetPercentValue(aValue.GetPercentValue()); michael@0: } michael@0: else if (((aMask & SETCOORD_INTEGER) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Integer)) { michael@0: aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Integer); michael@0: } michael@0: else if (((aMask & SETCOORD_ENUMERATED) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Enumerated)) { michael@0: aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Enumerated); michael@0: } michael@0: else if (((aMask & SETCOORD_BOX_POSITION) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Enumerated)) { michael@0: aCoord.SetPercentValue(GetFloatFromBoxPosition(aValue.GetIntValue())); michael@0: } michael@0: else if (((aMask & SETCOORD_AUTO) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Auto)) { michael@0: aCoord.SetAutoValue(); michael@0: } michael@0: else if ((((aMask & SETCOORD_INHERIT) != 0) && michael@0: aValue.GetUnit() == eCSSUnit_Inherit) || michael@0: (((aMask & SETCOORD_UNSET_INHERIT) != 0) && michael@0: aValue.GetUnit() == eCSSUnit_Unset)) { michael@0: aCoord = aParentCoord; // just inherit value from parent michael@0: aCanStoreInRuleTree = false; michael@0: } michael@0: else if (((aMask & SETCOORD_NORMAL) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Normal)) { michael@0: aCoord.SetNormalValue(); michael@0: } michael@0: else if (((aMask & SETCOORD_NONE) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_None)) { michael@0: aCoord.SetNoneValue(); michael@0: } michael@0: else if (((aMask & SETCOORD_FACTOR) != 0) && michael@0: (aValue.GetUnit() == eCSSUnit_Number)) { michael@0: aCoord.SetFactorValue(aValue.GetFloatValue()); michael@0: } michael@0: else if (((aMask & SETCOORD_STORE_CALC) != 0) && michael@0: (aValue.IsCalcUnit())) { michael@0: SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext, michael@0: aCanStoreInRuleTree); michael@0: } michael@0: else if (aValue.GetUnit() == eCSSUnit_Initial || michael@0: (aValue.GetUnit() == eCSSUnit_Unset && michael@0: ((aMask & SETCOORD_UNSET_INITIAL) != 0))) { michael@0: if ((aMask & SETCOORD_INITIAL_AUTO) != 0) { michael@0: aCoord.SetAutoValue(); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_ZERO) != 0) { michael@0: aCoord.SetCoordValue(0); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) { michael@0: aCoord.SetFactorValue(0.0f); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_NONE) != 0) { michael@0: aCoord.SetNoneValue(); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_NORMAL) != 0) { michael@0: aCoord.SetNormalValue(); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_HALF) != 0) { michael@0: aCoord.SetPercentValue(0.5f); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) { michael@0: aCoord.SetPercentValue(1.0f); michael@0: } michael@0: else if ((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) { michael@0: aCoord.SetFactorValue(1.0f); michael@0: } michael@0: else { michael@0: result = false; // didn't set anything michael@0: } michael@0: } michael@0: else if ((aMask & SETCOORD_ANGLE) != 0 && michael@0: (aValue.IsAngularUnit())) { michael@0: nsStyleUnit unit; michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; michael@0: case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; michael@0: case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; michael@0: case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; michael@0: default: NS_NOTREACHED("unrecognized angular unit"); michael@0: unit = eStyleUnit_Degree; michael@0: } michael@0: aCoord.SetAngleValue(aValue.GetAngleValue(), unit); michael@0: } michael@0: else { michael@0: result = false; // didn't set anything michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // This inline function offers a shortcut for SetCoord() by refusing to accept michael@0: // SETCOORD_LENGTH, SETCOORD_INHERIT and SETCOORD_UNSET_* masks. michael@0: static inline bool SetAbsCoord(const nsCSSValue& aValue, michael@0: nsStyleCoord& aCoord, michael@0: int32_t aMask) michael@0: { michael@0: NS_ABORT_IF_FALSE((aMask & (SETCOORD_LH | SETCOORD_UNSET_INHERIT | michael@0: SETCOORD_UNSET_INITIAL)) == 0, michael@0: "does not handle SETCOORD_LENGTH, SETCOORD_INHERIT and " michael@0: "SETCOORD_UNSET_*"); michael@0: michael@0: // The values of the following variables will never be used; so it does not michael@0: // matter what to set. michael@0: const nsStyleCoord dummyParentCoord; michael@0: nsStyleContext* dummyStyleContext = nullptr; michael@0: nsPresContext* dummyPresContext = nullptr; michael@0: bool dummyCanStoreInRuleTree = true; michael@0: michael@0: bool rv = SetCoord(aValue, aCoord, dummyParentCoord, aMask, michael@0: dummyStyleContext, dummyPresContext, michael@0: dummyCanStoreInRuleTree); michael@0: NS_ABORT_IF_FALSE(dummyCanStoreInRuleTree, michael@0: "SetCoord() should not modify dummyCanStoreInRuleTree."); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* Given a specified value that might be a pair value, call SetCoord twice, michael@0: * either using each member of the pair, or using the unpaired value twice. michael@0: */ michael@0: static bool michael@0: SetPairCoords(const nsCSSValue& aValue, michael@0: nsStyleCoord& aCoordX, nsStyleCoord& aCoordY, michael@0: const nsStyleCoord& aParentX, const nsStyleCoord& aParentY, michael@0: int32_t aMask, nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, bool& aCanStoreInRuleTree) michael@0: { michael@0: const nsCSSValue& valX = michael@0: aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mXValue : aValue; michael@0: const nsCSSValue& valY = michael@0: aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mYValue : aValue; michael@0: michael@0: bool cX = SetCoord(valX, aCoordX, aParentX, aMask, aStyleContext, michael@0: aPresContext, aCanStoreInRuleTree); michael@0: mozilla::DebugOnly cY = SetCoord(valY, aCoordY, aParentY, aMask, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other"); michael@0: return cX; michael@0: } michael@0: michael@0: static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor, michael@0: nsPresContext* aPresContext, nsStyleContext *aContext, michael@0: nscolor& aResult, bool& aCanStoreInRuleTree) michael@0: { michael@0: bool result = false; michael@0: nsCSSUnit unit = aValue.GetUnit(); michael@0: michael@0: if (aValue.IsNumericColorUnit()) { michael@0: aResult = aValue.GetColorValue(); michael@0: result = true; michael@0: } michael@0: else if (eCSSUnit_Ident == unit) { michael@0: nsAutoString value; michael@0: aValue.GetStringValue(value); michael@0: nscolor rgba; michael@0: if (NS_ColorNameToRGB(value, &rgba)) { michael@0: aResult = rgba; michael@0: result = true; michael@0: } michael@0: } michael@0: else if (eCSSUnit_EnumColor == unit) { michael@0: int32_t intValue = aValue.GetIntValue(); michael@0: if (0 <= intValue) { michael@0: LookAndFeel::ColorID colorID = (LookAndFeel::ColorID) intValue; michael@0: bool useStandinsForNativeColors = aPresContext && michael@0: !aPresContext->IsChrome(); michael@0: if (NS_SUCCEEDED(LookAndFeel::GetColor(colorID, michael@0: useStandinsForNativeColors, &aResult))) { michael@0: result = true; michael@0: } michael@0: } michael@0: else { michael@0: aResult = NS_RGB(0, 0, 0); michael@0: result = false; michael@0: switch (intValue) { michael@0: case NS_COLOR_MOZ_HYPERLINKTEXT: michael@0: if (aPresContext) { michael@0: aResult = aPresContext->DefaultLinkColor(); michael@0: result = true; michael@0: } michael@0: break; michael@0: case NS_COLOR_MOZ_VISITEDHYPERLINKTEXT: michael@0: if (aPresContext) { michael@0: aResult = aPresContext->DefaultVisitedLinkColor(); michael@0: result = true; michael@0: } michael@0: break; michael@0: case NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT: michael@0: if (aPresContext) { michael@0: aResult = aPresContext->DefaultActiveLinkColor(); michael@0: result = true; michael@0: } michael@0: break; michael@0: case NS_COLOR_CURRENTCOLOR: michael@0: // The data computed from this can't be shared in the rule tree michael@0: // because they could be used on a node with a different color michael@0: aCanStoreInRuleTree = false; michael@0: if (aContext) { michael@0: aResult = aContext->StyleColor()->mColor; michael@0: result = true; michael@0: } michael@0: break; michael@0: case NS_COLOR_MOZ_DEFAULT_COLOR: michael@0: if (aPresContext) { michael@0: aResult = aPresContext->DefaultColor(); michael@0: result = true; michael@0: } michael@0: break; michael@0: case NS_COLOR_MOZ_DEFAULT_BACKGROUND_COLOR: michael@0: if (aPresContext) { michael@0: aResult = aPresContext->DefaultBackgroundColor(); michael@0: result = true; michael@0: } michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Should never have an unknown negative colorID."); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: else if (eCSSUnit_Inherit == unit) { michael@0: aResult = aParentColor; michael@0: result = true; michael@0: aCanStoreInRuleTree = false; michael@0: } michael@0: else if (eCSSUnit_Enumerated == unit && michael@0: aValue.GetIntValue() == NS_STYLE_COLOR_INHERIT_FROM_BODY) { michael@0: NS_ASSERTION(aPresContext->CompatibilityMode() == eCompatibility_NavQuirks, michael@0: "Should only get this value in quirks mode"); michael@0: // We just grab the color from the prescontext, and rely on the fact that michael@0: // if the body color ever changes all its descendants will get new style michael@0: // contexts (but NOT necessarily new rulenodes). michael@0: aResult = aPresContext->BodyTextColor(); michael@0: result = true; michael@0: aCanStoreInRuleTree = false; michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: static void SetGradientCoord(const nsCSSValue& aValue, nsPresContext* aPresContext, michael@0: nsStyleContext* aContext, nsStyleCoord& aResult, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT michael@0: if (!SetCoord(aValue, aResult, nsStyleCoord(), michael@0: SETCOORD_LPO | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC, michael@0: aContext, aPresContext, aCanStoreInRuleTree)) { michael@0: NS_NOTREACHED("unexpected unit for gradient anchor point"); michael@0: aResult.SetNoneValue(); michael@0: } michael@0: } michael@0: michael@0: static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext, michael@0: nsStyleContext* aContext, nsStyleGradient& aResult, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Gradient, michael@0: "The given data is not a gradient"); michael@0: michael@0: const nsCSSValueGradient* gradient = aValue.GetGradientValue(); michael@0: michael@0: if (gradient->mIsExplicitSize) { michael@0: SetCoord(gradient->GetRadiusX(), aResult.mRadiusX, nsStyleCoord(), michael@0: SETCOORD_LP | SETCOORD_STORE_CALC, michael@0: aContext, aPresContext, aCanStoreInRuleTree); michael@0: if (gradient->GetRadiusY().GetUnit() != eCSSUnit_None) { michael@0: SetCoord(gradient->GetRadiusY(), aResult.mRadiusY, nsStyleCoord(), michael@0: SETCOORD_LP | SETCOORD_STORE_CALC, michael@0: aContext, aPresContext, aCanStoreInRuleTree); michael@0: aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; michael@0: } else { michael@0: aResult.mRadiusY = aResult.mRadiusX; michael@0: aResult.mShape = NS_STYLE_GRADIENT_SHAPE_CIRCULAR; michael@0: } michael@0: aResult.mSize = NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE; michael@0: } else if (gradient->mIsRadial) { michael@0: if (gradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated) { michael@0: aResult.mShape = gradient->GetRadialShape().GetIntValue(); michael@0: } else { michael@0: NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, michael@0: "bad unit for radial shape"); michael@0: aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; michael@0: } michael@0: if (gradient->GetRadialSize().GetUnit() == eCSSUnit_Enumerated) { michael@0: aResult.mSize = gradient->GetRadialSize().GetIntValue(); michael@0: } else { michael@0: NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, michael@0: "bad unit for radial shape"); michael@0: aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; michael@0: } michael@0: } else { michael@0: NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, michael@0: "bad unit for linear shape"); michael@0: NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, michael@0: "bad unit for linear size"); michael@0: aResult.mShape = NS_STYLE_GRADIENT_SHAPE_LINEAR; michael@0: aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; michael@0: } michael@0: michael@0: aResult.mLegacySyntax = gradient->mIsLegacySyntax; michael@0: michael@0: // bg-position michael@0: SetGradientCoord(gradient->mBgPos.mXValue, aPresContext, aContext, michael@0: aResult.mBgPosX, aCanStoreInRuleTree); michael@0: michael@0: SetGradientCoord(gradient->mBgPos.mYValue, aPresContext, aContext, michael@0: aResult.mBgPosY, aCanStoreInRuleTree); michael@0: michael@0: aResult.mRepeating = gradient->mIsRepeating; michael@0: michael@0: // angle michael@0: const nsStyleCoord dummyParentCoord; michael@0: if (!SetCoord(gradient->mAngle, aResult.mAngle, dummyParentCoord, SETCOORD_ANGLE, michael@0: aContext, aPresContext, aCanStoreInRuleTree)) { michael@0: NS_ASSERTION(gradient->mAngle.GetUnit() == eCSSUnit_None, michael@0: "bad unit for gradient angle"); michael@0: aResult.mAngle.SetNoneValue(); michael@0: } michael@0: michael@0: // stops michael@0: for (uint32_t i = 0; i < gradient->mStops.Length(); i++) { michael@0: nsStyleGradientStop stop; michael@0: const nsCSSValueGradientStop &valueStop = gradient->mStops[i]; michael@0: michael@0: if (!SetCoord(valueStop.mLocation, stop.mLocation, michael@0: nsStyleCoord(), SETCOORD_LPO | SETCOORD_STORE_CALC, michael@0: aContext, aPresContext, aCanStoreInRuleTree)) { michael@0: NS_NOTREACHED("unexpected unit for gradient stop location"); michael@0: } michael@0: michael@0: // inherit is not a valid color for stops, so we pass in a dummy michael@0: // parent color michael@0: NS_ASSERTION(valueStop.mColor.GetUnit() != eCSSUnit_Inherit, michael@0: "inherit is not a valid color for gradient stops"); michael@0: SetColor(valueStop.mColor, NS_RGB(0, 0, 0), aPresContext, michael@0: aContext, stop.mColor, aCanStoreInRuleTree); michael@0: michael@0: aResult.mStops.AppendElement(stop); michael@0: } michael@0: } michael@0: michael@0: // -moz-image-rect(, , , , ) michael@0: static void SetStyleImageToImageRect(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aValue, michael@0: nsStyleImage& aResult) michael@0: { michael@0: NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Function && michael@0: aValue.EqualsFunction(eCSSKeyword__moz_image_rect), michael@0: "the value is not valid -moz-image-rect()"); michael@0: michael@0: nsCSSValue::Array* arr = aValue.GetArrayValue(); michael@0: NS_ABORT_IF_FALSE(arr && arr->Count() == 6, "invalid number of arguments"); michael@0: michael@0: // michael@0: if (arr->Item(1).GetUnit() == eCSSUnit_Image) { michael@0: NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData, michael@0: aStyleContext, michael@0: arr->Item(1).GetImageValue) michael@0: } else { michael@0: NS_WARNING("nsCSSValue::Image::Image() failed?"); michael@0: } michael@0: michael@0: // , , , michael@0: nsStyleSides cropRect; michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nsStyleCoord coord; michael@0: const nsCSSValue& val = arr->Item(2 + side); michael@0: michael@0: #ifdef DEBUG michael@0: bool unitOk = michael@0: #endif michael@0: SetAbsCoord(val, coord, SETCOORD_FACTOR | SETCOORD_PERCENT); michael@0: NS_ABORT_IF_FALSE(unitOk, "Incorrect data structure created by CSS parser"); michael@0: cropRect.Set(side, coord); michael@0: } michael@0: aResult.SetCropRect(&cropRect); michael@0: } michael@0: michael@0: static void SetStyleImage(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aValue, michael@0: nsStyleImage& aResult, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: if (aValue.GetUnit() == eCSSUnit_Null) { michael@0: return; michael@0: } michael@0: michael@0: aResult.SetNull(); michael@0: michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Image: michael@0: NS_SET_IMAGE_REQUEST_WITH_DOC(aResult.SetImageData, michael@0: aStyleContext, michael@0: aValue.GetImageValue) michael@0: break; michael@0: case eCSSUnit_Function: michael@0: if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) { michael@0: SetStyleImageToImageRect(aStyleContext, aValue, aResult); michael@0: } else { michael@0: NS_NOTREACHED("-moz-image-rect() is the only expected function"); michael@0: } michael@0: break; michael@0: case eCSSUnit_Gradient: michael@0: { michael@0: nsStyleGradient* gradient = new nsStyleGradient(); michael@0: if (gradient) { michael@0: SetGradient(aValue, aStyleContext->PresContext(), aStyleContext, michael@0: *gradient, aCanStoreInRuleTree); michael@0: aResult.SetGradientData(gradient); michael@0: } michael@0: break; michael@0: } michael@0: case eCSSUnit_Element: michael@0: aResult.SetElementId(aValue.GetStringBufferValue()); michael@0: break; michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: break; michael@0: default: michael@0: // We might have eCSSUnit_URL values for if-visited style michael@0: // contexts, which we can safely treat like 'none'. Otherwise michael@0: // this is an unexpected unit. michael@0: NS_ASSERTION(aStyleContext->IsStyleIfVisited() && michael@0: aValue.GetUnit() == eCSSUnit_URL, michael@0: "unexpected unit; maybe nsCSSValue::Image::Image() failed?"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // flags for SetDiscrete - align values with SETCOORD_* constants michael@0: // where possible michael@0: michael@0: #define SETDSC_NORMAL 0x01 // N michael@0: #define SETDSC_AUTO 0x02 // A michael@0: #define SETDSC_INTEGER 0x40 // I michael@0: #define SETDSC_ENUMERATED 0x80 // E michael@0: #define SETDSC_NONE 0x100 // O michael@0: #define SETDSC_SYSTEM_FONT 0x2000 michael@0: #define SETDSC_UNSET_INHERIT 0x00400000 michael@0: #define SETDSC_UNSET_INITIAL 0x00800000 michael@0: michael@0: // no caller cares whether aField was changed or not michael@0: template michael@0: static void michael@0: SetDiscrete(const nsCSSValue& aValue, FieldT & aField, michael@0: bool& aCanStoreInRuleTree, uint32_t aMask, michael@0: FieldT aParentValue, michael@0: T1 aInitialValue, michael@0: T2 aAutoValue, michael@0: T3 aNoneValue, michael@0: T4 aNormalValue, michael@0: T5 aSystemFontValue) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: return; michael@0: michael@0: // every caller of SetDiscrete provides inherit and initial michael@0: // alternatives, so we don't require them to say so in the mask michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: aField = aParentValue; michael@0: return; michael@0: michael@0: case eCSSUnit_Initial: michael@0: aField = aInitialValue; michael@0: return; michael@0: michael@0: // every caller provides one or other of these alternatives, michael@0: // but they have to say which michael@0: case eCSSUnit_Enumerated: michael@0: if (aMask & SETDSC_ENUMERATED) { michael@0: aField = aValue.GetIntValue(); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Integer: michael@0: if (aMask & SETDSC_INTEGER) { michael@0: aField = aValue.GetIntValue(); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: // remaining possibilities in descending order of frequency of use michael@0: case eCSSUnit_Auto: michael@0: if (aMask & SETDSC_AUTO) { michael@0: aField = aAutoValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_None: michael@0: if (aMask & SETDSC_NONE) { michael@0: aField = aNoneValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Normal: michael@0: if (aMask & SETDSC_NORMAL) { michael@0: aField = aNormalValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_System_Font: michael@0: if (aMask & SETDSC_SYSTEM_FONT) { michael@0: aField = aSystemFontValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Unset: michael@0: if (aMask & SETDSC_UNSET_INHERIT) { michael@0: aCanStoreInRuleTree = false; michael@0: aField = aParentValue; michael@0: return; michael@0: } michael@0: if (aMask & SETDSC_UNSET_INITIAL) { michael@0: aField = aInitialValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: NS_NOTREACHED("SetDiscrete: inappropriate unit"); michael@0: } michael@0: michael@0: // flags for SetFactor michael@0: #define SETFCT_POSITIVE 0x01 // assert value is >= 0.0f michael@0: #define SETFCT_OPACITY 0x02 // clamp value to [0.0f .. 1.0f] michael@0: #define SETFCT_NONE 0x04 // allow _None (uses aInitialValue). michael@0: #define SETFCT_UNSET_INHERIT 0x00400000 michael@0: #define SETFCT_UNSET_INITIAL 0x00800000 michael@0: michael@0: static void michael@0: SetFactor(const nsCSSValue& aValue, float& aField, bool& aCanStoreInRuleTree, michael@0: float aParentValue, float aInitialValue, uint32_t aFlags = 0) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: return; michael@0: michael@0: case eCSSUnit_Number: michael@0: aField = aValue.GetFloatValue(); michael@0: if (aFlags & SETFCT_POSITIVE) { michael@0: NS_ASSERTION(aField >= 0.0f, "negative value for positive-only property"); michael@0: if (aField < 0.0f) michael@0: aField = 0.0f; michael@0: } michael@0: if (aFlags & SETFCT_OPACITY) { michael@0: if (aField < 0.0f) michael@0: aField = 0.0f; michael@0: if (aField > 1.0f) michael@0: aField = 1.0f; michael@0: } michael@0: return; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: aField = aParentValue; michael@0: return; michael@0: michael@0: case eCSSUnit_Initial: michael@0: aField = aInitialValue; michael@0: return; michael@0: michael@0: case eCSSUnit_None: michael@0: if (aFlags & SETFCT_NONE) { michael@0: aField = aInitialValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Unset: michael@0: if (aFlags & SETFCT_UNSET_INHERIT) { michael@0: aCanStoreInRuleTree = false; michael@0: aField = aParentValue; michael@0: return; michael@0: } michael@0: if (aFlags & SETFCT_UNSET_INITIAL) { michael@0: aField = aInitialValue; michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: NS_NOTREACHED("SetFactor: inappropriate unit"); michael@0: } michael@0: michael@0: // Overloaded new operator. Initializes the memory to 0 and relies on an arena michael@0: // (which comes from the presShell) to perform the allocation. michael@0: void* michael@0: nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW michael@0: { michael@0: // Check the recycle list first. michael@0: return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsRuleNode_id, sz); michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: nsRuleNode::EnqueueRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ChildrenHashEntry *entry = static_cast(hdr); michael@0: nsRuleNode ***destroyQueueTail = static_cast(arg); michael@0: **destroyQueueTail = entry->mRuleNode; michael@0: *destroyQueueTail = &entry->mRuleNode->mNextSibling; michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: // Overridden to prevent the global delete from being called, since the memory michael@0: // came out of an nsIArena instead of the global delete operator's heap. michael@0: void michael@0: nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) michael@0: { michael@0: nsRuleNode *destroyQueue, **destroyQueueTail; michael@0: if (aDestroyQueueTail) { michael@0: destroyQueueTail = *aDestroyQueueTail; michael@0: } else { michael@0: destroyQueue = nullptr; michael@0: destroyQueueTail = &destroyQueue; michael@0: } michael@0: michael@0: if (ChildrenAreHashed()) { michael@0: PLDHashTable *children = ChildrenHash(); michael@0: PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren, michael@0: &destroyQueueTail); michael@0: *destroyQueueTail = nullptr; // ensure null-termination michael@0: PL_DHashTableDestroy(children); michael@0: } else if (HaveChildren()) { michael@0: *destroyQueueTail = ChildrenList(); michael@0: do { michael@0: destroyQueueTail = &(*destroyQueueTail)->mNextSibling; michael@0: } while (*destroyQueueTail); michael@0: } michael@0: mChildren.asVoid = nullptr; michael@0: michael@0: if (aDestroyQueueTail) { michael@0: // Our caller destroys the queue. michael@0: *aDestroyQueueTail = destroyQueueTail; michael@0: } else { michael@0: // We have to do destroy the queue. When we destroy each node, it michael@0: // will add its children to the queue. michael@0: while (destroyQueue) { michael@0: nsRuleNode *cur = destroyQueue; michael@0: destroyQueue = destroyQueue->mNextSibling; michael@0: if (!destroyQueue) { michael@0: NS_ASSERTION(destroyQueueTail == &cur->mNextSibling, "mangled list"); michael@0: destroyQueueTail = &destroyQueue; michael@0: } michael@0: cur->DestroyInternal(&destroyQueueTail); michael@0: } michael@0: } michael@0: michael@0: // Destroy ourselves. michael@0: this->~nsRuleNode(); michael@0: michael@0: // Don't let the memory be freed, since it will be recycled michael@0: // instead. Don't call the global operator delete. michael@0: mPresContext->PresShell()->FreeByObjectID(nsPresArena::nsRuleNode_id, this); michael@0: } michael@0: michael@0: nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) michael@0: { michael@0: return new (aPresContext) michael@0: nsRuleNode(aPresContext, nullptr, nullptr, 0xff, false); michael@0: } michael@0: michael@0: nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, michael@0: nsIStyleRule* aRule, uint8_t aLevel, michael@0: bool aIsImportant) michael@0: : mPresContext(aContext), michael@0: mParent(aParent), michael@0: mRule(aRule), michael@0: mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) | michael@0: (aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)), michael@0: mNoneBits(0), michael@0: mRefCnt(0) michael@0: { michael@0: MOZ_ASSERT(aContext); michael@0: NS_ABORT_IF_FALSE(IsRoot() == !aRule, michael@0: "non-root rule nodes must have a rule"); michael@0: michael@0: mChildren.asVoid = nullptr; michael@0: MOZ_COUNT_CTOR(nsRuleNode); michael@0: michael@0: if (mRule) { michael@0: mRule->AddRef(); michael@0: } michael@0: michael@0: NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits"); michael@0: NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes"); michael@0: /* If IsRoot(), then aContext->StyleSet() is typically null at this michael@0: point. In any case, we don't want to treat the root rulenode as michael@0: unused. */ michael@0: if (!IsRoot()) { michael@0: mParent->AddRef(); michael@0: aContext->StyleSet()->RuleNodeUnused(); michael@0: } michael@0: michael@0: // nsStyleSet::GetContext depends on there being only one animation michael@0: // rule. michael@0: NS_ABORT_IF_FALSE(IsRoot() || GetLevel() != nsStyleSet::eAnimationSheet || michael@0: mParent->IsRoot() || michael@0: mParent->GetLevel() != nsStyleSet::eAnimationSheet, michael@0: "must be only one rule at animation level"); michael@0: } michael@0: michael@0: nsRuleNode::~nsRuleNode() michael@0: { michael@0: MOZ_COUNT_DTOR(nsRuleNode); michael@0: if (mStyleData.mResetData || mStyleData.mInheritedData) michael@0: mStyleData.Destroy(mDependentBits, mPresContext); michael@0: if (mRule) { michael@0: mRule->Release(); michael@0: } michael@0: } michael@0: michael@0: nsRuleNode* michael@0: nsRuleNode::Transition(nsIStyleRule* aRule, uint8_t aLevel, michael@0: bool aIsImportantRule) michael@0: { michael@0: nsRuleNode* next = nullptr; michael@0: nsRuleNode::Key key(aRule, aLevel, aIsImportantRule); michael@0: michael@0: if (HaveChildren() && !ChildrenAreHashed()) { michael@0: int32_t numKids = 0; michael@0: nsRuleNode* curr = ChildrenList(); michael@0: while (curr && curr->GetKey() != key) { michael@0: curr = curr->mNextSibling; michael@0: ++numKids; michael@0: } michael@0: if (curr) michael@0: next = curr; michael@0: else if (numKids >= kMaxChildrenInList) michael@0: ConvertChildrenToHash(); michael@0: } michael@0: michael@0: if (ChildrenAreHashed()) { michael@0: ChildrenHashEntry *entry = static_cast michael@0: (PL_DHashTableOperate(ChildrenHash(), &key, PL_DHASH_ADD)); michael@0: if (!entry) { michael@0: NS_WARNING("out of memory"); michael@0: return this; michael@0: } michael@0: if (entry->mRuleNode) michael@0: next = entry->mRuleNode; michael@0: else { michael@0: next = entry->mRuleNode = new (mPresContext) michael@0: nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); michael@0: if (!next) { michael@0: PL_DHashTableRawRemove(ChildrenHash(), entry); michael@0: NS_WARNING("out of memory"); michael@0: return this; michael@0: } michael@0: } michael@0: } else if (!next) { michael@0: // Create the new entry in our list. michael@0: next = new (mPresContext) michael@0: nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); michael@0: if (!next) { michael@0: NS_WARNING("out of memory"); michael@0: return this; michael@0: } michael@0: next->mNextSibling = ChildrenList(); michael@0: SetChildrenList(next); michael@0: } michael@0: michael@0: return next; michael@0: } michael@0: michael@0: void nsRuleNode::SetUsedDirectly() michael@0: { michael@0: mDependentBits |= NS_RULE_NODE_USED_DIRECTLY; michael@0: michael@0: // Maintain the invariant that any rule node that is used directly has michael@0: // all structs that live in the rule tree cached (which michael@0: // nsRuleNode::GetStyleData depends on for speed). michael@0: if (mDependentBits & NS_STYLE_INHERIT_MASK) { michael@0: for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length; michael@0: sid = nsStyleStructID(sid + 1)) { michael@0: uint32_t bit = nsCachedStyleData::GetBitForSID(sid); michael@0: if (mDependentBits & bit) { michael@0: nsRuleNode *source = mParent; michael@0: while ((source->mDependentBits & bit) && !source->IsUsedDirectly()) { michael@0: source = source->mParent; michael@0: } michael@0: void *data = source->mStyleData.GetStyleData(sid); michael@0: NS_ASSERTION(data, "unexpected null struct"); michael@0: mStyleData.SetStyleData(sid, mPresContext, data); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsRuleNode::ConvertChildrenToHash() michael@0: { michael@0: NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(), michael@0: "must have a non-empty list of children"); michael@0: PLDHashTable *hash = PL_NewDHashTable(&ChildrenHashOps, nullptr, michael@0: sizeof(ChildrenHashEntry), michael@0: kMaxChildrenInList * 4); michael@0: if (!hash) michael@0: return; michael@0: for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) { michael@0: // This will never fail because of the initial size we gave the table. michael@0: ChildrenHashEntry *entry = static_cast( michael@0: PL_DHashTableOperate(hash, curr->mRule, PL_DHASH_ADD)); michael@0: NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list"); michael@0: entry->mRuleNode = curr; michael@0: } michael@0: SetChildrenHash(hash); michael@0: } michael@0: michael@0: inline void michael@0: nsRuleNode::PropagateNoneBit(uint32_t aBit, nsRuleNode* aHighestNode) michael@0: { michael@0: nsRuleNode* curr = this; michael@0: for (;;) { michael@0: NS_ASSERTION(!(curr->mNoneBits & aBit), "propagating too far"); michael@0: curr->mNoneBits |= aBit; michael@0: if (curr == aHighestNode) michael@0: break; michael@0: curr = curr->mParent; michael@0: } michael@0: } michael@0: michael@0: inline void michael@0: nsRuleNode::PropagateDependentBit(nsStyleStructID aSID, nsRuleNode* aHighestNode, michael@0: void* aStruct) michael@0: { michael@0: NS_ASSERTION(aStruct, "expected struct"); michael@0: michael@0: uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); michael@0: for (nsRuleNode* curr = this; curr != aHighestNode; curr = curr->mParent) { michael@0: if (curr->mDependentBits & bit) { michael@0: #ifdef DEBUG michael@0: while (curr != aHighestNode) { michael@0: NS_ASSERTION(curr->mDependentBits & bit, "bit not set"); michael@0: curr = curr->mParent; michael@0: } michael@0: #endif michael@0: break; michael@0: } michael@0: michael@0: curr->mDependentBits |= bit; michael@0: michael@0: if (curr->IsUsedDirectly()) { michael@0: curr->mStyleData.SetStyleData(aSID, mPresContext, aStruct); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * The following "Check" functions are used for determining what type of michael@0: * sharing can be used for the data on this rule node. MORE HERE... michael@0: */ michael@0: michael@0: /* michael@0: * a callback function that that can revise the result of michael@0: * CheckSpecifiedProperties before finishing; aResult is the current michael@0: * result, and it returns the revised one. michael@0: */ michael@0: typedef nsRuleNode::RuleDetail michael@0: (* CheckCallbackFn)(const nsRuleData* aRuleData, michael@0: nsRuleNode::RuleDetail aResult); michael@0: michael@0: /** michael@0: * @param aValue the value being examined michael@0: * @param aSpecifiedCount to be incremented by one if the value is specified michael@0: * @param aInheritedCount to be incremented by one if the value is set to inherit michael@0: * @param aUnsetCount to be incremented by one if the value is set to unset michael@0: */ michael@0: inline void michael@0: ExamineCSSValue(const nsCSSValue& aValue, michael@0: uint32_t& aSpecifiedCount, michael@0: uint32_t& aInheritedCount, michael@0: uint32_t& aUnsetCount) michael@0: { michael@0: if (aValue.GetUnit() != eCSSUnit_Null) { michael@0: ++aSpecifiedCount; michael@0: if (aValue.GetUnit() == eCSSUnit_Inherit) { michael@0: ++aInheritedCount; michael@0: } else if (aValue.GetUnit() == eCSSUnit_Unset) { michael@0: ++aUnsetCount; michael@0: } michael@0: } michael@0: } michael@0: michael@0: static nsRuleNode::RuleDetail michael@0: CheckFontCallback(const nsRuleData* aRuleData, michael@0: nsRuleNode::RuleDetail aResult) michael@0: { michael@0: // em, ex, percent, 'larger', and 'smaller' values on font-size depend michael@0: // on the parent context's font-size michael@0: // Likewise, 'lighter' and 'bolder' values of 'font-weight', and 'wider' michael@0: // and 'narrower' values of 'font-stretch' depend on the parent. michael@0: const nsCSSValue& size = *aRuleData->ValueForFontSize(); michael@0: const nsCSSValue& weight = *aRuleData->ValueForFontWeight(); michael@0: if ((size.IsRelativeLengthUnit() && size.GetUnit() != eCSSUnit_RootEM) || michael@0: size.GetUnit() == eCSSUnit_Percent || michael@0: (size.GetUnit() == eCSSUnit_Enumerated && michael@0: (size.GetIntValue() == NS_STYLE_FONT_SIZE_SMALLER || michael@0: size.GetIntValue() == NS_STYLE_FONT_SIZE_LARGER)) || michael@0: aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Integer || michael@0: (weight.GetUnit() == eCSSUnit_Enumerated && michael@0: (weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_BOLDER || michael@0: weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_LIGHTER))) { michael@0: NS_ASSERTION(aResult == nsRuleNode::eRulePartialReset || michael@0: aResult == nsRuleNode::eRuleFullReset || michael@0: aResult == nsRuleNode::eRulePartialMixed || michael@0: aResult == nsRuleNode::eRuleFullMixed, michael@0: "we know we already have a reset-counted property"); michael@0: // Promote reset to mixed since we have something that depends on michael@0: // the parent. But never promote to inherited since that could michael@0: // cause inheritance of the exact value. michael@0: if (aResult == nsRuleNode::eRulePartialReset) michael@0: aResult = nsRuleNode::eRulePartialMixed; michael@0: else if (aResult == nsRuleNode::eRuleFullReset) michael@0: aResult = nsRuleNode::eRuleFullMixed; michael@0: } michael@0: michael@0: return aResult; michael@0: } michael@0: michael@0: static nsRuleNode::RuleDetail michael@0: CheckColorCallback(const nsRuleData* aRuleData, michael@0: nsRuleNode::RuleDetail aResult) michael@0: { michael@0: // currentColor values for color require inheritance michael@0: const nsCSSValue* colorValue = aRuleData->ValueForColor(); michael@0: if (colorValue->GetUnit() == eCSSUnit_EnumColor && michael@0: colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) { michael@0: NS_ASSERTION(aResult == nsRuleNode::eRuleFullReset, michael@0: "we should already be counted as full-reset"); michael@0: aResult = nsRuleNode::eRuleFullInherited; michael@0: } michael@0: michael@0: return aResult; michael@0: } michael@0: michael@0: static nsRuleNode::RuleDetail michael@0: CheckTextCallback(const nsRuleData* aRuleData, michael@0: nsRuleNode::RuleDetail aResult) michael@0: { michael@0: const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); michael@0: if (textAlignValue->GetUnit() == eCSSUnit_Enumerated && michael@0: textAlignValue->GetIntValue() == michael@0: NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT) { michael@0: // Promote reset to mixed since we have something that depends on michael@0: // the parent. michael@0: if (aResult == nsRuleNode::eRulePartialReset) michael@0: aResult = nsRuleNode::eRulePartialMixed; michael@0: else if (aResult == nsRuleNode::eRuleFullReset) michael@0: aResult = nsRuleNode::eRuleFullMixed; michael@0: } michael@0: michael@0: return aResult; michael@0: } michael@0: michael@0: static nsRuleNode::RuleDetail michael@0: CheckVariablesCallback(const nsRuleData* aRuleData, michael@0: nsRuleNode::RuleDetail aResult) michael@0: { michael@0: // We don't actually have any properties on nsStyleVariables, so we do michael@0: // all of the RuleDetail calculation in here. michael@0: if (aRuleData->mVariables) { michael@0: return nsRuleNode::eRulePartialMixed; michael@0: } michael@0: return nsRuleNode::eRuleNone; michael@0: } michael@0: michael@0: #define FLAG_DATA_FOR_PROPERTY(name_, id_, method_, flags_, pref_, \ michael@0: parsevariant_, kwtable_, stylestructoffset_, \ michael@0: animtype_) \ michael@0: flags_, michael@0: michael@0: // The order here must match the enums in *CheckCounter in nsCSSProps.cpp. michael@0: michael@0: static const uint32_t gFontFlags[] = { michael@0: #define CSS_PROP_FONT FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_FONT michael@0: }; michael@0: michael@0: static const uint32_t gDisplayFlags[] = { michael@0: #define CSS_PROP_DISPLAY FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_DISPLAY michael@0: }; michael@0: michael@0: static const uint32_t gVisibilityFlags[] = { michael@0: #define CSS_PROP_VISIBILITY FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_VISIBILITY michael@0: }; michael@0: michael@0: static const uint32_t gMarginFlags[] = { michael@0: #define CSS_PROP_MARGIN FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_MARGIN michael@0: }; michael@0: michael@0: static const uint32_t gBorderFlags[] = { michael@0: #define CSS_PROP_BORDER FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_BORDER michael@0: }; michael@0: michael@0: static const uint32_t gPaddingFlags[] = { michael@0: #define CSS_PROP_PADDING FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_PADDING michael@0: }; michael@0: michael@0: static const uint32_t gOutlineFlags[] = { michael@0: #define CSS_PROP_OUTLINE FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_OUTLINE michael@0: }; michael@0: michael@0: static const uint32_t gListFlags[] = { michael@0: #define CSS_PROP_LIST FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_LIST michael@0: }; michael@0: michael@0: static const uint32_t gColorFlags[] = { michael@0: #define CSS_PROP_COLOR FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_COLOR michael@0: }; michael@0: michael@0: static const uint32_t gBackgroundFlags[] = { michael@0: #define CSS_PROP_BACKGROUND FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_BACKGROUND michael@0: }; michael@0: michael@0: static const uint32_t gPositionFlags[] = { michael@0: #define CSS_PROP_POSITION FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_POSITION michael@0: }; michael@0: michael@0: static const uint32_t gTableFlags[] = { michael@0: #define CSS_PROP_TABLE FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_TABLE michael@0: }; michael@0: michael@0: static const uint32_t gTableBorderFlags[] = { michael@0: #define CSS_PROP_TABLEBORDER FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_TABLEBORDER michael@0: }; michael@0: michael@0: static const uint32_t gContentFlags[] = { michael@0: #define CSS_PROP_CONTENT FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_CONTENT michael@0: }; michael@0: michael@0: static const uint32_t gQuotesFlags[] = { michael@0: #define CSS_PROP_QUOTES FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_QUOTES michael@0: }; michael@0: michael@0: static const uint32_t gTextFlags[] = { michael@0: #define CSS_PROP_TEXT FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_TEXT michael@0: }; michael@0: michael@0: static const uint32_t gTextResetFlags[] = { michael@0: #define CSS_PROP_TEXTRESET FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_TEXTRESET michael@0: }; michael@0: michael@0: static const uint32_t gUserInterfaceFlags[] = { michael@0: #define CSS_PROP_USERINTERFACE FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_USERINTERFACE michael@0: }; michael@0: michael@0: static const uint32_t gUIResetFlags[] = { michael@0: #define CSS_PROP_UIRESET FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_UIRESET michael@0: }; michael@0: michael@0: static const uint32_t gXULFlags[] = { michael@0: #define CSS_PROP_XUL FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_XUL michael@0: }; michael@0: michael@0: static const uint32_t gSVGFlags[] = { michael@0: #define CSS_PROP_SVG FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_SVG michael@0: }; michael@0: michael@0: static const uint32_t gSVGResetFlags[] = { michael@0: #define CSS_PROP_SVGRESET FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_SVGRESET michael@0: }; michael@0: michael@0: static const uint32_t gColumnFlags[] = { michael@0: #define CSS_PROP_COLUMN FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_COLUMN michael@0: }; michael@0: michael@0: // There are no properties in nsStyleVariables, but we can't have a michael@0: // zero length array. michael@0: static const uint32_t gVariablesFlags[] = { michael@0: 0, michael@0: #define CSS_PROP_VARIABLES FLAG_DATA_FOR_PROPERTY michael@0: #include "nsCSSPropList.h" michael@0: #undef CSS_PROP_VARIABLES michael@0: }; michael@0: static_assert(sizeof(gVariablesFlags) == sizeof(uint32_t), michael@0: "if nsStyleVariables has properties now you can remove the dummy " michael@0: "gVariablesFlags entry"); michael@0: michael@0: #undef FLAG_DATA_FOR_PROPERTY michael@0: michael@0: static const uint32_t* gFlagsByStruct[] = { michael@0: michael@0: #define STYLE_STRUCT(name, checkdata_cb) \ michael@0: g##name##Flags, michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: }; michael@0: michael@0: static const CheckCallbackFn gCheckCallbacks[] = { michael@0: michael@0: #define STYLE_STRUCT(name, checkdata_cb) \ michael@0: checkdata_cb, michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: }; michael@0: michael@0: #ifdef DEBUG michael@0: static bool michael@0: AreAllMathMLPropertiesUndefined(const nsRuleData* aRuleData) michael@0: { michael@0: return michael@0: aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Null && michael@0: aRuleData->ValueForScriptSizeMultiplier()->GetUnit() == eCSSUnit_Null && michael@0: aRuleData->ValueForScriptMinSize()->GetUnit() == eCSSUnit_Null && michael@0: aRuleData->ValueForMathVariant()->GetUnit() == eCSSUnit_Null && michael@0: aRuleData->ValueForMathDisplay()->GetUnit() == eCSSUnit_Null; michael@0: } michael@0: #endif michael@0: michael@0: inline nsRuleNode::RuleDetail michael@0: nsRuleNode::CheckSpecifiedProperties(const nsStyleStructID aSID, michael@0: const nsRuleData* aRuleData) michael@0: { michael@0: // Build a count of the: michael@0: uint32_t total = 0, // total number of props in the struct michael@0: specified = 0, // number that were specified for this node michael@0: inherited = 0, // number that were 'inherit' (and not michael@0: // eCSSUnit_Inherit) for this node michael@0: unset = 0; // number that were 'unset' michael@0: michael@0: // See comment in nsRuleData.h above mValueOffsets. michael@0: NS_ABORT_IF_FALSE(aRuleData->mValueOffsets[aSID] == 0, michael@0: "we assume the value offset is zero instead of adding it"); michael@0: for (nsCSSValue *values = aRuleData->mValueStorage, michael@0: *values_end = values + nsCSSProps::PropertyCountInStruct(aSID); michael@0: values != values_end; ++values) { michael@0: ++total; michael@0: ExamineCSSValue(*values, specified, inherited, unset); michael@0: } michael@0: michael@0: if (!nsCachedStyleData::IsReset(aSID)) { michael@0: // For inherited properties, 'unset' means the same as 'inherit'. michael@0: inherited += unset; michael@0: unset = 0; michael@0: } michael@0: michael@0: #if 0 michael@0: printf("CheckSpecifiedProperties: SID=%d total=%d spec=%d inh=%d.\n", michael@0: aSID, total, specified, inherited); michael@0: #endif michael@0: michael@0: NS_ASSERTION(aSID != eStyleStruct_Font || michael@0: mPresContext->Document()->GetMathMLEnabled() || michael@0: AreAllMathMLPropertiesUndefined(aRuleData), michael@0: "MathML style property was defined even though MathML is disabled"); michael@0: michael@0: /* michael@0: * Return the most specific information we can: prefer None or Full michael@0: * over Partial, and Reset or Inherited over Mixed, since we can michael@0: * optimize based on the edge cases and not the in-between cases. michael@0: */ michael@0: nsRuleNode::RuleDetail result; michael@0: if (inherited == total) michael@0: result = eRuleFullInherited; michael@0: else if (specified == total michael@0: // MathML defines 5 properties in Font that will never be set when michael@0: // MathML is not in use. Therefore if all but five michael@0: // properties have been set, and MathML is not enabled, we can treat michael@0: // this as fully specified. Code in nsMathMLElementFactory will michael@0: // rebuild the rule tree and style data when MathML is first enabled michael@0: // (see nsMathMLElement::BindToTree). michael@0: || (aSID == eStyleStruct_Font && specified + 5 == total && michael@0: !mPresContext->Document()->GetMathMLEnabled()) michael@0: ) { michael@0: if (inherited == 0) michael@0: result = eRuleFullReset; michael@0: else michael@0: result = eRuleFullMixed; michael@0: } else if (specified == 0) michael@0: result = eRuleNone; michael@0: else if (specified == inherited) michael@0: result = eRulePartialInherited; michael@0: else if (inherited == 0) michael@0: result = eRulePartialReset; michael@0: else michael@0: result = eRulePartialMixed; michael@0: michael@0: CheckCallbackFn cb = gCheckCallbacks[aSID]; michael@0: if (cb) { michael@0: result = (*cb)(aRuleData, result); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: // If we need to restrict which properties apply to the style context, michael@0: // return the bit to check in nsCSSProp's flags table. Otherwise, michael@0: // return 0. michael@0: inline uint32_t michael@0: GetPseudoRestriction(nsStyleContext *aContext) michael@0: { michael@0: // This needs to match nsStyleSet::WalkRestrictionRule. michael@0: uint32_t pseudoRestriction = 0; michael@0: nsIAtom *pseudoType = aContext->GetPseudo(); michael@0: if (pseudoType) { michael@0: if (pseudoType == nsCSSPseudoElements::firstLetter) { michael@0: pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LETTER; michael@0: } else if (pseudoType == nsCSSPseudoElements::firstLine) { michael@0: pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LINE; michael@0: } else if (pseudoType == nsCSSPseudoElements::mozPlaceholder) { michael@0: pseudoRestriction = CSS_PROPERTY_APPLIES_TO_PLACEHOLDER; michael@0: } michael@0: } michael@0: return pseudoRestriction; michael@0: } michael@0: michael@0: static void michael@0: UnsetPropertiesWithoutFlags(const nsStyleStructID aSID, michael@0: nsRuleData* aRuleData, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_ASSERTION(aFlags != 0, "aFlags must be nonzero"); michael@0: michael@0: const uint32_t *flagData = gFlagsByStruct[aSID]; michael@0: michael@0: // See comment in nsRuleData.h above mValueOffsets. michael@0: NS_ABORT_IF_FALSE(aRuleData->mValueOffsets[aSID] == 0, michael@0: "we assume the value offset is zero instead of adding it"); michael@0: nsCSSValue *values = aRuleData->mValueStorage; michael@0: michael@0: for (size_t i = 0, i_end = nsCSSProps::PropertyCountInStruct(aSID); michael@0: i != i_end; ++i) { michael@0: if ((flagData[i] & aFlags) != aFlags) michael@0: values[i].Reset(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * We allocate arrays of CSS values with alloca. (These arrays are a michael@0: * fixed size per style struct, but we don't want to waste the michael@0: * allocation and construction/destruction costs of the big structs when michael@0: * we're handling much smaller ones.) Since the lifetime of an alloca michael@0: * allocation is the life of the calling function, the caller must call michael@0: * alloca. However, to ensure that constructors and destructors are michael@0: * balanced, we do the constructor and destructor calling from this RAII michael@0: * class, AutoCSSValueArray. michael@0: */ michael@0: struct AutoCSSValueArray { michael@0: /** michael@0: * aStorage must be the result of alloca(aCount * sizeof(nsCSSValue)) michael@0: */ michael@0: AutoCSSValueArray(void* aStorage, size_t aCount) { michael@0: NS_ABORT_IF_FALSE(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0, michael@0: "bad alignment from alloca"); michael@0: mCount = aCount; michael@0: // Don't use placement new[], since it might store extra data michael@0: // for the count (on Windows!). michael@0: mArray = static_cast(aStorage); michael@0: for (size_t i = 0; i < mCount; ++i) { michael@0: new (mArray + i) nsCSSValue(); michael@0: } michael@0: } michael@0: michael@0: ~AutoCSSValueArray() { michael@0: for (size_t i = 0; i < mCount; ++i) { michael@0: mArray[i].~nsCSSValue(); michael@0: } michael@0: } michael@0: michael@0: nsCSSValue* get() { return mArray; } michael@0: michael@0: private: michael@0: nsCSSValue *mArray; michael@0: size_t mCount; michael@0: }; michael@0: michael@0: /* static */ bool michael@0: nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID, michael@0: nsRuleData* aRuleData, michael@0: nsStyleContext* aContext) michael@0: { michael@0: MOZ_ASSERT(aSID != eStyleStruct_Variables); michael@0: MOZ_ASSERT(aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(aSID)); michael@0: MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0); michael@0: michael@0: nsCSSParser parser; michael@0: bool anyTokenStreams = false; michael@0: michael@0: // Look at each property in the nsRuleData for the given style struct. michael@0: size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); michael@0: for (nsCSSValue* value = aRuleData->mValueStorage, michael@0: *values_end = aRuleData->mValueStorage + nprops; michael@0: value != values_end; value++) { michael@0: if (value->GetUnit() != eCSSUnit_TokenStream) { michael@0: continue; michael@0: } michael@0: michael@0: const CSSVariableValues* variables = michael@0: &aContext->StyleVariables()->mVariables; michael@0: nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue(); michael@0: michael@0: // Note that ParsePropertyWithVariableReferences relies on the fact michael@0: // that the nsCSSValue in aRuleData for the property we are re-parsing michael@0: // is still the token stream value. When michael@0: // ParsePropertyWithVariableReferences calls michael@0: // nsCSSExpandedDataBlock::MapRuleInfoInto, that function will add michael@0: // the ImageValue that is created into the token stream object's michael@0: // mImageValues table; see the comment above mImageValues for why. michael@0: michael@0: // XXX Should pass in sheet here (see bug 952338). michael@0: parser.ParsePropertyWithVariableReferences( michael@0: tokenStream->mPropertyID, tokenStream->mShorthandPropertyID, michael@0: tokenStream->mTokenStream, variables, aRuleData, michael@0: tokenStream->mSheetURI, tokenStream->mBaseURI, michael@0: tokenStream->mSheetPrincipal, nullptr, michael@0: tokenStream->mLineNumber, tokenStream->mLineOffset); michael@0: aRuleData->mCanStoreInRuleTree = false; michael@0: anyTokenStreams = true; michael@0: } michael@0: michael@0: return anyTokenStreams; michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::WalkRuleTree(const nsStyleStructID aSID, michael@0: nsStyleContext* aContext) michael@0: { michael@0: // use placement new[] on the result of alloca() to allocate a michael@0: // variable-sized stack array, including execution of constructors, michael@0: // and use an RAII class to run the destructors too. michael@0: size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); michael@0: void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); michael@0: AutoCSSValueArray dataArray(dataStorage, nprops); michael@0: michael@0: nsRuleData ruleData(nsCachedStyleData::GetBitForSID(aSID), michael@0: dataArray.get(), mPresContext, aContext); michael@0: ruleData.mValueOffsets[aSID] = 0; michael@0: michael@0: // We start at the most specific rule in the tree. michael@0: void* startStruct = nullptr; michael@0: michael@0: nsRuleNode* ruleNode = this; michael@0: nsRuleNode* highestNode = nullptr; // The highest node in the rule tree michael@0: // that has the same properties michael@0: // specified for struct |aSID| as michael@0: // |this| does. michael@0: nsRuleNode* rootNode = this; // After the loop below, this will be the michael@0: // highest node that we've walked without michael@0: // finding cached data on the rule tree. michael@0: // If we don't find any cached data, it michael@0: // will be the root. (XXX misnamed) michael@0: RuleDetail detail = eRuleNone; michael@0: uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); michael@0: michael@0: while (ruleNode) { michael@0: // See if this rule node has cached the fact that the remaining michael@0: // nodes along this path specify no data whatsoever. michael@0: if (ruleNode->mNoneBits & bit) michael@0: break; michael@0: michael@0: // If the dependent bit is set on a rule node for this struct, that michael@0: // means its rule won't have any information to add, so skip it. michael@0: // NOTE: If we exit the loop because of the !IsUsedDirectly() check, michael@0: // then we're guaranteed to break immediately afterwards due to a michael@0: // non-null startStruct. michael@0: while ((ruleNode->mDependentBits & bit) && !ruleNode->IsUsedDirectly()) { michael@0: NS_ASSERTION(ruleNode->mStyleData.GetStyleData(aSID) == nullptr, michael@0: "dependent bit with cached data makes no sense"); michael@0: // Climb up to the next rule in the tree (a less specific rule). michael@0: rootNode = ruleNode; michael@0: ruleNode = ruleNode->mParent; michael@0: NS_ASSERTION(!(ruleNode->mNoneBits & bit), "can't have both bits set"); michael@0: } michael@0: michael@0: // Check for cached data after the inner loop above -- otherwise michael@0: // we'll miss it. michael@0: startStruct = ruleNode->mStyleData.GetStyleData(aSID); michael@0: if (startStruct) michael@0: break; // We found a rule with fully specified data. We don't michael@0: // need to go up the tree any further, since the remainder michael@0: // of this branch has already been computed. michael@0: michael@0: // Ask the rule to fill in the properties that it specifies. michael@0: nsIStyleRule *rule = ruleNode->mRule; michael@0: if (rule) { michael@0: ruleData.mLevel = ruleNode->GetLevel(); michael@0: ruleData.mIsImportantRule = ruleNode->IsImportantRule(); michael@0: rule->MapRuleInfoInto(&ruleData); michael@0: } michael@0: michael@0: // Now we check to see how many properties have been specified by michael@0: // the rules we've examined so far. michael@0: RuleDetail oldDetail = detail; michael@0: detail = CheckSpecifiedProperties(aSID, &ruleData); michael@0: michael@0: if (oldDetail == eRuleNone && detail != eRuleNone) michael@0: highestNode = ruleNode; michael@0: michael@0: if (detail == eRuleFullReset || michael@0: detail == eRuleFullMixed || michael@0: detail == eRuleFullInherited) michael@0: break; // We don't need to examine any more rules. All properties michael@0: // have been fully specified. michael@0: michael@0: // Climb up to the next rule in the tree (a less specific rule). michael@0: rootNode = ruleNode; michael@0: ruleNode = ruleNode->mParent; michael@0: } michael@0: michael@0: bool recomputeDetail = false; michael@0: michael@0: // If we are computing a style struct other than nsStyleVariables, and michael@0: // ruleData has any properties with variable references (nsCSSValues of michael@0: // type eCSSUnit_TokenStream), then we need to resolve these. michael@0: if (aSID != eStyleStruct_Variables) { michael@0: // A property's value might have became 'inherit' after resolving michael@0: // variable references. (This happens when an inherited property michael@0: // fails to parse its resolved value.) We need to recompute michael@0: // |detail| in case this happened. michael@0: recomputeDetail = ResolveVariableReferences(aSID, &ruleData, aContext); michael@0: } michael@0: michael@0: // If needed, unset the properties that don't have a flag that allows michael@0: // them to be set for this style context. (For example, only some michael@0: // properties apply to :first-line and :first-letter.) michael@0: uint32_t pseudoRestriction = GetPseudoRestriction(aContext); michael@0: if (pseudoRestriction) { michael@0: UnsetPropertiesWithoutFlags(aSID, &ruleData, pseudoRestriction); michael@0: michael@0: // We need to recompute |detail| based on the restrictions we just applied. michael@0: // We can adjust |detail| arbitrarily because of the restriction michael@0: // rule added in nsStyleSet::WalkRestrictionRule. michael@0: recomputeDetail = true; michael@0: } michael@0: michael@0: if (recomputeDetail) { michael@0: detail = CheckSpecifiedProperties(aSID, &ruleData); michael@0: } michael@0: michael@0: NS_ASSERTION(!startStruct || (detail != eRuleFullReset && michael@0: detail != eRuleFullMixed && michael@0: detail != eRuleFullInherited), michael@0: "can't have start struct and be fully specified"); michael@0: michael@0: bool isReset = nsCachedStyleData::IsReset(aSID); michael@0: if (!highestNode) michael@0: highestNode = rootNode; michael@0: michael@0: if (!ruleData.mCanStoreInRuleTree) michael@0: detail = eRulePartialMixed; // Treat as though some data is specified to avoid michael@0: // the optimizations and force data computation. michael@0: michael@0: if (detail == eRuleNone && startStruct) { michael@0: // We specified absolutely no rule information, but a parent rule in the tree michael@0: // specified all the rule information. We set a bit along the branch from our michael@0: // node in the tree to the node that specified the data that tells nodes on that michael@0: // branch that they never need to examine their rules for this particular struct type michael@0: // ever again. michael@0: PropagateDependentBit(aSID, ruleNode, startStruct); michael@0: return startStruct; michael@0: } michael@0: if ((!startStruct && !isReset && michael@0: (detail == eRuleNone || detail == eRulePartialInherited)) || michael@0: detail == eRuleFullInherited) { michael@0: // We specified no non-inherited information and neither did any of michael@0: // our parent rules. michael@0: michael@0: // We set a bit along the branch from the highest node (ruleNode) michael@0: // down to our node (this) indicating that no non-inherited data was michael@0: // specified. This bit is guaranteed to be set already on the path michael@0: // from the highest node to the root node in the case where michael@0: // (detail == eRuleNone), which is the most common case here. michael@0: // We must check |!isReset| because the Compute*Data functions for michael@0: // reset structs wouldn't handle none bits correctly. michael@0: if (highestNode != this && !isReset) michael@0: PropagateNoneBit(bit, highestNode); michael@0: michael@0: // All information must necessarily be inherited from our parent style context. michael@0: // In the absence of any computed data in the rule tree and with michael@0: // no rules specified that didn't have values of 'inherit', we should check our parent. michael@0: nsStyleContext* parentContext = aContext->GetParent(); michael@0: if (isReset) { michael@0: /* Reset structs don't inherit from first-line. */ michael@0: /* See similar code in COMPUTE_START_RESET */ michael@0: while (parentContext && michael@0: parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { michael@0: parentContext = parentContext->GetParent(); michael@0: } michael@0: } michael@0: if (parentContext) { michael@0: // We have a parent, and so we should just inherit from the parent. michael@0: // Set the inherit bits on our context. These bits tell the style context that michael@0: // it never has to go back to the rule tree for data. Instead the style context tree michael@0: // should be walked to find the data. michael@0: const void* parentStruct = parentContext->StyleData(aSID); michael@0: aContext->AddStyleBit(bit); // makes const_cast OK. michael@0: aContext->SetStyle(aSID, const_cast(parentStruct)); michael@0: return parentStruct; michael@0: } michael@0: else michael@0: // We are the root. In the case of fonts, the default values just michael@0: // come from the pres context. michael@0: return SetDefaultOnRoot(aSID, aContext); michael@0: } michael@0: michael@0: // We need to compute the data from the information that the rules specified. michael@0: const void* res; michael@0: #define STYLE_STRUCT_TEST aSID michael@0: #define STYLE_STRUCT(name, checkdata_cb) \ michael@0: res = Compute##name##Data(startStruct, &ruleData, aContext, \ michael@0: highestNode, detail, ruleData.mCanStoreInRuleTree); michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: #undef STYLE_STRUCT_TEST michael@0: michael@0: // Now return the result. michael@0: return res; michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContext) michael@0: { michael@0: switch (aSID) { michael@0: case eStyleStruct_Font: michael@0: { michael@0: nsStyleFont* fontData = new (mPresContext) nsStyleFont(mPresContext); michael@0: nscoord minimumFontSize = mPresContext->MinFontSize(fontData->mLanguage); michael@0: michael@0: if (minimumFontSize > 0 && !mPresContext->IsChrome()) { michael@0: fontData->mFont.size = std::max(fontData->mSize, minimumFontSize); michael@0: } michael@0: else { michael@0: fontData->mFont.size = fontData->mSize; michael@0: } michael@0: aContext->SetStyle(eStyleStruct_Font, fontData); michael@0: return fontData; michael@0: } michael@0: case eStyleStruct_Display: michael@0: { michael@0: nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay(); michael@0: aContext->SetStyle(eStyleStruct_Display, disp); michael@0: return disp; michael@0: } michael@0: case eStyleStruct_Visibility: michael@0: { michael@0: nsStyleVisibility* vis = new (mPresContext) nsStyleVisibility(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_Visibility, vis); michael@0: return vis; michael@0: } michael@0: case eStyleStruct_Text: michael@0: { michael@0: nsStyleText* text = new (mPresContext) nsStyleText(); michael@0: aContext->SetStyle(eStyleStruct_Text, text); michael@0: return text; michael@0: } michael@0: case eStyleStruct_TextReset: michael@0: { michael@0: nsStyleTextReset* text = new (mPresContext) nsStyleTextReset(); michael@0: aContext->SetStyle(eStyleStruct_TextReset, text); michael@0: return text; michael@0: } michael@0: case eStyleStruct_Color: michael@0: { michael@0: nsStyleColor* color = new (mPresContext) nsStyleColor(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_Color, color); michael@0: return color; michael@0: } michael@0: case eStyleStruct_Background: michael@0: { michael@0: nsStyleBackground* bg = new (mPresContext) nsStyleBackground(); michael@0: aContext->SetStyle(eStyleStruct_Background, bg); michael@0: return bg; michael@0: } michael@0: case eStyleStruct_Margin: michael@0: { michael@0: nsStyleMargin* margin = new (mPresContext) nsStyleMargin(); michael@0: aContext->SetStyle(eStyleStruct_Margin, margin); michael@0: return margin; michael@0: } michael@0: case eStyleStruct_Border: michael@0: { michael@0: nsStyleBorder* border = new (mPresContext) nsStyleBorder(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_Border, border); michael@0: return border; michael@0: } michael@0: case eStyleStruct_Padding: michael@0: { michael@0: nsStylePadding* padding = new (mPresContext) nsStylePadding(); michael@0: aContext->SetStyle(eStyleStruct_Padding, padding); michael@0: return padding; michael@0: } michael@0: case eStyleStruct_Outline: michael@0: { michael@0: nsStyleOutline* outline = new (mPresContext) nsStyleOutline(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_Outline, outline); michael@0: return outline; michael@0: } michael@0: case eStyleStruct_List: michael@0: { michael@0: nsStyleList* list = new (mPresContext) nsStyleList(); michael@0: aContext->SetStyle(eStyleStruct_List, list); michael@0: return list; michael@0: } michael@0: case eStyleStruct_Position: michael@0: { michael@0: nsStylePosition* pos = new (mPresContext) nsStylePosition(); michael@0: aContext->SetStyle(eStyleStruct_Position, pos); michael@0: return pos; michael@0: } michael@0: case eStyleStruct_Table: michael@0: { michael@0: nsStyleTable* table = new (mPresContext) nsStyleTable(); michael@0: aContext->SetStyle(eStyleStruct_Table, table); michael@0: return table; michael@0: } michael@0: case eStyleStruct_TableBorder: michael@0: { michael@0: nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_TableBorder, table); michael@0: return table; michael@0: } michael@0: case eStyleStruct_Content: michael@0: { michael@0: nsStyleContent* content = new (mPresContext) nsStyleContent(); michael@0: aContext->SetStyle(eStyleStruct_Content, content); michael@0: return content; michael@0: } michael@0: case eStyleStruct_Quotes: michael@0: { michael@0: nsStyleQuotes* quotes = new (mPresContext) nsStyleQuotes(); michael@0: aContext->SetStyle(eStyleStruct_Quotes, quotes); michael@0: return quotes; michael@0: } michael@0: case eStyleStruct_UserInterface: michael@0: { michael@0: nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface(); michael@0: aContext->SetStyle(eStyleStruct_UserInterface, ui); michael@0: return ui; michael@0: } michael@0: case eStyleStruct_UIReset: michael@0: { michael@0: nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset(); michael@0: aContext->SetStyle(eStyleStruct_UIReset, ui); michael@0: return ui; michael@0: } michael@0: case eStyleStruct_XUL: michael@0: { michael@0: nsStyleXUL* xul = new (mPresContext) nsStyleXUL(); michael@0: aContext->SetStyle(eStyleStruct_XUL, xul); michael@0: return xul; michael@0: } michael@0: case eStyleStruct_Column: michael@0: { michael@0: nsStyleColumn* column = new (mPresContext) nsStyleColumn(mPresContext); michael@0: aContext->SetStyle(eStyleStruct_Column, column); michael@0: return column; michael@0: } michael@0: case eStyleStruct_SVG: michael@0: { michael@0: nsStyleSVG* svg = new (mPresContext) nsStyleSVG(); michael@0: aContext->SetStyle(eStyleStruct_SVG, svg); michael@0: return svg; michael@0: } michael@0: case eStyleStruct_SVGReset: michael@0: { michael@0: nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset(); michael@0: aContext->SetStyle(eStyleStruct_SVGReset, svgReset); michael@0: return svgReset; michael@0: } michael@0: case eStyleStruct_Variables: michael@0: { michael@0: nsStyleVariables* vars = new (mPresContext) nsStyleVariables(); michael@0: aContext->SetStyle(eStyleStruct_Variables, vars); michael@0: return vars; michael@0: } michael@0: default: michael@0: /* michael@0: * unhandled case: nsStyleStructID_Length. michael@0: * last item of nsStyleStructID, to know its length. michael@0: */ michael@0: NS_ABORT_IF_FALSE(false, "unexpected SID"); michael@0: return nullptr; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: /* michael@0: * This function handles cascading of *-left or *-right box properties michael@0: * against *-start (which is L for LTR and R for RTL) or *-end (which is michael@0: * R for LTR and L for RTL). michael@0: * michael@0: * Cascading these properties correctly is hard because we need to michael@0: * cascade two properties as one, but which two properties depends on a michael@0: * third property ('direction'). We solve this by treating each of michael@0: * these properties (say, 'margin-start') as a shorthand that sets a michael@0: * property containing the value of the property specified michael@0: * ('margin-start-value') and sets a pair of properties michael@0: * ('margin-left-ltr-source' and 'margin-right-rtl-source') saying which michael@0: * of the properties we use. Thus, when we want to compute the value of michael@0: * 'margin-left' when 'direction' is 'ltr', we look at the value of michael@0: * 'margin-left-ltr-source', which tells us whether to use the highest michael@0: * 'margin-left' in the cascade or the highest 'margin-start'. michael@0: * michael@0: * Finally, since we can compute the normal (*-left and *-right) michael@0: * properties in a loop, this function works by modifying the data we michael@0: * will use in that loop (which the caller must copy from the const michael@0: * input). michael@0: */ michael@0: void michael@0: nsRuleNode::AdjustLogicalBoxProp(nsStyleContext* aContext, michael@0: const nsCSSValue& aLTRSource, michael@0: const nsCSSValue& aRTLSource, michael@0: const nsCSSValue& aLTRLogicalValue, michael@0: const nsCSSValue& aRTLLogicalValue, michael@0: mozilla::css::Side aSide, michael@0: nsCSSRect& aValueRect, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: bool LTRlogical = aLTRSource.GetUnit() == eCSSUnit_Enumerated && michael@0: aLTRSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL; michael@0: bool RTLlogical = aRTLSource.GetUnit() == eCSSUnit_Enumerated && michael@0: aRTLSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL; michael@0: if (LTRlogical || RTLlogical) { michael@0: // We can't cache anything on the rule tree if we use any data from michael@0: // the style context, since data cached in the rule tree could be michael@0: // used with a style context with a different value. michael@0: aCanStoreInRuleTree = false; michael@0: uint8_t dir = aContext->StyleVisibility()->mDirection; michael@0: michael@0: if (dir == NS_STYLE_DIRECTION_LTR) { michael@0: if (LTRlogical) michael@0: aValueRect.*(nsCSSRect::sides[aSide]) = aLTRLogicalValue; michael@0: } else { michael@0: if (RTLlogical) michael@0: aValueRect.*(nsCSSRect::sides[aSide]) = aRTLLogicalValue; michael@0: } michael@0: } else if (aLTRLogicalValue.GetUnit() == eCSSUnit_Inherit || michael@0: aRTLLogicalValue.GetUnit() == eCSSUnit_Inherit) { michael@0: // It actually is valid to store this in the ruletree, since michael@0: // LTRlogical and RTLlogical are both false, but doing that will michael@0: // trigger asserts. Silence those. michael@0: aCanStoreInRuleTree = false; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Begin an nsRuleNode::Compute*Data function for an inherited struct. michael@0: * michael@0: * @param type_ The nsStyle* type this function computes. michael@0: * @param ctorargs_ The arguments used for the default nsStyle* constructor. michael@0: * @param data_ Variable (declared here) holding the result of this michael@0: * function. michael@0: * @param parentdata_ Variable (declared here) holding the parent style michael@0: * context's data for this struct. michael@0: */ michael@0: #define COMPUTE_START_INHERITED(type_, ctorargs_, data_, parentdata_) \ michael@0: NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ michael@0: "should not have bothered calling Compute*Data"); \ michael@0: \ michael@0: nsStyleContext* parentContext = aContext->GetParent(); \ michael@0: \ michael@0: nsStyle##type_* data_ = nullptr; \ michael@0: mozilla::Maybe maybeFakeParentData; \ michael@0: const nsStyle##type_* parentdata_ = nullptr; \ michael@0: bool canStoreInRuleTree = aCanStoreInRuleTree; \ michael@0: \ michael@0: /* If |canStoreInRuleTree| might be true by the time we're done, we */ \ michael@0: /* can't call parentContext->Style##type_() since it could recur into */ \ michael@0: /* setting the same struct on the same rule node, causing a leak. */ \ michael@0: if (aRuleDetail != eRuleFullReset && \ michael@0: (!aStartStruct || (aRuleDetail != eRulePartialReset && \ michael@0: aRuleDetail != eRuleNone))) { \ michael@0: if (parentContext) { \ michael@0: parentdata_ = parentContext->Style##type_(); \ michael@0: } else { \ michael@0: maybeFakeParentData.construct ctorargs_; \ michael@0: parentdata_ = maybeFakeParentData.addr(); \ michael@0: } \ michael@0: } \ michael@0: if (aStartStruct) \ michael@0: /* We only need to compute the delta between this computed data and */ \ michael@0: /* our computed data. */ \ michael@0: data_ = new (mPresContext) \ michael@0: nsStyle##type_(*static_cast(aStartStruct)); \ michael@0: else { \ michael@0: if (aRuleDetail != eRuleFullMixed && aRuleDetail != eRuleFullReset) { \ michael@0: /* No question. We will have to inherit. Go ahead and init */ \ michael@0: /* with inherited vals from parent. */ \ michael@0: canStoreInRuleTree = false; \ michael@0: if (parentdata_) \ michael@0: data_ = new (mPresContext) nsStyle##type_(*parentdata_); \ michael@0: else \ michael@0: data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ michael@0: } \ michael@0: else \ michael@0: data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ michael@0: } \ michael@0: \ michael@0: if (!parentdata_) \ michael@0: parentdata_ = data_; michael@0: michael@0: /** michael@0: * Begin an nsRuleNode::Compute*Data function for a reset struct. michael@0: * michael@0: * @param type_ The nsStyle* type this function computes. michael@0: * @param ctorargs_ The arguments used for the default nsStyle* constructor. michael@0: * @param data_ Variable (declared here) holding the result of this michael@0: * function. michael@0: * @param parentdata_ Variable (declared here) holding the parent style michael@0: * context's data for this struct. michael@0: */ michael@0: #define COMPUTE_START_RESET(type_, ctorargs_, data_, parentdata_) \ michael@0: NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ michael@0: "should not have bothered calling Compute*Data"); \ michael@0: \ michael@0: nsStyleContext* parentContext = aContext->GetParent(); \ michael@0: /* Reset structs don't inherit from first-line */ \ michael@0: /* See similar code in WalkRuleTree */ \ michael@0: while (parentContext && \ michael@0: parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { \ michael@0: parentContext = parentContext->GetParent(); \ michael@0: } \ michael@0: \ michael@0: nsStyle##type_* data_; \ michael@0: if (aStartStruct) \ michael@0: /* We only need to compute the delta between this computed data and */ \ michael@0: /* our computed data. */ \ michael@0: data_ = new (mPresContext) \ michael@0: nsStyle##type_(*static_cast(aStartStruct)); \ michael@0: else \ michael@0: data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ michael@0: \ michael@0: /* If |canStoreInRuleTree| might be true by the time we're done, we */ \ michael@0: /* can't call parentContext->Style##type_() since it could recur into */ \ michael@0: /* setting the same struct on the same rule node, causing a leak. */ \ michael@0: mozilla::Maybe maybeFakeParentData; \ michael@0: const nsStyle##type_* parentdata_ = data_; \ michael@0: if (aRuleDetail != eRuleFullReset && \ michael@0: aRuleDetail != eRulePartialReset && \ michael@0: aRuleDetail != eRuleNone) { \ michael@0: if (parentContext) { \ michael@0: parentdata_ = parentContext->Style##type_(); \ michael@0: } else { \ michael@0: maybeFakeParentData.construct ctorargs_; \ michael@0: parentdata_ = maybeFakeParentData.addr(); \ michael@0: } \ michael@0: } \ michael@0: bool canStoreInRuleTree = aCanStoreInRuleTree; michael@0: michael@0: /** michael@0: * End an nsRuleNode::Compute*Data function for an inherited struct. michael@0: * michael@0: * @param type_ The nsStyle* type this function computes. michael@0: * @param data_ Variable holding the result of this function. michael@0: */ michael@0: #define COMPUTE_END_INHERITED(type_, data_) \ michael@0: NS_POSTCONDITION(!canStoreInRuleTree || aRuleDetail == eRuleFullReset || \ michael@0: (aStartStruct && aRuleDetail == eRulePartialReset), \ michael@0: "canStoreInRuleTree must be false for inherited structs " \ michael@0: "unless all properties have been specified with values " \ michael@0: "other than inherit"); \ michael@0: if (canStoreInRuleTree) { \ michael@0: /* We were fully specified and can therefore be cached right on the */ \ michael@0: /* rule node. */ \ michael@0: if (!aHighestNode->mStyleData.mInheritedData) { \ michael@0: aHighestNode->mStyleData.mInheritedData = \ michael@0: new (mPresContext) nsInheritedStyleData; \ michael@0: } \ michael@0: NS_ASSERTION(!aHighestNode->mStyleData.mInheritedData-> \ michael@0: mStyleStructs[eStyleStruct_##type_], \ michael@0: "Going to leak style data"); \ michael@0: aHighestNode->mStyleData.mInheritedData-> \ michael@0: mStyleStructs[eStyleStruct_##type_] = data_; \ michael@0: /* Propagate the bit down. */ \ michael@0: PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ michael@0: /* Tell the style context that it doesn't own the data */ \ michael@0: aContext-> \ michael@0: AddStyleBit(nsCachedStyleData::GetBitForSID(eStyleStruct_##type_)); \ michael@0: } \ michael@0: /* Always cache inherited data on the style context */ \ michael@0: aContext->SetStyle##type_(data_); \ michael@0: \ michael@0: return data_; michael@0: michael@0: /** michael@0: * End an nsRuleNode::Compute*Data function for a reset struct. michael@0: * michael@0: * @param type_ The nsStyle* type this function computes. michael@0: * @param data_ Variable holding the result of this function. michael@0: */ michael@0: #define COMPUTE_END_RESET(type_, data_) \ michael@0: NS_POSTCONDITION(!canStoreInRuleTree || \ michael@0: aRuleDetail == eRuleNone || \ michael@0: aRuleDetail == eRulePartialReset || \ michael@0: aRuleDetail == eRuleFullReset, \ michael@0: "canStoreInRuleTree must be false for reset structs " \ michael@0: "if any properties were specified as inherit"); \ michael@0: if (!canStoreInRuleTree) \ michael@0: /* We can't be cached in the rule node. We have to be put right */ \ michael@0: /* on the style context. */ \ michael@0: aContext->SetStyle(eStyleStruct_##type_, data_); \ michael@0: else { \ michael@0: /* We were fully specified and can therefore be cached right on the */ \ michael@0: /* rule node. */ \ michael@0: if (!aHighestNode->mStyleData.mResetData) { \ michael@0: aHighestNode->mStyleData.mResetData = \ michael@0: new (mPresContext) nsResetStyleData; \ michael@0: } \ michael@0: NS_ASSERTION(!aHighestNode->mStyleData.mResetData-> \ michael@0: mStyleStructs[eStyleStruct_##type_], \ michael@0: "Going to leak style data"); \ michael@0: aHighestNode->mStyleData.mResetData-> \ michael@0: mStyleStructs[eStyleStruct_##type_] = data_; \ michael@0: /* Propagate the bit down. */ \ michael@0: PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ michael@0: } \ michael@0: \ michael@0: return data_; michael@0: michael@0: // This function figures out how much scaling should be suppressed to michael@0: // satisfy scriptminsize. This is our attempt to implement michael@0: // http://www.w3.org/TR/MathML2/chapter3.html#id.3.3.4.2.2 michael@0: // This is called after mScriptLevel, mScriptMinSize and mScriptSizeMultiplier michael@0: // have been set in aFont. michael@0: // michael@0: // Here are the invariants we enforce: michael@0: // 1) A decrease in size must not reduce the size below minscriptsize. michael@0: // 2) An increase in size must not increase the size above the size we would michael@0: // have if minscriptsize had not been applied anywhere. michael@0: // 3) The scriptlevel-induced size change must between 1.0 and the parent's michael@0: // scriptsizemultiplier^(new script level - old script level), as close to the michael@0: // latter as possible subject to constraints 1 and 2. michael@0: static nscoord michael@0: ComputeScriptLevelSize(const nsStyleFont* aFont, const nsStyleFont* aParentFont, michael@0: nsPresContext* aPresContext, nscoord* aUnconstrainedSize) michael@0: { michael@0: int32_t scriptLevelChange = michael@0: aFont->mScriptLevel - aParentFont->mScriptLevel; michael@0: if (scriptLevelChange == 0) { michael@0: *aUnconstrainedSize = aParentFont->mScriptUnconstrainedSize; michael@0: // Constraint #3 says that we cannot change size, and #1 and #2 are always michael@0: // satisfied with no change. It's important this be fast because it covers michael@0: // all non-MathML content. michael@0: return aParentFont->mSize; michael@0: } michael@0: michael@0: // Compute actual value of minScriptSize michael@0: nscoord minScriptSize = aParentFont->mScriptMinSize; michael@0: if (aFont->mAllowZoom) { michael@0: minScriptSize = nsStyleFont::ZoomText(aPresContext, minScriptSize); michael@0: } michael@0: michael@0: double scriptLevelScale = michael@0: pow(aParentFont->mScriptSizeMultiplier, scriptLevelChange); michael@0: // Compute the size we would have had if minscriptsize had never been michael@0: // applied, also prevent overflow (bug 413274) michael@0: *aUnconstrainedSize = michael@0: NSToCoordRound(std::min(aParentFont->mScriptUnconstrainedSize*scriptLevelScale, michael@0: double(nscoord_MAX))); michael@0: // Compute the size we could get via scriptlevel change michael@0: nscoord scriptLevelSize = michael@0: NSToCoordRound(std::min(aParentFont->mSize*scriptLevelScale, michael@0: double(nscoord_MAX))); michael@0: if (scriptLevelScale <= 1.0) { michael@0: if (aParentFont->mSize <= minScriptSize) { michael@0: // We can't decrease the font size at all, so just stick to no change michael@0: // (authors are allowed to explicitly set the font size smaller than michael@0: // minscriptsize) michael@0: return aParentFont->mSize; michael@0: } michael@0: // We can decrease, so apply constraint #1 michael@0: return std::max(minScriptSize, scriptLevelSize); michael@0: } else { michael@0: // scriptminsize can only make sizes larger than the unconstrained size michael@0: NS_ASSERTION(*aUnconstrainedSize <= scriptLevelSize, "How can this ever happen?"); michael@0: // Apply constraint #2 michael@0: return std::min(scriptLevelSize, std::max(*aUnconstrainedSize, minScriptSize)); michael@0: } michael@0: } michael@0: michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::CalcFontPointSize(int32_t aHTMLSize, int32_t aBasePointSize, michael@0: nsPresContext* aPresContext, michael@0: nsFontSizeType aFontSizeType) michael@0: { michael@0: #define sFontSizeTableMin 9 michael@0: #define sFontSizeTableMax 16 michael@0: michael@0: // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla michael@0: // and eventually in WinIE5.5 will help to establish a standard rendering across michael@0: // platforms and browsers. For now, it is used only in Strict mode. More can be read michael@0: // in the document written by Todd Farhner at: michael@0: // http://style.verso.com/font_size_intervals/altintervals.html michael@0: // michael@0: static int32_t sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = michael@0: { michael@0: { 9, 9, 9, 9, 11, 14, 18, 27}, michael@0: { 9, 9, 9, 10, 12, 15, 20, 30}, michael@0: { 9, 9, 10, 11, 13, 17, 22, 33}, michael@0: { 9, 9, 10, 12, 14, 18, 24, 36}, michael@0: { 9, 10, 12, 13, 16, 20, 26, 39}, michael@0: { 9, 10, 12, 14, 17, 21, 28, 42}, michael@0: { 9, 10, 13, 15, 18, 23, 30, 45}, michael@0: { 9, 10, 13, 16, 18, 24, 32, 48} michael@0: }; michael@0: // HTML 1 2 3 4 5 6 7 michael@0: // CSS xxs xs s m l xl xxl michael@0: // | michael@0: // user pref michael@0: // michael@0: //------------------------------------------------------------ michael@0: // michael@0: // This table gives us compatibility with WinNav4 for the default fonts only. michael@0: // In WinNav4, the default fonts were: michael@0: // michael@0: // Times/12pt == Times/16px at 96ppi michael@0: // Courier/10pt == Courier/13px at 96ppi michael@0: // michael@0: // The 2 lines below marked "anchored" have the exact pixel sizes used by michael@0: // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the michael@0: // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px. michael@0: // michael@0: // All values other than the anchored values were filled in by hand, never michael@0: // going below 9px, and maintaining a "diagonal" relationship. See for michael@0: // example the 13s -- they follow a diagonal line through the table. michael@0: // michael@0: static int32_t sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = michael@0: { michael@0: { 9, 9, 9, 9, 11, 14, 18, 28 }, michael@0: { 9, 9, 9, 10, 12, 15, 20, 31 }, michael@0: { 9, 9, 9, 11, 13, 17, 22, 34 }, michael@0: { 9, 9, 10, 12, 14, 18, 24, 37 }, michael@0: { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13) michael@0: { 9, 9, 11, 14, 17, 21, 28, 42 }, michael@0: { 9, 10, 12, 15, 17, 23, 30, 45 }, michael@0: { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16) michael@0: }; michael@0: // HTML 1 2 3 4 5 6 7 michael@0: // CSS xxs xs s m l xl xxl michael@0: // | michael@0: // user pref michael@0: michael@0: #if 0 michael@0: // michael@0: // These are the exact pixel values used by WinIE5 at 96ppi. michael@0: // michael@0: { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest michael@0: { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller michael@0: { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium michael@0: { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger michael@0: { ?, 16, 19, 21, 24, 32, 43, ?? } // largest michael@0: // michael@0: // HTML 1 2 3 4 5 6 7 michael@0: // CSS ? ? ? ? ? ? ? ? michael@0: // michael@0: // (CSS not tested yet.) michael@0: // michael@0: #endif michael@0: michael@0: static int32_t sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 }; michael@0: michael@0: static int32_t sCSSColumns[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl michael@0: static int32_t sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7 michael@0: michael@0: double dFontSize; michael@0: michael@0: if (aFontSizeType == eFontSize_HTML) { michael@0: aHTMLSize--; // input as 1-7 michael@0: } michael@0: michael@0: if (aHTMLSize < 0) michael@0: aHTMLSize = 0; michael@0: else if (aHTMLSize > 6) michael@0: aHTMLSize = 6; michael@0: michael@0: int32_t* column; michael@0: switch (aFontSizeType) michael@0: { michael@0: case eFontSize_HTML: column = sHTMLColumns; break; michael@0: case eFontSize_CSS: column = sCSSColumns; break; michael@0: } michael@0: michael@0: // Make special call specifically for fonts (needed PrintPreview) michael@0: int32_t fontSize = nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize); michael@0: michael@0: if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax)) michael@0: { michael@0: int32_t row = fontSize - sFontSizeTableMin; michael@0: michael@0: if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) { michael@0: dFontSize = nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable[row][column[aHTMLSize]]); michael@0: } else { michael@0: dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: int32_t factor = sFontSizeFactors[column[aHTMLSize]]; michael@0: dFontSize = (factor * aBasePointSize) / 100; michael@0: } michael@0: michael@0: michael@0: if (1.0 < dFontSize) { michael@0: return (nscoord)dFontSize; michael@0: } michael@0: return (nscoord)1; michael@0: } michael@0: michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::FindNextSmallerFontSize(nscoord aFontSize, int32_t aBasePointSize, michael@0: nsPresContext* aPresContext, michael@0: nsFontSizeType aFontSizeType) michael@0: { michael@0: int32_t index; michael@0: int32_t indexMin; michael@0: int32_t indexMax; michael@0: float relativePosition; michael@0: nscoord smallerSize; michael@0: nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning michael@0: nscoord smallestIndexFontSize; michael@0: nscoord largestIndexFontSize; michael@0: nscoord smallerIndexFontSize; michael@0: nscoord largerIndexFontSize; michael@0: michael@0: nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: if (aFontSizeType == eFontSize_HTML) { michael@0: indexMin = 1; michael@0: indexMax = 7; michael@0: } else { michael@0: indexMin = 0; michael@0: indexMax = 6; michael@0: } michael@0: michael@0: smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); michael@0: largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); michael@0: if (aFontSize > smallestIndexFontSize) { michael@0: if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table michael@0: // find largest index smaller than current michael@0: for (index = indexMax; index >= indexMin; index--) { michael@0: indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); michael@0: if (indexFontSize < aFontSize) michael@0: break; michael@0: } michael@0: // set up points beyond table for interpolation purposes michael@0: if (indexFontSize == smallestIndexFontSize) { michael@0: smallerIndexFontSize = indexFontSize - onePx; michael@0: largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); michael@0: } else if (indexFontSize == largestIndexFontSize) { michael@0: smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); michael@0: largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5); michael@0: } else { michael@0: smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); michael@0: largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); michael@0: } michael@0: // compute the relative position of the parent size between the two closest indexed sizes michael@0: relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize); michael@0: // set the new size to have the same relative position between the next smallest two indexed sizes michael@0: smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize)); michael@0: } michael@0: else { // larger than HTML table, drop by 33% michael@0: smallerSize = NSToCoordRound(float(aFontSize) / 1.5); michael@0: } michael@0: } michael@0: else { // smaller than HTML table, drop by 1px michael@0: smallerSize = std::max(aFontSize - onePx, onePx); michael@0: } michael@0: return smallerSize; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: /* static */ nscoord michael@0: nsRuleNode::FindNextLargerFontSize(nscoord aFontSize, int32_t aBasePointSize, michael@0: nsPresContext* aPresContext, michael@0: nsFontSizeType aFontSizeType) michael@0: { michael@0: int32_t index; michael@0: int32_t indexMin; michael@0: int32_t indexMax; michael@0: float relativePosition; michael@0: nscoord adjustment; michael@0: nscoord largerSize; michael@0: nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning michael@0: nscoord smallestIndexFontSize; michael@0: nscoord largestIndexFontSize; michael@0: nscoord smallerIndexFontSize; michael@0: nscoord largerIndexFontSize; michael@0: michael@0: nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: if (aFontSizeType == eFontSize_HTML) { michael@0: indexMin = 1; michael@0: indexMax = 7; michael@0: } else { michael@0: indexMin = 0; michael@0: indexMax = 6; michael@0: } michael@0: michael@0: smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); michael@0: largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); michael@0: if (aFontSize > (smallestIndexFontSize - onePx)) { michael@0: if (aFontSize < largestIndexFontSize) { // larger will be in HTML table michael@0: // find smallest index larger than current michael@0: for (index = indexMin; index <= indexMax; index++) { michael@0: indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); michael@0: if (indexFontSize > aFontSize) michael@0: break; michael@0: } michael@0: // set up points beyond table for interpolation purposes michael@0: if (indexFontSize == smallestIndexFontSize) { michael@0: smallerIndexFontSize = indexFontSize - onePx; michael@0: largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); michael@0: } else if (indexFontSize == largestIndexFontSize) { michael@0: smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); michael@0: largerIndexFontSize = NSCoordSaturatingMultiply(largestIndexFontSize, 1.5); michael@0: } else { michael@0: smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); michael@0: largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); michael@0: } michael@0: // compute the relative position of the parent size between the two closest indexed sizes michael@0: relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize); michael@0: // set the new size to have the same relative position between the next largest two indexed sizes michael@0: adjustment = NSCoordSaturatingNonnegativeMultiply(largerIndexFontSize - indexFontSize, relativePosition); michael@0: largerSize = NSCoordSaturatingAdd(indexFontSize, adjustment); michael@0: } michael@0: else { // larger than HTML table, increase by 50% michael@0: largerSize = NSCoordSaturatingMultiply(aFontSize, 1.5); michael@0: } michael@0: } michael@0: else { // smaller than HTML table, increase by 1px michael@0: largerSize = NSCoordSaturatingAdd(aFontSize, onePx); michael@0: } michael@0: return largerSize; michael@0: } michael@0: michael@0: struct SetFontSizeCalcOps : public css::BasicCoordCalcOps, michael@0: public css::NumbersAlreadyNormalizedOps michael@0: { michael@0: // The parameters beyond aValue that we need for CalcLengthWith. michael@0: const nscoord mParentSize; michael@0: const nsStyleFont* const mParentFont; michael@0: nsPresContext* const mPresContext; michael@0: const bool mAtRoot; michael@0: bool& mCanStoreInRuleTree; michael@0: michael@0: SetFontSizeCalcOps(nscoord aParentSize, const nsStyleFont* aParentFont, michael@0: nsPresContext* aPresContext, bool aAtRoot, michael@0: bool& aCanStoreInRuleTree) michael@0: : mParentSize(aParentSize), michael@0: mParentFont(aParentFont), michael@0: mPresContext(aPresContext), michael@0: mAtRoot(aAtRoot), michael@0: mCanStoreInRuleTree(aCanStoreInRuleTree) michael@0: { michael@0: } michael@0: michael@0: result_type ComputeLeafValue(const nsCSSValue& aValue) michael@0: { michael@0: nscoord size; michael@0: if (aValue.IsLengthUnit()) { michael@0: // Note that font-based length units use the parent's size michael@0: // unadjusted for scriptlevel changes. A scriptlevel change michael@0: // between us and the parent is simply ignored. michael@0: size = CalcLengthWith(aValue, mParentSize, michael@0: mParentFont, michael@0: nullptr, mPresContext, mAtRoot, michael@0: true, mCanStoreInRuleTree); michael@0: if (!aValue.IsRelativeLengthUnit() && mParentFont->mAllowZoom) { michael@0: size = nsStyleFont::ZoomText(mPresContext, size); michael@0: } michael@0: } michael@0: else if (eCSSUnit_Percent == aValue.GetUnit()) { michael@0: mCanStoreInRuleTree = false; michael@0: // Note that % units use the parent's size unadjusted for scriptlevel michael@0: // changes. A scriptlevel change between us and the parent is simply michael@0: // ignored. michael@0: // aValue.GetPercentValue() may be negative for, e.g., calc(-50%) michael@0: size = NSCoordSaturatingMultiply(mParentSize, aValue.GetPercentValue()); michael@0: } else { michael@0: NS_ABORT_IF_FALSE(false, "unexpected value"); michael@0: size = mParentSize; michael@0: } michael@0: michael@0: return size; michael@0: } michael@0: }; michael@0: michael@0: /* static */ void michael@0: nsRuleNode::SetFontSize(nsPresContext* aPresContext, michael@0: const nsRuleData* aRuleData, michael@0: const nsStyleFont* aFont, michael@0: const nsStyleFont* aParentFont, michael@0: nscoord* aSize, michael@0: const nsFont& aSystemFont, michael@0: nscoord aParentSize, michael@0: nscoord aScriptLevelAdjustedParentSize, michael@0: bool aUsedStartStruct, michael@0: bool aAtRoot, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: // If false, means that *aSize has not been zoomed. If true, means that michael@0: // *aSize has been zoomed iff aParentFont->mAllowZoom is true. michael@0: bool sizeIsZoomedAccordingToParent = false; michael@0: michael@0: int32_t baseSize = (int32_t) aPresContext-> michael@0: GetDefaultFont(aFont->mGenericID, aFont->mLanguage)->size; michael@0: const nsCSSValue* sizeValue = aRuleData->ValueForFontSize(); michael@0: if (eCSSUnit_Enumerated == sizeValue->GetUnit()) { michael@0: int32_t value = sizeValue->GetIntValue(); michael@0: michael@0: if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) && michael@0: (value <= NS_STYLE_FONT_SIZE_XXLARGE)) { michael@0: *aSize = CalcFontPointSize(value, baseSize, michael@0: aPresContext, eFontSize_CSS); michael@0: } michael@0: else if (NS_STYLE_FONT_SIZE_XXXLARGE == value) { michael@0: // is not specified in CSS, so we don't use eFontSize_CSS. michael@0: *aSize = CalcFontPointSize(value, baseSize, aPresContext); michael@0: } michael@0: else if (NS_STYLE_FONT_SIZE_LARGER == value || michael@0: NS_STYLE_FONT_SIZE_SMALLER == value) { michael@0: aCanStoreInRuleTree = false; michael@0: michael@0: // Un-zoom so we use the tables correctly. We'll then rezoom due michael@0: // to the |zoom = true| above. michael@0: // Note that relative units here use the parent's size unadjusted michael@0: // for scriptlevel changes. A scriptlevel change between us and the parent michael@0: // is simply ignored. michael@0: nscoord parentSize = aParentSize; michael@0: if (aParentFont->mAllowZoom) { michael@0: parentSize = nsStyleFont::UnZoomText(aPresContext, parentSize); michael@0: } michael@0: michael@0: if (NS_STYLE_FONT_SIZE_LARGER == value) { michael@0: *aSize = FindNextLargerFontSize(parentSize, michael@0: baseSize, aPresContext, eFontSize_CSS); michael@0: michael@0: NS_ASSERTION(*aSize >= parentSize, michael@0: "FindNextLargerFontSize failed"); michael@0: } michael@0: else { michael@0: *aSize = FindNextSmallerFontSize(parentSize, michael@0: baseSize, aPresContext, eFontSize_CSS); michael@0: NS_ASSERTION(*aSize < parentSize || michael@0: parentSize <= nsPresContext::CSSPixelsToAppUnits(1), michael@0: "FindNextSmallerFontSize failed"); michael@0: } michael@0: } else { michael@0: NS_NOTREACHED("unexpected value"); michael@0: } michael@0: } michael@0: else if (sizeValue->IsLengthUnit() || michael@0: sizeValue->GetUnit() == eCSSUnit_Percent || michael@0: sizeValue->IsCalcUnit()) { michael@0: SetFontSizeCalcOps ops(aParentSize, aParentFont, michael@0: aPresContext, aAtRoot, michael@0: aCanStoreInRuleTree); michael@0: *aSize = css::ComputeCalc(*sizeValue, ops); michael@0: if (*aSize < 0) { michael@0: NS_ABORT_IF_FALSE(sizeValue->IsCalcUnit(), michael@0: "negative lengths and percents should be rejected " michael@0: "by parser"); michael@0: *aSize = 0; michael@0: } michael@0: // The calc ops will always zoom its result according to the value michael@0: // of aParentFont->mAllowZoom. michael@0: sizeIsZoomedAccordingToParent = true; michael@0: } michael@0: else if (eCSSUnit_System_Font == sizeValue->GetUnit()) { michael@0: // this becomes our cascading size michael@0: *aSize = aSystemFont.size; michael@0: } michael@0: else if (eCSSUnit_Inherit == sizeValue->GetUnit() || michael@0: eCSSUnit_Unset == sizeValue->GetUnit()) { michael@0: aCanStoreInRuleTree = false; michael@0: // We apply scriptlevel change for this case, because the default is michael@0: // to inherit and we don't want explicit "inherit" to differ from the michael@0: // default. michael@0: *aSize = aScriptLevelAdjustedParentSize; michael@0: sizeIsZoomedAccordingToParent = true; michael@0: } michael@0: else if (eCSSUnit_Initial == sizeValue->GetUnit()) { michael@0: // The initial value is 'medium', which has magical sizing based on michael@0: // the generic font family, so do that here too. michael@0: *aSize = baseSize; michael@0: } else { michael@0: NS_ASSERTION(eCSSUnit_Null == sizeValue->GetUnit(), michael@0: "What kind of font-size value is this?"); michael@0: // if aUsedStartStruct is true, then every single property in the michael@0: // font struct is being set all at once. This means scriptlevel is not michael@0: // going to have any influence on the font size; there is no need to michael@0: // do anything here. michael@0: if (!aUsedStartStruct && aParentSize != aScriptLevelAdjustedParentSize) { michael@0: // There was no rule affecting the size but the size has been michael@0: // affected by the parent's size via scriptlevel change. So we cannot michael@0: // store the data in the rule tree. michael@0: aCanStoreInRuleTree = false; michael@0: *aSize = aScriptLevelAdjustedParentSize; michael@0: sizeIsZoomedAccordingToParent = true; michael@0: } else { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // We want to zoom the cascaded size so that em-based measurements, michael@0: // line-heights, etc., work. michael@0: bool currentlyZoomed = sizeIsZoomedAccordingToParent && michael@0: aParentFont->mAllowZoom; michael@0: if (!currentlyZoomed && aFont->mAllowZoom) { michael@0: *aSize = nsStyleFont::ZoomText(aPresContext, *aSize); michael@0: } else if (currentlyZoomed && !aFont->mAllowZoom) { michael@0: *aSize = nsStyleFont::UnZoomText(aPresContext, *aSize); michael@0: } michael@0: } michael@0: michael@0: static int8_t ClampTo8Bit(int32_t aValue) { michael@0: if (aValue < -128) michael@0: return -128; michael@0: if (aValue > 127) michael@0: return 127; michael@0: return int8_t(aValue); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, michael@0: uint8_t aGenericFontID, const nsRuleData* aRuleData, michael@0: const nsStyleFont* aParentFont, michael@0: nsStyleFont* aFont, bool aUsedStartStruct, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: bool atRoot = !aContext->GetParent(); michael@0: michael@0: // -x-text-zoom: none, inherit, initial michael@0: bool allowZoom; michael@0: const nsCSSValue* textZoomValue = aRuleData->ValueForTextZoom(); michael@0: if (eCSSUnit_Null != textZoomValue->GetUnit()) { michael@0: if (eCSSUnit_Inherit == textZoomValue->GetUnit()) { michael@0: allowZoom = aParentFont->mAllowZoom; michael@0: } else if (eCSSUnit_None == textZoomValue->GetUnit()) { michael@0: allowZoom = false; michael@0: } else { michael@0: MOZ_ASSERT(eCSSUnit_Initial == textZoomValue->GetUnit(), michael@0: "unexpected unit"); michael@0: allowZoom = true; michael@0: } michael@0: aFont->EnableZoom(aPresContext, allowZoom); michael@0: } michael@0: michael@0: // mLanguage must be set before before any of the CalcLengthWith calls michael@0: // (direct calls or calls via SetFontSize) for the cases where |aParentFont| michael@0: // is the same as |aFont|. michael@0: // michael@0: // -x-lang: string, inherit michael@0: // This is not a real CSS property, it is an HTML attribute mapped to CSS. michael@0: const nsCSSValue* langValue = aRuleData->ValueForLang(); michael@0: if (eCSSUnit_Ident == langValue->GetUnit()) { michael@0: nsAutoString lang; michael@0: langValue->GetStringValue(lang); michael@0: michael@0: nsContentUtils::ASCIIToLower(lang); michael@0: aFont->mLanguage = do_GetAtom(lang); michael@0: aFont->mExplicitLanguage = true; michael@0: } michael@0: michael@0: const nsFont* defaultVariableFont = michael@0: aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID, michael@0: aFont->mLanguage); michael@0: michael@0: // XXX: Bleh. Disable these somehow? michael@0: // -moz-system-font: enum (never inherit!) michael@0: static_assert( michael@0: NS_STYLE_FONT_CAPTION == LookAndFeel::eFont_Caption && michael@0: NS_STYLE_FONT_ICON == LookAndFeel::eFont_Icon && michael@0: NS_STYLE_FONT_MENU == LookAndFeel::eFont_Menu && michael@0: NS_STYLE_FONT_MESSAGE_BOX == LookAndFeel::eFont_MessageBox && michael@0: NS_STYLE_FONT_SMALL_CAPTION == LookAndFeel::eFont_SmallCaption && michael@0: NS_STYLE_FONT_STATUS_BAR == LookAndFeel::eFont_StatusBar && michael@0: NS_STYLE_FONT_WINDOW == LookAndFeel::eFont_Window && michael@0: NS_STYLE_FONT_DOCUMENT == LookAndFeel::eFont_Document && michael@0: NS_STYLE_FONT_WORKSPACE == LookAndFeel::eFont_Workspace && michael@0: NS_STYLE_FONT_DESKTOP == LookAndFeel::eFont_Desktop && michael@0: NS_STYLE_FONT_INFO == LookAndFeel::eFont_Info && michael@0: NS_STYLE_FONT_DIALOG == LookAndFeel::eFont_Dialog && michael@0: NS_STYLE_FONT_BUTTON == LookAndFeel::eFont_Button && michael@0: NS_STYLE_FONT_PULL_DOWN_MENU == LookAndFeel::eFont_PullDownMenu && michael@0: NS_STYLE_FONT_LIST == LookAndFeel::eFont_List && michael@0: NS_STYLE_FONT_FIELD == LookAndFeel::eFont_Field, michael@0: "LookAndFeel.h system-font constants out of sync with nsStyleConsts.h"); michael@0: michael@0: // Fall back to defaultVariableFont. michael@0: nsFont systemFont = *defaultVariableFont; michael@0: const nsCSSValue* systemFontValue = aRuleData->ValueForSystemFont(); michael@0: if (eCSSUnit_Enumerated == systemFontValue->GetUnit()) { michael@0: gfxFontStyle fontStyle; michael@0: LookAndFeel::FontID fontID = michael@0: (LookAndFeel::FontID)systemFontValue->GetIntValue(); michael@0: float devPerCSS = michael@0: (float)nsPresContext::AppUnitsPerCSSPixel() / michael@0: aPresContext->DeviceContext()->UnscaledAppUnitsPerDevPixel(); michael@0: if (LookAndFeel::GetFont(fontID, systemFont.name, fontStyle, devPerCSS)) { michael@0: systemFont.style = fontStyle.style; michael@0: systemFont.systemFont = fontStyle.systemFont; michael@0: systemFont.variant = NS_FONT_VARIANT_NORMAL; michael@0: systemFont.weight = fontStyle.weight; michael@0: systemFont.stretch = fontStyle.stretch; michael@0: systemFont.decorations = NS_FONT_DECORATION_NONE; michael@0: systemFont.size = NSFloatPixelsToAppUnits(fontStyle.size, michael@0: aPresContext->DeviceContext()-> michael@0: UnscaledAppUnitsPerDevPixel()); michael@0: //systemFont.langGroup = fontStyle.langGroup; michael@0: systemFont.sizeAdjust = fontStyle.sizeAdjust; michael@0: michael@0: #ifdef XP_WIN michael@0: // XXXldb This platform-specific stuff should be in the michael@0: // LookAndFeel implementation, not here. michael@0: // XXXzw Should we even still *have* this code? It looks to be making michael@0: // old, probably obsolete assumptions. michael@0: michael@0: if (fontID == LookAndFeel::eFont_Field || michael@0: fontID == LookAndFeel::eFont_Button || michael@0: fontID == LookAndFeel::eFont_List) { michael@0: // As far as I can tell the system default fonts and sizes michael@0: // on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are michael@0: // all pre-determined and cannot be changed by either the control panel michael@0: // or programmatically. michael@0: // Fields (text fields) michael@0: // Button and Selects (listboxes/comboboxes) michael@0: // We use whatever font is defined by the system. Which it appears michael@0: // (and the assumption is) it is always a proportional font. Then we michael@0: // always use 2 points smaller than what the browser has defined as michael@0: // the default proportional font. michael@0: // Assumption: system defined font is proportional michael@0: systemFont.size = michael@0: std::max(defaultVariableFont->size - michael@0: nsPresContext::CSSPointsToAppUnits(2), 0); michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: // font-family: string list, enum, inherit michael@0: const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); michael@0: NS_ASSERTION(eCSSUnit_Enumerated != familyValue->GetUnit(), michael@0: "system fonts should not be in mFamily anymore"); michael@0: if (eCSSUnit_Families == familyValue->GetUnit()) { michael@0: // set the correct font if we are using DocumentFonts OR we are overriding for XUL michael@0: // MJA: bug 31816 michael@0: if (aGenericFontID == kGenericFont_NONE) { michael@0: // only bother appending fallback fonts if this isn't a fallback generic font itself michael@0: if (!aFont->mFont.name.IsEmpty()) michael@0: aFont->mFont.name.Append((char16_t)','); michael@0: // defaultVariableFont.name should always be "serif" or "sans-serif". michael@0: aFont->mFont.name.Append(defaultVariableFont->name); michael@0: } michael@0: aFont->mFont.systemFont = false; michael@0: // Technically this is redundant with the code below, but it's good michael@0: // to have since we'll still want it once we get rid of michael@0: // SetGenericFont (bug 380915). michael@0: aFont->mGenericID = aGenericFontID; michael@0: } michael@0: else if (eCSSUnit_System_Font == familyValue->GetUnit()) { michael@0: aFont->mFont.name = systemFont.name; michael@0: aFont->mFont.systemFont = true; michael@0: aFont->mGenericID = kGenericFont_NONE; michael@0: } michael@0: else if (eCSSUnit_Inherit == familyValue->GetUnit() || michael@0: eCSSUnit_Unset == familyValue->GetUnit()) { michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mFont.name = aParentFont->mFont.name; michael@0: aFont->mFont.systemFont = aParentFont->mFont.systemFont; michael@0: aFont->mGenericID = aParentFont->mGenericID; michael@0: } michael@0: else if (eCSSUnit_Initial == familyValue->GetUnit()) { michael@0: aFont->mFont.name = defaultVariableFont->name; michael@0: aFont->mFont.systemFont = defaultVariableFont->systemFont; michael@0: aFont->mGenericID = kGenericFont_NONE; michael@0: } michael@0: michael@0: // When we're in the loop in SetGenericFont, we must ensure that we michael@0: // always keep aFont->mFlags set to the correct generic. But we have michael@0: // to be careful not to touch it when we're called directly from michael@0: // ComputeFontData, because we could have a start struct. michael@0: if (aGenericFontID != kGenericFont_NONE) { michael@0: aFont->mGenericID = aGenericFontID; michael@0: } michael@0: michael@0: // -moz-math-variant: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForMathVariant(), aFont->mMathVariant, michael@0: aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mMathVariant, NS_MATHML_MATHVARIANT_NONE, michael@0: 0, 0, 0, 0); michael@0: michael@0: // -moz-math-display: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForMathDisplay(), aFont->mMathDisplay, michael@0: aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mMathDisplay, NS_MATHML_DISPLAYSTYLE_INLINE, michael@0: 0, 0, 0, 0); michael@0: michael@0: // font-smoothing: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForOSXFontSmoothing(), michael@0: aFont->mFont.smoothing, aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.smoothing, michael@0: defaultVariableFont->smoothing, michael@0: 0, 0, 0, 0); michael@0: michael@0: // font-style: enum, inherit, initial, -moz-system-font michael@0: if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { michael@0: // -moz-math-variant overrides font-style michael@0: aFont->mFont.style = NS_FONT_STYLE_NORMAL; michael@0: } else { michael@0: SetDiscrete(*aRuleData->ValueForFontStyle(), michael@0: aFont->mFont.style, aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.style, michael@0: defaultVariableFont->style, michael@0: 0, 0, 0, systemFont.style); michael@0: } michael@0: michael@0: // font-variant: enum, inherit, initial, -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariant(), michael@0: aFont->mFont.variant, aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variant, michael@0: defaultVariableFont->variant, michael@0: 0, 0, 0, systemFont.variant); michael@0: michael@0: // font-weight: int, enum, inherit, initial, -moz-system-font michael@0: // special handling for enum michael@0: const nsCSSValue* weightValue = aRuleData->ValueForFontWeight(); michael@0: if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { michael@0: // -moz-math-variant overrides font-weight michael@0: aFont->mFont.weight = NS_FONT_WEIGHT_NORMAL; michael@0: } else if (eCSSUnit_Enumerated == weightValue->GetUnit()) { michael@0: int32_t value = weightValue->GetIntValue(); michael@0: switch (value) { michael@0: case NS_STYLE_FONT_WEIGHT_NORMAL: michael@0: case NS_STYLE_FONT_WEIGHT_BOLD: michael@0: aFont->mFont.weight = value; michael@0: break; michael@0: case NS_STYLE_FONT_WEIGHT_BOLDER: { michael@0: aCanStoreInRuleTree = false; michael@0: int32_t inheritedValue = aParentFont->mFont.weight; michael@0: if (inheritedValue <= 300) { michael@0: aFont->mFont.weight = 400; michael@0: } else if (inheritedValue <= 500) { michael@0: aFont->mFont.weight = 700; michael@0: } else { michael@0: aFont->mFont.weight = 900; michael@0: } michael@0: break; michael@0: } michael@0: case NS_STYLE_FONT_WEIGHT_LIGHTER: { michael@0: aCanStoreInRuleTree = false; michael@0: int32_t inheritedValue = aParentFont->mFont.weight; michael@0: if (inheritedValue < 600) { michael@0: aFont->mFont.weight = 100; michael@0: } else if (inheritedValue < 800) { michael@0: aFont->mFont.weight = 400; michael@0: } else { michael@0: aFont->mFont.weight = 700; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } else michael@0: SetDiscrete(*weightValue, aFont->mFont.weight, aCanStoreInRuleTree, michael@0: SETDSC_INTEGER | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.weight, michael@0: defaultVariableFont->weight, michael@0: 0, 0, 0, systemFont.weight); michael@0: michael@0: // font-stretch: enum, inherit, initial, -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontStretch(), michael@0: aFont->mFont.stretch, aCanStoreInRuleTree, michael@0: SETDSC_SYSTEM_FONT | SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.stretch, michael@0: defaultVariableFont->stretch, michael@0: 0, 0, 0, systemFont.stretch); michael@0: michael@0: // Compute scriptlevel, scriptminsize and scriptsizemultiplier now so michael@0: // they're available for font-size computation. michael@0: michael@0: // -moz-script-min-size: length michael@0: const nsCSSValue* scriptMinSizeValue = aRuleData->ValueForScriptMinSize(); michael@0: if (scriptMinSizeValue->IsLengthUnit()) { michael@0: // scriptminsize in font units (em, ex) has to be interpreted relative michael@0: // to the parent font, or the size definitions are circular and we michael@0: // michael@0: aFont->mScriptMinSize = michael@0: CalcLengthWith(*scriptMinSizeValue, aParentFont->mSize, michael@0: aParentFont, michael@0: nullptr, aPresContext, atRoot, true, michael@0: aCanStoreInRuleTree); michael@0: } michael@0: michael@0: // -moz-script-size-multiplier: factor, inherit, initial michael@0: SetFactor(*aRuleData->ValueForScriptSizeMultiplier(), michael@0: aFont->mScriptSizeMultiplier, michael@0: aCanStoreInRuleTree, aParentFont->mScriptSizeMultiplier, michael@0: NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER, michael@0: SETFCT_POSITIVE | SETFCT_UNSET_INHERIT); michael@0: michael@0: // -moz-script-level: integer, number, inherit michael@0: const nsCSSValue* scriptLevelValue = aRuleData->ValueForScriptLevel(); michael@0: if (eCSSUnit_Integer == scriptLevelValue->GetUnit()) { michael@0: // "relative" michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + scriptLevelValue->GetIntValue()); michael@0: } michael@0: else if (eCSSUnit_Number == scriptLevelValue->GetUnit()) { michael@0: // "absolute" michael@0: aFont->mScriptLevel = ClampTo8Bit(int32_t(scriptLevelValue->GetFloatValue())); michael@0: } michael@0: else if (eCSSUnit_Auto == scriptLevelValue->GetUnit()) { michael@0: // auto michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + michael@0: (aParentFont->mMathDisplay == michael@0: NS_MATHML_DISPLAYSTYLE_INLINE ? 1 : 0)); michael@0: } michael@0: else if (eCSSUnit_Inherit == scriptLevelValue->GetUnit() || michael@0: eCSSUnit_Unset == scriptLevelValue->GetUnit()) { michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mScriptLevel = aParentFont->mScriptLevel; michael@0: } michael@0: else if (eCSSUnit_Initial == scriptLevelValue->GetUnit()) { michael@0: aFont->mScriptLevel = 0; michael@0: } michael@0: michael@0: // font-kerning: none, enum, inherit, initial, -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontKerning(), michael@0: aFont->mFont.kerning, aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.kerning, michael@0: defaultVariableFont->kerning, michael@0: 0, 0, 0, systemFont.kerning); michael@0: michael@0: // font-synthesis: none, enum (bit field), inherit, initial, -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontSynthesis(), michael@0: aFont->mFont.synthesis, aCanStoreInRuleTree, michael@0: SETDSC_NONE | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.synthesis, michael@0: defaultVariableFont->synthesis, michael@0: 0, 0, 0, systemFont.synthesis); michael@0: michael@0: // font-variant-alternates: normal, enum (bit field) + functions, inherit, michael@0: // initial, -moz-system-font michael@0: const nsCSSValue* variantAlternatesValue = michael@0: aRuleData->ValueForFontVariantAlternates(); michael@0: int32_t variantAlternates = 0; michael@0: michael@0: switch (variantAlternatesValue->GetUnit()) { michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: aFont->mFont.CopyAlternates(aParentFont->mFont); michael@0: aCanStoreInRuleTree = false; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Normal: michael@0: aFont->mFont.variantAlternates = 0; michael@0: aFont->mFont.alternateValues.Clear(); michael@0: aFont->mFont.featureValueLookup = nullptr; michael@0: break; michael@0: michael@0: case eCSSUnit_Pair: michael@0: NS_ASSERTION(variantAlternatesValue->GetPairValue().mXValue.GetUnit() == michael@0: eCSSUnit_Enumerated, "strange unit for variantAlternates"); michael@0: variantAlternates = michael@0: variantAlternatesValue->GetPairValue().mXValue.GetIntValue(); michael@0: aFont->mFont.variantAlternates = variantAlternates; michael@0: michael@0: if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { michael@0: // fetch the feature lookup object from the styleset michael@0: aFont->mFont.featureValueLookup = michael@0: aPresContext->StyleSet()->GetFontFeatureValuesLookup(); michael@0: michael@0: NS_ASSERTION(variantAlternatesValue->GetPairValue().mYValue.GetUnit() == michael@0: eCSSUnit_List, "function list not a list value"); michael@0: nsStyleUtil::ComputeFunctionalAlternates( michael@0: variantAlternatesValue->GetPairValue().mYValue.GetListValue(), michael@0: aFont->mFont.alternateValues); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: // font-variant-caps: normal, enum, inherit, initial, -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariantCaps(), michael@0: aFont->mFont.variantCaps, aCanStoreInRuleTree, michael@0: SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variantCaps, michael@0: defaultVariableFont->variantCaps, michael@0: 0, 0, 0, systemFont.variantCaps); michael@0: michael@0: // font-variant-east-asian: normal, enum (bit field), inherit, initial, michael@0: // -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariantEastAsian(), michael@0: aFont->mFont.variantEastAsian, aCanStoreInRuleTree, michael@0: SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variantEastAsian, michael@0: defaultVariableFont->variantEastAsian, michael@0: 0, 0, 0, systemFont.variantEastAsian); michael@0: michael@0: // font-variant-ligatures: normal, enum (bit field), inherit, initial, michael@0: // -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariantLigatures(), michael@0: aFont->mFont.variantLigatures, aCanStoreInRuleTree, michael@0: SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variantLigatures, michael@0: defaultVariableFont->variantLigatures, michael@0: 0, 0, 0, systemFont.variantLigatures); michael@0: michael@0: // font-variant-numeric: normal, enum (bit field), inherit, initial, michael@0: // -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariantNumeric(), michael@0: aFont->mFont.variantNumeric, aCanStoreInRuleTree, michael@0: SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variantNumeric, michael@0: defaultVariableFont->variantNumeric, michael@0: 0, 0, 0, systemFont.variantNumeric); michael@0: michael@0: // font-variant-position: normal, enum, inherit, initial, michael@0: // -moz-system-font michael@0: SetDiscrete(*aRuleData->ValueForFontVariantPosition(), michael@0: aFont->mFont.variantPosition, aCanStoreInRuleTree, michael@0: SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT | michael@0: SETDSC_UNSET_INHERIT, michael@0: aParentFont->mFont.variantPosition, michael@0: defaultVariableFont->variantPosition, michael@0: 0, 0, 0, systemFont.variantPosition); michael@0: michael@0: // font-feature-settings michael@0: const nsCSSValue* featureSettingsValue = michael@0: aRuleData->ValueForFontFeatureSettings(); michael@0: michael@0: switch (featureSettingsValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Normal: michael@0: case eCSSUnit_Initial: michael@0: aFont->mFont.fontFeatureSettings.Clear(); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings; michael@0: break; michael@0: michael@0: case eCSSUnit_System_Font: michael@0: aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings; michael@0: break; michael@0: michael@0: case eCSSUnit_PairList: michael@0: case eCSSUnit_PairListDep: michael@0: ComputeFontFeatures(featureSettingsValue->GetPairListValue(), michael@0: aFont->mFont.fontFeatureSettings); michael@0: break; michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected value unit"); michael@0: break; michael@0: } michael@0: michael@0: // font-language-override michael@0: const nsCSSValue* languageOverrideValue = michael@0: aRuleData->ValueForFontLanguageOverride(); michael@0: if (eCSSUnit_Inherit == languageOverrideValue->GetUnit() || michael@0: eCSSUnit_Unset == languageOverrideValue->GetUnit()) { michael@0: aCanStoreInRuleTree = false; michael@0: aFont->mFont.languageOverride = aParentFont->mFont.languageOverride; michael@0: } else if (eCSSUnit_Normal == languageOverrideValue->GetUnit() || michael@0: eCSSUnit_Initial == languageOverrideValue->GetUnit()) { michael@0: aFont->mFont.languageOverride.Truncate(); michael@0: } else if (eCSSUnit_System_Font == languageOverrideValue->GetUnit()) { michael@0: aFont->mFont.languageOverride = systemFont.languageOverride; michael@0: } else if (eCSSUnit_String == languageOverrideValue->GetUnit()) { michael@0: languageOverrideValue->GetStringValue(aFont->mFont.languageOverride); michael@0: } michael@0: michael@0: // font-size: enum, length, percent, inherit michael@0: nscoord scriptLevelAdjustedParentSize = aParentFont->mSize; michael@0: nscoord scriptLevelAdjustedUnconstrainedParentSize; michael@0: scriptLevelAdjustedParentSize = michael@0: ComputeScriptLevelSize(aFont, aParentFont, aPresContext, michael@0: &scriptLevelAdjustedUnconstrainedParentSize); michael@0: NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize, michael@0: "If we have a start struct, we should have reset everything coming in here"); michael@0: SetFontSize(aPresContext, aRuleData, aFont, aParentFont, michael@0: &aFont->mSize, michael@0: systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize, michael@0: aUsedStartStruct, atRoot, aCanStoreInRuleTree); michael@0: if (aParentFont->mSize == aParentFont->mScriptUnconstrainedSize && michael@0: scriptLevelAdjustedParentSize == scriptLevelAdjustedUnconstrainedParentSize) { michael@0: // Fast path: we have not been affected by scriptminsize so we don't michael@0: // need to call SetFontSize again to compute the michael@0: // scriptminsize-unconstrained size. This is OK even if we have a michael@0: // start struct, because if we have a start struct then 'font-size' michael@0: // was specified and so scriptminsize has no effect. michael@0: aFont->mScriptUnconstrainedSize = aFont->mSize; michael@0: } else { michael@0: SetFontSize(aPresContext, aRuleData, aFont, aParentFont, michael@0: &aFont->mScriptUnconstrainedSize, michael@0: systemFont, aParentFont->mScriptUnconstrainedSize, michael@0: scriptLevelAdjustedUnconstrainedParentSize, michael@0: aUsedStartStruct, atRoot, aCanStoreInRuleTree); michael@0: } michael@0: NS_ASSERTION(aFont->mScriptUnconstrainedSize <= aFont->mSize, michael@0: "scriptminsize should never be making things bigger"); michael@0: michael@0: nscoord fontSize = aFont->mSize; michael@0: michael@0: // enforce the user' specified minimum font-size on the value that we expose michael@0: // (but don't change font-size:0, since that would unhide hidden text) michael@0: if (fontSize > 0) { michael@0: nscoord minFontSize = aPresContext->MinFontSize(aFont->mLanguage); michael@0: if (minFontSize < 0) { michael@0: minFontSize = 0; michael@0: } michael@0: if (fontSize < minFontSize && !aPresContext->IsChrome()) { michael@0: // override the minimum font-size constraint michael@0: fontSize = minFontSize; michael@0: } michael@0: } michael@0: aFont->mFont.size = fontSize; michael@0: michael@0: // font-size-adjust: number, none, inherit, initial, -moz-system-font michael@0: const nsCSSValue* sizeAdjustValue = aRuleData->ValueForFontSizeAdjust(); michael@0: if (eCSSUnit_System_Font == sizeAdjustValue->GetUnit()) { michael@0: aFont->mFont.sizeAdjust = systemFont.sizeAdjust; michael@0: } else michael@0: SetFactor(*sizeAdjustValue, aFont->mFont.sizeAdjust, michael@0: aCanStoreInRuleTree, aParentFont->mFont.sizeAdjust, 0.0f, michael@0: SETFCT_NONE | SETFCT_UNSET_INHERIT); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsRuleNode::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList, michael@0: nsTArray& aFeatureSettings) michael@0: { michael@0: aFeatureSettings.Clear(); michael@0: for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) { michael@0: gfxFontFeature feat = {0, 0}; michael@0: michael@0: NS_ABORT_IF_FALSE(aFeaturesList->mXValue.GetUnit() == eCSSUnit_String, michael@0: "unexpected value unit"); michael@0: michael@0: // tag is a 4-byte ASCII sequence michael@0: nsAutoString tag; michael@0: p->mXValue.GetStringValue(tag); michael@0: if (tag.Length() != 4) { michael@0: continue; michael@0: } michael@0: // parsing validates that these are ASCII chars michael@0: // tags are always big-endian michael@0: feat.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3]; michael@0: michael@0: // value michael@0: NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Integer, michael@0: "should have found an integer unit"); michael@0: feat.mValue = p->mYValue.GetIntValue(); michael@0: michael@0: aFeatureSettings.AppendElement(feat); michael@0: } michael@0: } michael@0: michael@0: // This should die (bug 380915). michael@0: // michael@0: // SetGenericFont: michael@0: // - backtrack to an ancestor with the same generic font name (possibly michael@0: // up to the root where default values come from the presentation context) michael@0: // - re-apply cascading rules from there without caching intermediate values michael@0: /* static */ void michael@0: nsRuleNode::SetGenericFont(nsPresContext* aPresContext, michael@0: nsStyleContext* aContext, michael@0: uint8_t aGenericFontID, michael@0: nsStyleFont* aFont) michael@0: { michael@0: // walk up the contexts until a context with the desired generic font michael@0: nsAutoTArray contextPath; michael@0: contextPath.AppendElement(aContext); michael@0: nsStyleContext* higherContext = aContext->GetParent(); michael@0: while (higherContext) { michael@0: if (higherContext->StyleFont()->mGenericID == aGenericFontID) { michael@0: // done walking up the higher contexts michael@0: break; michael@0: } michael@0: contextPath.AppendElement(higherContext); michael@0: higherContext = higherContext->GetParent(); michael@0: } michael@0: michael@0: // re-apply the cascading rules, starting from the higher context michael@0: michael@0: // If we stopped earlier because we reached the root of the style tree, michael@0: // we will start with the default generic font from the presentation michael@0: // context. Otherwise we start with the higher context. michael@0: const nsFont* defaultFont = michael@0: aPresContext->GetDefaultFont(aGenericFontID, aFont->mLanguage); michael@0: nsStyleFont parentFont(*defaultFont, aPresContext); michael@0: if (higherContext) { michael@0: const nsStyleFont* tmpFont = higherContext->StyleFont(); michael@0: parentFont = *tmpFont; michael@0: } michael@0: *aFont = parentFont; michael@0: michael@0: bool dummy; michael@0: uint32_t fontBit = nsCachedStyleData::GetBitForSID(eStyleStruct_Font); michael@0: michael@0: // use placement new[] on the result of alloca() to allocate a michael@0: // variable-sized stack array, including execution of constructors, michael@0: // and use an RAII class to run the destructors too. michael@0: size_t nprops = nsCSSProps::PropertyCountInStruct(eStyleStruct_Font); michael@0: void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); michael@0: michael@0: for (int32_t i = contextPath.Length() - 1; i >= 0; --i) { michael@0: nsStyleContext* context = contextPath[i]; michael@0: AutoCSSValueArray dataArray(dataStorage, nprops); michael@0: michael@0: nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Font), dataArray.get(), michael@0: aPresContext, context); michael@0: ruleData.mValueOffsets[eStyleStruct_Font] = 0; michael@0: michael@0: // Trimmed down version of ::WalkRuleTree() to re-apply the style rules michael@0: // Note that we *do* need to do this for our own data, since what is michael@0: // in |fontData| in ComputeFontData is only for the rules below michael@0: // aStartStruct. michael@0: for (nsRuleNode* ruleNode = context->RuleNode(); ruleNode; michael@0: ruleNode = ruleNode->GetParent()) { michael@0: if (ruleNode->mNoneBits & fontBit) michael@0: // no more font rules on this branch, get out michael@0: break; michael@0: michael@0: nsIStyleRule *rule = ruleNode->GetRule(); michael@0: if (rule) { michael@0: ruleData.mLevel = ruleNode->GetLevel(); michael@0: ruleData.mIsImportantRule = ruleNode->IsImportantRule(); michael@0: rule->MapRuleInfoInto(&ruleData); michael@0: } michael@0: } michael@0: michael@0: // Compute the delta from the information that the rules specified michael@0: michael@0: // Avoid unnecessary operations in SetFont(). But we care if it's michael@0: // the final value that we're computing. michael@0: if (i != 0) michael@0: ruleData.ValueForFontFamily()->Reset(); michael@0: michael@0: ResolveVariableReferences(eStyleStruct_Font, &ruleData, aContext); michael@0: michael@0: nsRuleNode::SetFont(aPresContext, context, michael@0: aGenericFontID, &ruleData, &parentFont, aFont, michael@0: false, dummy); michael@0: michael@0: parentFont = *aFont; michael@0: } michael@0: } michael@0: michael@0: static bool ExtractGeneric(const nsString& aFamily, bool aGeneric, michael@0: void *aData) michael@0: { michael@0: nsAutoString *data = static_cast(aData); michael@0: michael@0: if (aGeneric) { michael@0: *data = aFamily; michael@0: return false; // stop enumeration michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: struct smugglerStruct { michael@0: nsStyleFont *font; michael@0: gfxUserFontSet *userFonts; michael@0: }; michael@0: michael@0: /* This function forces the use of the first @font-face font we find */ michael@0: static bool ForceFirstWebFont(const nsString& aFamily, bool aGeneric, michael@0: void *smuggled) michael@0: { michael@0: smugglerStruct *sm = static_cast(smuggled); michael@0: michael@0: if (aGeneric) { michael@0: return true; michael@0: } michael@0: michael@0: if (sm->userFonts->HasFamily(aFamily)) { michael@0: // Force use of this exact @font-face font since we have it. michael@0: sm->font->mFont.name = aFamily; michael@0: michael@0: return false; // Stop enumeration. michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeFontData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Font, (mPresContext), font, parentFont) michael@0: michael@0: // NOTE: The |aRuleDetail| passed in is a little bit conservative due michael@0: // to the -moz-system-font property. We really don't need to consider michael@0: // it here in determining whether to cache in the rule tree. However, michael@0: // we do need to consider it in WalkRuleTree when deciding whether to michael@0: // walk further up the tree. So this means that when the font struct michael@0: // is fully specified using *longhand* properties (excluding michael@0: // -moz-system-font), we won't cache in the rule tree even though we michael@0: // could. However, it's pretty unlikely authors will do that michael@0: // (although there is a pretty good chance they'll fully specify it michael@0: // using the 'font' shorthand). michael@0: michael@0: bool useDocumentFonts = michael@0: mPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts); michael@0: bool isXUL = PR_FALSE; michael@0: bool forcedWebFont = false; michael@0: michael@0: // See if we are in the chrome michael@0: // We only need to know this to determine if we have to use the michael@0: // document fonts (overriding the useDocumentFonts flag). michael@0: if (mPresContext->IsChrome()) { michael@0: // if we are not using document fonts, but this is a XUL document, michael@0: // then we use the document fonts anyway michael@0: isXUL = true; michael@0: } michael@0: michael@0: // Figure out if we are a generic font michael@0: uint8_t generic = kGenericFont_NONE; michael@0: // XXXldb What if we would have had a string if we hadn't been doing michael@0: // the optimization with a non-null aStartStruct? michael@0: const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); michael@0: if (eCSSUnit_Families == familyValue->GetUnit()) { michael@0: familyValue->GetStringValue(font->mFont.name); michael@0: // XXXldb Do we want to extract the generic for this if it's not only a michael@0: // generic? michael@0: nsFont::GetGenericID(font->mFont.name, &generic); michael@0: michael@0: if (!isXUL) { michael@0: gfxUserFontSet *userFonts = mPresContext->GetUserFontSet(); michael@0: if (userFonts) { michael@0: smugglerStruct sm; michael@0: sm.userFonts = userFonts; michael@0: sm.font = font; michael@0: michael@0: if (!sm.font->mFont.EnumerateFamilies(ForceFirstWebFont, &sm)) { michael@0: isXUL = true; // Always allow WebFont use. michael@0: forcedWebFont = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!forcedWebFont && generic == kGenericFont_NONE) michael@0: mPresContext->AddFontAttempt(font->mFont); michael@0: michael@0: // If we aren't allowed to use document fonts, then we are only entitled michael@0: // to use the user's default variable-width font and fixed-width font michael@0: if (!isXUL && (!useDocumentFonts || michael@0: mPresContext->FontAttemptCountReached(font->mFont) || michael@0: mPresContext->FontUseCountReached(font->mFont))) { michael@0: // Extract the generic from the specified font family... michael@0: nsAutoString genericName; michael@0: if (!font->mFont.EnumerateFamilies(ExtractGeneric, &genericName)) { michael@0: // The specified font had a generic family. michael@0: font->mFont.name = genericName; michael@0: nsFont::GetGenericID(genericName, &generic); michael@0: michael@0: // ... and only use it if it's -moz-fixed or monospace michael@0: if (generic != kGenericFont_moz_fixed && michael@0: generic != kGenericFont_monospace) { michael@0: font->mFont.name.Truncate(); michael@0: generic = kGenericFont_NONE; michael@0: } michael@0: } else { michael@0: // The specified font did not have a generic family. michael@0: font->mFont.name.Truncate(); michael@0: generic = kGenericFont_NONE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now compute our font struct michael@0: if (generic == kGenericFont_NONE) { michael@0: // continue the normal processing michael@0: nsRuleNode::SetFont(mPresContext, aContext, generic, michael@0: aRuleData, parentFont, font, michael@0: aStartStruct != nullptr, canStoreInRuleTree); michael@0: } michael@0: else { michael@0: // re-calculate the font as a generic font michael@0: canStoreInRuleTree = false; michael@0: nsRuleNode::SetGenericFont(mPresContext, aContext, generic, michael@0: font); michael@0: } michael@0: michael@0: if (!forcedWebFont && font->mGenericID == kGenericFont_NONE) michael@0: mPresContext->AddFontUse(font->mFont); michael@0: COMPUTE_END_INHERITED(Font, font) michael@0: } michael@0: michael@0: template michael@0: inline uint32_t ListLength(const T* aList) michael@0: { michael@0: uint32_t len = 0; michael@0: while (aList) { michael@0: len++; michael@0: aList = aList->mNext; michael@0: } michael@0: return len; michael@0: } michael@0: michael@0: michael@0: michael@0: already_AddRefed michael@0: nsRuleNode::GetShadowData(const nsCSSValueList* aList, michael@0: nsStyleContext* aContext, michael@0: bool aIsBoxShadow, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: uint32_t arrayLength = ListLength(aList); michael@0: michael@0: NS_ABORT_IF_FALSE(arrayLength > 0, michael@0: "Non-null text-shadow list, yet we counted 0 items."); michael@0: nsRefPtr shadowList = michael@0: new(arrayLength) nsCSSShadowArray(arrayLength); michael@0: michael@0: if (!shadowList) michael@0: return nullptr; michael@0: michael@0: nsStyleCoord tempCoord; michael@0: DebugOnly unitOK; michael@0: for (nsCSSShadowItem* item = shadowList->ShadowAt(0); michael@0: aList; michael@0: aList = aList->mNext, ++item) { michael@0: NS_ABORT_IF_FALSE(aList->mValue.GetUnit() == eCSSUnit_Array, michael@0: "expecting a plain array value"); michael@0: nsCSSValue::Array *arr = aList->mValue.GetArrayValue(); michael@0: // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT michael@0: unitOK = SetCoord(arr->Item(0), tempCoord, nsStyleCoord(), michael@0: SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, michael@0: aContext, mPresContext, aCanStoreInRuleTree); michael@0: NS_ASSERTION(unitOK, "unexpected unit"); michael@0: item->mXOffset = tempCoord.GetCoordValue(); michael@0: michael@0: unitOK = SetCoord(arr->Item(1), tempCoord, nsStyleCoord(), michael@0: SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, michael@0: aContext, mPresContext, aCanStoreInRuleTree); michael@0: NS_ASSERTION(unitOK, "unexpected unit"); michael@0: item->mYOffset = tempCoord.GetCoordValue(); michael@0: michael@0: // Blur radius is optional in the current box-shadow spec michael@0: if (arr->Item(2).GetUnit() != eCSSUnit_Null) { michael@0: unitOK = SetCoord(arr->Item(2), tempCoord, nsStyleCoord(), michael@0: SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY | michael@0: SETCOORD_CALC_CLAMP_NONNEGATIVE, michael@0: aContext, mPresContext, aCanStoreInRuleTree); michael@0: NS_ASSERTION(unitOK, "unexpected unit"); michael@0: item->mRadius = tempCoord.GetCoordValue(); michael@0: } else { michael@0: item->mRadius = 0; michael@0: } michael@0: michael@0: // Find the spread radius michael@0: if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) { michael@0: unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(), michael@0: SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, michael@0: aContext, mPresContext, aCanStoreInRuleTree); michael@0: NS_ASSERTION(unitOK, "unexpected unit"); michael@0: item->mSpread = tempCoord.GetCoordValue(); michael@0: } else { michael@0: item->mSpread = 0; michael@0: } michael@0: michael@0: if (arr->Item(4).GetUnit() != eCSSUnit_Null) { michael@0: item->mHasColor = true; michael@0: // 2nd argument can be bogus since inherit is not a valid color michael@0: unitOK = SetColor(arr->Item(4), 0, mPresContext, aContext, item->mColor, michael@0: aCanStoreInRuleTree); michael@0: NS_ASSERTION(unitOK, "unexpected unit"); michael@0: } michael@0: michael@0: if (aIsBoxShadow && arr->Item(5).GetUnit() == eCSSUnit_Enumerated) { michael@0: NS_ASSERTION(arr->Item(5).GetIntValue() == NS_STYLE_BOX_SHADOW_INSET, michael@0: "invalid keyword type for box shadow"); michael@0: item->mInset = true; michael@0: } else { michael@0: item->mInset = false; michael@0: } michael@0: } michael@0: michael@0: return shadowList.forget(); michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeTextData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Text, (), text, parentText) michael@0: michael@0: // tab-size: integer, inherit michael@0: SetDiscrete(*aRuleData->ValueForTabSize(), michael@0: text->mTabSize, canStoreInRuleTree, michael@0: SETDSC_INTEGER | SETDSC_UNSET_INHERIT, parentText->mTabSize, michael@0: NS_STYLE_TABSIZE_INITIAL, 0, 0, 0, 0); michael@0: michael@0: // letter-spacing: normal, length, inherit michael@0: SetCoord(*aRuleData->ValueForLetterSpacing(), michael@0: text->mLetterSpacing, parentText->mLetterSpacing, michael@0: SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | michael@0: SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // text-shadow: none, list, inherit, initial michael@0: const nsCSSValue* textShadowValue = aRuleData->ValueForTextShadow(); michael@0: if (textShadowValue->GetUnit() != eCSSUnit_Null) { michael@0: text->mTextShadow = nullptr; michael@0: michael@0: // Don't need to handle none/initial explicitly: The above assignment michael@0: // takes care of that michael@0: if (textShadowValue->GetUnit() == eCSSUnit_Inherit || michael@0: textShadowValue->GetUnit() == eCSSUnit_Unset) { michael@0: canStoreInRuleTree = false; michael@0: text->mTextShadow = parentText->mTextShadow; michael@0: } else if (textShadowValue->GetUnit() == eCSSUnit_List || michael@0: textShadowValue->GetUnit() == eCSSUnit_ListDep) { michael@0: // List of arrays michael@0: text->mTextShadow = GetShadowData(textShadowValue->GetListValue(), michael@0: aContext, false, canStoreInRuleTree); michael@0: } michael@0: } michael@0: michael@0: // line-height: normal, number, length, percent, inherit michael@0: const nsCSSValue* lineHeightValue = aRuleData->ValueForLineHeight(); michael@0: if (eCSSUnit_Percent == lineHeightValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: // Use |mFont.size| to pick up minimum font size. michael@0: text->mLineHeight.SetCoordValue( michael@0: NSToCoordRound(float(aContext->StyleFont()->mFont.size) * michael@0: lineHeightValue->GetPercentValue())); michael@0: } michael@0: else if (eCSSUnit_Initial == lineHeightValue->GetUnit() || michael@0: eCSSUnit_System_Font == lineHeightValue->GetUnit()) { michael@0: text->mLineHeight.SetNormalValue(); michael@0: } michael@0: else { michael@0: SetCoord(*lineHeightValue, text->mLineHeight, parentText->mLineHeight, michael@0: SETCOORD_LEH | SETCOORD_FACTOR | SETCOORD_NORMAL | michael@0: SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: if (lineHeightValue->IsLengthUnit() && michael@0: !lineHeightValue->IsRelativeLengthUnit()) { michael@0: nscoord lh = nsStyleFont::ZoomText(mPresContext, michael@0: text->mLineHeight.GetCoordValue()); michael@0: michael@0: canStoreInRuleTree = false; michael@0: const nsStyleFont *font = aContext->StyleFont(); michael@0: nscoord minimumFontSize = mPresContext->MinFontSize(font->mLanguage); michael@0: michael@0: if (minimumFontSize > 0 && !mPresContext->IsChrome()) { michael@0: if (font->mSize != 0) { michael@0: lh = nscoord(float(lh) * float(font->mFont.size) / float(font->mSize)); michael@0: } else { michael@0: lh = minimumFontSize; michael@0: } michael@0: } michael@0: text->mLineHeight.SetCoordValue(lh); michael@0: } michael@0: } michael@0: michael@0: michael@0: // text-align: enum, string, pair(enum|string), inherit, initial michael@0: // NOTE: string is not implemented yet. michael@0: const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); michael@0: text->mTextAlignTrue = false; michael@0: if (eCSSUnit_String == textAlignValue->GetUnit()) { michael@0: NS_NOTYETIMPLEMENTED("align string"); michael@0: } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() && michael@0: NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT == michael@0: textAlignValue->GetIntValue()) { michael@0: canStoreInRuleTree = false; michael@0: uint8_t parentAlign = parentText->mTextAlign; michael@0: text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ? michael@0: NS_STYLE_TEXT_ALIGN_CENTER : parentAlign; michael@0: } else { michael@0: if (eCSSUnit_Pair == textAlignValue->GetUnit()) { michael@0: // Two values were specified, one must be 'true'. michael@0: text->mTextAlignTrue = true; michael@0: const nsCSSValuePair& textAlignValuePair = textAlignValue->GetPairValue(); michael@0: textAlignValue = &textAlignValuePair.mXValue; michael@0: if (eCSSUnit_Enumerated == textAlignValue->GetUnit()) { michael@0: if (textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { michael@0: textAlignValue = &textAlignValuePair.mYValue; michael@0: } michael@0: } else if (eCSSUnit_String == textAlignValue->GetUnit()) { michael@0: NS_NOTYETIMPLEMENTED("align string"); michael@0: } michael@0: } else if (eCSSUnit_Inherit == textAlignValue->GetUnit() || michael@0: eCSSUnit_Unset == textAlignValue->GetUnit()) { michael@0: text->mTextAlignTrue = parentText->mTextAlignTrue; michael@0: } michael@0: SetDiscrete(*textAlignValue, text->mTextAlign, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextAlign, michael@0: NS_STYLE_TEXT_ALIGN_DEFAULT, 0, 0, 0, 0); michael@0: } michael@0: michael@0: // text-align-last: enum, pair(enum), inherit, initial michael@0: const nsCSSValue* textAlignLastValue = aRuleData->ValueForTextAlignLast(); michael@0: text->mTextAlignLastTrue = false; michael@0: if (eCSSUnit_Pair == textAlignLastValue->GetUnit()) { michael@0: // Two values were specified, one must be 'true'. michael@0: text->mTextAlignLastTrue = true; michael@0: const nsCSSValuePair& textAlignLastValuePair = textAlignLastValue->GetPairValue(); michael@0: textAlignLastValue = &textAlignLastValuePair.mXValue; michael@0: if (eCSSUnit_Enumerated == textAlignLastValue->GetUnit()) { michael@0: if (textAlignLastValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) { michael@0: textAlignLastValue = &textAlignLastValuePair.mYValue; michael@0: } michael@0: } michael@0: } else if (eCSSUnit_Inherit == textAlignLastValue->GetUnit() || michael@0: eCSSUnit_Unset == textAlignLastValue->GetUnit()) { michael@0: text->mTextAlignLastTrue = parentText->mTextAlignLastTrue; michael@0: } michael@0: SetDiscrete(*textAlignLastValue, text->mTextAlignLast, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextAlignLast, michael@0: NS_STYLE_TEXT_ALIGN_AUTO, 0, 0, 0, 0); michael@0: michael@0: // text-indent: length, percent, calc, inherit, initial michael@0: SetCoord(*aRuleData->ValueForTextIndent(), text->mTextIndent, parentText->mTextIndent, michael@0: SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // text-transform: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextTransform(), text->mTextTransform, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextTransform, michael@0: NS_STYLE_TEXT_TRANSFORM_NONE, 0, 0, 0, 0); michael@0: michael@0: // white-space: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForWhiteSpace(), text->mWhiteSpace, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mWhiteSpace, michael@0: NS_STYLE_WHITESPACE_NORMAL, 0, 0, 0, 0); michael@0: michael@0: // word-break: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForWordBreak(), text->mWordBreak, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mWordBreak, michael@0: NS_STYLE_WORDBREAK_NORMAL, 0, 0, 0, 0); michael@0: michael@0: // word-spacing: normal, length, inherit michael@0: nsStyleCoord tempCoord; michael@0: const nsCSSValue* wordSpacingValue = aRuleData->ValueForWordSpacing(); michael@0: if (SetCoord(*wordSpacingValue, tempCoord, michael@0: nsStyleCoord(parentText->mWordSpacing, michael@0: nsStyleCoord::CoordConstructor), michael@0: SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | michael@0: SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: if (tempCoord.GetUnit() == eStyleUnit_Coord) { michael@0: text->mWordSpacing = tempCoord.GetCoordValue(); michael@0: } else if (tempCoord.GetUnit() == eStyleUnit_Normal) { michael@0: text->mWordSpacing = 0; michael@0: } else { michael@0: NS_NOTREACHED("unexpected unit"); michael@0: } michael@0: } else { michael@0: NS_ASSERTION(wordSpacingValue->GetUnit() == eCSSUnit_Null, michael@0: "unexpected unit"); michael@0: } michael@0: michael@0: // word-wrap: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForWordWrap(), text->mWordWrap, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mWordWrap, michael@0: NS_STYLE_WORDWRAP_NORMAL, 0, 0, 0, 0); michael@0: michael@0: // hyphens: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForHyphens(), text->mHyphens, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mHyphens, michael@0: NS_STYLE_HYPHENS_MANUAL, 0, 0, 0, 0); michael@0: michael@0: // text-size-adjust: none, auto, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust, michael@0: canStoreInRuleTree, michael@0: SETDSC_NONE | SETDSC_AUTO | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextSizeAdjust, michael@0: NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // initial value michael@0: NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // auto value michael@0: NS_STYLE_TEXT_SIZE_ADJUST_NONE, // none value michael@0: 0, 0); michael@0: michael@0: // -moz-text-discard: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForControlCharacterVisibility(), michael@0: text->mControlCharacterVisibility, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mControlCharacterVisibility, michael@0: NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN, 0, 0, 0, 0); michael@0: michael@0: // text-orientation: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextOrientation(), text->mTextOrientation, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextOrientation, michael@0: NS_STYLE_TEXT_ORIENTATION_AUTO, 0, 0, 0, 0); michael@0: michael@0: // text-combine-upright: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextCombineUpright(), michael@0: text->mTextCombineUpright, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentText->mTextCombineUpright, michael@0: NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_INHERITED(Text, text) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeTextResetData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(TextReset, (), text, parentText) michael@0: michael@0: // vertical-align: enum, length, percent, calc, inherit michael@0: const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign(); michael@0: if (!SetCoord(*verticalAlignValue, text->mVerticalAlign, michael@0: parentText->mVerticalAlign, michael@0: SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: if (eCSSUnit_Initial == verticalAlignValue->GetUnit() || michael@0: eCSSUnit_Unset == verticalAlignValue->GetUnit()) { michael@0: text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, michael@0: eStyleUnit_Enumerated); michael@0: } michael@0: } michael@0: michael@0: // text-decoration-line: enum (bit field), inherit, initial michael@0: const nsCSSValue* decorationLineValue = michael@0: aRuleData->ValueForTextDecorationLine(); michael@0: if (eCSSUnit_Enumerated == decorationLineValue->GetUnit()) { michael@0: int32_t td = decorationLineValue->GetIntValue(); michael@0: text->mTextDecorationLine = td; michael@0: if (td & NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) { michael@0: bool underlineLinks = michael@0: mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks); michael@0: if (underlineLinks) { michael@0: text->mTextDecorationLine |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; michael@0: } michael@0: else { michael@0: text->mTextDecorationLine &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; michael@0: } michael@0: } michael@0: } else if (eCSSUnit_Inherit == decorationLineValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: text->mTextDecorationLine = parentText->mTextDecorationLine; michael@0: } else if (eCSSUnit_Initial == decorationLineValue->GetUnit() || michael@0: eCSSUnit_Unset == decorationLineValue->GetUnit()) { michael@0: text->mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE; michael@0: } michael@0: michael@0: // text-decoration-color: color, string, enum, inherit, initial michael@0: const nsCSSValue* decorationColorValue = michael@0: aRuleData->ValueForTextDecorationColor(); michael@0: nscolor decorationColor; michael@0: if (eCSSUnit_Inherit == decorationColorValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: if (parentContext) { michael@0: bool isForeground; michael@0: parentText->GetDecorationColor(decorationColor, isForeground); michael@0: if (isForeground) { michael@0: text->SetDecorationColor(parentContext->StyleColor()->mColor); michael@0: } else { michael@0: text->SetDecorationColor(decorationColor); michael@0: } michael@0: } else { michael@0: text->SetDecorationColorToForeground(); michael@0: } michael@0: } michael@0: else if (eCSSUnit_EnumColor == decorationColorValue->GetUnit() && michael@0: decorationColorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) { michael@0: text->SetDecorationColorToForeground(); michael@0: } michael@0: else if (SetColor(*decorationColorValue, 0, mPresContext, aContext, michael@0: decorationColor, canStoreInRuleTree)) { michael@0: text->SetDecorationColor(decorationColor); michael@0: } michael@0: else if (eCSSUnit_Initial == decorationColorValue->GetUnit() || michael@0: eCSSUnit_Unset == decorationColorValue->GetUnit() || michael@0: eCSSUnit_Enumerated == decorationColorValue->GetUnit()) { michael@0: NS_ABORT_IF_FALSE(eCSSUnit_Enumerated != decorationColorValue->GetUnit() || michael@0: decorationColorValue->GetIntValue() == michael@0: NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR, michael@0: "unexpected enumerated value"); michael@0: text->SetDecorationColorToForeground(); michael@0: } michael@0: michael@0: // text-decoration-style: enum, inherit, initial michael@0: const nsCSSValue* decorationStyleValue = michael@0: aRuleData->ValueForTextDecorationStyle(); michael@0: if (eCSSUnit_Enumerated == decorationStyleValue->GetUnit()) { michael@0: text->SetDecorationStyle(decorationStyleValue->GetIntValue()); michael@0: } else if (eCSSUnit_Inherit == decorationStyleValue->GetUnit()) { michael@0: text->SetDecorationStyle(parentText->GetDecorationStyle()); michael@0: canStoreInRuleTree = false; michael@0: } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit() || michael@0: eCSSUnit_Unset == decorationStyleValue->GetUnit()) { michael@0: text->SetDecorationStyle(NS_STYLE_TEXT_DECORATION_STYLE_SOLID); michael@0: } michael@0: michael@0: // text-overflow: enum, string, pair(enum|string), inherit, initial michael@0: const nsCSSValue* textOverflowValue = michael@0: aRuleData->ValueForTextOverflow(); michael@0: if (eCSSUnit_Initial == textOverflowValue->GetUnit() || michael@0: eCSSUnit_Unset == textOverflowValue->GetUnit()) { michael@0: text->mTextOverflow = nsStyleTextOverflow(); michael@0: } else if (eCSSUnit_Inherit == textOverflowValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: text->mTextOverflow = parentText->mTextOverflow; michael@0: } else if (eCSSUnit_Enumerated == textOverflowValue->GetUnit()) { michael@0: // A single enumerated value. michael@0: SetDiscrete(*textOverflowValue, text->mTextOverflow.mRight.mType, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED, parentText->mTextOverflow.mRight.mType, michael@0: NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); michael@0: text->mTextOverflow.mRight.mString.Truncate(); michael@0: text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; michael@0: text->mTextOverflow.mLeft.mString.Truncate(); michael@0: text->mTextOverflow.mLogicalDirections = true; michael@0: } else if (eCSSUnit_String == textOverflowValue->GetUnit()) { michael@0: // A single string value. michael@0: text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; michael@0: textOverflowValue->GetStringValue(text->mTextOverflow.mRight.mString); michael@0: text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; michael@0: text->mTextOverflow.mLeft.mString.Truncate(); michael@0: text->mTextOverflow.mLogicalDirections = true; michael@0: } else if (eCSSUnit_Pair == textOverflowValue->GetUnit()) { michael@0: // Two values were specified. michael@0: text->mTextOverflow.mLogicalDirections = false; michael@0: const nsCSSValuePair& textOverflowValuePair = michael@0: textOverflowValue->GetPairValue(); michael@0: michael@0: const nsCSSValue *textOverflowLeftValue = &textOverflowValuePair.mXValue; michael@0: if (eCSSUnit_Enumerated == textOverflowLeftValue->GetUnit()) { michael@0: SetDiscrete(*textOverflowLeftValue, text->mTextOverflow.mLeft.mType, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED, parentText->mTextOverflow.mLeft.mType, michael@0: NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); michael@0: text->mTextOverflow.mLeft.mString.Truncate(); michael@0: } else if (eCSSUnit_String == textOverflowLeftValue->GetUnit()) { michael@0: textOverflowLeftValue->GetStringValue(text->mTextOverflow.mLeft.mString); michael@0: text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_STRING; michael@0: } michael@0: michael@0: const nsCSSValue *textOverflowRightValue = &textOverflowValuePair.mYValue; michael@0: if (eCSSUnit_Enumerated == textOverflowRightValue->GetUnit()) { michael@0: SetDiscrete(*textOverflowRightValue, text->mTextOverflow.mRight.mType, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED, parentText->mTextOverflow.mRight.mType, michael@0: NS_STYLE_TEXT_OVERFLOW_CLIP, 0, 0, 0, 0); michael@0: text->mTextOverflow.mRight.mString.Truncate(); michael@0: } else if (eCSSUnit_String == textOverflowRightValue->GetUnit()) { michael@0: textOverflowRightValue->GetStringValue(text->mTextOverflow.mRight.mString); michael@0: text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; michael@0: } michael@0: } michael@0: michael@0: // unicode-bidi: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentText->mUnicodeBidi, michael@0: NS_STYLE_UNICODE_BIDI_NORMAL, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_RESET(TextReset, text) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(UserInterface, (), ui, parentUI) michael@0: michael@0: // cursor: enum, url, inherit michael@0: const nsCSSValue* cursorValue = aRuleData->ValueForCursor(); michael@0: nsCSSUnit cursorUnit = cursorValue->GetUnit(); michael@0: if (cursorUnit != eCSSUnit_Null) { michael@0: delete [] ui->mCursorArray; michael@0: ui->mCursorArray = nullptr; michael@0: ui->mCursorArrayLength = 0; michael@0: michael@0: if (cursorUnit == eCSSUnit_Inherit || michael@0: cursorUnit == eCSSUnit_Unset) { michael@0: canStoreInRuleTree = false; michael@0: ui->mCursor = parentUI->mCursor; michael@0: ui->CopyCursorArrayFrom(*parentUI); michael@0: } michael@0: else if (cursorUnit == eCSSUnit_Initial) { michael@0: ui->mCursor = NS_STYLE_CURSOR_AUTO; michael@0: } michael@0: else { michael@0: // The parser will never create a list that is *all* URL values -- michael@0: // that's invalid. michael@0: NS_ABORT_IF_FALSE(cursorUnit == eCSSUnit_List || michael@0: cursorUnit == eCSSUnit_ListDep, michael@0: nsPrintfCString("unrecognized cursor unit %d", michael@0: cursorUnit).get()); michael@0: const nsCSSValueList* list = cursorValue->GetListValue(); michael@0: const nsCSSValueList* list2 = list; michael@0: nsIDocument* doc = aContext->PresContext()->Document(); michael@0: uint32_t arrayLength = 0; michael@0: for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext) michael@0: if (list->mValue.GetArrayValue()->Item(0).GetImageValue(doc)) michael@0: ++arrayLength; michael@0: michael@0: if (arrayLength != 0) { michael@0: ui->mCursorArray = new nsCursorImage[arrayLength]; michael@0: if (ui->mCursorArray) { michael@0: ui->mCursorArrayLength = arrayLength; michael@0: michael@0: for (nsCursorImage *item = ui->mCursorArray; michael@0: list2->mValue.GetUnit() == eCSSUnit_Array; michael@0: list2 = list2->mNext) { michael@0: nsCSSValue::Array *arr = list2->mValue.GetArrayValue(); michael@0: imgIRequest *req = arr->Item(0).GetImageValue(doc); michael@0: if (req) { michael@0: item->SetImage(req); michael@0: if (arr->Item(1).GetUnit() != eCSSUnit_Null) { michael@0: item->mHaveHotspot = true; michael@0: item->mHotspotX = arr->Item(1).GetFloatValue(), michael@0: item->mHotspotY = arr->Item(2).GetFloatValue(); michael@0: } michael@0: ++item; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_ASSERTION(list, "Must have non-array value at the end"); michael@0: NS_ASSERTION(list->mValue.GetUnit() == eCSSUnit_Enumerated, michael@0: "Unexpected fallback value at end of cursor list"); michael@0: ui->mCursor = list->mValue.GetIntValue(); michael@0: } michael@0: } michael@0: michael@0: // user-input: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForUserInput(), michael@0: ui->mUserInput, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentUI->mUserInput, michael@0: NS_STYLE_USER_INPUT_AUTO, 0, 0, 0, 0); michael@0: michael@0: // user-modify: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForUserModify(), michael@0: ui->mUserModify, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentUI->mUserModify, michael@0: NS_STYLE_USER_MODIFY_READ_ONLY, michael@0: 0, 0, 0, 0); michael@0: michael@0: // user-focus: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForUserFocus(), michael@0: ui->mUserFocus, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentUI->mUserFocus, michael@0: NS_STYLE_USER_FOCUS_NONE, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_INHERITED(UserInterface, ui) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeUIResetData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(UIReset, (), ui, parentUI) michael@0: michael@0: // user-select: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForUserSelect(), michael@0: ui->mUserSelect, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentUI->mUserSelect, michael@0: NS_STYLE_USER_SELECT_AUTO, 0, 0, 0, 0); michael@0: michael@0: // ime-mode: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForImeMode(), michael@0: ui->mIMEMode, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentUI->mIMEMode, michael@0: NS_STYLE_IME_MODE_AUTO, 0, 0, 0, 0); michael@0: michael@0: // force-broken-image-icons: integer, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForForceBrokenImageIcon(), michael@0: ui->mForceBrokenImageIcon, michael@0: canStoreInRuleTree, michael@0: SETDSC_INTEGER | SETDSC_UNSET_INITIAL, michael@0: parentUI->mForceBrokenImageIcon, michael@0: 0, 0, 0, 0, 0); michael@0: michael@0: // -moz-window-shadow: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForWindowShadow(), michael@0: ui->mWindowShadow, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentUI->mWindowShadow, michael@0: NS_STYLE_WINDOW_SHADOW_DEFAULT, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_RESET(UIReset, ui) michael@0: } michael@0: michael@0: // Information about each transition or animation property that is michael@0: // constant. michael@0: struct TransitionPropInfo { michael@0: nsCSSProperty property; michael@0: // Location of the count of the property's computed value. michael@0: uint32_t nsStyleDisplay::* sdCount; michael@0: }; michael@0: michael@0: // Each property's index in this array must match its index in the michael@0: // mutable array |transitionPropData| below. michael@0: static const TransitionPropInfo transitionPropInfo[4] = { michael@0: { eCSSProperty_transition_delay, michael@0: &nsStyleDisplay::mTransitionDelayCount }, michael@0: { eCSSProperty_transition_duration, michael@0: &nsStyleDisplay::mTransitionDurationCount }, michael@0: { eCSSProperty_transition_property, michael@0: &nsStyleDisplay::mTransitionPropertyCount }, michael@0: { eCSSProperty_transition_timing_function, michael@0: &nsStyleDisplay::mTransitionTimingFunctionCount }, michael@0: }; michael@0: michael@0: // Each property's index in this array must match its index in the michael@0: // mutable array |animationPropData| below. michael@0: static const TransitionPropInfo animationPropInfo[8] = { michael@0: { eCSSProperty_animation_delay, michael@0: &nsStyleDisplay::mAnimationDelayCount }, michael@0: { eCSSProperty_animation_duration, michael@0: &nsStyleDisplay::mAnimationDurationCount }, michael@0: { eCSSProperty_animation_name, michael@0: &nsStyleDisplay::mAnimationNameCount }, michael@0: { eCSSProperty_animation_timing_function, michael@0: &nsStyleDisplay::mAnimationTimingFunctionCount }, michael@0: { eCSSProperty_animation_direction, michael@0: &nsStyleDisplay::mAnimationDirectionCount }, michael@0: { eCSSProperty_animation_fill_mode, michael@0: &nsStyleDisplay::mAnimationFillModeCount }, michael@0: { eCSSProperty_animation_play_state, michael@0: &nsStyleDisplay::mAnimationPlayStateCount }, michael@0: { eCSSProperty_animation_iteration_count, michael@0: &nsStyleDisplay::mAnimationIterationCountCount }, michael@0: }; michael@0: michael@0: // Information about each transition or animation property that changes michael@0: // during ComputeDisplayData. michael@0: struct TransitionPropData { michael@0: const nsCSSValueList *list; michael@0: nsCSSUnit unit; michael@0: uint32_t num; michael@0: }; michael@0: michael@0: static uint32_t michael@0: CountTransitionProps(const TransitionPropInfo* aInfo, michael@0: TransitionPropData* aData, michael@0: size_t aLength, michael@0: nsStyleDisplay* aDisplay, michael@0: const nsStyleDisplay* aParentDisplay, michael@0: const nsRuleData* aRuleData, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: // The four transition properties or eight animation properties are michael@0: // stored in nsCSSDisplay in a single array for all properties. The michael@0: // number of transitions is equal to the number of items in the michael@0: // longest property's value. Properties that have fewer values than michael@0: // the longest are filled in by repeating the list. However, this michael@0: // repetition does not extend the computed value of that particular michael@0: // property (for purposes of inheritance, or, in our code, for when michael@0: // other properties are overridden by a more specific rule). michael@0: michael@0: // But actually, since the spec isn't clear yet, we'll fully compute michael@0: // all of them (so we can switch easily later), but only care about michael@0: // the ones up to the number of items for 'transition-property', per michael@0: // http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html . michael@0: michael@0: // Transitions are difficult to handle correctly because of this. For michael@0: // example, we need to handle scenarios such as: michael@0: // * a more general rule specifies transition-property: a, b, c; michael@0: // * a more specific rule overrides as transition-property: d; michael@0: // michael@0: // If only the general rule applied, we would fill in the extra michael@0: // properties (duration, delay, etc) with initial values to create 3 michael@0: // fully-specified transitions. But when the more specific rule michael@0: // applies, we should only create a single transition. In order to do michael@0: // this we need to remember which properties were explicitly specified michael@0: // and which ones were just filled in with initial values to get a michael@0: // fully-specified transition, which we do by remembering the number michael@0: // of values for each property. michael@0: michael@0: uint32_t numTransitions = 0; michael@0: for (size_t i = 0; i < aLength; ++i) { michael@0: const TransitionPropInfo& info = aInfo[i]; michael@0: TransitionPropData& data = aData[i]; michael@0: michael@0: // cache whether any of the properties are specified as 'inherit' so michael@0: // we can use it below michael@0: michael@0: const nsCSSValue& value = *aRuleData->ValueFor(info.property); michael@0: data.unit = value.GetUnit(); michael@0: data.list = (value.GetUnit() == eCSSUnit_List || michael@0: value.GetUnit() == eCSSUnit_ListDep) michael@0: ? value.GetListValue() : nullptr; michael@0: michael@0: // General algorithm to determine how many total transitions we need michael@0: // to build. For each property: michael@0: // - if there is no value specified in for the property in michael@0: // displayData, use the values from the start struct, but only if michael@0: // they were explicitly specified michael@0: // - if there is a value specified for the property in displayData: michael@0: // - if the value is 'inherit', count the number of values for michael@0: // that property are specified by the parent, but only those michael@0: // that were explicitly specified michael@0: // - otherwise, count the number of values specified in displayData michael@0: michael@0: michael@0: // calculate number of elements michael@0: if (data.unit == eCSSUnit_Inherit) { michael@0: data.num = aParentDisplay->*(info.sdCount); michael@0: aCanStoreInRuleTree = false; michael@0: } else if (data.list) { michael@0: data.num = ListLength(data.list); michael@0: } else { michael@0: data.num = aDisplay->*(info.sdCount); michael@0: } michael@0: if (data.num > numTransitions) michael@0: numTransitions = data.num; michael@0: } michael@0: michael@0: return numTransitions; michael@0: } michael@0: michael@0: static void michael@0: ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Enumerated: michael@0: aResult = nsTimingFunction(aValue.GetIntValue()); michael@0: break; michael@0: case eCSSUnit_Cubic_Bezier: michael@0: { michael@0: nsCSSValue::Array* array = aValue.GetArrayValue(); michael@0: NS_ASSERTION(array && array->Count() == 4, michael@0: "Need 4 control points"); michael@0: aResult = nsTimingFunction(array->Item(0).GetFloatValue(), michael@0: array->Item(1).GetFloatValue(), michael@0: array->Item(2).GetFloatValue(), michael@0: array->Item(3).GetFloatValue()); michael@0: } michael@0: break; michael@0: case eCSSUnit_Steps: michael@0: { michael@0: nsCSSValue::Array* array = aValue.GetArrayValue(); michael@0: NS_ASSERTION(array && array->Count() == 2, michael@0: "Need 2 items"); michael@0: NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer, michael@0: "unexpected first value"); michael@0: NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Enumerated && michael@0: (array->Item(1).GetIntValue() == michael@0: NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START || michael@0: array->Item(1).GetIntValue() == michael@0: NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END), michael@0: "unexpected second value"); michael@0: nsTimingFunction::Type type = michael@0: (array->Item(1).GetIntValue() == michael@0: NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END) michael@0: ? nsTimingFunction::StepEnd : nsTimingFunction::StepStart; michael@0: aResult = nsTimingFunction(type, array->Item(0).GetIntValue()); michael@0: } michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid transition property unit"); michael@0: } michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeDisplayData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Display, (), display, parentDisplay) michael@0: michael@0: // We may have ended up with aStartStruct's values of mDisplay and michael@0: // mFloats, but those may not be correct if our style data overrides michael@0: // its position or float properties. Reset to mOriginalDisplay and michael@0: // mOriginalFloats; it if turns out we still need the display/floats michael@0: // adjustments we'll do them below. michael@0: display->mDisplay = display->mOriginalDisplay; michael@0: display->mFloats = display->mOriginalFloats; michael@0: michael@0: // Each property's index in this array must match its index in the michael@0: // const array |transitionPropInfo| above. michael@0: TransitionPropData transitionPropData[4]; michael@0: TransitionPropData& delay = transitionPropData[0]; michael@0: TransitionPropData& duration = transitionPropData[1]; michael@0: TransitionPropData& property = transitionPropData[2]; michael@0: TransitionPropData& timingFunction = transitionPropData[3]; michael@0: michael@0: #define FOR_ALL_TRANSITION_PROPS(var_) \ michael@0: for (uint32_t var_ = 0; var_ < 4; ++var_) michael@0: michael@0: // CSS Transitions michael@0: uint32_t numTransitions = michael@0: CountTransitionProps(transitionPropInfo, transitionPropData, michael@0: ArrayLength(transitionPropData), michael@0: display, parentDisplay, aRuleData, michael@0: canStoreInRuleTree); michael@0: michael@0: display->mTransitions.SetLength(numTransitions); michael@0: michael@0: FOR_ALL_TRANSITION_PROPS(p) { michael@0: const TransitionPropInfo& i = transitionPropInfo[p]; michael@0: TransitionPropData& d = transitionPropData[p]; michael@0: michael@0: display->*(i.sdCount) = d.num; michael@0: } michael@0: michael@0: // Fill in the transitions we just allocated with the appropriate values. michael@0: for (uint32_t i = 0; i < numTransitions; ++i) { michael@0: nsTransition *transition = &display->mTransitions[i]; michael@0: michael@0: if (i >= delay.num) { michael@0: transition->SetDelay(display->mTransitions[i % delay.num].GetDelay()); michael@0: } else if (delay.unit == eCSSUnit_Inherit) { michael@0: // FIXME (Bug 522599) (for all transition properties): write a test that michael@0: // detects when this was wrong for i >= delay.num if parent had michael@0: // count for this property not equal to length michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDelayCount, michael@0: "delay.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: transition->SetDelay(parentDisplay->mTransitions[i].GetDelay()); michael@0: } else if (delay.unit == eCSSUnit_Initial || michael@0: delay.unit == eCSSUnit_Unset) { michael@0: transition->SetDelay(0.0); michael@0: } else if (delay.list) { michael@0: switch (delay.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Seconds: michael@0: transition->SetDelay(PR_MSEC_PER_SEC * michael@0: delay.list->mValue.GetFloatValue()); michael@0: break; michael@0: case eCSSUnit_Milliseconds: michael@0: transition->SetDelay(delay.list->mValue.GetFloatValue()); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid delay unit"); michael@0: } michael@0: } michael@0: michael@0: if (i >= duration.num) { michael@0: transition->SetDuration( michael@0: display->mTransitions[i % duration.num].GetDuration()); michael@0: } else if (duration.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionDurationCount, michael@0: "duration.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: transition->SetDuration(parentDisplay->mTransitions[i].GetDuration()); michael@0: } else if (duration.unit == eCSSUnit_Initial || michael@0: duration.unit == eCSSUnit_Unset) { michael@0: transition->SetDuration(0.0); michael@0: } else if (duration.list) { michael@0: switch (duration.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Seconds: michael@0: transition->SetDuration(PR_MSEC_PER_SEC * michael@0: duration.list->mValue.GetFloatValue()); michael@0: break; michael@0: case eCSSUnit_Milliseconds: michael@0: transition->SetDuration(duration.list->mValue.GetFloatValue()); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid duration unit"); michael@0: } michael@0: } michael@0: michael@0: if (i >= property.num) { michael@0: transition->CopyPropertyFrom(display->mTransitions[i % property.num]); michael@0: } else if (property.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionPropertyCount, michael@0: "property.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: transition->CopyPropertyFrom(parentDisplay->mTransitions[i]); michael@0: } else if (property.unit == eCSSUnit_Initial || michael@0: property.unit == eCSSUnit_Unset) { michael@0: transition->SetProperty(eCSSPropertyExtra_all_properties); michael@0: } else if (property.unit == eCSSUnit_None) { michael@0: transition->SetProperty(eCSSPropertyExtra_no_properties); michael@0: } else if (property.list) { michael@0: const nsCSSValue &val = property.list->mValue; michael@0: michael@0: if (val.GetUnit() == eCSSUnit_Ident) { michael@0: nsDependentString michael@0: propertyStr(property.list->mValue.GetStringBufferValue()); michael@0: nsCSSProperty prop = michael@0: nsCSSProps::LookupProperty(propertyStr, michael@0: nsCSSProps::eEnabledForAllContent); michael@0: if (prop == eCSSProperty_UNKNOWN) { michael@0: transition->SetUnknownProperty(propertyStr); michael@0: } else { michael@0: transition->SetProperty(prop); michael@0: } michael@0: } else { michael@0: NS_ABORT_IF_FALSE(val.GetUnit() == eCSSUnit_All, michael@0: nsPrintfCString("Invalid transition property unit %d", michael@0: val.GetUnit()).get()); michael@0: transition->SetProperty(eCSSPropertyExtra_all_properties); michael@0: } michael@0: } michael@0: michael@0: if (i >= timingFunction.num) { michael@0: transition->SetTimingFunction( michael@0: display->mTransitions[i % timingFunction.num].GetTimingFunction()); michael@0: } else if (timingFunction.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mTransitionTimingFunctionCount, michael@0: "timingFunction.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: transition->SetTimingFunction( michael@0: parentDisplay->mTransitions[i].GetTimingFunction()); michael@0: } else if (timingFunction.unit == eCSSUnit_Initial || michael@0: timingFunction.unit == eCSSUnit_Unset) { michael@0: transition->SetTimingFunction( michael@0: nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); michael@0: } else if (timingFunction.list) { michael@0: ComputeTimingFunction(timingFunction.list->mValue, michael@0: transition->TimingFunctionSlot()); michael@0: } michael@0: michael@0: FOR_ALL_TRANSITION_PROPS(p) { michael@0: const TransitionPropInfo& info = transitionPropInfo[p]; michael@0: TransitionPropData& d = transitionPropData[p]; michael@0: michael@0: // if we're at the end of the list, start at the beginning and repeat michael@0: // until we're out of transitions to populate michael@0: if (d.list) { michael@0: d.list = d.list->mNext ? d.list->mNext : michael@0: aRuleData->ValueFor(info.property)->GetListValue(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Each property's index in this array must match its index in the michael@0: // const array |animationPropInfo| above. michael@0: TransitionPropData animationPropData[8]; michael@0: TransitionPropData& animDelay = animationPropData[0]; michael@0: TransitionPropData& animDuration = animationPropData[1]; michael@0: TransitionPropData& animName = animationPropData[2]; michael@0: TransitionPropData& animTimingFunction = animationPropData[3]; michael@0: TransitionPropData& animDirection = animationPropData[4]; michael@0: TransitionPropData& animFillMode = animationPropData[5]; michael@0: TransitionPropData& animPlayState = animationPropData[6]; michael@0: TransitionPropData& animIterationCount = animationPropData[7]; michael@0: michael@0: #define FOR_ALL_ANIMATION_PROPS(var_) \ michael@0: for (uint32_t var_ = 0; var_ < 8; ++var_) michael@0: michael@0: // CSS Animations. michael@0: michael@0: uint32_t numAnimations = michael@0: CountTransitionProps(animationPropInfo, animationPropData, michael@0: ArrayLength(animationPropData), michael@0: display, parentDisplay, aRuleData, michael@0: canStoreInRuleTree); michael@0: michael@0: display->mAnimations.SetLength(numAnimations); michael@0: michael@0: FOR_ALL_ANIMATION_PROPS(p) { michael@0: const TransitionPropInfo& i = animationPropInfo[p]; michael@0: TransitionPropData& d = animationPropData[p]; michael@0: michael@0: display->*(i.sdCount) = d.num; michael@0: } michael@0: michael@0: // Fill in the animations we just allocated with the appropriate values. michael@0: for (uint32_t i = 0; i < numAnimations; ++i) { michael@0: nsAnimation *animation = &display->mAnimations[i]; michael@0: michael@0: if (i >= animDelay.num) { michael@0: animation->SetDelay(display->mAnimations[i % animDelay.num].GetDelay()); michael@0: } else if (animDelay.unit == eCSSUnit_Inherit) { michael@0: // FIXME (Bug 522599) (for all animation properties): write a test that michael@0: // detects when this was wrong for i >= animDelay.num if parent had michael@0: // count for this property not equal to length michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDelayCount, michael@0: "animDelay.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetDelay(parentDisplay->mAnimations[i].GetDelay()); michael@0: } else if (animDelay.unit == eCSSUnit_Initial || michael@0: animDelay.unit == eCSSUnit_Unset) { michael@0: animation->SetDelay(0.0); michael@0: } else if (animDelay.list) { michael@0: switch (animDelay.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Seconds: michael@0: animation->SetDelay(PR_MSEC_PER_SEC * michael@0: animDelay.list->mValue.GetFloatValue()); michael@0: break; michael@0: case eCSSUnit_Milliseconds: michael@0: animation->SetDelay(animDelay.list->mValue.GetFloatValue()); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid delay unit"); michael@0: } michael@0: } michael@0: michael@0: if (i >= animDuration.num) { michael@0: animation->SetDuration( michael@0: display->mAnimations[i % animDuration.num].GetDuration()); michael@0: } else if (animDuration.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDurationCount, michael@0: "animDuration.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetDuration(parentDisplay->mAnimations[i].GetDuration()); michael@0: } else if (animDuration.unit == eCSSUnit_Initial || michael@0: animDuration.unit == eCSSUnit_Unset) { michael@0: animation->SetDuration(0.0); michael@0: } else if (animDuration.list) { michael@0: switch (animDuration.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Seconds: michael@0: animation->SetDuration(PR_MSEC_PER_SEC * michael@0: animDuration.list->mValue.GetFloatValue()); michael@0: break; michael@0: case eCSSUnit_Milliseconds: michael@0: animation->SetDuration(animDuration.list->mValue.GetFloatValue()); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid duration unit"); michael@0: } michael@0: } michael@0: michael@0: if (i >= animName.num) { michael@0: animation->SetName(display->mAnimations[i % animName.num].GetName()); michael@0: } else if (animName.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationNameCount, michael@0: "animName.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetName(parentDisplay->mAnimations[i].GetName()); michael@0: } else if (animName.unit == eCSSUnit_Initial || michael@0: animName.unit == eCSSUnit_Unset) { michael@0: animation->SetName(EmptyString()); michael@0: } else if (animName.list) { michael@0: switch (animName.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Ident: { michael@0: nsDependentString michael@0: nameStr(animName.list->mValue.GetStringBufferValue()); michael@0: animation->SetName(nameStr); michael@0: break; michael@0: } michael@0: case eCSSUnit_None: { michael@0: animation->SetName(EmptyString()); michael@0: break; michael@0: } michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: nsPrintfCString("Invalid animation-name unit %d", michael@0: animName.list->mValue.GetUnit()).get()); michael@0: } michael@0: } michael@0: michael@0: if (i >= animTimingFunction.num) { michael@0: animation->SetTimingFunction( michael@0: display->mAnimations[i % animTimingFunction.num].GetTimingFunction()); michael@0: } else if (animTimingFunction.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationTimingFunctionCount, michael@0: "animTimingFunction.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetTimingFunction( michael@0: parentDisplay->mAnimations[i].GetTimingFunction()); michael@0: } else if (animTimingFunction.unit == eCSSUnit_Initial || michael@0: animTimingFunction.unit == eCSSUnit_Unset) { michael@0: animation->SetTimingFunction( michael@0: nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); michael@0: } else if (animTimingFunction.list) { michael@0: ComputeTimingFunction(animTimingFunction.list->mValue, michael@0: animation->TimingFunctionSlot()); michael@0: } michael@0: michael@0: if (i >= animDirection.num) { michael@0: animation->SetDirection(display->mAnimations[i % animDirection.num].GetDirection()); michael@0: } else if (animDirection.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationDirectionCount, michael@0: "animDirection.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetDirection(parentDisplay->mAnimations[i].GetDirection()); michael@0: } else if (animDirection.unit == eCSSUnit_Initial || michael@0: animDirection.unit == eCSSUnit_Unset) { michael@0: animation->SetDirection(NS_STYLE_ANIMATION_DIRECTION_NORMAL); michael@0: } else if (animDirection.list) { michael@0: NS_ABORT_IF_FALSE(animDirection.list->mValue.GetUnit() == eCSSUnit_Enumerated, michael@0: nsPrintfCString("Invalid animation-direction unit %d", michael@0: animDirection.list->mValue.GetUnit()).get()); michael@0: michael@0: animation->SetDirection(animDirection.list->mValue.GetIntValue()); michael@0: } michael@0: michael@0: if (i >= animFillMode.num) { michael@0: animation->SetFillMode(display->mAnimations[i % animFillMode.num].GetFillMode()); michael@0: } else if (animFillMode.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationFillModeCount, michael@0: "animFillMode.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetFillMode(parentDisplay->mAnimations[i].GetFillMode()); michael@0: } else if (animFillMode.unit == eCSSUnit_Initial || michael@0: animFillMode.unit == eCSSUnit_Unset) { michael@0: animation->SetFillMode(NS_STYLE_ANIMATION_FILL_MODE_NONE); michael@0: } else if (animFillMode.list) { michael@0: NS_ABORT_IF_FALSE(animFillMode.list->mValue.GetUnit() == eCSSUnit_Enumerated, michael@0: nsPrintfCString("Invalid animation-fill-mode unit %d", michael@0: animFillMode.list->mValue.GetUnit()).get()); michael@0: michael@0: animation->SetFillMode(animFillMode.list->mValue.GetIntValue()); michael@0: } michael@0: michael@0: if (i >= animPlayState.num) { michael@0: animation->SetPlayState(display->mAnimations[i % animPlayState.num].GetPlayState()); michael@0: } else if (animPlayState.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationPlayStateCount, michael@0: "animPlayState.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetPlayState(parentDisplay->mAnimations[i].GetPlayState()); michael@0: } else if (animPlayState.unit == eCSSUnit_Initial || michael@0: animPlayState.unit == eCSSUnit_Unset) { michael@0: animation->SetPlayState(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING); michael@0: } else if (animPlayState.list) { michael@0: NS_ABORT_IF_FALSE(animPlayState.list->mValue.GetUnit() == eCSSUnit_Enumerated, michael@0: nsPrintfCString("Invalid animation-play-state unit %d", michael@0: animPlayState.list->mValue.GetUnit()).get()); michael@0: michael@0: animation->SetPlayState(animPlayState.list->mValue.GetIntValue()); michael@0: } michael@0: michael@0: if (i >= animIterationCount.num) { michael@0: animation->SetIterationCount(display->mAnimations[i % animIterationCount.num].GetIterationCount()); michael@0: } else if (animIterationCount.unit == eCSSUnit_Inherit) { michael@0: NS_ABORT_IF_FALSE(i < parentDisplay->mAnimationIterationCountCount, michael@0: "animIterationCount.num computed incorrectly"); michael@0: NS_ABORT_IF_FALSE(!canStoreInRuleTree, michael@0: "should have made canStoreInRuleTree false above"); michael@0: animation->SetIterationCount(parentDisplay->mAnimations[i].GetIterationCount()); michael@0: } else if (animIterationCount.unit == eCSSUnit_Initial || michael@0: animIterationCount.unit == eCSSUnit_Unset) { michael@0: animation->SetIterationCount(1.0f); michael@0: } else if (animIterationCount.list) { michael@0: switch (animIterationCount.list->mValue.GetUnit()) { michael@0: case eCSSUnit_Enumerated: michael@0: NS_ABORT_IF_FALSE(animIterationCount.list->mValue.GetIntValue() == michael@0: NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE, michael@0: "unexpected value"); michael@0: animation->SetIterationCount(NS_IEEEPositiveInfinity()); michael@0: break; michael@0: case eCSSUnit_Number: michael@0: animation->SetIterationCount( michael@0: animIterationCount.list->mValue.GetFloatValue()); michael@0: break; michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: "unexpected animation-iteration-count unit"); michael@0: } michael@0: } michael@0: michael@0: FOR_ALL_ANIMATION_PROPS(p) { michael@0: const TransitionPropInfo& info = animationPropInfo[p]; michael@0: TransitionPropData& d = animationPropData[p]; michael@0: michael@0: // if we're at the end of the list, start at the beginning and repeat michael@0: // until we're out of animations to populate michael@0: if (d.list) { michael@0: d.list = d.list->mNext ? d.list->mNext : michael@0: aRuleData->ValueFor(info.property)->GetListValue(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // opacity: factor, inherit, initial michael@0: SetFactor(*aRuleData->ValueForOpacity(), display->mOpacity, canStoreInRuleTree, michael@0: parentDisplay->mOpacity, 1.0f, michael@0: SETFCT_OPACITY | SETFCT_UNSET_INITIAL); michael@0: michael@0: // display: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForDisplay(), display->mDisplay, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mDisplay, michael@0: NS_STYLE_DISPLAY_INLINE, 0, 0, 0, 0); michael@0: michael@0: // mix-blend-mode: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForMixBlendMode(), display->mMixBlendMode, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mMixBlendMode, NS_STYLE_BLEND_NORMAL, michael@0: 0, 0, 0, 0); michael@0: michael@0: // Backup original display value for calculation of a hypothetical michael@0: // box (CSS2 10.6.4/10.6.5), in addition to getting our style data right later. michael@0: // See nsHTMLReflowState::CalculateHypotheticalBox michael@0: display->mOriginalDisplay = display->mDisplay; michael@0: michael@0: // appearance: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForAppearance(), michael@0: display->mAppearance, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mAppearance, michael@0: NS_THEME_NONE, 0, 0, 0, 0); michael@0: michael@0: // binding: url, none, inherit michael@0: const nsCSSValue* bindingValue = aRuleData->ValueForBinding(); michael@0: if (eCSSUnit_URL == bindingValue->GetUnit()) { michael@0: mozilla::css::URLValue* url = bindingValue->GetURLStructValue(); michael@0: NS_ASSERTION(url, "What's going on here?"); michael@0: michael@0: if (MOZ_LIKELY(url->GetURI())) { michael@0: display->mBinding = url; michael@0: } else { michael@0: display->mBinding = nullptr; michael@0: } michael@0: } michael@0: else if (eCSSUnit_None == bindingValue->GetUnit() || michael@0: eCSSUnit_Initial == bindingValue->GetUnit() || michael@0: eCSSUnit_Unset == bindingValue->GetUnit()) { michael@0: display->mBinding = nullptr; michael@0: } michael@0: else if (eCSSUnit_Inherit == bindingValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: display->mBinding = parentDisplay->mBinding; michael@0: } michael@0: michael@0: // position: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForPosition(), display->mPosition, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mPosition, michael@0: NS_STYLE_POSITION_STATIC, 0, 0, 0, 0); michael@0: michael@0: // clear: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForClear(), display->mBreakType, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mBreakType, michael@0: NS_STYLE_CLEAR_NONE, 0, 0, 0, 0); michael@0: michael@0: // temp fix for bug 24000 michael@0: // Map 'auto' and 'avoid' to false, and 'always', 'left', and michael@0: // 'right' to true. michael@0: // "A conforming user agent may interpret the values 'left' and michael@0: // 'right' as 'always'." - CSS2.1, section 13.3.1 michael@0: const nsCSSValue* breakBeforeValue = aRuleData->ValueForPageBreakBefore(); michael@0: if (eCSSUnit_Enumerated == breakBeforeValue->GetUnit()) { michael@0: display->mBreakBefore = michael@0: (NS_STYLE_PAGE_BREAK_AVOID != breakBeforeValue->GetIntValue() && michael@0: NS_STYLE_PAGE_BREAK_AUTO != breakBeforeValue->GetIntValue()); michael@0: } michael@0: else if (eCSSUnit_Initial == breakBeforeValue->GetUnit() || michael@0: eCSSUnit_Unset == breakBeforeValue->GetUnit()) { michael@0: display->mBreakBefore = false; michael@0: } michael@0: else if (eCSSUnit_Inherit == breakBeforeValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: display->mBreakBefore = parentDisplay->mBreakBefore; michael@0: } michael@0: michael@0: const nsCSSValue* breakAfterValue = aRuleData->ValueForPageBreakAfter(); michael@0: if (eCSSUnit_Enumerated == breakAfterValue->GetUnit()) { michael@0: display->mBreakAfter = michael@0: (NS_STYLE_PAGE_BREAK_AVOID != breakAfterValue->GetIntValue() && michael@0: NS_STYLE_PAGE_BREAK_AUTO != breakAfterValue->GetIntValue()); michael@0: } michael@0: else if (eCSSUnit_Initial == breakAfterValue->GetUnit() || michael@0: eCSSUnit_Unset == breakAfterValue->GetUnit()) { michael@0: display->mBreakAfter = false; michael@0: } michael@0: else if (eCSSUnit_Inherit == breakAfterValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: display->mBreakAfter = parentDisplay->mBreakAfter; michael@0: } michael@0: // end temp fix michael@0: michael@0: // page-break-inside: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForPageBreakInside(), michael@0: display->mBreakInside, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mBreakInside, michael@0: NS_STYLE_PAGE_BREAK_AUTO, 0, 0, 0, 0); michael@0: michael@0: // touch-action: none, auto, enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTouchAction(), display->mTouchAction, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_AUTO | SETDSC_NONE | michael@0: SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mTouchAction, michael@0: NS_STYLE_TOUCH_ACTION_AUTO, michael@0: NS_STYLE_TOUCH_ACTION_AUTO, michael@0: NS_STYLE_TOUCH_ACTION_NONE, 0, 0); michael@0: michael@0: // float: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForFloat(), michael@0: display->mFloats, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mFloats, michael@0: NS_STYLE_FLOAT_NONE, 0, 0, 0, 0); michael@0: // Save mFloats in mOriginalFloats in case we need it later michael@0: display->mOriginalFloats = display->mFloats; michael@0: michael@0: // overflow-x: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForOverflowX(), michael@0: display->mOverflowX, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mOverflowX, michael@0: NS_STYLE_OVERFLOW_VISIBLE, 0, 0, 0, 0); michael@0: michael@0: // overflow-y: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForOverflowY(), michael@0: display->mOverflowY, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mOverflowY, michael@0: NS_STYLE_OVERFLOW_VISIBLE, 0, 0, 0, 0); michael@0: michael@0: // CSS3 overflow-x and overflow-y require some fixup as well in some michael@0: // cases. NS_STYLE_OVERFLOW_VISIBLE and NS_STYLE_OVERFLOW_CLIP are michael@0: // meaningful only when used in both dimensions. michael@0: if (display->mOverflowX != display->mOverflowY && michael@0: (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE || michael@0: display->mOverflowX == NS_STYLE_OVERFLOW_CLIP || michael@0: display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE || michael@0: display->mOverflowY == NS_STYLE_OVERFLOW_CLIP)) { michael@0: // We can't store in the rule tree since a more specific rule might michael@0: // change these conditions. michael@0: canStoreInRuleTree = false; michael@0: michael@0: // NS_STYLE_OVERFLOW_CLIP is a deprecated value, so if it's specified michael@0: // in only one dimension, convert it to NS_STYLE_OVERFLOW_HIDDEN. michael@0: if (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP) michael@0: display->mOverflowX = NS_STYLE_OVERFLOW_HIDDEN; michael@0: if (display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) michael@0: display->mOverflowY = NS_STYLE_OVERFLOW_HIDDEN; michael@0: michael@0: // If 'visible' is specified but doesn't match the other dimension, it michael@0: // turns into 'auto'. michael@0: if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) michael@0: display->mOverflowX = NS_STYLE_OVERFLOW_AUTO; michael@0: if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) michael@0: display->mOverflowY = NS_STYLE_OVERFLOW_AUTO; michael@0: } michael@0: michael@0: SetDiscrete(*aRuleData->ValueForOverflowClipBox(), display->mOverflowClipBox, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mOverflowClipBox, michael@0: NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX, 0, 0, 0, 0); michael@0: michael@0: SetDiscrete(*aRuleData->ValueForResize(), display->mResize, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mResize, michael@0: NS_STYLE_RESIZE_NONE, 0, 0, 0, 0); michael@0: michael@0: // clip property: length, auto, inherit michael@0: const nsCSSValue* clipValue = aRuleData->ValueForClip(); michael@0: switch (clipValue->GetUnit()) { michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: display->mClipFlags = parentDisplay->mClipFlags; michael@0: display->mClip = parentDisplay->mClip; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_Auto: michael@0: display->mClipFlags = NS_STYLE_CLIP_AUTO; michael@0: display->mClip.SetRect(0,0,0,0); michael@0: break; michael@0: michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Rect: { michael@0: const nsCSSRect& clipRect = clipValue->GetRectValue(); michael@0: michael@0: display->mClipFlags = NS_STYLE_CLIP_RECT; michael@0: michael@0: if (clipRect.mTop.GetUnit() == eCSSUnit_Auto) { michael@0: display->mClip.y = 0; michael@0: display->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO; michael@0: } michael@0: else if (clipRect.mTop.IsLengthUnit()) { michael@0: display->mClip.y = CalcLength(clipRect.mTop, aContext, michael@0: mPresContext, canStoreInRuleTree); michael@0: } michael@0: michael@0: if (clipRect.mBottom.GetUnit() == eCSSUnit_Auto) { michael@0: // Setting to NS_MAXSIZE for the 'auto' case ensures that michael@0: // the clip rect is nonempty. It is important that mClip be michael@0: // nonempty if the actual clip rect could be nonempty. michael@0: display->mClip.height = NS_MAXSIZE; michael@0: display->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO; michael@0: } michael@0: else if (clipRect.mBottom.IsLengthUnit()) { michael@0: display->mClip.height = CalcLength(clipRect.mBottom, aContext, michael@0: mPresContext, canStoreInRuleTree) - michael@0: display->mClip.y; michael@0: } michael@0: michael@0: if (clipRect.mLeft.GetUnit() == eCSSUnit_Auto) { michael@0: display->mClip.x = 0; michael@0: display->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO; michael@0: } michael@0: else if (clipRect.mLeft.IsLengthUnit()) { michael@0: display->mClip.x = CalcLength(clipRect.mLeft, aContext, michael@0: mPresContext, canStoreInRuleTree); michael@0: } michael@0: michael@0: if (clipRect.mRight.GetUnit() == eCSSUnit_Auto) { michael@0: // Setting to NS_MAXSIZE for the 'auto' case ensures that michael@0: // the clip rect is nonempty. It is important that mClip be michael@0: // nonempty if the actual clip rect could be nonempty. michael@0: display->mClip.width = NS_MAXSIZE; michael@0: display->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO; michael@0: } michael@0: else if (clipRect.mRight.IsLengthUnit()) { michael@0: display->mClip.width = CalcLength(clipRect.mRight, aContext, michael@0: mPresContext, canStoreInRuleTree) - michael@0: display->mClip.x; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unrecognized clip unit"); michael@0: } michael@0: michael@0: if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { michael@0: // CSS2 9.7 specifies display type corrections dealing with 'float' michael@0: // and 'position'. Since generated content can't be floated or michael@0: // positioned, we can deal with it here. michael@0: michael@0: if (nsCSSPseudoElements::firstLetter == aContext->GetPseudo()) { michael@0: // a non-floating first-letter must be inline michael@0: // XXX this fix can go away once bug 103189 is fixed correctly michael@0: // Note that we reset mOriginalDisplay to enforce the invariant that it equals mDisplay if we're not positioned or floating. michael@0: display->mOriginalDisplay = display->mDisplay = NS_STYLE_DISPLAY_INLINE; michael@0: michael@0: // We can't cache the data in the rule tree since if a more specific michael@0: // rule has 'float: left' we'll end up with the wrong 'display' michael@0: // property. michael@0: canStoreInRuleTree = false; michael@0: } michael@0: michael@0: if (display->IsAbsolutelyPositionedStyle()) { michael@0: // 1) if position is 'absolute' or 'fixed' then display must be michael@0: // block-level and float must be 'none' michael@0: EnsureBlockDisplay(display->mDisplay); michael@0: display->mFloats = NS_STYLE_FLOAT_NONE; michael@0: michael@0: // Note that it's OK to cache this struct in the ruletree michael@0: // because it's fine as-is for any style context that points to michael@0: // it directly, and any use of it as aStartStruct (e.g. if a michael@0: // more specific rule sets "position: static") will use michael@0: // mOriginalDisplay and mOriginalFloats, which we have carefully michael@0: // not changed. michael@0: } else if (display->mFloats != NS_STYLE_FLOAT_NONE) { michael@0: // 2) if float is not none, and display is not none, then we must michael@0: // set a block-level 'display' type per CSS2.1 section 9.7. michael@0: EnsureBlockDisplay(display->mDisplay); michael@0: michael@0: // Note that it's OK to cache this struct in the ruletree michael@0: // because it's fine as-is for any style context that points to michael@0: // it directly, and any use of it as aStartStruct (e.g. if a michael@0: // more specific rule sets "float: none") will use michael@0: // mOriginalDisplay, which we have carefully not changed. michael@0: } michael@0: michael@0: } michael@0: michael@0: /* Convert the nsCSSValueList into an nsTArray. */ michael@0: const nsCSSValue* transformValue = aRuleData->ValueForTransform(); michael@0: switch (transformValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: display->mSpecifiedTransform = nullptr; michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform; michael@0: canStoreInRuleTree = false; michael@0: break; michael@0: michael@0: case eCSSUnit_SharedList: { michael@0: nsCSSValueSharedList* list = transformValue->GetSharedListValue(); michael@0: nsCSSValueList* head = list->mHead; michael@0: MOZ_ASSERT(head, "transform list must have at least one item"); michael@0: // can get a _None in here from transform animation michael@0: if (head->mValue.GetUnit() == eCSSUnit_None) { michael@0: NS_ABORT_IF_FALSE(head->mNext == nullptr, "none must be alone"); michael@0: display->mSpecifiedTransform = nullptr; michael@0: } else { michael@0: display->mSpecifiedTransform = list; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unrecognized transform unit"); michael@0: } michael@0: michael@0: /* Convert the nsCSSValueList into a will-change bitfield for fast lookup */ michael@0: const nsCSSValue* willChangeValue = aRuleData->ValueForWillChange(); michael@0: switch (willChangeValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: display->mWillChange.Clear(); michael@0: display->mWillChangeBitField = 0; michael@0: for (const nsCSSValueList* item = willChangeValue->GetListValue(); michael@0: item; item = item->mNext) michael@0: { michael@0: if (item->mValue.UnitHasStringValue()) { michael@0: nsAutoString buffer; michael@0: item->mValue.GetStringValue(buffer); michael@0: display->mWillChange.AppendElement(buffer); michael@0: michael@0: if (buffer.EqualsLiteral("transform")) { michael@0: display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM; michael@0: } michael@0: if (buffer.EqualsLiteral("opacity")) { michael@0: display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY; michael@0: } michael@0: if (buffer.EqualsLiteral("scroll-position")) { michael@0: display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL; michael@0: } michael@0: michael@0: nsCSSProperty prop = michael@0: nsCSSProps::LookupProperty(buffer, michael@0: nsCSSProps::eEnabledForAllContent); michael@0: if (prop != eCSSProperty_UNKNOWN && michael@0: nsCSSProps::PropHasFlags(prop, michael@0: CSS_PROPERTY_CREATES_STACKING_CONTEXT)) michael@0: { michael@0: display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case eCSSUnit_Inherit: michael@0: display->mWillChange = parentDisplay->mWillChange; michael@0: display->mWillChangeBitField = parentDisplay->mWillChangeBitField; michael@0: canStoreInRuleTree = false; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_Auto: michael@0: display->mWillChange.Clear(); michael@0: display->mWillChangeBitField = 0; michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSERT(false, "unrecognized will-change unit"); michael@0: } michael@0: michael@0: /* Convert -moz-transform-origin. */ michael@0: const nsCSSValue* transformOriginValue = michael@0: aRuleData->ValueForTransformOrigin(); michael@0: if (transformOriginValue->GetUnit() != eCSSUnit_Null) { michael@0: const nsCSSValue& valX = michael@0: transformOriginValue->GetUnit() == eCSSUnit_Triplet ? michael@0: transformOriginValue->GetTripletValue().mXValue : *transformOriginValue; michael@0: const nsCSSValue& valY = michael@0: transformOriginValue->GetUnit() == eCSSUnit_Triplet ? michael@0: transformOriginValue->GetTripletValue().mYValue : *transformOriginValue; michael@0: const nsCSSValue& valZ = michael@0: transformOriginValue->GetUnit() == eCSSUnit_Triplet ? michael@0: transformOriginValue->GetTripletValue().mZValue : *transformOriginValue; michael@0: michael@0: mozilla::DebugOnly cX = michael@0: SetCoord(valX, display->mTransformOrigin[0], michael@0: parentDisplay->mTransformOrigin[0], michael@0: SETCOORD_LPH | SETCOORD_INITIAL_HALF | michael@0: SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: mozilla::DebugOnly cY = michael@0: SetCoord(valY, display->mTransformOrigin[1], michael@0: parentDisplay->mTransformOrigin[1], michael@0: SETCOORD_LPH | SETCOORD_INITIAL_HALF | michael@0: SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: if (valZ.GetUnit() == eCSSUnit_Null) { michael@0: // Null for the z component means a 0 translation, not michael@0: // unspecified, as we have already checked the triplet michael@0: // value for Null. michael@0: display->mTransformOrigin[2].SetCoordValue(0); michael@0: } else { michael@0: mozilla::DebugOnly cZ = michael@0: SetCoord(valZ, display->mTransformOrigin[2], michael@0: parentDisplay->mTransformOrigin[2], michael@0: SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: NS_ABORT_IF_FALSE(cY == cZ, "changed one but not the other"); michael@0: } michael@0: NS_ABORT_IF_FALSE(cX == cY, "changed one but not the other"); michael@0: NS_ASSERTION(cX, "Malformed -moz-transform-origin parse!"); michael@0: } michael@0: michael@0: const nsCSSValue* perspectiveOriginValue = michael@0: aRuleData->ValueForPerspectiveOrigin(); michael@0: if (perspectiveOriginValue->GetUnit() != eCSSUnit_Null) { michael@0: mozilla::DebugOnly result = michael@0: SetPairCoords(*perspectiveOriginValue, michael@0: display->mPerspectiveOrigin[0], michael@0: display->mPerspectiveOrigin[1], michael@0: parentDisplay->mPerspectiveOrigin[0], michael@0: parentDisplay->mPerspectiveOrigin[1], michael@0: SETCOORD_LPH | SETCOORD_INITIAL_HALF | michael@0: SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: NS_ASSERTION(result, "Malformed -moz-perspective-origin parse!"); michael@0: } michael@0: michael@0: SetCoord(*aRuleData->ValueForPerspective(), michael@0: display->mChildPerspective, parentDisplay->mChildPerspective, michael@0: SETCOORD_LAH | SETCOORD_INITIAL_NONE | SETCOORD_NONE | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: SetDiscrete(*aRuleData->ValueForBackfaceVisibility(), michael@0: display->mBackfaceVisibility, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mBackfaceVisibility, michael@0: NS_STYLE_BACKFACE_VISIBILITY_VISIBLE, 0, 0, 0, 0); michael@0: michael@0: // transform-style: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTransformStyle(), michael@0: display->mTransformStyle, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mTransformStyle, michael@0: NS_STYLE_TRANSFORM_STYLE_FLAT, 0, 0, 0, 0); michael@0: michael@0: // orient: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForOrient(), michael@0: display->mOrient, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentDisplay->mOrient, michael@0: NS_STYLE_ORIENT_AUTO, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_RESET(Display, display) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeVisibilityData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Visibility, (mPresContext), michael@0: visibility, parentVisibility) michael@0: michael@0: // IMPORTANT: No properties in this struct have lengths in them. We michael@0: // depend on this since CalcLengthWith can call StyleVisibility() michael@0: // to get the language for resolving fonts! michael@0: michael@0: // direction: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForDirection(), visibility->mDirection, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentVisibility->mDirection, michael@0: (GET_BIDI_OPTION_DIRECTION(mPresContext->GetBidi()) michael@0: == IBMBIDI_TEXTDIRECTION_RTL) michael@0: ? NS_STYLE_DIRECTION_RTL : NS_STYLE_DIRECTION_LTR, michael@0: 0, 0, 0, 0); michael@0: michael@0: // visibility: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForVisibility(), visibility->mVisible, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentVisibility->mVisible, michael@0: NS_STYLE_VISIBILITY_VISIBLE, 0, 0, 0, 0); michael@0: michael@0: // pointer-events: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForPointerEvents(), visibility->mPointerEvents, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentVisibility->mPointerEvents, michael@0: NS_STYLE_POINTER_EVENTS_AUTO, 0, 0, 0, 0); michael@0: michael@0: // writing-mode: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForWritingMode(), visibility->mWritingMode, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentVisibility->mWritingMode, michael@0: NS_STYLE_WRITING_MODE_HORIZONTAL_TB, 0, 0, 0, 0); michael@0: michael@0: // image-orientation: enum, inherit, initial michael@0: const nsCSSValue* orientation = aRuleData->ValueForImageOrientation(); michael@0: if (orientation->GetUnit() == eCSSUnit_Inherit || michael@0: orientation->GetUnit() == eCSSUnit_Unset) { michael@0: canStoreInRuleTree = false; michael@0: visibility->mImageOrientation = parentVisibility->mImageOrientation; michael@0: } else if (orientation->GetUnit() == eCSSUnit_Initial) { michael@0: visibility->mImageOrientation = nsStyleImageOrientation(); michael@0: } else if (orientation->IsAngularUnit()) { michael@0: double angle = orientation->GetAngleValueInRadians(); michael@0: visibility->mImageOrientation = michael@0: nsStyleImageOrientation::CreateAsAngleAndFlip(angle, false); michael@0: } else if (orientation->GetUnit() == eCSSUnit_Array) { michael@0: const nsCSSValue::Array* array = orientation->GetArrayValue(); michael@0: MOZ_ASSERT(array->Item(0).IsAngularUnit(), michael@0: "First image-orientation value is not an angle"); michael@0: MOZ_ASSERT(array->Item(1).GetUnit() == eCSSUnit_Enumerated && michael@0: array->Item(1).GetIntValue() == NS_STYLE_IMAGE_ORIENTATION_FLIP, michael@0: "Second image-orientation value is not 'flip'"); michael@0: double angle = array->Item(0).GetAngleValueInRadians(); michael@0: visibility->mImageOrientation = michael@0: nsStyleImageOrientation::CreateAsAngleAndFlip(angle, true); michael@0: michael@0: } else if (orientation->GetUnit() == eCSSUnit_Enumerated) { michael@0: switch (orientation->GetIntValue()) { michael@0: case NS_STYLE_IMAGE_ORIENTATION_FLIP: michael@0: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFlip(); michael@0: break; michael@0: case NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE: michael@0: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFromImage(); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Invalid image-orientation enumerated value"); michael@0: } michael@0: } else { michael@0: MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); michael@0: } michael@0: michael@0: COMPUTE_END_INHERITED(Visibility, visibility) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeColorData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Color, (mPresContext), color, parentColor) michael@0: michael@0: // color: color, string, inherit michael@0: // Special case for currentColor. According to CSS3, setting color to 'currentColor' michael@0: // should behave as if it is inherited michael@0: const nsCSSValue* colorValue = aRuleData->ValueForColor(); michael@0: if ((colorValue->GetUnit() == eCSSUnit_EnumColor && michael@0: colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) || michael@0: colorValue->GetUnit() == eCSSUnit_Unset) { michael@0: color->mColor = parentColor->mColor; michael@0: canStoreInRuleTree = false; michael@0: } michael@0: else if (colorValue->GetUnit() == eCSSUnit_Initial) { michael@0: color->mColor = mPresContext->DefaultColor(); michael@0: } michael@0: else { michael@0: SetColor(*colorValue, parentColor->mColor, mPresContext, aContext, michael@0: color->mColor, canStoreInRuleTree); michael@0: } michael@0: michael@0: COMPUTE_END_INHERITED(Color, color) michael@0: } michael@0: michael@0: // information about how to compute values for background-* properties michael@0: template michael@0: struct BackgroundItemComputer { michael@0: }; michael@0: michael@0: template <> michael@0: struct BackgroundItemComputer michael@0: { michael@0: static void ComputeValue(nsStyleContext* aStyleContext, michael@0: const nsCSSValueList* aSpecifiedValue, michael@0: uint8_t& aComputedValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: SetDiscrete(aSpecifiedValue->mValue, aComputedValue, aCanStoreInRuleTree, michael@0: SETDSC_ENUMERATED, uint8_t(0), 0, 0, 0, 0, 0); michael@0: } michael@0: }; michael@0: michael@0: template <> michael@0: struct BackgroundItemComputer michael@0: { michael@0: static void ComputeValue(nsStyleContext* aStyleContext, michael@0: const nsCSSValuePairList* aSpecifiedValue, michael@0: nsStyleBackground::Repeat& aComputedValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: NS_ASSERTION(aSpecifiedValue->mXValue.GetUnit() == eCSSUnit_Enumerated && michael@0: (aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Enumerated || michael@0: aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null), michael@0: "Invalid unit"); michael@0: michael@0: bool hasContraction = true; michael@0: uint8_t value = aSpecifiedValue->mXValue.GetIntValue(); michael@0: switch (value) { michael@0: case NS_STYLE_BG_REPEAT_REPEAT_X: michael@0: aComputedValue.mXRepeat = NS_STYLE_BG_REPEAT_REPEAT; michael@0: aComputedValue.mYRepeat = NS_STYLE_BG_REPEAT_NO_REPEAT; michael@0: break; michael@0: case NS_STYLE_BG_REPEAT_REPEAT_Y: michael@0: aComputedValue.mXRepeat = NS_STYLE_BG_REPEAT_NO_REPEAT; michael@0: aComputedValue.mYRepeat = NS_STYLE_BG_REPEAT_REPEAT; michael@0: break; michael@0: default: michael@0: aComputedValue.mXRepeat = value; michael@0: hasContraction = false; michael@0: break; michael@0: } michael@0: michael@0: if (hasContraction) { michael@0: NS_ASSERTION(aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null, michael@0: "Invalid unit."); michael@0: return; michael@0: } michael@0: michael@0: switch (aSpecifiedValue->mYValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: aComputedValue.mYRepeat = aComputedValue.mXRepeat; michael@0: break; michael@0: case eCSSUnit_Enumerated: michael@0: value = aSpecifiedValue->mYValue.GetIntValue(); michael@0: NS_ASSERTION(value == NS_STYLE_BG_REPEAT_NO_REPEAT || michael@0: value == NS_STYLE_BG_REPEAT_REPEAT, "Unexpected value"); michael@0: aComputedValue.mYRepeat = value; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unexpected CSS value"); michael@0: break; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: template <> michael@0: struct BackgroundItemComputer michael@0: { michael@0: static void ComputeValue(nsStyleContext* aStyleContext, michael@0: const nsCSSValueList* aSpecifiedValue, michael@0: nsStyleImage& aComputedValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: SetStyleImage(aStyleContext, aSpecifiedValue->mValue, aComputedValue, michael@0: aCanStoreInRuleTree); michael@0: } michael@0: }; michael@0: michael@0: /* Helper function for michael@0: * BackgroundItemComputer michael@0: * It computes a single PositionCoord from an nsCSSValue object michael@0: * (contained in a list). michael@0: */ michael@0: typedef nsStyleBackground::Position::PositionCoord PositionCoord; michael@0: static void michael@0: ComputeBackgroundPositionCoord(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aEdge, michael@0: const nsCSSValue& aOffset, michael@0: PositionCoord* aResult, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: if (eCSSUnit_Percent == aOffset.GetUnit()) { michael@0: aResult->mLength = 0; michael@0: aResult->mPercent = aOffset.GetPercentValue(); michael@0: aResult->mHasPercent = true; michael@0: } else if (aOffset.IsLengthUnit()) { michael@0: aResult->mLength = CalcLength(aOffset, aStyleContext, michael@0: aStyleContext->PresContext(), michael@0: aCanStoreInRuleTree); michael@0: aResult->mPercent = 0.0f; michael@0: aResult->mHasPercent = false; michael@0: } else if (aOffset.IsCalcUnit()) { michael@0: LengthPercentPairCalcOps ops(aStyleContext, michael@0: aStyleContext->PresContext(), michael@0: aCanStoreInRuleTree); michael@0: nsRuleNode::ComputedCalc vals = ComputeCalc(aOffset, ops); michael@0: aResult->mLength = vals.mLength; michael@0: aResult->mPercent = vals.mPercent; michael@0: aResult->mHasPercent = ops.mHasPercent; michael@0: } else { michael@0: aResult->mLength = 0; michael@0: aResult->mPercent = 0.0f; michael@0: aResult->mHasPercent = false; michael@0: NS_ASSERTION(aOffset.GetUnit() == eCSSUnit_Null, "unexpected unit"); michael@0: } michael@0: michael@0: if (eCSSUnit_Enumerated == aEdge.GetUnit()) { michael@0: int sign; michael@0: if (aEdge.GetIntValue() & (NS_STYLE_BG_POSITION_BOTTOM | michael@0: NS_STYLE_BG_POSITION_RIGHT)) { michael@0: sign = -1; michael@0: } else { michael@0: sign = 1; michael@0: } michael@0: aResult->mPercent = GetFloatFromBoxPosition(aEdge.GetIntValue()) + michael@0: sign * aResult->mPercent; michael@0: aResult->mLength = sign * aResult->mLength; michael@0: aResult->mHasPercent = true; michael@0: } else { michael@0: NS_ASSERTION(eCSSUnit_Null == aEdge.GetUnit(), "unexpected unit"); michael@0: } michael@0: } michael@0: michael@0: template <> michael@0: struct BackgroundItemComputer michael@0: { michael@0: static void ComputeValue(nsStyleContext* aStyleContext, michael@0: const nsCSSValueList* aSpecifiedValue, michael@0: nsStyleBackground::Position& aComputedValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: NS_ASSERTION(aSpecifiedValue->mValue.GetUnit() == eCSSUnit_Array, "bg-position not an array"); michael@0: michael@0: nsRefPtr bgPositionArray = michael@0: aSpecifiedValue->mValue.GetArrayValue(); michael@0: const nsCSSValue &xEdge = bgPositionArray->Item(0); michael@0: const nsCSSValue &xOffset = bgPositionArray->Item(1); michael@0: const nsCSSValue &yEdge = bgPositionArray->Item(2); michael@0: const nsCSSValue &yOffset = bgPositionArray->Item(3); michael@0: michael@0: NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || michael@0: eCSSUnit_Null == xEdge.GetUnit()) && michael@0: (eCSSUnit_Enumerated == yEdge.GetUnit() || michael@0: eCSSUnit_Null == yEdge.GetUnit()) && michael@0: eCSSUnit_Enumerated != xOffset.GetUnit() && michael@0: eCSSUnit_Enumerated != yOffset.GetUnit(), michael@0: "Invalid background position"); michael@0: michael@0: ComputeBackgroundPositionCoord(aStyleContext, xEdge, xOffset, michael@0: &aComputedValue.mXPosition, michael@0: aCanStoreInRuleTree); michael@0: michael@0: ComputeBackgroundPositionCoord(aStyleContext, yEdge, yOffset, michael@0: &aComputedValue.mYPosition, michael@0: aCanStoreInRuleTree); michael@0: } michael@0: }; michael@0: michael@0: michael@0: struct BackgroundSizeAxis { michael@0: nsCSSValue nsCSSValuePairList::* specified; michael@0: nsStyleBackground::Size::Dimension nsStyleBackground::Size::* result; michael@0: uint8_t nsStyleBackground::Size::* type; michael@0: }; michael@0: michael@0: static const BackgroundSizeAxis gBGSizeAxes[] = { michael@0: { &nsCSSValuePairList::mXValue, michael@0: &nsStyleBackground::Size::mWidth, michael@0: &nsStyleBackground::Size::mWidthType }, michael@0: { &nsCSSValuePairList::mYValue, michael@0: &nsStyleBackground::Size::mHeight, michael@0: &nsStyleBackground::Size::mHeightType } michael@0: }; michael@0: michael@0: template <> michael@0: struct BackgroundItemComputer michael@0: { michael@0: static void ComputeValue(nsStyleContext* aStyleContext, michael@0: const nsCSSValuePairList* aSpecifiedValue, michael@0: nsStyleBackground::Size& aComputedValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: nsStyleBackground::Size &size = aComputedValue; michael@0: for (const BackgroundSizeAxis *axis = gBGSizeAxes, michael@0: *axis_end = ArrayEnd(gBGSizeAxes); michael@0: axis < axis_end; ++axis) { michael@0: const nsCSSValue &specified = aSpecifiedValue->*(axis->specified); michael@0: if (eCSSUnit_Auto == specified.GetUnit()) { michael@0: size.*(axis->type) = nsStyleBackground::Size::eAuto; michael@0: } michael@0: else if (eCSSUnit_Enumerated == specified.GetUnit()) { michael@0: static_assert(nsStyleBackground::Size::eContain == michael@0: NS_STYLE_BG_SIZE_CONTAIN && michael@0: nsStyleBackground::Size::eCover == michael@0: NS_STYLE_BG_SIZE_COVER, michael@0: "background size constants out of sync"); michael@0: NS_ABORT_IF_FALSE(specified.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || michael@0: specified.GetIntValue() == NS_STYLE_BG_SIZE_COVER, michael@0: "invalid enumerated value for size coordinate"); michael@0: size.*(axis->type) = specified.GetIntValue(); michael@0: } michael@0: else if (eCSSUnit_Null == specified.GetUnit()) { michael@0: NS_ABORT_IF_FALSE(axis == gBGSizeAxes + 1, michael@0: "null allowed only as height value, and only " michael@0: "for contain/cover/initial/inherit"); michael@0: #ifdef DEBUG michael@0: { michael@0: const nsCSSValue &widthValue = aSpecifiedValue->mXValue; michael@0: NS_ABORT_IF_FALSE(widthValue.GetUnit() != eCSSUnit_Inherit && michael@0: widthValue.GetUnit() != eCSSUnit_Initial && michael@0: widthValue.GetUnit() != eCSSUnit_Unset, michael@0: "initial/inherit/unset should already have been handled"); michael@0: NS_ABORT_IF_FALSE(widthValue.GetUnit() == eCSSUnit_Enumerated && michael@0: (widthValue.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || michael@0: widthValue.GetIntValue() == NS_STYLE_BG_SIZE_COVER), michael@0: "null height value not corresponding to allowable " michael@0: "non-null width value"); michael@0: } michael@0: #endif michael@0: size.*(axis->type) = size.mWidthType; michael@0: } michael@0: else if (eCSSUnit_Percent == specified.GetUnit()) { michael@0: (size.*(axis->result)).mLength = 0; michael@0: (size.*(axis->result)).mPercent = specified.GetPercentValue(); michael@0: (size.*(axis->result)).mHasPercent = true; michael@0: size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; michael@0: } michael@0: else if (specified.IsLengthUnit()) { michael@0: (size.*(axis->result)).mLength = michael@0: CalcLength(specified, aStyleContext, aStyleContext->PresContext(), michael@0: aCanStoreInRuleTree); michael@0: (size.*(axis->result)).mPercent = 0.0f; michael@0: (size.*(axis->result)).mHasPercent = false; michael@0: size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; michael@0: } else { michael@0: NS_ABORT_IF_FALSE(specified.IsCalcUnit(), "unexpected unit"); michael@0: LengthPercentPairCalcOps ops(aStyleContext, michael@0: aStyleContext->PresContext(), michael@0: aCanStoreInRuleTree); michael@0: nsRuleNode::ComputedCalc vals = ComputeCalc(specified, ops); michael@0: (size.*(axis->result)).mLength = vals.mLength; michael@0: (size.*(axis->result)).mPercent = vals.mPercent; michael@0: (size.*(axis->result)).mHasPercent = ops.mHasPercent; michael@0: size.*(axis->type) = nsStyleBackground::Size::eLengthPercentage; michael@0: } michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(size.mWidthType < nsStyleBackground::Size::eDimensionType_COUNT, michael@0: "bad width type"); michael@0: NS_ABORT_IF_FALSE(size.mHeightType < nsStyleBackground::Size::eDimensionType_COUNT, michael@0: "bad height type"); michael@0: NS_ABORT_IF_FALSE((size.mWidthType != nsStyleBackground::Size::eContain && michael@0: size.mWidthType != nsStyleBackground::Size::eCover) || michael@0: size.mWidthType == size.mHeightType, michael@0: "contain/cover apply to both dimensions or to neither"); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: static void michael@0: SetBackgroundList(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aValue, michael@0: nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, michael@0: const nsAutoTArray &aParentLayers, michael@0: ComputedValueItem nsStyleBackground::Layer::* aResultLocation, michael@0: ComputedValueItem aInitialValue, michael@0: uint32_t aParentItemCount, michael@0: uint32_t& aItemCount, michael@0: uint32_t& aMaxItemCount, michael@0: bool& aRebuild, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aRebuild = true; michael@0: aCanStoreInRuleTree = false; michael@0: aLayers.EnsureLengthAtLeast(aParentItemCount); michael@0: aItemCount = aParentItemCount; michael@0: for (uint32_t i = 0; i < aParentItemCount; ++i) { michael@0: aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: aRebuild = true; michael@0: aItemCount = 1; michael@0: aLayers[0].*aResultLocation = aInitialValue; michael@0: break; michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: aRebuild = true; michael@0: aItemCount = 0; michael@0: const nsCSSValueList* item = aValue.GetListValue(); michael@0: do { michael@0: NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null && michael@0: item->mValue.GetUnit() != eCSSUnit_Inherit && michael@0: item->mValue.GetUnit() != eCSSUnit_Initial && michael@0: item->mValue.GetUnit() != eCSSUnit_Unset, michael@0: "unexpected unit"); michael@0: ++aItemCount; michael@0: aLayers.EnsureLengthAtLeast(aItemCount); michael@0: BackgroundItemComputer michael@0: ::ComputeValue(aStyleContext, item, michael@0: aLayers[aItemCount-1].*aResultLocation, michael@0: aCanStoreInRuleTree); michael@0: item = item->mNext; michael@0: } while (item); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: nsPrintfCString("unexpected unit %d", michael@0: aValue.GetUnit()).get()); michael@0: } michael@0: michael@0: if (aItemCount > aMaxItemCount) michael@0: aMaxItemCount = aItemCount; michael@0: } michael@0: michael@0: template michael@0: static void michael@0: SetBackgroundPairList(nsStyleContext* aStyleContext, michael@0: const nsCSSValue& aValue, michael@0: nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, michael@0: const nsAutoTArray michael@0: &aParentLayers, michael@0: ComputedValueItem nsStyleBackground::Layer::* michael@0: aResultLocation, michael@0: ComputedValueItem aInitialValue, michael@0: uint32_t aParentItemCount, michael@0: uint32_t& aItemCount, michael@0: uint32_t& aMaxItemCount, michael@0: bool& aRebuild, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aRebuild = true; michael@0: aCanStoreInRuleTree = false; michael@0: aLayers.EnsureLengthAtLeast(aParentItemCount); michael@0: aItemCount = aParentItemCount; michael@0: for (uint32_t i = 0; i < aParentItemCount; ++i) { michael@0: aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: aRebuild = true; michael@0: aItemCount = 1; michael@0: aLayers[0].*aResultLocation = aInitialValue; michael@0: break; michael@0: michael@0: case eCSSUnit_PairList: michael@0: case eCSSUnit_PairListDep: { michael@0: aRebuild = true; michael@0: aItemCount = 0; michael@0: const nsCSSValuePairList* item = aValue.GetPairListValue(); michael@0: do { michael@0: NS_ASSERTION(item->mXValue.GetUnit() != eCSSUnit_Inherit && michael@0: item->mXValue.GetUnit() != eCSSUnit_Initial && michael@0: item->mXValue.GetUnit() != eCSSUnit_Unset && michael@0: item->mYValue.GetUnit() != eCSSUnit_Inherit && michael@0: item->mYValue.GetUnit() != eCSSUnit_Initial && michael@0: item->mYValue.GetUnit() != eCSSUnit_Unset, michael@0: "unexpected unit"); michael@0: ++aItemCount; michael@0: aLayers.EnsureLengthAtLeast(aItemCount); michael@0: BackgroundItemComputer michael@0: ::ComputeValue(aStyleContext, item, michael@0: aLayers[aItemCount-1].*aResultLocation, michael@0: aCanStoreInRuleTree); michael@0: item = item->mNext; michael@0: } while (item); michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: nsPrintfCString("unexpected unit %d", michael@0: aValue.GetUnit()).get()); michael@0: } michael@0: michael@0: if (aItemCount > aMaxItemCount) michael@0: aMaxItemCount = aItemCount; michael@0: } michael@0: michael@0: template michael@0: static void michael@0: FillBackgroundList(nsAutoTArray< nsStyleBackground::Layer, 1> &aLayers, michael@0: ComputedValueItem nsStyleBackground::Layer::* aResultLocation, michael@0: uint32_t aItemCount, uint32_t aFillCount) michael@0: { michael@0: NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length"); michael@0: for (uint32_t sourceLayer = 0, destLayer = aItemCount; michael@0: destLayer < aFillCount; michael@0: ++sourceLayer, ++destLayer) { michael@0: aLayers[destLayer].*aResultLocation = michael@0: aLayers[sourceLayer].*aResultLocation; michael@0: } michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeBackgroundData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Background, (), bg, parentBG) michael@0: michael@0: // background-color: color, string, inherit michael@0: const nsCSSValue* backColorValue = aRuleData->ValueForBackgroundColor(); michael@0: if (eCSSUnit_Initial == backColorValue->GetUnit() || michael@0: eCSSUnit_Unset == backColorValue->GetUnit()) { michael@0: bg->mBackgroundColor = NS_RGBA(0, 0, 0, 0); michael@0: } else if (!SetColor(*backColorValue, parentBG->mBackgroundColor, michael@0: mPresContext, aContext, bg->mBackgroundColor, michael@0: canStoreInRuleTree)) { michael@0: NS_ASSERTION(eCSSUnit_Null == backColorValue->GetUnit(), michael@0: "unexpected color unit"); michael@0: } michael@0: michael@0: uint32_t maxItemCount = 1; michael@0: bool rebuild = false; michael@0: michael@0: // background-image: url (stored as image), none, inherit [list] michael@0: nsStyleImage initialImage; michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundImage(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mImage, michael@0: initialImage, parentBG->mImageCount, bg->mImageCount, michael@0: maxItemCount, rebuild, canStoreInRuleTree); michael@0: michael@0: // background-repeat: enum, inherit, initial [pair list] michael@0: nsStyleBackground::Repeat initialRepeat; michael@0: initialRepeat.SetInitialValues(); michael@0: SetBackgroundPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mRepeat, michael@0: initialRepeat, parentBG->mRepeatCount, michael@0: bg->mRepeatCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: // background-attachment: enum, inherit, initial [list] michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundAttachment(), michael@0: bg->mLayers, parentBG->mLayers, michael@0: &nsStyleBackground::Layer::mAttachment, michael@0: uint8_t(NS_STYLE_BG_ATTACHMENT_SCROLL), michael@0: parentBG->mAttachmentCount, michael@0: bg->mAttachmentCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: // background-clip: enum, inherit, initial [list] michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundClip(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mClip, michael@0: uint8_t(NS_STYLE_BG_CLIP_BORDER), parentBG->mClipCount, michael@0: bg->mClipCount, maxItemCount, rebuild, canStoreInRuleTree); michael@0: michael@0: // background-inline-policy: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBackgroundInlinePolicy(), michael@0: bg->mBackgroundInlinePolicy, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentBG->mBackgroundInlinePolicy, michael@0: NS_STYLE_BG_INLINE_POLICY_CONTINUOUS, 0, 0, 0, 0); michael@0: michael@0: // background-blend-mode: enum, inherit, initial [list] michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundBlendMode(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mBlendMode, michael@0: uint8_t(NS_STYLE_BLEND_NORMAL), parentBG->mBlendModeCount, michael@0: bg->mBlendModeCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: // background-origin: enum, inherit, initial [list] michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundOrigin(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mOrigin, michael@0: uint8_t(NS_STYLE_BG_ORIGIN_PADDING), parentBG->mOriginCount, michael@0: bg->mOriginCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: // background-position: enum, length, percent (flags), inherit [pair list] michael@0: nsStyleBackground::Position initialPosition; michael@0: initialPosition.SetInitialValues(); michael@0: SetBackgroundList(aContext, *aRuleData->ValueForBackgroundPosition(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mPosition, michael@0: initialPosition, parentBG->mPositionCount, michael@0: bg->mPositionCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: // background-size: enum, length, auto, inherit, initial [pair list] michael@0: nsStyleBackground::Size initialSize; michael@0: initialSize.SetInitialValues(); michael@0: SetBackgroundPairList(aContext, *aRuleData->ValueForBackgroundSize(), michael@0: bg->mLayers, michael@0: parentBG->mLayers, &nsStyleBackground::Layer::mSize, michael@0: initialSize, parentBG->mSizeCount, michael@0: bg->mSizeCount, maxItemCount, rebuild, michael@0: canStoreInRuleTree); michael@0: michael@0: if (rebuild) { michael@0: // Delete any extra items. We need to keep layers in which any michael@0: // property was specified. michael@0: bg->mLayers.TruncateLength(maxItemCount); michael@0: michael@0: uint32_t fillCount = bg->mImageCount; michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mImage, michael@0: bg->mImageCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mRepeat, michael@0: bg->mRepeatCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mAttachment, michael@0: bg->mAttachmentCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mClip, michael@0: bg->mClipCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mBlendMode, michael@0: bg->mBlendModeCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mOrigin, michael@0: bg->mOriginCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mPosition, michael@0: bg->mPositionCount, fillCount); michael@0: FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mSize, michael@0: bg->mSizeCount, fillCount); michael@0: } michael@0: michael@0: // Now that the dust has settled, register the images with the document michael@0: for (uint32_t i = 0; i < bg->mImageCount; ++i) michael@0: bg->mLayers[i].TrackImages(aContext->PresContext()); michael@0: michael@0: COMPUTE_END_RESET(Background, bg) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeMarginData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Margin, (), margin, parentMargin) michael@0: michael@0: // margin: length, percent, auto, inherit michael@0: nsStyleCoord coord; michael@0: nsCSSRect ourMargin; michael@0: ourMargin.mTop = *aRuleData->ValueForMarginTop(); michael@0: ourMargin.mRight = *aRuleData->ValueForMarginRightValue(); michael@0: ourMargin.mBottom = *aRuleData->ValueForMarginBottom(); michael@0: ourMargin.mLeft = *aRuleData->ValueForMarginLeftValue(); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForMarginLeftLTRSource(), michael@0: *aRuleData->ValueForMarginLeftRTLSource(), michael@0: *aRuleData->ValueForMarginStartValue(), michael@0: *aRuleData->ValueForMarginEndValue(), michael@0: NS_SIDE_LEFT, ourMargin, canStoreInRuleTree); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForMarginRightLTRSource(), michael@0: *aRuleData->ValueForMarginRightRTLSource(), michael@0: *aRuleData->ValueForMarginEndValue(), michael@0: *aRuleData->ValueForMarginStartValue(), michael@0: NS_SIDE_RIGHT, ourMargin, canStoreInRuleTree); michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nsStyleCoord parentCoord = parentMargin->mMargin.Get(side); michael@0: if (SetCoord(ourMargin.*(nsCSSRect::sides[side]), michael@0: coord, parentCoord, michael@0: SETCOORD_LPAH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: margin->mMargin.Set(side, coord); michael@0: } michael@0: } michael@0: michael@0: margin->RecalcData(); michael@0: COMPUTE_END_RESET(Margin, margin) michael@0: } michael@0: michael@0: static void michael@0: SetBorderImageRect(const nsCSSValue& aValue, michael@0: /** outparam */ nsCSSRect& aRect) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: aRect.Reset(); michael@0: break; michael@0: case eCSSUnit_Rect: michael@0: aRect = aValue.GetRectValue(); michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: aRect.SetAllSidesTo(aValue); michael@0: break; michael@0: default: michael@0: NS_ASSERTION(false, "Unexpected border image value for rect."); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetBorderImagePair(const nsCSSValue& aValue, michael@0: /** outparam */ nsCSSValuePair& aPair) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: aPair.Reset(); michael@0: break; michael@0: case eCSSUnit_Pair: michael@0: aPair = aValue.GetPairValue(); michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: aPair.SetBothValuesTo(aValue); michael@0: break; michael@0: default: michael@0: NS_ASSERTION(false, "Unexpected border image value for pair."); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetBorderImageSlice(const nsCSSValue& aValue, michael@0: /** outparam */ nsCSSValue& aSlice, michael@0: /** outparam */ nsCSSValue& aFill) michael@0: { michael@0: const nsCSSValueList* valueList; michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: aSlice.Reset(); michael@0: aFill.Reset(); michael@0: break; michael@0: case eCSSUnit_List: michael@0: // Get slice dimensions. michael@0: valueList = aValue.GetListValue(); michael@0: aSlice = valueList->mValue; michael@0: michael@0: // Get "fill" keyword. michael@0: valueList = valueList->mNext; michael@0: if (valueList) { michael@0: aFill = valueList->mValue; michael@0: } else { michael@0: aFill.SetInitialValue(); michael@0: } michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: aSlice = aValue; michael@0: aFill = aValue; michael@0: break; michael@0: default: michael@0: NS_ASSERTION(false, "Unexpected border image value for pair."); michael@0: } michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeBorderData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Border, (mPresContext), border, parentBorder) michael@0: michael@0: // box-shadow: none, list, inherit, initial michael@0: const nsCSSValue* boxShadowValue = aRuleData->ValueForBoxShadow(); michael@0: switch (boxShadowValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: border->mBoxShadow = nullptr; michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: border->mBoxShadow = parentBorder->mBoxShadow; michael@0: canStoreInRuleTree = false; michael@0: break; michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: michael@0: border->mBoxShadow = GetShadowData(boxShadowValue->GetListValue(), michael@0: aContext, true, canStoreInRuleTree); michael@0: break; michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: nsPrintfCString("unrecognized shadow unit %d", michael@0: boxShadowValue->GetUnit()).get()); michael@0: } michael@0: michael@0: // border-width, border-*-width: length, enum, inherit michael@0: nsStyleCoord coord; michael@0: nsCSSRect ourBorderWidth; michael@0: ourBorderWidth.mTop = *aRuleData->ValueForBorderTopWidth(); michael@0: ourBorderWidth.mRight = *aRuleData->ValueForBorderRightWidthValue(); michael@0: ourBorderWidth.mBottom = *aRuleData->ValueForBorderBottomWidth(); michael@0: ourBorderWidth.mLeft = *aRuleData->ValueForBorderLeftWidthValue(); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderLeftWidthLTRSource(), michael@0: *aRuleData->ValueForBorderLeftWidthRTLSource(), michael@0: *aRuleData->ValueForBorderStartWidthValue(), michael@0: *aRuleData->ValueForBorderEndWidthValue(), michael@0: NS_SIDE_LEFT, ourBorderWidth, canStoreInRuleTree); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderRightWidthLTRSource(), michael@0: *aRuleData->ValueForBorderRightWidthRTLSource(), michael@0: *aRuleData->ValueForBorderEndWidthValue(), michael@0: *aRuleData->ValueForBorderStartWidthValue(), michael@0: NS_SIDE_RIGHT, ourBorderWidth, canStoreInRuleTree); michael@0: { // scope for compilers with broken |for| loop scoping michael@0: NS_FOR_CSS_SIDES(side) { michael@0: const nsCSSValue &value = ourBorderWidth.*(nsCSSRect::sides[side]); michael@0: NS_ASSERTION(eCSSUnit_Percent != value.GetUnit(), michael@0: "Percentage borders not implemented yet " michael@0: "If implementing, make sure to fix all consumers of " michael@0: "nsStyleBorder, the IsPercentageAwareChild method, " michael@0: "the nsAbsoluteContainingBlock::FrameDependsOnContainer " michael@0: "method, the " michael@0: "nsLineLayout::IsPercentageAwareReplacedElement method " michael@0: "and probably some other places"); michael@0: if (eCSSUnit_Enumerated == value.GetUnit()) { michael@0: NS_ASSERTION(value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || michael@0: value.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || michael@0: value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, michael@0: "Unexpected enum value"); michael@0: border->SetBorderWidth(side, michael@0: (mPresContext->GetBorderWidthTable())[value.GetIntValue()]); michael@0: } michael@0: // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT michael@0: else if (SetCoord(value, coord, nsStyleCoord(), michael@0: SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); michael@0: // clamp negative calc() to 0. michael@0: border->SetBorderWidth(side, std::max(coord.GetCoordValue(), 0)); michael@0: } michael@0: else if (eCSSUnit_Inherit == value.GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: border->SetBorderWidth(side, michael@0: parentBorder->GetComputedBorder().Side(side)); michael@0: } michael@0: else if (eCSSUnit_Initial == value.GetUnit() || michael@0: eCSSUnit_Unset == value.GetUnit()) { michael@0: border->SetBorderWidth(side, michael@0: (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); michael@0: } michael@0: else { michael@0: NS_ASSERTION(eCSSUnit_Null == value.GetUnit(), michael@0: "missing case handling border width"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // border-style, border-*-style: enum, inherit michael@0: nsCSSRect ourBorderStyle; michael@0: ourBorderStyle.mTop = *aRuleData->ValueForBorderTopStyle(); michael@0: ourBorderStyle.mRight = *aRuleData->ValueForBorderRightStyleValue(); michael@0: ourBorderStyle.mBottom = *aRuleData->ValueForBorderBottomStyle(); michael@0: ourBorderStyle.mLeft = *aRuleData->ValueForBorderLeftStyleValue(); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderLeftStyleLTRSource(), michael@0: *aRuleData->ValueForBorderLeftStyleRTLSource(), michael@0: *aRuleData->ValueForBorderStartStyleValue(), michael@0: *aRuleData->ValueForBorderEndStyleValue(), michael@0: NS_SIDE_LEFT, ourBorderStyle, canStoreInRuleTree); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderRightStyleLTRSource(), michael@0: *aRuleData->ValueForBorderRightStyleRTLSource(), michael@0: *aRuleData->ValueForBorderEndStyleValue(), michael@0: *aRuleData->ValueForBorderStartStyleValue(), michael@0: NS_SIDE_RIGHT, ourBorderStyle, canStoreInRuleTree); michael@0: { // scope for compilers with broken |for| loop scoping michael@0: NS_FOR_CSS_SIDES(side) { michael@0: const nsCSSValue &value = ourBorderStyle.*(nsCSSRect::sides[side]); michael@0: nsCSSUnit unit = value.GetUnit(); michael@0: NS_ABORT_IF_FALSE(eCSSUnit_None != unit, michael@0: "'none' should be handled as enumerated value"); michael@0: if (eCSSUnit_Enumerated == unit) { michael@0: border->SetBorderStyle(side, value.GetIntValue()); michael@0: } michael@0: else if (eCSSUnit_Initial == unit || michael@0: eCSSUnit_Unset == unit) { michael@0: border->SetBorderStyle(side, NS_STYLE_BORDER_STYLE_NONE); michael@0: } michael@0: else if (eCSSUnit_Inherit == unit) { michael@0: canStoreInRuleTree = false; michael@0: border->SetBorderStyle(side, parentBorder->GetBorderStyle(side)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // -moz-border-*-colors: color, string, enum, none, inherit/initial michael@0: nscolor borderColor; michael@0: nscolor unused = NS_RGB(0,0,0); michael@0: michael@0: static const nsCSSProperty borderColorsProps[] = { michael@0: eCSSProperty_border_top_colors, michael@0: eCSSProperty_border_right_colors, michael@0: eCSSProperty_border_bottom_colors, michael@0: eCSSProperty_border_left_colors michael@0: }; michael@0: michael@0: NS_FOR_CSS_SIDES(side) { michael@0: const nsCSSValue& value = *aRuleData->ValueFor(borderColorsProps[side]); michael@0: switch (value.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: border->ClearBorderColors(side); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: { michael@0: canStoreInRuleTree = false; michael@0: border->ClearBorderColors(side); michael@0: if (parentContext) { michael@0: nsBorderColors *parentColors; michael@0: parentBorder->GetCompositeColors(side, &parentColors); michael@0: if (parentColors) { michael@0: border->EnsureBorderColors(); michael@0: border->mBorderColors[side] = parentColors->Clone(); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: // Some composite border color information has been specified for this michael@0: // border side. michael@0: border->EnsureBorderColors(); michael@0: border->ClearBorderColors(side); michael@0: const nsCSSValueList* list = value.GetListValue(); michael@0: while (list) { michael@0: if (SetColor(list->mValue, unused, mPresContext, michael@0: aContext, borderColor, canStoreInRuleTree)) michael@0: border->AppendBorderColor(side, borderColor); michael@0: else { michael@0: NS_NOTREACHED("unexpected item in -moz-border-*-colors list"); michael@0: } michael@0: list = list->mNext; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unrecognized border color unit"); michael@0: } michael@0: } michael@0: michael@0: // border-color, border-*-color: color, string, enum, inherit michael@0: bool foreground; michael@0: nsCSSRect ourBorderColor; michael@0: ourBorderColor.mTop = *aRuleData->ValueForBorderTopColor(); michael@0: ourBorderColor.mRight = *aRuleData->ValueForBorderRightColorValue(); michael@0: ourBorderColor.mBottom = *aRuleData->ValueForBorderBottomColor(); michael@0: ourBorderColor.mLeft = *aRuleData->ValueForBorderLeftColorValue(); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderLeftColorLTRSource(), michael@0: *aRuleData->ValueForBorderLeftColorRTLSource(), michael@0: *aRuleData->ValueForBorderStartColorValue(), michael@0: *aRuleData->ValueForBorderEndColorValue(), michael@0: NS_SIDE_LEFT, ourBorderColor, canStoreInRuleTree); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForBorderRightColorLTRSource(), michael@0: *aRuleData->ValueForBorderRightColorRTLSource(), michael@0: *aRuleData->ValueForBorderEndColorValue(), michael@0: *aRuleData->ValueForBorderStartColorValue(), michael@0: NS_SIDE_RIGHT, ourBorderColor, canStoreInRuleTree); michael@0: { // scope for compilers with broken |for| loop scoping michael@0: NS_FOR_CSS_SIDES(side) { michael@0: const nsCSSValue &value = ourBorderColor.*(nsCSSRect::sides[side]); michael@0: if (eCSSUnit_Inherit == value.GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: if (parentContext) { michael@0: parentBorder->GetBorderColor(side, borderColor, foreground); michael@0: if (foreground) { michael@0: // We want to inherit the color from the parent, not use the michael@0: // color on the element where this chunk of style data will be michael@0: // used. We can ensure that the data for the parent are fully michael@0: // computed (unlike for the element where this will be used, for michael@0: // which the color could be specified on a more specific rule). michael@0: border->SetBorderColor(side, parentContext->StyleColor()->mColor); michael@0: } else michael@0: border->SetBorderColor(side, borderColor); michael@0: } else { michael@0: // We're the root michael@0: border->SetBorderToForeground(side); michael@0: } michael@0: } michael@0: else if (SetColor(value, unused, mPresContext, aContext, borderColor, michael@0: canStoreInRuleTree)) { michael@0: border->SetBorderColor(side, borderColor); michael@0: } michael@0: else if (eCSSUnit_Enumerated == value.GetUnit()) { michael@0: switch (value.GetIntValue()) { michael@0: case NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR: michael@0: border->SetBorderToForeground(side); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unexpected enumerated color"); michael@0: break; michael@0: } michael@0: } michael@0: else if (eCSSUnit_Initial == value.GetUnit() || michael@0: eCSSUnit_Unset == value.GetUnit()) { michael@0: border->SetBorderToForeground(side); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // border-radius: length, percent, inherit michael@0: { michael@0: const nsCSSProperty* subprops = michael@0: nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_radius); michael@0: NS_FOR_CSS_FULL_CORNERS(corner) { michael@0: int cx = NS_FULL_TO_HALF_CORNER(corner, false); michael@0: int cy = NS_FULL_TO_HALF_CORNER(corner, true); michael@0: const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); michael@0: nsStyleCoord parentX = parentBorder->mBorderRadius.Get(cx); michael@0: nsStyleCoord parentY = parentBorder->mBorderRadius.Get(cy); michael@0: nsStyleCoord coordX, coordY; michael@0: michael@0: if (SetPairCoords(radius, coordX, coordY, parentX, parentY, michael@0: SETCOORD_LPH | SETCOORD_INITIAL_ZERO | michael@0: SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: border->mBorderRadius.Set(cx, coordX); michael@0: border->mBorderRadius.Set(cy, coordY); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // float-edge: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForFloatEdge(), michael@0: border->mFloatEdge, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentBorder->mFloatEdge, michael@0: NS_STYLE_FLOAT_EDGE_CONTENT, 0, 0, 0, 0); michael@0: michael@0: // border-image-source michael@0: const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource(); michael@0: if (borderImageSource->GetUnit() == eCSSUnit_Inherit) { michael@0: canStoreInRuleTree = false; michael@0: border->mBorderImageSource = parentBorder->mBorderImageSource; michael@0: } else { michael@0: SetStyleImage(aContext, michael@0: *borderImageSource, michael@0: border->mBorderImageSource, michael@0: canStoreInRuleTree); michael@0: } michael@0: michael@0: nsCSSValue borderImageSliceValue; michael@0: nsCSSValue borderImageSliceFill; michael@0: SetBorderImageSlice(*aRuleData->ValueForBorderImageSlice(), michael@0: borderImageSliceValue, borderImageSliceFill); michael@0: michael@0: // border-image-slice: fill michael@0: SetDiscrete(borderImageSliceFill, michael@0: border->mBorderImageFill, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentBorder->mBorderImageFill, michael@0: NS_STYLE_BORDER_IMAGE_SLICE_NOFILL, 0, 0, 0, 0); michael@0: michael@0: nsCSSRect borderImageSlice; michael@0: SetBorderImageRect(borderImageSliceValue, borderImageSlice); michael@0: michael@0: nsCSSRect borderImageWidth; michael@0: SetBorderImageRect(*aRuleData->ValueForBorderImageWidth(), michael@0: borderImageWidth); michael@0: michael@0: nsCSSRect borderImageOutset; michael@0: SetBorderImageRect(*aRuleData->ValueForBorderImageOutset(), michael@0: borderImageOutset); michael@0: michael@0: NS_FOR_CSS_SIDES (side) { michael@0: // border-image-slice michael@0: if (SetCoord(borderImageSlice.*(nsCSSRect::sides[side]), coord, michael@0: parentBorder->mBorderImageSlice.Get(side), michael@0: SETCOORD_FACTOR | SETCOORD_PERCENT | michael@0: SETCOORD_INHERIT | SETCOORD_INITIAL_HUNDRED_PCT | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: border->mBorderImageSlice.Set(side, coord); michael@0: } michael@0: michael@0: // border-image-width michael@0: // 'auto' here means "same as slice" michael@0: if (SetCoord(borderImageWidth.*(nsCSSRect::sides[side]), coord, michael@0: parentBorder->mBorderImageWidth.Get(side), michael@0: SETCOORD_LPAH | SETCOORD_FACTOR | SETCOORD_INITIAL_FACTOR_ONE | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: border->mBorderImageWidth.Set(side, coord); michael@0: } michael@0: michael@0: // border-image-outset michael@0: if (SetCoord(borderImageOutset.*(nsCSSRect::sides[side]), coord, michael@0: parentBorder->mBorderImageOutset.Get(side), michael@0: SETCOORD_LENGTH | SETCOORD_FACTOR | michael@0: SETCOORD_INHERIT | SETCOORD_INITIAL_FACTOR_ZERO | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: border->mBorderImageOutset.Set(side, coord); michael@0: } michael@0: } michael@0: michael@0: // border-image-repeat michael@0: nsCSSValuePair borderImageRepeat; michael@0: SetBorderImagePair(*aRuleData->ValueForBorderImageRepeat(), michael@0: borderImageRepeat); michael@0: michael@0: SetDiscrete(borderImageRepeat.mXValue, michael@0: border->mBorderImageRepeatH, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentBorder->mBorderImageRepeatH, michael@0: NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); michael@0: michael@0: SetDiscrete(borderImageRepeat.mYValue, michael@0: border->mBorderImageRepeatV, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentBorder->mBorderImageRepeatV, michael@0: NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, 0, 0, 0, 0); michael@0: michael@0: border->TrackImage(aContext->PresContext()); michael@0: michael@0: COMPUTE_END_RESET(Border, border) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputePaddingData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Padding, (), padding, parentPadding) michael@0: michael@0: // padding: length, percent, inherit michael@0: nsStyleCoord coord; michael@0: nsCSSRect ourPadding; michael@0: ourPadding.mTop = *aRuleData->ValueForPaddingTop(); michael@0: ourPadding.mRight = *aRuleData->ValueForPaddingRightValue(); michael@0: ourPadding.mBottom = *aRuleData->ValueForPaddingBottom(); michael@0: ourPadding.mLeft = *aRuleData->ValueForPaddingLeftValue(); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForPaddingLeftLTRSource(), michael@0: *aRuleData->ValueForPaddingLeftRTLSource(), michael@0: *aRuleData->ValueForPaddingStartValue(), michael@0: *aRuleData->ValueForPaddingEndValue(), michael@0: NS_SIDE_LEFT, ourPadding, canStoreInRuleTree); michael@0: AdjustLogicalBoxProp(aContext, michael@0: *aRuleData->ValueForPaddingRightLTRSource(), michael@0: *aRuleData->ValueForPaddingRightRTLSource(), michael@0: *aRuleData->ValueForPaddingEndValue(), michael@0: *aRuleData->ValueForPaddingStartValue(), michael@0: NS_SIDE_RIGHT, ourPadding, canStoreInRuleTree); michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nsStyleCoord parentCoord = parentPadding->mPadding.Get(side); michael@0: if (SetCoord(ourPadding.*(nsCSSRect::sides[side]), michael@0: coord, parentCoord, michael@0: SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: padding->mPadding.Set(side, coord); michael@0: } michael@0: } michael@0: michael@0: padding->RecalcData(); michael@0: COMPUTE_END_RESET(Padding, padding) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeOutlineData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Outline, (mPresContext), outline, parentOutline) michael@0: michael@0: // outline-width: length, enum, inherit michael@0: const nsCSSValue* outlineWidthValue = aRuleData->ValueForOutlineWidth(); michael@0: if (eCSSUnit_Initial == outlineWidthValue->GetUnit() || michael@0: eCSSUnit_Unset == outlineWidthValue->GetUnit()) { michael@0: outline->mOutlineWidth = michael@0: nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated); michael@0: } michael@0: else { michael@0: SetCoord(*outlineWidthValue, outline->mOutlineWidth, michael@0: parentOutline->mOutlineWidth, michael@0: SETCOORD_LEH | SETCOORD_CALC_LENGTH_ONLY, aContext, michael@0: mPresContext, canStoreInRuleTree); michael@0: } michael@0: michael@0: // outline-offset: length, inherit michael@0: nsStyleCoord tempCoord; michael@0: const nsCSSValue* outlineOffsetValue = aRuleData->ValueForOutlineOffset(); michael@0: if (SetCoord(*outlineOffsetValue, tempCoord, michael@0: nsStyleCoord(parentOutline->mOutlineOffset, michael@0: nsStyleCoord::CoordConstructor), michael@0: SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: outline->mOutlineOffset = tempCoord.GetCoordValue(); michael@0: } else { michael@0: NS_ASSERTION(outlineOffsetValue->GetUnit() == eCSSUnit_Null, michael@0: "unexpected unit"); michael@0: } michael@0: michael@0: // outline-color: color, string, enum, inherit michael@0: nscolor outlineColor; michael@0: nscolor unused = NS_RGB(0,0,0); michael@0: const nsCSSValue* outlineColorValue = aRuleData->ValueForOutlineColor(); michael@0: if (eCSSUnit_Inherit == outlineColorValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: if (parentContext) { michael@0: if (parentOutline->GetOutlineColor(outlineColor)) michael@0: outline->SetOutlineColor(outlineColor); michael@0: else { michael@0: // We want to inherit the color from the parent, not use the michael@0: // color on the element where this chunk of style data will be michael@0: // used. We can ensure that the data for the parent are fully michael@0: // computed (unlike for the element where this will be used, for michael@0: // which the color could be specified on a more specific rule). michael@0: outline->SetOutlineColor(parentContext->StyleColor()->mColor); michael@0: } michael@0: } else { michael@0: outline->SetOutlineInitialColor(); michael@0: } michael@0: } michael@0: else if (SetColor(*outlineColorValue, unused, mPresContext, michael@0: aContext, outlineColor, canStoreInRuleTree)) michael@0: outline->SetOutlineColor(outlineColor); michael@0: else if (eCSSUnit_Enumerated == outlineColorValue->GetUnit() || michael@0: eCSSUnit_Initial == outlineColorValue->GetUnit() || michael@0: eCSSUnit_Unset == outlineColorValue->GetUnit()) { michael@0: outline->SetOutlineInitialColor(); michael@0: } michael@0: michael@0: // -moz-outline-radius: length, percent, inherit michael@0: { michael@0: const nsCSSProperty* subprops = michael@0: nsCSSProps::SubpropertyEntryFor(eCSSProperty__moz_outline_radius); michael@0: NS_FOR_CSS_FULL_CORNERS(corner) { michael@0: int cx = NS_FULL_TO_HALF_CORNER(corner, false); michael@0: int cy = NS_FULL_TO_HALF_CORNER(corner, true); michael@0: const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); michael@0: nsStyleCoord parentX = parentOutline->mOutlineRadius.Get(cx); michael@0: nsStyleCoord parentY = parentOutline->mOutlineRadius.Get(cy); michael@0: nsStyleCoord coordX, coordY; michael@0: michael@0: if (SetPairCoords(radius, coordX, coordY, parentX, parentY, michael@0: SETCOORD_LPH | SETCOORD_INITIAL_ZERO | michael@0: SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: outline->mOutlineRadius.Set(cx, coordX); michael@0: outline->mOutlineRadius.Set(cy, coordY); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // outline-style: enum, inherit, initial michael@0: // cannot use SetDiscrete because of SetOutlineStyle michael@0: const nsCSSValue* outlineStyleValue = aRuleData->ValueForOutlineStyle(); michael@0: nsCSSUnit unit = outlineStyleValue->GetUnit(); michael@0: NS_ABORT_IF_FALSE(eCSSUnit_None != unit && eCSSUnit_Auto != unit, michael@0: "'none' and 'auto' should be handled as enumerated values"); michael@0: if (eCSSUnit_Enumerated == unit) { michael@0: outline->SetOutlineStyle(outlineStyleValue->GetIntValue()); michael@0: } else if (eCSSUnit_Initial == unit || michael@0: eCSSUnit_Unset == unit) { michael@0: outline->SetOutlineStyle(NS_STYLE_BORDER_STYLE_NONE); michael@0: } else if (eCSSUnit_Inherit == unit) { michael@0: canStoreInRuleTree = false; michael@0: outline->SetOutlineStyle(parentOutline->GetOutlineStyle()); michael@0: } michael@0: michael@0: outline->RecalcData(mPresContext); michael@0: COMPUTE_END_RESET(Outline, outline) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeListData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(List, (), list, parentList) michael@0: michael@0: // list-style-type: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForListStyleType(), michael@0: list->mListStyleType, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentList->mListStyleType, michael@0: NS_STYLE_LIST_STYLE_DISC, 0, 0, 0, 0); michael@0: michael@0: // list-style-image: url, none, inherit michael@0: const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage(); michael@0: if (eCSSUnit_Image == imageValue->GetUnit()) { michael@0: NS_SET_IMAGE_REQUEST_WITH_DOC(list->SetListStyleImage, michael@0: aContext, michael@0: imageValue->GetImageValue) michael@0: } michael@0: else if (eCSSUnit_None == imageValue->GetUnit() || michael@0: eCSSUnit_Initial == imageValue->GetUnit()) { michael@0: list->SetListStyleImage(nullptr); michael@0: } michael@0: else if (eCSSUnit_Inherit == imageValue->GetUnit() || michael@0: eCSSUnit_Unset == imageValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: NS_SET_IMAGE_REQUEST(list->SetListStyleImage, michael@0: aContext, michael@0: parentList->GetListStyleImage()) michael@0: } michael@0: michael@0: // list-style-position: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForListStylePosition(), michael@0: list->mListStylePosition, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentList->mListStylePosition, michael@0: NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, 0, 0, 0, 0); michael@0: michael@0: // image region property: length, auto, inherit michael@0: const nsCSSValue* imageRegionValue = aRuleData->ValueForImageRegion(); michael@0: switch (imageRegionValue->GetUnit()) { michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: canStoreInRuleTree = false; michael@0: list->mImageRegion = parentList->mImageRegion; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Auto: michael@0: list->mImageRegion.SetRect(0,0,0,0); michael@0: break; michael@0: michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Rect: { michael@0: const nsCSSRect& rgnRect = imageRegionValue->GetRectValue(); michael@0: michael@0: if (rgnRect.mTop.GetUnit() == eCSSUnit_Auto) michael@0: list->mImageRegion.y = 0; michael@0: else if (rgnRect.mTop.IsLengthUnit()) michael@0: list->mImageRegion.y = michael@0: CalcLength(rgnRect.mTop, aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: if (rgnRect.mBottom.GetUnit() == eCSSUnit_Auto) michael@0: list->mImageRegion.height = 0; michael@0: else if (rgnRect.mBottom.IsLengthUnit()) michael@0: list->mImageRegion.height = michael@0: CalcLength(rgnRect.mBottom, aContext, mPresContext, michael@0: canStoreInRuleTree) - list->mImageRegion.y; michael@0: michael@0: if (rgnRect.mLeft.GetUnit() == eCSSUnit_Auto) michael@0: list->mImageRegion.x = 0; michael@0: else if (rgnRect.mLeft.IsLengthUnit()) michael@0: list->mImageRegion.x = michael@0: CalcLength(rgnRect.mLeft, aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: if (rgnRect.mRight.GetUnit() == eCSSUnit_Auto) michael@0: list->mImageRegion.width = 0; michael@0: else if (rgnRect.mRight.IsLengthUnit()) michael@0: list->mImageRegion.width = michael@0: CalcLength(rgnRect.mRight, aContext, mPresContext, michael@0: canStoreInRuleTree) - list->mImageRegion.x; michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unrecognized image-region unit"); michael@0: } michael@0: michael@0: COMPUTE_END_INHERITED(List, list) michael@0: } michael@0: michael@0: static void michael@0: SetGridTrackBreadth(const nsCSSValue& aValue, michael@0: nsStyleCoord& aResult, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: nsCSSUnit unit = aValue.GetUnit(); michael@0: if (unit == eCSSUnit_FlexFraction) { michael@0: aResult.SetFlexFractionValue(aValue.GetFloatValue()); michael@0: } else { michael@0: MOZ_ASSERT(unit != eCSSUnit_Inherit && unit != eCSSUnit_Unset, michael@0: "Unexpected value that would use dummyParentCoord"); michael@0: const nsStyleCoord dummyParentCoord; michael@0: SetCoord(aValue, aResult, dummyParentCoord, michael@0: SETCOORD_LPE | SETCOORD_STORE_CALC, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetGridTrackSize(const nsCSSValue& aValue, michael@0: nsStyleCoord& aResultMin, michael@0: nsStyleCoord& aResultMax, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: if (aValue.GetUnit() == eCSSUnit_Function) { michael@0: // A minmax() function. michael@0: nsCSSValue::Array* func = aValue.GetArrayValue(); michael@0: NS_ASSERTION(func->Item(0).GetKeywordValue() == eCSSKeyword_minmax, michael@0: "Expected minmax(), got another function name"); michael@0: SetGridTrackBreadth(func->Item(1), aResultMin, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: SetGridTrackBreadth(func->Item(2), aResultMax, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: } else if (aValue.GetUnit() == eCSSUnit_Auto) { michael@0: // 'auto' computes to 'minmax(min-content, max-content)' michael@0: aResultMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT, michael@0: eStyleUnit_Enumerated); michael@0: aResultMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT, michael@0: eStyleUnit_Enumerated); michael@0: } else { michael@0: // A single , michael@0: // specifies identical min and max sizing functions. michael@0: SetGridTrackBreadth(aValue, aResultMin, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: aResultMax = aResultMin; michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetGridAutoColumnsRows(const nsCSSValue& aValue, michael@0: nsStyleCoord& aResultMin, michael@0: nsStyleCoord& aResultMax, michael@0: const nsStyleCoord& aParentValueMin, michael@0: const nsStyleCoord& aParentValueMax, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: aResultMin = aParentValueMin; michael@0: aResultMax = aParentValueMax; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: // The initial value is 'auto', michael@0: // which computes to 'minmax(min-content, max-content)'. michael@0: // (Explicitly-specified 'auto' values are handled in SetGridTrackSize.) michael@0: aResultMin.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT, michael@0: eStyleUnit_Enumerated); michael@0: aResultMax.SetIntValue(NS_STYLE_GRID_TRACK_BREADTH_MAX_CONTENT, michael@0: eStyleUnit_Enumerated); michael@0: break; michael@0: michael@0: default: michael@0: SetGridTrackSize(aValue, aResultMin, aResultMax, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: AppendGridLineNames(const nsCSSValue& aValue, michael@0: nsStyleGridTemplate& aResult) michael@0: { michael@0: // Compute a value michael@0: nsTArray* nameList = aResult.mLineNameLists.AppendElement(); michael@0: // Null unit means empty list, nothing more to do. michael@0: if (aValue.GetUnit() != eCSSUnit_Null) { michael@0: const nsCSSValueList* item = aValue.GetListValue(); michael@0: do { michael@0: nsString* name = nameList->AppendElement(); michael@0: item->mValue.GetStringValue(*name); michael@0: item = item->mNext; michael@0: } while (item); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetGridTrackList(const nsCSSValue& aValue, michael@0: nsStyleGridTemplate& aResult, michael@0: const nsStyleGridTemplate& aParentValue, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: aResult.mIsSubgrid = aParentValue.mIsSubgrid; michael@0: aResult.mLineNameLists = aParentValue.mLineNameLists; michael@0: aResult.mMinTrackSizingFunctions = aParentValue.mMinTrackSizingFunctions; michael@0: aResult.mMaxTrackSizingFunctions = aParentValue.mMaxTrackSizingFunctions; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: aResult.mIsSubgrid = false; michael@0: aResult.mLineNameLists.Clear(); michael@0: aResult.mMinTrackSizingFunctions.Clear(); michael@0: aResult.mMaxTrackSizingFunctions.Clear(); michael@0: break; michael@0: michael@0: default: michael@0: aResult.mLineNameLists.Clear(); michael@0: aResult.mMinTrackSizingFunctions.Clear(); michael@0: aResult.mMaxTrackSizingFunctions.Clear(); michael@0: const nsCSSValueList* item = aValue.GetListValue(); michael@0: if (item->mValue.GetUnit() == eCSSUnit_Enumerated && michael@0: item->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) { michael@0: // subgrid ? michael@0: aResult.mIsSubgrid = true; michael@0: item = item->mNext; michael@0: while (item) { michael@0: AppendGridLineNames(item->mValue, aResult); michael@0: item = item->mNext; michael@0: } michael@0: } else { michael@0: // michael@0: // The list is expected to have odd number of items, at least 3 michael@0: // starting with a (sub list of identifiers), michael@0: // and alternating between that and . michael@0: aResult.mIsSubgrid = false; michael@0: for (;;) { michael@0: AppendGridLineNames(item->mValue, aResult); michael@0: item = item->mNext; michael@0: michael@0: if (!item) { michael@0: break; michael@0: } michael@0: michael@0: nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement(); michael@0: nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement(); michael@0: SetGridTrackSize(item->mValue, min, max, michael@0: aStyleContext, aPresContext, aCanStoreInRuleTree); michael@0: michael@0: item = item->mNext; michael@0: MOZ_ASSERT(item, "Expected a eCSSUnit_List of odd length"); michael@0: } michael@0: MOZ_ASSERT(!aResult.mMinTrackSizingFunctions.IsEmpty() && michael@0: aResult.mMinTrackSizingFunctions.Length() == michael@0: aResult.mMaxTrackSizingFunctions.Length() && michael@0: aResult.mMinTrackSizingFunctions.Length() + 1 == michael@0: aResult.mLineNameLists.Length(), michael@0: "Inconstistent array lengths for nsStyleGridTemplate"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetGridTemplateAreas(const nsCSSValue& aValue, michael@0: nsRefPtr* aResult, michael@0: css::GridTemplateAreasValue* aParentValue, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: *aResult = aParentValue; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_None: michael@0: *aResult = nullptr; michael@0: break; michael@0: michael@0: default: michael@0: *aResult = aValue.GetGridTemplateAreas(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetGridLine(const nsCSSValue& aValue, michael@0: nsStyleGridLine& aResult, michael@0: const nsStyleGridLine& aParentValue, michael@0: bool& aCanStoreInRuleTree) michael@0: michael@0: { michael@0: switch (aValue.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: aCanStoreInRuleTree = false; michael@0: aResult = aParentValue; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: case eCSSUnit_Auto: michael@0: aResult.SetAuto(); michael@0: break; michael@0: michael@0: default: michael@0: aResult.SetAuto(); // Reset any existing value. michael@0: const nsCSSValueList* item = aValue.GetListValue(); michael@0: do { michael@0: if (item->mValue.GetUnit() == eCSSUnit_Enumerated) { michael@0: aResult.mHasSpan = true; michael@0: } else if (item->mValue.GetUnit() == eCSSUnit_Integer) { michael@0: aResult.mInteger = item->mValue.GetIntValue(); michael@0: } else if (item->mValue.GetUnit() == eCSSUnit_Ident) { michael@0: item->mValue.GetStringValue(aResult.mLineName); michael@0: } else { michael@0: NS_ASSERTION(false, "Unexpected unit"); michael@0: } michael@0: item = item->mNext; michael@0: } while (item); michael@0: MOZ_ASSERT(!aResult.IsAuto(), michael@0: "should have set something away from default value"); michael@0: } michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputePositionData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Position, (), pos, parentPos) michael@0: michael@0: // box offsets: length, percent, calc, auto, inherit michael@0: static const nsCSSProperty offsetProps[] = { michael@0: eCSSProperty_top, michael@0: eCSSProperty_right, michael@0: eCSSProperty_bottom, michael@0: eCSSProperty_left michael@0: }; michael@0: nsStyleCoord coord; michael@0: NS_FOR_CSS_SIDES(side) { michael@0: nsStyleCoord parentCoord = parentPos->mOffset.Get(side); michael@0: if (SetCoord(*aRuleData->ValueFor(offsetProps[side]), michael@0: coord, parentCoord, michael@0: SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree)) { michael@0: pos->mOffset.Set(side, coord); michael@0: } michael@0: } michael@0: michael@0: SetCoord(*aRuleData->ValueForWidth(), pos->mWidth, parentPos->mWidth, michael@0: SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: SetCoord(*aRuleData->ValueForMinWidth(), pos->mMinWidth, parentPos->mMinWidth, michael@0: SETCOORD_LPEH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: SetCoord(*aRuleData->ValueForMaxWidth(), pos->mMaxWidth, parentPos->mMaxWidth, michael@0: SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: SetCoord(*aRuleData->ValueForHeight(), pos->mHeight, parentPos->mHeight, michael@0: SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: SetCoord(*aRuleData->ValueForMinHeight(), pos->mMinHeight, parentPos->mMinHeight, michael@0: SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: SetCoord(*aRuleData->ValueForMaxHeight(), pos->mMaxHeight, parentPos->mMaxHeight, michael@0: SETCOORD_LPOH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // box-sizing: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxSizing(), michael@0: pos->mBoxSizing, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mBoxSizing, michael@0: NS_STYLE_BOX_SIZING_CONTENT, 0, 0, 0, 0); michael@0: michael@0: // align-content: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForAlignContent(), michael@0: pos->mAlignContent, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mAlignContent, michael@0: NS_STYLE_ALIGN_CONTENT_STRETCH, 0, 0, 0, 0); michael@0: michael@0: // align-items: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForAlignItems(), michael@0: pos->mAlignItems, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mAlignItems, michael@0: NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE, 0, 0, 0, 0); michael@0: michael@0: // align-self: enum, inherit, initial michael@0: // NOTE: align-self's initial value is the special keyword "auto", which is michael@0: // supposed to compute to our parent's computed value of "align-items". So michael@0: // technically, "auto" itself is never a valid computed value for align-self, michael@0: // since it always computes to something else. Despite that, we do actually michael@0: // store "auto" in nsStylePosition::mAlignSelf, as NS_STYLE_ALIGN_SELF_AUTO michael@0: // (and then resolve it as-necessary). We do this because "auto" is the michael@0: // initial value for this property, so if we were to actually resolve it in michael@0: // nsStylePosition, we'd never be able to share any nsStylePosition structs michael@0: // in the rule tree, since their mAlignSelf values would depend on the parent michael@0: // style, by default. michael@0: if (aRuleData->ValueForAlignSelf()->GetUnit() == eCSSUnit_Inherit) { michael@0: // Special handling for "align-self: inherit", in case we're inheriting michael@0: // "align-self: auto", in which case we need to resolve the parent's "auto" michael@0: // and inherit that resolved value. michael@0: uint8_t inheritedAlignSelf = parentPos->mAlignSelf; michael@0: if (inheritedAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) { michael@0: if (!parentContext) { michael@0: // We're the root node. Nothing to inherit from --> just use default michael@0: // value. michael@0: inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE; michael@0: } else { michael@0: // Our parent's "auto" value should resolve to our grandparent's value michael@0: // for "align-items". So, that's what we're supposed to inherit. michael@0: nsStyleContext* grandparentContext = parentContext->GetParent(); michael@0: if (!grandparentContext) { michael@0: // No grandparent --> our parent is the root node, so its michael@0: // "align-self: auto" computes to the default "align-items" value: michael@0: inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE; michael@0: } else { michael@0: // Normal case -- we have a grandparent. michael@0: // Its "align-items" value is what we should end up inheriting. michael@0: const nsStylePosition* grandparentPos = michael@0: grandparentContext->StylePosition(); michael@0: inheritedAlignSelf = grandparentPos->mAlignItems; michael@0: } michael@0: } michael@0: } michael@0: michael@0: pos->mAlignSelf = inheritedAlignSelf; michael@0: canStoreInRuleTree = false; michael@0: } else { michael@0: SetDiscrete(*aRuleData->ValueForAlignSelf(), michael@0: pos->mAlignSelf, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mAlignSelf, // (unused -- we handled inherit above) michael@0: NS_STYLE_ALIGN_SELF_AUTO, // initial == auto michael@0: 0, 0, 0, 0); michael@0: } michael@0: michael@0: // flex-basis: auto, length, percent, enum, calc, inherit, initial michael@0: // (Note: The flags here should match those used for 'width' property above.) michael@0: SetCoord(*aRuleData->ValueForFlexBasis(), pos->mFlexBasis, parentPos->mFlexBasis, michael@0: SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // flex-direction: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForFlexDirection(), michael@0: pos->mFlexDirection, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mFlexDirection, michael@0: NS_STYLE_FLEX_DIRECTION_ROW, 0, 0, 0, 0); michael@0: michael@0: // flex-grow: float, inherit, initial michael@0: SetFactor(*aRuleData->ValueForFlexGrow(), michael@0: pos->mFlexGrow, canStoreInRuleTree, michael@0: parentPos->mFlexGrow, 0.0f, michael@0: SETFCT_UNSET_INITIAL); michael@0: michael@0: // flex-shrink: float, inherit, initial michael@0: SetFactor(*aRuleData->ValueForFlexShrink(), michael@0: pos->mFlexShrink, canStoreInRuleTree, michael@0: parentPos->mFlexShrink, 1.0f, michael@0: SETFCT_UNSET_INITIAL); michael@0: michael@0: // flex-wrap: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForFlexWrap(), michael@0: pos->mFlexWrap, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mFlexWrap, michael@0: NS_STYLE_FLEX_WRAP_NOWRAP, 0, 0, 0, 0); michael@0: michael@0: // order: integer, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForOrder(), michael@0: pos->mOrder, canStoreInRuleTree, michael@0: SETDSC_INTEGER | SETDSC_UNSET_INITIAL, michael@0: parentPos->mOrder, michael@0: NS_STYLE_ORDER_INITIAL, 0, 0, 0, 0); michael@0: michael@0: // justify-content: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForJustifyContent(), michael@0: pos->mJustifyContent, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentPos->mJustifyContent, michael@0: NS_STYLE_JUSTIFY_CONTENT_FLEX_START, 0, 0, 0, 0); michael@0: michael@0: // grid-auto-flow michael@0: const nsCSSValue& gridAutoFlow = *aRuleData->ValueForGridAutoFlow(); michael@0: switch (gridAutoFlow.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: pos->mGridAutoFlow = parentPos->mGridAutoFlow; michael@0: break; michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: pos->mGridAutoFlow = NS_STYLE_GRID_AUTO_FLOW_NONE; michael@0: break; michael@0: default: michael@0: NS_ASSERTION(gridAutoFlow.GetUnit() == eCSSUnit_Enumerated, michael@0: "Unexpected unit"); michael@0: pos->mGridAutoFlow = gridAutoFlow.GetIntValue(); michael@0: } michael@0: michael@0: // grid-auto-columns michael@0: SetGridAutoColumnsRows(*aRuleData->ValueForGridAutoColumns(), michael@0: pos->mGridAutoColumnsMin, michael@0: pos->mGridAutoColumnsMax, michael@0: parentPos->mGridAutoColumnsMin, michael@0: parentPos->mGridAutoColumnsMax, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // grid-auto-rows michael@0: SetGridAutoColumnsRows(*aRuleData->ValueForGridAutoRows(), michael@0: pos->mGridAutoRowsMin, michael@0: pos->mGridAutoRowsMax, michael@0: parentPos->mGridAutoRowsMin, michael@0: parentPos->mGridAutoRowsMax, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // grid-template-columns michael@0: SetGridTrackList(*aRuleData->ValueForGridTemplateColumns(), michael@0: pos->mGridTemplateColumns, parentPos->mGridTemplateColumns, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // grid-template-rows michael@0: SetGridTrackList(*aRuleData->ValueForGridTemplateRows(), michael@0: pos->mGridTemplateRows, parentPos->mGridTemplateRows, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // grid-tempate-areas michael@0: SetGridTemplateAreas(*aRuleData->ValueForGridTemplateAreas(), michael@0: &pos->mGridTemplateAreas, michael@0: parentPos->mGridTemplateAreas, michael@0: canStoreInRuleTree); michael@0: michael@0: // grid-auto-position michael@0: const nsCSSValue& gridAutoPosition = *aRuleData->ValueForGridAutoPosition(); michael@0: switch (gridAutoPosition.GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: pos->mGridAutoPositionColumn = parentPos->mGridAutoPositionColumn; michael@0: pos->mGridAutoPositionRow = parentPos->mGridAutoPositionRow; michael@0: break; michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: // '1 / 1' michael@0: pos->mGridAutoPositionColumn.SetToInteger(1); michael@0: pos->mGridAutoPositionRow.SetToInteger(1); michael@0: break; michael@0: default: michael@0: SetGridLine(gridAutoPosition.GetPairValue().mXValue, michael@0: pos->mGridAutoPositionColumn, michael@0: parentPos->mGridAutoPositionColumn, michael@0: canStoreInRuleTree); michael@0: SetGridLine(gridAutoPosition.GetPairValue().mYValue, michael@0: pos->mGridAutoPositionRow, michael@0: parentPos->mGridAutoPositionRow, michael@0: canStoreInRuleTree); michael@0: } michael@0: michael@0: // grid-column-start michael@0: SetGridLine(*aRuleData->ValueForGridColumnStart(), michael@0: pos->mGridColumnStart, michael@0: parentPos->mGridColumnStart, michael@0: canStoreInRuleTree); michael@0: michael@0: // grid-column-end michael@0: SetGridLine(*aRuleData->ValueForGridColumnEnd(), michael@0: pos->mGridColumnEnd, michael@0: parentPos->mGridColumnEnd, michael@0: canStoreInRuleTree); michael@0: michael@0: // grid-row-start michael@0: SetGridLine(*aRuleData->ValueForGridRowStart(), michael@0: pos->mGridRowStart, michael@0: parentPos->mGridRowStart, michael@0: canStoreInRuleTree); michael@0: michael@0: // grid-row-end michael@0: SetGridLine(*aRuleData->ValueForGridRowEnd(), michael@0: pos->mGridRowEnd, michael@0: parentPos->mGridRowEnd, michael@0: canStoreInRuleTree); michael@0: michael@0: // z-index michael@0: const nsCSSValue* zIndexValue = aRuleData->ValueForZIndex(); michael@0: if (! SetCoord(*zIndexValue, pos->mZIndex, parentPos->mZIndex, michael@0: SETCOORD_IA | SETCOORD_INITIAL_AUTO | SETCOORD_UNSET_INITIAL, michael@0: aContext, nullptr, canStoreInRuleTree)) { michael@0: if (eCSSUnit_Inherit == zIndexValue->GetUnit()) { michael@0: // handle inherit, because it's ok to inherit 'auto' here michael@0: canStoreInRuleTree = false; michael@0: pos->mZIndex = parentPos->mZIndex; michael@0: } michael@0: } michael@0: michael@0: COMPUTE_END_RESET(Position, pos) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeTableData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Table, (), table, parentTable) michael@0: michael@0: // table-layout: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTableLayout(), michael@0: table->mLayoutStrategy, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentTable->mLayoutStrategy, michael@0: NS_STYLE_TABLE_LAYOUT_AUTO, 0, 0, 0, 0); michael@0: michael@0: // span: pixels (not a real CSS prop) michael@0: const nsCSSValue* spanValue = aRuleData->ValueForSpan(); michael@0: if (eCSSUnit_Enumerated == spanValue->GetUnit() || michael@0: eCSSUnit_Integer == spanValue->GetUnit()) michael@0: table->mSpan = spanValue->GetIntValue(); michael@0: michael@0: COMPUTE_END_RESET(Table, table) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeTableBorderData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(TableBorder, (mPresContext), table, parentTable) michael@0: michael@0: // border-collapse: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBorderCollapse(), table->mBorderCollapse, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentTable->mBorderCollapse, michael@0: NS_STYLE_BORDER_SEPARATE, 0, 0, 0, 0); michael@0: michael@0: const nsCSSValue* borderSpacingValue = aRuleData->ValueForBorderSpacing(); michael@0: if (borderSpacingValue->GetUnit() != eCSSUnit_Null) { michael@0: // border-spacing-x/y: length, inherit michael@0: nsStyleCoord parentX(parentTable->mBorderSpacingX, michael@0: nsStyleCoord::CoordConstructor); michael@0: nsStyleCoord parentY(parentTable->mBorderSpacingY, michael@0: nsStyleCoord::CoordConstructor); michael@0: nsStyleCoord coordX, coordY; michael@0: michael@0: #ifdef DEBUG michael@0: bool result = michael@0: #endif michael@0: SetPairCoords(*borderSpacingValue, michael@0: coordX, coordY, parentX, parentY, michael@0: SETCOORD_LH | SETCOORD_INITIAL_ZERO | michael@0: SETCOORD_CALC_LENGTH_ONLY | michael@0: SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: NS_ASSERTION(result, "malformed table border value"); michael@0: table->mBorderSpacingX = coordX.GetCoordValue(); michael@0: table->mBorderSpacingY = coordY.GetCoordValue(); michael@0: } michael@0: michael@0: // caption-side: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForCaptionSide(), michael@0: table->mCaptionSide, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentTable->mCaptionSide, michael@0: NS_STYLE_CAPTION_SIDE_TOP, 0, 0, 0, 0); michael@0: michael@0: // empty-cells: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForEmptyCells(), michael@0: table->mEmptyCells, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentTable->mEmptyCells, michael@0: (mPresContext->CompatibilityMode() == eCompatibility_NavQuirks) michael@0: ? NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND michael@0: : NS_STYLE_TABLE_EMPTY_CELLS_SHOW, michael@0: 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_INHERITED(TableBorder, table) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeContentData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: uint32_t count; michael@0: nsAutoString buffer; michael@0: michael@0: COMPUTE_START_RESET(Content, (), content, parentContent) michael@0: michael@0: // content: [string, url, counter, attr, enum]+, normal, none, inherit michael@0: const nsCSSValue* contentValue = aRuleData->ValueForContent(); michael@0: switch (contentValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Normal: michael@0: case eCSSUnit_None: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: // "normal", "none", "initial" and "unset" all mean no content michael@0: content->AllocateContents(0); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: count = parentContent->ContentCount(); michael@0: if (NS_SUCCEEDED(content->AllocateContents(count))) { michael@0: while (0 < count--) { michael@0: content->ContentAt(count) = parentContent->ContentAt(count); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Enumerated: { michael@0: NS_ABORT_IF_FALSE(contentValue->GetIntValue() == michael@0: NS_STYLE_CONTENT_ALT_CONTENT, michael@0: "unrecognized solitary content keyword"); michael@0: content->AllocateContents(1); michael@0: nsStyleContentData& data = content->ContentAt(0); michael@0: data.mType = eStyleContentType_AltContent; michael@0: data.mContent.mString = nullptr; michael@0: break; michael@0: } michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: const nsCSSValueList* contentValueList = contentValue->GetListValue(); michael@0: count = 0; michael@0: while (contentValueList) { michael@0: count++; michael@0: contentValueList = contentValueList->mNext; michael@0: } michael@0: if (NS_SUCCEEDED(content->AllocateContents(count))) { michael@0: const nsAutoString nullStr; michael@0: count = 0; michael@0: contentValueList = contentValue->GetListValue(); michael@0: while (contentValueList) { michael@0: const nsCSSValue& value = contentValueList->mValue; michael@0: nsCSSUnit unit = value.GetUnit(); michael@0: nsStyleContentType type; michael@0: nsStyleContentData &data = content->ContentAt(count++); michael@0: switch (unit) { michael@0: case eCSSUnit_String: type = eStyleContentType_String; break; michael@0: case eCSSUnit_Image: type = eStyleContentType_Image; break; michael@0: case eCSSUnit_Attr: type = eStyleContentType_Attr; break; michael@0: case eCSSUnit_Counter: type = eStyleContentType_Counter; break; michael@0: case eCSSUnit_Counters: type = eStyleContentType_Counters; break; michael@0: case eCSSUnit_Enumerated: michael@0: switch (value.GetIntValue()) { michael@0: case NS_STYLE_CONTENT_OPEN_QUOTE: michael@0: type = eStyleContentType_OpenQuote; break; michael@0: case NS_STYLE_CONTENT_CLOSE_QUOTE: michael@0: type = eStyleContentType_CloseQuote; break; michael@0: case NS_STYLE_CONTENT_NO_OPEN_QUOTE: michael@0: type = eStyleContentType_NoOpenQuote; break; michael@0: case NS_STYLE_CONTENT_NO_CLOSE_QUOTE: michael@0: type = eStyleContentType_NoCloseQuote; break; michael@0: default: michael@0: NS_ERROR("bad content value"); michael@0: type = eStyleContentType_Uninitialized; michael@0: } michael@0: break; michael@0: default: michael@0: NS_ERROR("bad content type"); michael@0: type = eStyleContentType_Uninitialized; michael@0: } michael@0: data.mType = type; michael@0: if (type == eStyleContentType_Image) { michael@0: NS_SET_IMAGE_REQUEST_WITH_DOC(data.SetImage, michael@0: aContext, michael@0: value.GetImageValue); michael@0: } michael@0: else if (type <= eStyleContentType_Attr) { michael@0: value.GetStringValue(buffer); michael@0: data.mContent.mString = NS_strdup(buffer.get()); michael@0: } michael@0: else if (type <= eStyleContentType_Counters) { michael@0: data.mContent.mCounters = value.GetArrayValue(); michael@0: data.mContent.mCounters->AddRef(); michael@0: } michael@0: else { michael@0: data.mContent.mString = nullptr; michael@0: } michael@0: contentValueList = contentValueList->mNext; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, michael@0: nsPrintfCString("unrecognized content unit %d", michael@0: contentValue->GetUnit()).get()); michael@0: } michael@0: michael@0: // counter-increment: [string [int]]+, none, inherit michael@0: const nsCSSValue* counterIncrementValue = michael@0: aRuleData->ValueForCounterIncrement(); michael@0: switch (counterIncrementValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_None: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: content->AllocateCounterIncrements(0); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: count = parentContent->CounterIncrementCount(); michael@0: if (NS_SUCCEEDED(content->AllocateCounterIncrements(count))) { michael@0: while (0 < count--) { michael@0: const nsStyleCounterData *data = michael@0: parentContent->GetCounterIncrementAt(count); michael@0: content->SetCounterIncrementAt(count, data->mCounter, data->mValue); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_PairList: michael@0: case eCSSUnit_PairListDep: { michael@0: const nsCSSValuePairList* ourIncrement = michael@0: counterIncrementValue->GetPairListValue(); michael@0: NS_ABORT_IF_FALSE(ourIncrement->mXValue.GetUnit() == eCSSUnit_Ident, michael@0: "unexpected value unit"); michael@0: count = ListLength(ourIncrement); michael@0: if (NS_FAILED(content->AllocateCounterIncrements(count))) { michael@0: break; michael@0: } michael@0: michael@0: count = 0; michael@0: for (const nsCSSValuePairList* p = ourIncrement; p; p = p->mNext, count++) { michael@0: int32_t increment; michael@0: if (p->mYValue.GetUnit() == eCSSUnit_Integer) { michael@0: increment = p->mYValue.GetIntValue(); michael@0: } else { michael@0: increment = 1; michael@0: } michael@0: p->mXValue.GetStringValue(buffer); michael@0: content->SetCounterIncrementAt(count, buffer, increment); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected value unit"); michael@0: } michael@0: michael@0: // counter-reset: [string [int]]+, none, inherit michael@0: const nsCSSValue* counterResetValue = aRuleData->ValueForCounterReset(); michael@0: switch (counterResetValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_None: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: content->AllocateCounterResets(0); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: count = parentContent->CounterResetCount(); michael@0: if (NS_SUCCEEDED(content->AllocateCounterResets(count))) { michael@0: while (0 < count--) { michael@0: const nsStyleCounterData *data = michael@0: parentContent->GetCounterResetAt(count); michael@0: content->SetCounterResetAt(count, data->mCounter, data->mValue); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_PairList: michael@0: case eCSSUnit_PairListDep: { michael@0: const nsCSSValuePairList* ourReset = michael@0: counterResetValue->GetPairListValue(); michael@0: NS_ABORT_IF_FALSE(ourReset->mXValue.GetUnit() == eCSSUnit_Ident, michael@0: "unexpected value unit"); michael@0: count = ListLength(ourReset); michael@0: if (NS_FAILED(content->AllocateCounterResets(count))) { michael@0: break; michael@0: } michael@0: michael@0: count = 0; michael@0: for (const nsCSSValuePairList* p = ourReset; p; p = p->mNext, count++) { michael@0: int32_t reset; michael@0: if (p->mYValue.GetUnit() == eCSSUnit_Integer) { michael@0: reset = p->mYValue.GetIntValue(); michael@0: } else { michael@0: reset = 0; michael@0: } michael@0: p->mXValue.GetStringValue(buffer); michael@0: content->SetCounterResetAt(count, buffer, reset); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected value unit"); michael@0: } michael@0: michael@0: // marker-offset: length, auto, inherit michael@0: SetCoord(*aRuleData->ValueForMarkerOffset(), content->mMarkerOffset, parentContent->mMarkerOffset, michael@0: SETCOORD_LH | SETCOORD_AUTO | SETCOORD_INITIAL_AUTO | michael@0: SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // If we ended up with an image, track it. michael@0: for (uint32_t i = 0; i < content->ContentCount(); ++i) { michael@0: if ((content->ContentAt(i).mType == eStyleContentType_Image) && michael@0: content->ContentAt(i).mContent.mImage) { michael@0: content->ContentAt(i).TrackImage(aContext->PresContext()); michael@0: } michael@0: } michael@0: michael@0: COMPUTE_END_RESET(Content, content) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeQuotesData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Quotes, (), quotes, parentQuotes) michael@0: michael@0: // quotes: inherit, initial, none, [string string]+ michael@0: const nsCSSValue* quotesValue = aRuleData->ValueForQuotes(); michael@0: switch (quotesValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: canStoreInRuleTree = false; michael@0: quotes->CopyFrom(*parentQuotes); michael@0: break; michael@0: case eCSSUnit_Initial: michael@0: quotes->SetInitial(); michael@0: break; michael@0: case eCSSUnit_None: michael@0: quotes->AllocateQuotes(0); michael@0: break; michael@0: case eCSSUnit_PairList: michael@0: case eCSSUnit_PairListDep: { michael@0: const nsCSSValuePairList* ourQuotes michael@0: = quotesValue->GetPairListValue(); michael@0: nsAutoString buffer; michael@0: nsAutoString closeBuffer; michael@0: uint32_t count = ListLength(ourQuotes); michael@0: if (NS_FAILED(quotes->AllocateQuotes(count))) { michael@0: break; michael@0: } michael@0: count = 0; michael@0: while (ourQuotes) { michael@0: NS_ABORT_IF_FALSE(ourQuotes->mXValue.GetUnit() == eCSSUnit_String && michael@0: ourQuotes->mYValue.GetUnit() == eCSSUnit_String, michael@0: "improper list contents for quotes"); michael@0: ourQuotes->mXValue.GetStringValue(buffer); michael@0: ourQuotes->mYValue.GetStringValue(closeBuffer); michael@0: quotes->SetQuotesAt(count++, buffer, closeBuffer); michael@0: ourQuotes = ourQuotes->mNext; michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unexpected value unit"); michael@0: } michael@0: michael@0: COMPUTE_END_INHERITED(Quotes, quotes) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeXULData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(XUL, (), xul, parentXUL) michael@0: michael@0: // box-align: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxAlign(), michael@0: xul->mBoxAlign, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentXUL->mBoxAlign, michael@0: NS_STYLE_BOX_ALIGN_STRETCH, 0, 0, 0, 0); michael@0: michael@0: // box-direction: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxDirection(), michael@0: xul->mBoxDirection, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentXUL->mBoxDirection, michael@0: NS_STYLE_BOX_DIRECTION_NORMAL, 0, 0, 0, 0); michael@0: michael@0: // box-flex: factor, inherit michael@0: SetFactor(*aRuleData->ValueForBoxFlex(), michael@0: xul->mBoxFlex, canStoreInRuleTree, michael@0: parentXUL->mBoxFlex, 0.0f, michael@0: SETFCT_UNSET_INITIAL); michael@0: michael@0: // box-orient: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxOrient(), michael@0: xul->mBoxOrient, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentXUL->mBoxOrient, michael@0: NS_STYLE_BOX_ORIENT_HORIZONTAL, 0, 0, 0, 0); michael@0: michael@0: // box-pack: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxPack(), michael@0: xul->mBoxPack, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentXUL->mBoxPack, michael@0: NS_STYLE_BOX_PACK_START, 0, 0, 0, 0); michael@0: michael@0: // box-ordinal-group: integer, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForBoxOrdinalGroup(), michael@0: xul->mBoxOrdinal, canStoreInRuleTree, michael@0: SETDSC_INTEGER | SETDSC_UNSET_INITIAL, michael@0: parentXUL->mBoxOrdinal, 1, michael@0: 0, 0, 0, 0); michael@0: michael@0: const nsCSSValue* stackSizingValue = aRuleData->ValueForStackSizing(); michael@0: if (eCSSUnit_Inherit == stackSizingValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: xul->mStretchStack = parentXUL->mStretchStack; michael@0: } else if (eCSSUnit_Initial == stackSizingValue->GetUnit() || michael@0: eCSSUnit_Unset == stackSizingValue->GetUnit()) { michael@0: xul->mStretchStack = true; michael@0: } else if (eCSSUnit_Enumerated == stackSizingValue->GetUnit()) { michael@0: xul->mStretchStack = stackSizingValue->GetIntValue() == michael@0: NS_STYLE_STACK_SIZING_STRETCH_TO_FIT; michael@0: } michael@0: michael@0: COMPUTE_END_RESET(XUL, xul) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeColumnData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(Column, (mPresContext), column, parent) michael@0: michael@0: // column-width: length, auto, inherit michael@0: SetCoord(*aRuleData->ValueForColumnWidth(), michael@0: column->mColumnWidth, parent->mColumnWidth, michael@0: SETCOORD_LAH | SETCOORD_INITIAL_AUTO | michael@0: SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | michael@0: SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: michael@0: // column-gap: length, inherit, normal michael@0: SetCoord(*aRuleData->ValueForColumnGap(), michael@0: column->mColumnGap, parent->mColumnGap, michael@0: SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | michael@0: SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: // clamp negative calc() to 0 michael@0: if (column->mColumnGap.GetUnit() == eStyleUnit_Coord) { michael@0: column->mColumnGap.SetCoordValue( michael@0: std::max(column->mColumnGap.GetCoordValue(), 0)); michael@0: } michael@0: michael@0: // column-count: auto, integer, inherit michael@0: const nsCSSValue* columnCountValue = aRuleData->ValueForColumnCount(); michael@0: if (eCSSUnit_Auto == columnCountValue->GetUnit() || michael@0: eCSSUnit_Initial == columnCountValue->GetUnit() || michael@0: eCSSUnit_Unset == columnCountValue->GetUnit()) { michael@0: column->mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO; michael@0: } else if (eCSSUnit_Integer == columnCountValue->GetUnit()) { michael@0: column->mColumnCount = columnCountValue->GetIntValue(); michael@0: // Max kMaxColumnCount columns - wallpaper for bug 345583. michael@0: column->mColumnCount = std::min(column->mColumnCount, michael@0: nsStyleColumn::kMaxColumnCount); michael@0: } else if (eCSSUnit_Inherit == columnCountValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: column->mColumnCount = parent->mColumnCount; michael@0: } michael@0: michael@0: // column-rule-width: length, enum, inherit michael@0: const nsCSSValue& widthValue = *aRuleData->ValueForColumnRuleWidth(); michael@0: if (eCSSUnit_Initial == widthValue.GetUnit() || michael@0: eCSSUnit_Unset == widthValue.GetUnit()) { michael@0: column->SetColumnRuleWidth( michael@0: (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); michael@0: } michael@0: else if (eCSSUnit_Enumerated == widthValue.GetUnit()) { michael@0: NS_ASSERTION(widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || michael@0: widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || michael@0: widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, michael@0: "Unexpected enum value"); michael@0: column->SetColumnRuleWidth( michael@0: (mPresContext->GetBorderWidthTable())[widthValue.GetIntValue()]); michael@0: } michael@0: else if (eCSSUnit_Inherit == widthValue.GetUnit()) { michael@0: column->SetColumnRuleWidth(parent->GetComputedColumnRuleWidth()); michael@0: canStoreInRuleTree = false; michael@0: } michael@0: else if (widthValue.IsLengthUnit() || widthValue.IsCalcUnit()) { michael@0: nscoord len = michael@0: CalcLength(widthValue, aContext, mPresContext, canStoreInRuleTree); michael@0: if (len < 0) { michael@0: // FIXME: This is untested (by test_value_storage.html) for michael@0: // column-rule-width since it gets covered up by the border michael@0: // rounding code. michael@0: NS_ASSERTION(widthValue.IsCalcUnit(), michael@0: "parser should have rejected negative length"); michael@0: len = 0; michael@0: } michael@0: column->SetColumnRuleWidth(len); michael@0: } michael@0: michael@0: // column-rule-style: enum, inherit michael@0: const nsCSSValue& styleValue = *aRuleData->ValueForColumnRuleStyle(); michael@0: NS_ABORT_IF_FALSE(eCSSUnit_None != styleValue.GetUnit(), michael@0: "'none' should be handled as enumerated value"); michael@0: if (eCSSUnit_Enumerated == styleValue.GetUnit()) { michael@0: column->mColumnRuleStyle = styleValue.GetIntValue(); michael@0: } michael@0: else if (eCSSUnit_Initial == styleValue.GetUnit() || michael@0: eCSSUnit_Unset == styleValue.GetUnit()) { michael@0: column->mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE; michael@0: } michael@0: else if (eCSSUnit_Inherit == styleValue.GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: column->mColumnRuleStyle = parent->mColumnRuleStyle; michael@0: } michael@0: michael@0: // column-rule-color: color, inherit michael@0: const nsCSSValue& colorValue = *aRuleData->ValueForColumnRuleColor(); michael@0: if (eCSSUnit_Inherit == colorValue.GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: column->mColumnRuleColorIsForeground = false; michael@0: if (parent->mColumnRuleColorIsForeground) { michael@0: if (parentContext) { michael@0: column->mColumnRuleColor = parentContext->StyleColor()->mColor; michael@0: } else { michael@0: nsStyleColor defaultColumnRuleColor(mPresContext); michael@0: column->mColumnRuleColor = defaultColumnRuleColor.mColor; michael@0: } michael@0: } else { michael@0: column->mColumnRuleColor = parent->mColumnRuleColor; michael@0: } michael@0: } michael@0: else if (eCSSUnit_Initial == colorValue.GetUnit() || michael@0: eCSSUnit_Unset == colorValue.GetUnit() || michael@0: eCSSUnit_Enumerated == colorValue.GetUnit()) { michael@0: column->mColumnRuleColorIsForeground = true; michael@0: } michael@0: else if (SetColor(colorValue, 0, mPresContext, aContext, michael@0: column->mColumnRuleColor, canStoreInRuleTree)) { michael@0: column->mColumnRuleColorIsForeground = false; michael@0: } michael@0: michael@0: // column-fill: enum michael@0: SetDiscrete(*aRuleData->ValueForColumnFill(), michael@0: column->mColumnFill, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parent->mColumnFill, michael@0: NS_STYLE_COLUMN_FILL_BALANCE, michael@0: 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_RESET(Column, column) michael@0: } michael@0: michael@0: static void michael@0: SetSVGPaint(const nsCSSValue& aValue, const nsStyleSVGPaint& parentPaint, michael@0: nsPresContext* aPresContext, nsStyleContext *aContext, michael@0: nsStyleSVGPaint& aResult, nsStyleSVGPaintType aInitialPaintType, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: nscolor color; michael@0: michael@0: if (aValue.GetUnit() == eCSSUnit_Inherit || michael@0: aValue.GetUnit() == eCSSUnit_Unset) { michael@0: aResult = parentPaint; michael@0: aCanStoreInRuleTree = false; michael@0: } else if (aValue.GetUnit() == eCSSUnit_None) { michael@0: aResult.SetType(eStyleSVGPaintType_None); michael@0: } else if (aValue.GetUnit() == eCSSUnit_Initial) { michael@0: aResult.SetType(aInitialPaintType); michael@0: aResult.mPaint.mColor = NS_RGB(0, 0, 0); michael@0: aResult.mFallbackColor = NS_RGB(0, 0, 0); michael@0: } else if (SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aContext, michael@0: color, aCanStoreInRuleTree)) { michael@0: aResult.SetType(eStyleSVGPaintType_Color); michael@0: aResult.mPaint.mColor = color; michael@0: } else if (aValue.GetUnit() == eCSSUnit_Pair) { michael@0: const nsCSSValuePair& pair = aValue.GetPairValue(); michael@0: michael@0: if (pair.mXValue.GetUnit() == eCSSUnit_URL) { michael@0: aResult.SetType(eStyleSVGPaintType_Server); michael@0: aResult.mPaint.mPaintServer = pair.mXValue.GetURLValue(); michael@0: NS_IF_ADDREF(aResult.mPaint.mPaintServer); michael@0: } else if (pair.mXValue.GetUnit() == eCSSUnit_Enumerated) { michael@0: michael@0: switch (pair.mXValue.GetIntValue()) { michael@0: case NS_COLOR_CONTEXT_FILL: michael@0: aResult.SetType(eStyleSVGPaintType_ContextFill); michael@0: break; michael@0: case NS_COLOR_CONTEXT_STROKE: michael@0: aResult.SetType(eStyleSVGPaintType_ContextStroke); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("unknown keyword as paint server value"); michael@0: } michael@0: michael@0: } else { michael@0: NS_NOTREACHED("malformed paint server value"); michael@0: } michael@0: michael@0: if (pair.mYValue.GetUnit() == eCSSUnit_None) { michael@0: aResult.mFallbackColor = NS_RGBA(0, 0, 0, 0); michael@0: } else { michael@0: NS_ABORT_IF_FALSE(pair.mYValue.GetUnit() != eCSSUnit_Inherit, michael@0: "cannot inherit fallback colour"); michael@0: SetColor(pair.mYValue, NS_RGB(0, 0, 0), aPresContext, aContext, michael@0: aResult.mFallbackColor, aCanStoreInRuleTree); michael@0: } michael@0: } else { michael@0: NS_ABORT_IF_FALSE(aValue.GetUnit() == eCSSUnit_Null, michael@0: "malformed paint server value"); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: SetSVGOpacity(const nsCSSValue& aValue, michael@0: float& aOpacityField, nsStyleSVGOpacitySource& aOpacityTypeField, michael@0: bool& aCanStoreInRuleTree, michael@0: float aParentOpacity, nsStyleSVGOpacitySource aParentOpacityType) michael@0: { michael@0: if (eCSSUnit_Enumerated == aValue.GetUnit()) { michael@0: switch (aValue.GetIntValue()) { michael@0: case NS_STYLE_CONTEXT_FILL_OPACITY: michael@0: aOpacityTypeField = eStyleSVGOpacitySource_ContextFillOpacity; michael@0: break; michael@0: case NS_STYLE_CONTEXT_STROKE_OPACITY: michael@0: aOpacityTypeField = eStyleSVGOpacitySource_ContextStrokeOpacity; michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("SetSVGOpacity: Unknown keyword"); michael@0: } michael@0: // Fall back on fully opaque michael@0: aOpacityField = 1.0f; michael@0: } else if (eCSSUnit_Inherit == aValue.GetUnit() || michael@0: eCSSUnit_Unset == aValue.GetUnit()) { michael@0: aCanStoreInRuleTree = false; michael@0: aOpacityField = aParentOpacity; michael@0: aOpacityTypeField = aParentOpacityType; michael@0: } else if (eCSSUnit_Null != aValue.GetUnit()) { michael@0: SetFactor(aValue, aOpacityField, aCanStoreInRuleTree, michael@0: aParentOpacity, 1.0f, SETFCT_OPACITY); michael@0: aOpacityTypeField = eStyleSVGOpacitySource_Normal; michael@0: } michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeSVGData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(SVG, (), svg, parentSVG) michael@0: michael@0: // clip-rule: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForClipRule(), michael@0: svg->mClipRule, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mClipRule, michael@0: NS_STYLE_FILL_RULE_NONZERO, 0, 0, 0, 0); michael@0: michael@0: // color-interpolation: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForColorInterpolation(), michael@0: svg->mColorInterpolation, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mColorInterpolation, michael@0: NS_STYLE_COLOR_INTERPOLATION_SRGB, 0, 0, 0, 0); michael@0: michael@0: // color-interpolation-filters: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForColorInterpolationFilters(), michael@0: svg->mColorInterpolationFilters, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mColorInterpolationFilters, michael@0: NS_STYLE_COLOR_INTERPOLATION_LINEARRGB, 0, 0, 0, 0); michael@0: michael@0: // fill: michael@0: SetSVGPaint(*aRuleData->ValueForFill(), michael@0: parentSVG->mFill, mPresContext, aContext, michael@0: svg->mFill, eStyleSVGPaintType_Color, canStoreInRuleTree); michael@0: michael@0: // fill-opacity: factor, inherit, initial, michael@0: // context-fill-opacity, context-stroke-opacity michael@0: nsStyleSVGOpacitySource contextFillOpacity = svg->mFillOpacitySource; michael@0: SetSVGOpacity(*aRuleData->ValueForFillOpacity(), michael@0: svg->mFillOpacity, contextFillOpacity, canStoreInRuleTree, michael@0: parentSVG->mFillOpacity, parentSVG->mFillOpacitySource); michael@0: svg->mFillOpacitySource = contextFillOpacity; michael@0: michael@0: // fill-rule: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForFillRule(), michael@0: svg->mFillRule, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mFillRule, michael@0: NS_STYLE_FILL_RULE_NONZERO, 0, 0, 0, 0); michael@0: michael@0: // image-rendering: enum, inherit michael@0: SetDiscrete(*aRuleData->ValueForImageRendering(), michael@0: svg->mImageRendering, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mImageRendering, michael@0: NS_STYLE_IMAGE_RENDERING_AUTO, 0, 0, 0, 0); michael@0: michael@0: // marker-end: url, none, inherit michael@0: const nsCSSValue* markerEndValue = aRuleData->ValueForMarkerEnd(); michael@0: if (eCSSUnit_URL == markerEndValue->GetUnit()) { michael@0: svg->mMarkerEnd = markerEndValue->GetURLValue(); michael@0: } else if (eCSSUnit_None == markerEndValue->GetUnit() || michael@0: eCSSUnit_Initial == markerEndValue->GetUnit()) { michael@0: svg->mMarkerEnd = nullptr; michael@0: } else if (eCSSUnit_Inherit == markerEndValue->GetUnit() || michael@0: eCSSUnit_Unset == markerEndValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: svg->mMarkerEnd = parentSVG->mMarkerEnd; michael@0: } michael@0: michael@0: // marker-mid: url, none, inherit michael@0: const nsCSSValue* markerMidValue = aRuleData->ValueForMarkerMid(); michael@0: if (eCSSUnit_URL == markerMidValue->GetUnit()) { michael@0: svg->mMarkerMid = markerMidValue->GetURLValue(); michael@0: } else if (eCSSUnit_None == markerMidValue->GetUnit() || michael@0: eCSSUnit_Initial == markerMidValue->GetUnit()) { michael@0: svg->mMarkerMid = nullptr; michael@0: } else if (eCSSUnit_Inherit == markerMidValue->GetUnit() || michael@0: eCSSUnit_Unset == markerMidValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: svg->mMarkerMid = parentSVG->mMarkerMid; michael@0: } michael@0: michael@0: // marker-start: url, none, inherit michael@0: const nsCSSValue* markerStartValue = aRuleData->ValueForMarkerStart(); michael@0: if (eCSSUnit_URL == markerStartValue->GetUnit()) { michael@0: svg->mMarkerStart = markerStartValue->GetURLValue(); michael@0: } else if (eCSSUnit_None == markerStartValue->GetUnit() || michael@0: eCSSUnit_Initial == markerStartValue->GetUnit()) { michael@0: svg->mMarkerStart = nullptr; michael@0: } else if (eCSSUnit_Inherit == markerStartValue->GetUnit() || michael@0: eCSSUnit_Unset == markerStartValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: svg->mMarkerStart = parentSVG->mMarkerStart; michael@0: } michael@0: michael@0: // paint-order: enum (bit field), inherit, initial michael@0: const nsCSSValue* paintOrderValue = aRuleData->ValueForPaintOrder(); michael@0: switch (paintOrderValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Enumerated: michael@0: static_assert michael@0: (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8, michael@0: "SVGStyleStruct::mPaintOrder not big enough"); michael@0: svg->mPaintOrder = static_cast(paintOrderValue->GetIntValue()); michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: canStoreInRuleTree = false; michael@0: svg->mPaintOrder = parentSVG->mPaintOrder; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: svg->mPaintOrder = NS_STYLE_PAINT_ORDER_NORMAL; michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("unexpected unit"); michael@0: } michael@0: michael@0: // shape-rendering: enum, inherit michael@0: SetDiscrete(*aRuleData->ValueForShapeRendering(), michael@0: svg->mShapeRendering, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mShapeRendering, michael@0: NS_STYLE_SHAPE_RENDERING_AUTO, 0, 0, 0, 0); michael@0: michael@0: // stroke: michael@0: SetSVGPaint(*aRuleData->ValueForStroke(), michael@0: parentSVG->mStroke, mPresContext, aContext, michael@0: svg->mStroke, eStyleSVGPaintType_None, canStoreInRuleTree); michael@0: michael@0: // stroke-dasharray: , none, inherit, context-value michael@0: const nsCSSValue* strokeDasharrayValue = aRuleData->ValueForStrokeDasharray(); michael@0: switch (strokeDasharrayValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: michael@0: case eCSSUnit_Inherit: michael@0: case eCSSUnit_Unset: michael@0: canStoreInRuleTree = false; michael@0: svg->mStrokeDasharrayFromObject = parentSVG->mStrokeDasharrayFromObject; michael@0: // only do the copy if weren't already set up by the copy constructor michael@0: // FIXME Bug 389408: This is broken when aStartStruct is non-null! michael@0: if (!svg->mStrokeDasharray) { michael@0: svg->mStrokeDasharrayLength = parentSVG->mStrokeDasharrayLength; michael@0: if (svg->mStrokeDasharrayLength) { michael@0: svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength]; michael@0: if (svg->mStrokeDasharray) michael@0: memcpy(svg->mStrokeDasharray, michael@0: parentSVG->mStrokeDasharray, michael@0: svg->mStrokeDasharrayLength * sizeof(nsStyleCoord)); michael@0: else michael@0: svg->mStrokeDasharrayLength = 0; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case eCSSUnit_Enumerated: michael@0: NS_ABORT_IF_FALSE(strokeDasharrayValue->GetIntValue() == michael@0: NS_STYLE_STROKE_PROP_CONTEXT_VALUE, michael@0: "Unknown keyword for stroke-dasharray"); michael@0: svg->mStrokeDasharrayFromObject = true; michael@0: delete [] svg->mStrokeDasharray; michael@0: svg->mStrokeDasharray = nullptr; michael@0: svg->mStrokeDasharrayLength = 0; michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_None: michael@0: svg->mStrokeDasharrayFromObject = false; michael@0: delete [] svg->mStrokeDasharray; michael@0: svg->mStrokeDasharray = nullptr; michael@0: svg->mStrokeDasharrayLength = 0; michael@0: break; michael@0: michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: svg->mStrokeDasharrayFromObject = false; michael@0: delete [] svg->mStrokeDasharray; michael@0: svg->mStrokeDasharray = nullptr; michael@0: svg->mStrokeDasharrayLength = 0; michael@0: michael@0: // count number of values michael@0: const nsCSSValueList *value = strokeDasharrayValue->GetListValue(); michael@0: svg->mStrokeDasharrayLength = ListLength(value); michael@0: michael@0: NS_ASSERTION(svg->mStrokeDasharrayLength != 0, "no dasharray items"); michael@0: michael@0: svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength]; michael@0: michael@0: if (svg->mStrokeDasharray) { michael@0: uint32_t i = 0; michael@0: while (nullptr != value) { michael@0: SetCoord(value->mValue, michael@0: svg->mStrokeDasharray[i++], nsStyleCoord(), michael@0: SETCOORD_LP | SETCOORD_FACTOR, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: value = value->mNext; michael@0: } michael@0: } else { michael@0: svg->mStrokeDasharrayLength = 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: NS_ABORT_IF_FALSE(false, "unrecognized dasharray unit"); michael@0: } michael@0: michael@0: // stroke-dashoffset: , inherit michael@0: const nsCSSValue *strokeDashoffsetValue = michael@0: aRuleData->ValueForStrokeDashoffset(); michael@0: svg->mStrokeDashoffsetFromObject = michael@0: strokeDashoffsetValue->GetUnit() == eCSSUnit_Enumerated && michael@0: strokeDashoffsetValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE; michael@0: if (svg->mStrokeDashoffsetFromObject) { michael@0: svg->mStrokeDashoffset.SetCoordValue(0); michael@0: } else { michael@0: SetCoord(*aRuleData->ValueForStrokeDashoffset(), michael@0: svg->mStrokeDashoffset, parentSVG->mStrokeDashoffset, michael@0: SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_INITIAL_ZERO | michael@0: SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: } michael@0: michael@0: // stroke-linecap: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForStrokeLinecap(), michael@0: svg->mStrokeLinecap, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mStrokeLinecap, michael@0: NS_STYLE_STROKE_LINECAP_BUTT, 0, 0, 0, 0); michael@0: michael@0: // stroke-linejoin: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForStrokeLinejoin(), michael@0: svg->mStrokeLinejoin, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mStrokeLinejoin, michael@0: NS_STYLE_STROKE_LINEJOIN_MITER, 0, 0, 0, 0); michael@0: michael@0: // stroke-miterlimit: , inherit michael@0: SetFactor(*aRuleData->ValueForStrokeMiterlimit(), michael@0: svg->mStrokeMiterlimit, michael@0: canStoreInRuleTree, michael@0: parentSVG->mStrokeMiterlimit, 4.0f, michael@0: SETFCT_UNSET_INHERIT); michael@0: michael@0: // stroke-opacity: michael@0: nsStyleSVGOpacitySource contextStrokeOpacity = svg->mStrokeOpacitySource; michael@0: SetSVGOpacity(*aRuleData->ValueForStrokeOpacity(), michael@0: svg->mStrokeOpacity, contextStrokeOpacity, canStoreInRuleTree, michael@0: parentSVG->mStrokeOpacity, parentSVG->mStrokeOpacitySource); michael@0: svg->mStrokeOpacitySource = contextStrokeOpacity; michael@0: michael@0: // stroke-width: michael@0: const nsCSSValue* strokeWidthValue = aRuleData->ValueForStrokeWidth(); michael@0: switch (strokeWidthValue->GetUnit()) { michael@0: case eCSSUnit_Enumerated: michael@0: NS_ABORT_IF_FALSE(strokeWidthValue->GetIntValue() == michael@0: NS_STYLE_STROKE_PROP_CONTEXT_VALUE, michael@0: "Unrecognized keyword for stroke-width"); michael@0: svg->mStrokeWidthFromObject = true; michael@0: svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); michael@0: break; michael@0: michael@0: case eCSSUnit_Initial: michael@0: svg->mStrokeWidthFromObject = false; michael@0: svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); michael@0: break; michael@0: michael@0: default: michael@0: svg->mStrokeWidthFromObject = false; michael@0: SetCoord(*strokeWidthValue, michael@0: svg->mStrokeWidth, parentSVG->mStrokeWidth, michael@0: SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_UNSET_INHERIT, michael@0: aContext, mPresContext, canStoreInRuleTree); michael@0: } michael@0: michael@0: // text-anchor: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextAnchor(), michael@0: svg->mTextAnchor, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mTextAnchor, michael@0: NS_STYLE_TEXT_ANCHOR_START, 0, 0, 0, 0); michael@0: michael@0: // text-rendering: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForTextRendering(), michael@0: svg->mTextRendering, canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, michael@0: parentSVG->mTextRendering, michael@0: NS_STYLE_TEXT_RENDERING_AUTO, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_INHERITED(SVG, svg) michael@0: } michael@0: michael@0: // Returns true if the nsStyleFilter was successfully set using the nsCSSValue. michael@0: bool michael@0: nsRuleNode::SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, michael@0: const nsCSSValue& aValue, michael@0: nsStyleContext* aStyleContext, michael@0: nsPresContext* aPresContext, michael@0: bool& aCanStoreInRuleTree) michael@0: { michael@0: nsCSSUnit unit = aValue.GetUnit(); michael@0: if (unit == eCSSUnit_URL) { michael@0: nsIURI* url = aValue.GetURLValue(); michael@0: if (!url) michael@0: return false; michael@0: aStyleFilter->SetURL(url); michael@0: return true; michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function"); michael@0: michael@0: nsCSSValue::Array* filterFunction = aValue.GetArrayValue(); michael@0: nsCSSKeyword functionName = michael@0: (nsCSSKeyword)filterFunction->Item(0).GetIntValue(); michael@0: michael@0: int32_t type; michael@0: DebugOnly foundKeyword = michael@0: nsCSSProps::FindKeyword(functionName, michael@0: nsCSSProps::kFilterFunctionKTable, michael@0: type); michael@0: NS_ABORT_IF_FALSE(foundKeyword, "unknown filter type"); michael@0: if (type == NS_STYLE_FILTER_DROP_SHADOW) { michael@0: nsRefPtr shadowArray = GetShadowData( michael@0: filterFunction->Item(1).GetListValue(), michael@0: aStyleContext, michael@0: false, michael@0: aCanStoreInRuleTree); michael@0: aStyleFilter->SetDropShadow(shadowArray); michael@0: return true; michael@0: } michael@0: michael@0: int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR; michael@0: if (type == NS_STYLE_FILTER_BLUR) { michael@0: mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC; michael@0: } else if (type == NS_STYLE_FILTER_HUE_ROTATE) { michael@0: mask = SETCOORD_ANGLE; michael@0: } michael@0: michael@0: NS_ABORT_IF_FALSE(filterFunction->Count() == 2, michael@0: "all filter functions should have " michael@0: "exactly one argument"); michael@0: michael@0: nsCSSValue& arg = filterFunction->Item(1); michael@0: nsStyleCoord filterParameter; michael@0: DebugOnly didSetCoord = SetCoord(arg, filterParameter, michael@0: nsStyleCoord(), mask, michael@0: aStyleContext, aPresContext, michael@0: aCanStoreInRuleTree); michael@0: aStyleFilter->SetFilterParameter(filterParameter, type); michael@0: NS_ABORT_IF_FALSE(didSetCoord, "unexpected unit"); michael@0: return true; michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeSVGResetData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_RESET(SVGReset, (), svgReset, parentSVGReset) michael@0: michael@0: // stop-color: michael@0: const nsCSSValue* stopColorValue = aRuleData->ValueForStopColor(); michael@0: if (eCSSUnit_Initial == stopColorValue->GetUnit() || michael@0: eCSSUnit_Unset == stopColorValue->GetUnit()) { michael@0: svgReset->mStopColor = NS_RGB(0, 0, 0); michael@0: } else { michael@0: SetColor(*stopColorValue, parentSVGReset->mStopColor, michael@0: mPresContext, aContext, svgReset->mStopColor, canStoreInRuleTree); michael@0: } michael@0: michael@0: // flood-color: michael@0: const nsCSSValue* floodColorValue = aRuleData->ValueForFloodColor(); michael@0: if (eCSSUnit_Initial == floodColorValue->GetUnit() || michael@0: eCSSUnit_Unset == floodColorValue->GetUnit()) { michael@0: svgReset->mFloodColor = NS_RGB(0, 0, 0); michael@0: } else { michael@0: SetColor(*floodColorValue, parentSVGReset->mFloodColor, michael@0: mPresContext, aContext, svgReset->mFloodColor, canStoreInRuleTree); michael@0: } michael@0: michael@0: // lighting-color: michael@0: const nsCSSValue* lightingColorValue = aRuleData->ValueForLightingColor(); michael@0: if (eCSSUnit_Initial == lightingColorValue->GetUnit() || michael@0: eCSSUnit_Unset == lightingColorValue->GetUnit()) { michael@0: svgReset->mLightingColor = NS_RGB(255, 255, 255); michael@0: } else { michael@0: SetColor(*lightingColorValue, parentSVGReset->mLightingColor, michael@0: mPresContext, aContext, svgReset->mLightingColor, michael@0: canStoreInRuleTree); michael@0: } michael@0: michael@0: // clip-path: url, none, inherit michael@0: const nsCSSValue* clipPathValue = aRuleData->ValueForClipPath(); michael@0: if (eCSSUnit_URL == clipPathValue->GetUnit()) { michael@0: svgReset->mClipPath = clipPathValue->GetURLValue(); michael@0: } else if (eCSSUnit_None == clipPathValue->GetUnit() || michael@0: eCSSUnit_Initial == clipPathValue->GetUnit() || michael@0: eCSSUnit_Unset == clipPathValue->GetUnit()) { michael@0: svgReset->mClipPath = nullptr; michael@0: } else if (eCSSUnit_Inherit == clipPathValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: svgReset->mClipPath = parentSVGReset->mClipPath; michael@0: } michael@0: michael@0: // stop-opacity: michael@0: SetFactor(*aRuleData->ValueForStopOpacity(), michael@0: svgReset->mStopOpacity, canStoreInRuleTree, michael@0: parentSVGReset->mStopOpacity, 1.0f, michael@0: SETFCT_OPACITY | SETFCT_UNSET_INITIAL); michael@0: michael@0: // flood-opacity: michael@0: SetFactor(*aRuleData->ValueForFloodOpacity(), michael@0: svgReset->mFloodOpacity, canStoreInRuleTree, michael@0: parentSVGReset->mFloodOpacity, 1.0f, michael@0: SETFCT_OPACITY | SETFCT_UNSET_INITIAL); michael@0: michael@0: // dominant-baseline: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForDominantBaseline(), michael@0: svgReset->mDominantBaseline, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentSVGReset->mDominantBaseline, michael@0: NS_STYLE_DOMINANT_BASELINE_AUTO, 0, 0, 0, 0); michael@0: michael@0: // vector-effect: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForVectorEffect(), michael@0: svgReset->mVectorEffect, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentSVGReset->mVectorEffect, michael@0: NS_STYLE_VECTOR_EFFECT_NONE, 0, 0, 0, 0); michael@0: michael@0: // filter: url, none, inherit michael@0: const nsCSSValue* filterValue = aRuleData->ValueForFilter(); michael@0: switch (filterValue->GetUnit()) { michael@0: case eCSSUnit_Null: michael@0: break; michael@0: case eCSSUnit_None: michael@0: case eCSSUnit_Initial: michael@0: case eCSSUnit_Unset: michael@0: svgReset->mFilters.Clear(); michael@0: break; michael@0: case eCSSUnit_Inherit: michael@0: canStoreInRuleTree = false; michael@0: svgReset->mFilters = parentSVGReset->mFilters; michael@0: break; michael@0: case eCSSUnit_List: michael@0: case eCSSUnit_ListDep: { michael@0: svgReset->mFilters.Clear(); michael@0: const nsCSSValueList* cur = filterValue->GetListValue(); michael@0: while (cur) { michael@0: nsStyleFilter styleFilter; michael@0: if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, michael@0: mPresContext, canStoreInRuleTree)) { michael@0: svgReset->mFilters.Clear(); michael@0: break; michael@0: } michael@0: NS_ABORT_IF_FALSE(styleFilter.GetType() != NS_STYLE_FILTER_NONE, michael@0: "filter should be set"); michael@0: svgReset->mFilters.AppendElement(styleFilter); michael@0: cur = cur->mNext; michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: NS_NOTREACHED("unexpected unit"); michael@0: } michael@0: michael@0: // mask: url, none, inherit michael@0: const nsCSSValue* maskValue = aRuleData->ValueForMask(); michael@0: if (eCSSUnit_URL == maskValue->GetUnit()) { michael@0: svgReset->mMask = maskValue->GetURLValue(); michael@0: } else if (eCSSUnit_None == maskValue->GetUnit() || michael@0: eCSSUnit_Initial == maskValue->GetUnit() || michael@0: eCSSUnit_Unset == maskValue->GetUnit()) { michael@0: svgReset->mMask = nullptr; michael@0: } else if (eCSSUnit_Inherit == maskValue->GetUnit()) { michael@0: canStoreInRuleTree = false; michael@0: svgReset->mMask = parentSVGReset->mMask; michael@0: } michael@0: michael@0: // mask-type: enum, inherit, initial michael@0: SetDiscrete(*aRuleData->ValueForMaskType(), michael@0: svgReset->mMaskType, michael@0: canStoreInRuleTree, michael@0: SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, michael@0: parentSVGReset->mMaskType, michael@0: NS_STYLE_MASK_TYPE_LUMINANCE, 0, 0, 0, 0); michael@0: michael@0: COMPUTE_END_RESET(SVGReset, svgReset) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::ComputeVariablesData(void* aStartStruct, michael@0: const nsRuleData* aRuleData, michael@0: nsStyleContext* aContext, michael@0: nsRuleNode* aHighestNode, michael@0: const RuleDetail aRuleDetail, michael@0: const bool aCanStoreInRuleTree) michael@0: { michael@0: COMPUTE_START_INHERITED(Variables, (), variables, parentVariables) michael@0: michael@0: MOZ_ASSERT(aRuleData->mVariables, michael@0: "shouldn't be in ComputeVariablesData if there were no variable " michael@0: "declarations specified"); michael@0: michael@0: CSSVariableResolver resolver(&variables->mVariables); michael@0: resolver.Resolve(&parentVariables->mVariables, michael@0: aRuleData->mVariables); michael@0: canStoreInRuleTree = false; michael@0: michael@0: COMPUTE_END_INHERITED(Variables, variables) michael@0: } michael@0: michael@0: const void* michael@0: nsRuleNode::GetStyleData(nsStyleStructID aSID, michael@0: nsStyleContext* aContext, michael@0: bool aComputeData) michael@0: { michael@0: NS_ASSERTION(IsUsedDirectly(), michael@0: "if we ever call this on rule nodes that aren't used " michael@0: "directly, we should adjust handling of mDependentBits " michael@0: "in some way."); michael@0: michael@0: const void *data; michael@0: data = mStyleData.GetStyleData(aSID); michael@0: if (MOZ_LIKELY(data != nullptr)) michael@0: return data; // We have a fully specified struct. Just return it. michael@0: michael@0: if (MOZ_UNLIKELY(!aComputeData)) michael@0: return nullptr; michael@0: michael@0: // Nothing is cached. We'll have to delve further and examine our rules. michael@0: data = WalkRuleTree(aSID, aContext); michael@0: michael@0: NS_ABORT_IF_FALSE(data, "should have aborted on out-of-memory"); michael@0: return data; michael@0: } michael@0: michael@0: // See comments above in GetStyleData for an explanation of what the michael@0: // code below does. michael@0: #define STYLE_STRUCT(name_, checkdata_cb_) \ michael@0: const nsStyle##name_* \ michael@0: nsRuleNode::GetStyle##name_(nsStyleContext* aContext, bool aComputeData) \ michael@0: { \ michael@0: NS_ASSERTION(IsUsedDirectly(), \ michael@0: "if we ever call this on rule nodes that aren't used " \ michael@0: "directly, we should adjust handling of mDependentBits " \ michael@0: "in some way."); \ michael@0: \ michael@0: const nsStyle##name_ *data; \ michael@0: data = mStyleData.GetStyle##name_(); \ michael@0: if (MOZ_LIKELY(data != nullptr)) \ michael@0: return data; \ michael@0: \ michael@0: if (MOZ_UNLIKELY(!aComputeData)) \ michael@0: return nullptr; \ michael@0: \ michael@0: data = static_cast \ michael@0: (WalkRuleTree(eStyleStruct_##name_, aContext)); \ michael@0: \ michael@0: NS_ABORT_IF_FALSE(data, "should have aborted on out-of-memory"); \ michael@0: return data; \ michael@0: } michael@0: #include "nsStyleStructList.h" michael@0: #undef STYLE_STRUCT michael@0: michael@0: void michael@0: nsRuleNode::Mark() michael@0: { michael@0: for (nsRuleNode *node = this; michael@0: node && !(node->mDependentBits & NS_RULE_NODE_GC_MARK); michael@0: node = node->mParent) michael@0: node->mDependentBits |= NS_RULE_NODE_GC_MARK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: SweepRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr, michael@0: uint32_t number, void *arg) michael@0: { michael@0: ChildrenHashEntry *entry = static_cast(hdr); michael@0: if (entry->mRuleNode->Sweep()) michael@0: return PL_DHASH_REMOVE; // implies NEXT, unless |ed with STOP michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: bool michael@0: nsRuleNode::Sweep() michael@0: { michael@0: // If we're not marked, then we have to delete ourself. michael@0: // However, we never allow the root node to GC itself, because nsStyleSet michael@0: // wants to hold onto the root node and not worry about re-creating a michael@0: // rule walker if the root node is deleted. michael@0: if (!(mDependentBits & NS_RULE_NODE_GC_MARK) && michael@0: // Skip this only if we're the *current* root and not an old one. michael@0: !(IsRoot() && mPresContext->StyleSet()->GetRuleTree() == this)) { michael@0: Destroy(); michael@0: return true; michael@0: } michael@0: michael@0: // Clear our mark, for the next time around. michael@0: mDependentBits &= ~NS_RULE_NODE_GC_MARK; michael@0: michael@0: // Call sweep on the children, since some may not be marked, and michael@0: // remove any deleted children from the child lists. michael@0: if (HaveChildren()) { michael@0: uint32_t childrenDestroyed; michael@0: if (ChildrenAreHashed()) { michael@0: PLDHashTable *children = ChildrenHash(); michael@0: uint32_t oldChildCount = children->entryCount; michael@0: PL_DHashTableEnumerate(children, SweepRuleNodeChildren, nullptr); michael@0: childrenDestroyed = children->entryCount - oldChildCount; michael@0: } else { michael@0: childrenDestroyed = 0; michael@0: for (nsRuleNode **children = ChildrenListPtr(); *children; ) { michael@0: nsRuleNode *next = (*children)->mNextSibling; michael@0: if ((*children)->Sweep()) { michael@0: // This rule node was destroyed, so implicitly advance by michael@0: // making *children point to the next entry. michael@0: *children = next; michael@0: ++childrenDestroyed; michael@0: } else { michael@0: // Advance. michael@0: children = &(*children)->mNextSibling; michael@0: } michael@0: } michael@0: } michael@0: mRefCnt -= childrenDestroyed; michael@0: NS_POSTCONDITION(IsRoot() || mRefCnt > 0, michael@0: "We didn't get swept, so we'd better have style contexts " michael@0: "pointing to us or to one of our descendants, which means " michael@0: "we'd better have a nonzero mRefCnt here!"); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, michael@0: uint32_t ruleTypeMask, michael@0: bool aAuthorColorsAllowed) michael@0: { michael@0: uint32_t inheritBits = 0; michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) michael@0: inheritBits |= NS_STYLE_INHERIT_BIT(Background); michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) michael@0: inheritBits |= NS_STYLE_INHERIT_BIT(Border); michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) michael@0: inheritBits |= NS_STYLE_INHERIT_BIT(Padding); michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) michael@0: inheritBits |= NS_STYLE_INHERIT_BIT(Text); michael@0: michael@0: // properties in the SIDS, whether or not we care about them michael@0: size_t nprops = 0, michael@0: backgroundOffset, borderOffset, paddingOffset, textShadowOffset; michael@0: michael@0: // We put the reset properties the start of the nsCSSValue array.... michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { michael@0: backgroundOffset = nprops; michael@0: nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Background); michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { michael@0: borderOffset = nprops; michael@0: nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Border); michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { michael@0: paddingOffset = nprops; michael@0: nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Padding); michael@0: } michael@0: michael@0: // ...and the inherited properties at the end of the array. michael@0: size_t inheritedOffset = nprops; michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { michael@0: textShadowOffset = nprops; michael@0: nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Text); michael@0: } michael@0: michael@0: void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); michael@0: AutoCSSValueArray dataArray(dataStorage, nprops); michael@0: michael@0: /* We're relying on the use of |aStyleContext| not mutating it! */ michael@0: nsRuleData ruleData(inheritBits, dataArray.get(), michael@0: aStyleContext->PresContext(), aStyleContext); michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { michael@0: ruleData.mValueOffsets[eStyleStruct_Background] = backgroundOffset; michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { michael@0: ruleData.mValueOffsets[eStyleStruct_Border] = borderOffset; michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { michael@0: ruleData.mValueOffsets[eStyleStruct_Padding] = paddingOffset; michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { michael@0: ruleData.mValueOffsets[eStyleStruct_Text] = textShadowOffset; michael@0: } michael@0: michael@0: static const nsCSSProperty backgroundValues[] = { michael@0: eCSSProperty_background_color, michael@0: eCSSProperty_background_image, michael@0: }; michael@0: michael@0: static const nsCSSProperty borderValues[] = { michael@0: eCSSProperty_border_top_color, michael@0: eCSSProperty_border_top_style, michael@0: eCSSProperty_border_top_width, michael@0: eCSSProperty_border_right_color_value, michael@0: eCSSProperty_border_right_style_value, michael@0: eCSSProperty_border_right_width_value, michael@0: eCSSProperty_border_bottom_color, michael@0: eCSSProperty_border_bottom_style, michael@0: eCSSProperty_border_bottom_width, michael@0: eCSSProperty_border_left_color_value, michael@0: eCSSProperty_border_left_style_value, michael@0: eCSSProperty_border_left_width_value, michael@0: eCSSProperty_border_start_color_value, michael@0: eCSSProperty_border_start_style_value, michael@0: eCSSProperty_border_start_width_value, michael@0: eCSSProperty_border_end_color_value, michael@0: eCSSProperty_border_end_style_value, michael@0: eCSSProperty_border_end_width_value, michael@0: eCSSProperty_border_top_left_radius, michael@0: eCSSProperty_border_top_right_radius, michael@0: eCSSProperty_border_bottom_right_radius, michael@0: eCSSProperty_border_bottom_left_radius, michael@0: }; michael@0: michael@0: static const nsCSSProperty paddingValues[] = { michael@0: eCSSProperty_padding_top, michael@0: eCSSProperty_padding_right_value, michael@0: eCSSProperty_padding_bottom, michael@0: eCSSProperty_padding_left_value, michael@0: eCSSProperty_padding_start_value, michael@0: eCSSProperty_padding_end_value, michael@0: }; michael@0: michael@0: static const nsCSSProperty textShadowValues[] = { michael@0: eCSSProperty_text_shadow michael@0: }; michael@0: michael@0: // Number of properties we care about michael@0: size_t nValues = 0; michael@0: michael@0: nsCSSValue* values[MOZ_ARRAY_LENGTH(backgroundValues) + michael@0: MOZ_ARRAY_LENGTH(borderValues) + michael@0: MOZ_ARRAY_LENGTH(paddingValues) + michael@0: MOZ_ARRAY_LENGTH(textShadowValues)]; michael@0: michael@0: nsCSSProperty properties[MOZ_ARRAY_LENGTH(backgroundValues) + michael@0: MOZ_ARRAY_LENGTH(borderValues) + michael@0: MOZ_ARRAY_LENGTH(paddingValues) + michael@0: MOZ_ARRAY_LENGTH(textShadowValues)]; michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { michael@0: for (uint32_t i = 0, i_end = ArrayLength(backgroundValues); michael@0: i < i_end; ++i) { michael@0: properties[nValues] = backgroundValues[i]; michael@0: values[nValues++] = ruleData.ValueFor(backgroundValues[i]); michael@0: } michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { michael@0: for (uint32_t i = 0, i_end = ArrayLength(borderValues); michael@0: i < i_end; ++i) { michael@0: properties[nValues] = borderValues[i]; michael@0: values[nValues++] = ruleData.ValueFor(borderValues[i]); michael@0: } michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { michael@0: for (uint32_t i = 0, i_end = ArrayLength(paddingValues); michael@0: i < i_end; ++i) { michael@0: properties[nValues] = paddingValues[i]; michael@0: values[nValues++] = ruleData.ValueFor(paddingValues[i]); michael@0: } michael@0: } michael@0: michael@0: if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { michael@0: for (uint32_t i = 0, i_end = ArrayLength(textShadowValues); michael@0: i < i_end; ++i) { michael@0: properties[nValues] = textShadowValues[i]; michael@0: values[nValues++] = ruleData.ValueFor(textShadowValues[i]); michael@0: } michael@0: } michael@0: michael@0: nsStyleContext* styleContext = aStyleContext; michael@0: michael@0: // We need to be careful not to count styles covered up by user-important or michael@0: // UA-important declarations. But we do want to catch explicit inherit michael@0: // styling in those and check our parent style context to see whether we have michael@0: // user styling for those properties. Note that we don't care here about michael@0: // inheritance due to lack of a specified value, since all the properties we michael@0: // care about are reset properties. michael@0: bool haveExplicitUAInherit; michael@0: do { michael@0: haveExplicitUAInherit = false; michael@0: for (nsRuleNode* ruleNode = styleContext->RuleNode(); ruleNode; michael@0: ruleNode = ruleNode->GetParent()) { michael@0: nsIStyleRule *rule = ruleNode->GetRule(); michael@0: if (rule) { michael@0: ruleData.mLevel = ruleNode->GetLevel(); michael@0: ruleData.mIsImportantRule = ruleNode->IsImportantRule(); michael@0: michael@0: rule->MapRuleInfoInto(&ruleData); michael@0: michael@0: if (ruleData.mLevel == nsStyleSet::eAgentSheet || michael@0: ruleData.mLevel == nsStyleSet::eUserSheet) { michael@0: // This is a rule whose effect we want to ignore, so if any of michael@0: // the properties we care about were set, set them to the dummy michael@0: // value that they'll never otherwise get. michael@0: for (uint32_t i = 0; i < nValues; ++i) { michael@0: nsCSSUnit unit = values[i]->GetUnit(); michael@0: if (unit != eCSSUnit_Null && michael@0: unit != eCSSUnit_Dummy && michael@0: unit != eCSSUnit_DummyInherit) { michael@0: if (unit == eCSSUnit_Inherit || michael@0: (i >= inheritedOffset && unit == eCSSUnit_Unset)) { michael@0: haveExplicitUAInherit = true; michael@0: values[i]->SetDummyInheritValue(); michael@0: } else { michael@0: values[i]->SetDummyValue(); michael@0: } michael@0: } michael@0: } michael@0: } else { michael@0: // If any of the values we care about was set by the above rule, michael@0: // we have author style. michael@0: for (uint32_t i = 0; i < nValues; ++i) { michael@0: if (values[i]->GetUnit() != eCSSUnit_Null && michael@0: values[i]->GetUnit() != eCSSUnit_Dummy && // see above michael@0: values[i]->GetUnit() != eCSSUnit_DummyInherit) { michael@0: // If author colors are not allowed, only claim to have michael@0: // author-specified rules if we're looking at a non-color michael@0: // property or if we're looking at the background color and it's michael@0: // set to transparent. Anything else should get set to a dummy michael@0: // value instead. michael@0: if (aAuthorColorsAllowed || michael@0: !nsCSSProps::PropHasFlags(properties[i], michael@0: CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) || michael@0: (properties[i] == eCSSProperty_background_color && michael@0: !values[i]->IsNonTransparentColor())) { michael@0: return true; michael@0: } michael@0: michael@0: values[i]->SetDummyValue(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (haveExplicitUAInherit) { michael@0: // reset all the eCSSUnit_Null values to eCSSUnit_Dummy (since they're michael@0: // not styled by the author, or by anyone else), and then reset all the michael@0: // eCSSUnit_DummyInherit values to eCSSUnit_Null (so we will be able to michael@0: // detect them being styled by the author) and move up to our parent michael@0: // style context. michael@0: for (uint32_t i = 0; i < nValues; ++i) michael@0: if (values[i]->GetUnit() == eCSSUnit_Null) michael@0: values[i]->SetDummyValue(); michael@0: for (uint32_t i = 0; i < nValues; ++i) michael@0: if (values[i]->GetUnit() == eCSSUnit_DummyInherit) michael@0: values[i]->Reset(); michael@0: styleContext = styleContext->GetParent(); michael@0: } michael@0: } while (haveExplicitUAInherit && styleContext); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsRuleNode::ComputeColor(const nsCSSValue& aValue, nsPresContext* aPresContext, michael@0: nsStyleContext* aStyleContext, nscolor& aResult) michael@0: { michael@0: MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Inherit, michael@0: "aValue shouldn't have eCSSUnit_Inherit"); michael@0: MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Initial, michael@0: "aValue shouldn't have eCSSUnit_Initial"); michael@0: MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Unset, michael@0: "aValue shouldn't have eCSSUnit_Unset"); michael@0: michael@0: bool canStoreInRuleTree; michael@0: bool ok = SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aStyleContext, michael@0: aResult, canStoreInRuleTree); michael@0: MOZ_ASSERT(ok || !(aPresContext && aStyleContext)); michael@0: return ok; michael@0: }