/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=78: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * a node in the lexicographic tree of rules that match an element, * responsible for converting the rules' information into computed style */ #include <algorithm> #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/DebugOnly.h" #include "mozilla/Function.h" #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection #include "mozilla/Likely.h" #include "mozilla/LookAndFeel.h" #include "mozilla/OperatorNewExtensions.h" #include "mozilla/Unused.h" #include "mozilla/css/Declaration.h" #include "mozilla/TypeTraits.h" #include "nsAlgorithm.h" // for clamped() #include "nsRuleNode.h" #include "nscore.h" #include "nsIWidget.h" #include "nsIPresShell.h" #include "nsFontMetrics.h" #include "gfxFont.h" #include "nsCSSAnonBoxes.h" #include "nsCSSPseudoElements.h" #include "nsThemeConstants.h" #include "PLDHashTable.h" #include "nsStyleContext.h" #include "nsStyleSet.h" #include "nsStyleStruct.h" #include "nsSize.h" #include "nsRuleData.h" #include "nsIStyleRule.h" #include "nsBidiUtils.h" #include "nsStyleStructInlines.h" #include "nsCSSProps.h" #include "nsTArray.h" #include "nsContentUtils.h" #include "CSSCalc.h" #include "nsPrintfCString.h" #include "nsRenderingContext.h" #include "nsStyleUtil.h" #include "nsIDocument.h" #include "prtime.h" #include "CSSVariableResolver.h" #include "nsCSSParser.h" #include "CounterStyleManager.h" #include "nsCSSPropertyIDSet.h" #include "mozilla/RuleNodeCacheConditions.h" #include "nsDeviceContext.h" #include "nsQueryObject.h" #include "nsUnicodeProperties.h" #if defined(_MSC_VER) || defined(__MINGW32__) #include <malloc.h> #ifdef _MSC_VER #define alloca _alloca #endif #endif #ifdef XP_SOLARIS #include <alloca.h> #endif using std::max; using std::min; using namespace mozilla; using namespace mozilla::dom; namespace mozilla { enum UnsetAction { eUnsetInitial, eUnsetInherit }; } // namespace mozilla void* nsConditionalResetStyleData::GetConditionalStyleData(nsStyleStructID aSID, nsStyleContext* aStyleContext) const { Entry* e = static_cast<Entry*>(mEntries[aSID]); MOZ_ASSERT(e, "if mConditionalBits bit is set, we must have at least one " "conditional style struct"); do { if (e->mConditions.Matches(aStyleContext)) { void* data = e->mStyleStruct; // For reset structs with conditions, we cache the data on the // style context. // Tell the style context that it doesn't own the data aStyleContext->AddStyleBit(GetBitForSID(aSID)); aStyleContext->SetStyle(aSID, data); return data; } e = e->mNext; } while (e); return nullptr; } // Creates and returns an imgRequestProxy based on the specified // value in aValue. static imgRequestProxy* GetImageRequest(nsPresContext* aPresContext, const nsCSSValue& aValue) { return aValue.GetImageValue(aPresContext->Document()); } // Creates an imgRequestProxy based on the specified value in // aValue and calls aCallback with it. If the nsPresContext // is static (e.g. for printing), then a static request (i.e. // showing the first frame, without animation) will be created. // (The expectation is then that aCallback will set the resulting // imgRequestProxy in a style struct somewhere.) static void SetImageRequest(function<void(imgRequestProxy*)> aCallback, nsPresContext* aPresContext, const nsCSSValue& aValue) { RefPtr<imgRequestProxy> req = aValue.GetPossiblyStaticImageValue(aPresContext->Document(), aPresContext); aCallback(req); } static void SetStyleImageRequest(function<void(nsStyleImageRequest*)> aCallback, nsPresContext* aPresContext, const nsCSSValue& aValue, nsStyleImageRequest::Mode aModeFlags = nsStyleImageRequest::Mode::Track) { SetImageRequest([&](imgRequestProxy* aProxy) { RefPtr<nsStyleImageRequest> request; if (aProxy) { css::ImageValue* imageValue = aValue.GetImageStructValue(); ImageTracker* imageTracker = (aModeFlags & nsStyleImageRequest::Mode::Track) ? aPresContext->Document()->ImageTracker() : nullptr; request = new nsStyleImageRequest(aModeFlags, aProxy, imageValue, imageTracker); } aCallback(request); }, aPresContext, aValue); } template<typename ReferenceBox> static void SetStyleShapeSourceToCSSValue(StyleShapeSource<ReferenceBox>* aShapeSource, const nsCSSValue* aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions); /* Helper function to convert a CSS <position> specified value into its * computed-style form. */ static void ComputePositionValue(nsStyleContext* aStyleContext, const nsCSSValue& aValue, Position& aComputedValue, RuleNodeCacheConditions& aConditions); /* * For storage of an |nsRuleNode|'s children in a PLDHashTable. */ struct ChildrenHashEntry : public PLDHashEntryHdr { // key is |mRuleNode->GetKey()| nsRuleNode *mRuleNode; }; /* static */ PLDHashNumber nsRuleNode::ChildrenHashHashKey(const void *aKey) { const nsRuleNode::Key *key = static_cast<const nsRuleNode::Key*>(aKey); // Disagreement on importance and level for the same rule is extremely // rare, so hash just on the rule. return PLDHashTable::HashVoidPtrKeyStub(key->mRule); } /* static */ bool nsRuleNode::ChildrenHashMatchEntry(const PLDHashEntryHdr *aHdr, const void *aKey) { const ChildrenHashEntry *entry = static_cast<const ChildrenHashEntry*>(aHdr); const nsRuleNode::Key *key = static_cast<const nsRuleNode::Key*>(aKey); return entry->mRuleNode->GetKey() == *key; } /* static */ const PLDHashTableOps nsRuleNode::ChildrenHashOps = { // It's probably better to allocate the table itself using malloc and // free rather than the pres shell's arena because the table doesn't // grow very often and the pres shell's arena doesn't recycle very // large size allocations. ChildrenHashHashKey, ChildrenHashMatchEntry, PLDHashTable::MoveEntryStub, PLDHashTable::ClearEntryStub, nullptr }; // EnsureBlockDisplay: // Never change display:none or display:contents *ever*, otherwise: // - if the display value (argument) is not a block-type // then we set it to a valid block display value // - For enforcing the floated/positioned element CSS2 rules // - We allow the behavior of "list-item" to be customized. // CSS21 says that position/float do not convert 'list-item' to 'block', // but it explicitly does not define whether 'list-item' should be // converted to block *on the root node*. To allow for flexibility // (so that we don't have to support a list-item root node), this method // lets the caller pick either behavior, using the 'aConvertListItem' arg. // Reference: http://www.w3.org/TR/CSS21/visuren.html#dis-pos-flo /* static */ void nsRuleNode::EnsureBlockDisplay(StyleDisplay& display, bool aConvertListItem /* = false */) { // see if the display value is already a block switch (display) { case StyleDisplay::ListItem: if (aConvertListItem) { display = StyleDisplay::Block; break; } // else, fall through to share the 'break' for non-changing display vals MOZ_FALLTHROUGH; case StyleDisplay::None: case StyleDisplay::Contents: // never change display:none or display:contents *ever* case StyleDisplay::Table: case StyleDisplay::Block: case StyleDisplay::Flex: case StyleDisplay::WebkitBox: case StyleDisplay::Grid: // do not muck with these at all - already blocks // This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we // should just call that?) // This needs to match the check done in // nsCSSFrameConstructor::FindMathMLData for <math>. break; case StyleDisplay::InlineTable: // make inline tables into tables display = StyleDisplay::Table; break; case StyleDisplay::InlineFlex: // make inline flex containers into flex containers display = StyleDisplay::Flex; break; case StyleDisplay::WebkitInlineBox: // make -webkit-inline-box containers into -webkit-box containers display = StyleDisplay::WebkitBox; break; case StyleDisplay::InlineGrid: // make inline grid containers into grid containers display = StyleDisplay::Grid; break; default: // make it a block display = StyleDisplay::Block; } } // EnsureInlineDisplay: // - if the display value (argument) is not an inline type // then we set it to a valid inline display value /* static */ void nsRuleNode::EnsureInlineDisplay(StyleDisplay& display) { // see if the display value is already inline switch (display) { case StyleDisplay::Block: display = StyleDisplay::InlineBlock; break; case StyleDisplay::Table: display = StyleDisplay::InlineTable; break; case StyleDisplay::Flex: display = StyleDisplay::InlineFlex; break; case StyleDisplay::WebkitBox: display = StyleDisplay::WebkitInlineBox; break; case StyleDisplay::Grid: display = StyleDisplay::InlineGrid; break; case StyleDisplay::Box: display = StyleDisplay::InlineBox; break; case StyleDisplay::Stack: display = StyleDisplay::InlineStack; break; default: break; // Do nothing } } static nscoord CalcLengthWith(const nsCSSValue& aValue, nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, bool aUseUserFontSet, RuleNodeCacheConditions& aConditions); struct CalcLengthCalcOps : public css::BasicCoordCalcOps, public css::NumbersAlreadyNormalizedOps { // All of the parameters to CalcLengthWith except aValue. const nscoord mFontSize; const nsStyleFont* const mStyleFont; nsStyleContext* const mStyleContext; nsPresContext* const mPresContext; const bool mUseProvidedRootEmSize; const bool mUseUserFontSet; RuleNodeCacheConditions& mConditions; CalcLengthCalcOps(nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, bool aUseUserFontSet, RuleNodeCacheConditions& aConditions) : mFontSize(aFontSize), mStyleFont(aStyleFont), mStyleContext(aStyleContext), mPresContext(aPresContext), mUseProvidedRootEmSize(aUseProvidedRootEmSize), mUseUserFontSet(aUseUserFontSet), mConditions(aConditions) { } result_type ComputeLeafValue(const nsCSSValue& aValue) { return CalcLengthWith(aValue, mFontSize, mStyleFont, mStyleContext, mPresContext, mUseProvidedRootEmSize, mUseUserFontSet, mConditions); } }; static inline nscoord ScaleCoordRound(const nsCSSValue& aValue, float aFactor) { return NSToCoordRoundWithClamp(aValue.GetFloatValue() * aFactor); } static inline nscoord ScaleViewportCoordTrunc(const nsCSSValue& aValue, nscoord aViewportSize) { // For units (like percentages and viewport units) where authors might // repeatedly use a value and expect some multiple of the value to be // smaller than a container, we need to use floor rather than round. // We need to use division by 100.0 rather than multiplication by 0.1f // to avoid introducing error. return NSToCoordTruncClamped(aValue.GetFloatValue() * aViewportSize / 100.0f); } already_AddRefed<nsFontMetrics> GetMetricsFor(nsPresContext* aPresContext, nsStyleContext* aStyleContext, const nsStyleFont* aStyleFont, nscoord aFontSize, // overrides value from aStyleFont bool aUseUserFontSet) { nsFont font = aStyleFont->mFont; font.size = aFontSize; gfxFont::Orientation orientation = gfxFont::eHorizontal; if (aStyleContext) { WritingMode wm(aStyleContext); if (wm.IsVertical() && !wm.IsSideways()) { orientation = gfxFont::eVertical; } } nsFontMetrics::Params params; params.language = aStyleFont->mLanguage; params.explicitLanguage = aStyleFont->mExplicitLanguage; params.orientation = orientation; params.userFontSet = aUseUserFontSet ? aPresContext->GetUserFontSet() : nullptr; params.textPerf = aPresContext->GetTextPerfMetrics(); return aPresContext->DeviceContext()->GetMetricsFor(font, params); } static nsSize CalcViewportUnitsScale(nsPresContext* aPresContext) { // The caller is making use of viewport units, so notify the pres context // that it will need to rebuild the rule tree if the size of the viewport // changes. aPresContext->SetUsesViewportUnits(true); // The default (when we have 'overflow: auto' on the root element, or // trivially for 'overflow: hidden' since we never have scrollbars in that // case) is to define the scale of the viewport units without considering // scrollbars. nsSize viewportSize(aPresContext->GetVisibleArea().Size()); // Check for 'overflow: scroll' styles on the root scroll frame. If we find // any, the standard requires us to take scrollbars into account. nsIScrollableFrame* scrollFrame = aPresContext->PresShell()->GetRootScrollFrameAsScrollable(); if (scrollFrame) { ScrollbarStyles styles(scrollFrame->GetScrollbarStyles()); if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL || styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { // Gather scrollbar size information. nsRenderingContext context( aPresContext->PresShell()->CreateReferenceRenderingContext()); nsMargin sizes(scrollFrame->GetDesiredScrollbarSizes(aPresContext, &context)); if (styles.mHorizontal == NS_STYLE_OVERFLOW_SCROLL) { // 'overflow-x: scroll' means we must consider the horizontal scrollbar, // which affects the scale of viewport height units. viewportSize.height -= sizes.TopBottom(); } if (styles.mVertical == NS_STYLE_OVERFLOW_SCROLL) { // 'overflow-y: scroll' means we must consider the vertical scrollbar, // which affects the scale of viewport width units. viewportSize.width -= sizes.LeftRight(); } } } return viewportSize; } // If |aStyleFont| is nullptr, aStyleContext->StyleFont() is used. // // In case that |aValue| is rem unit, if |aStyleContext| is null, callers must // specify a valid |aStyleFont| and |aUseProvidedRootEmSize| must be true so // that we can get the length from |aStyleFont|. static nscoord CalcLengthWith(const nsCSSValue& aValue, nscoord aFontSize, const nsStyleFont* aStyleFont, nsStyleContext* aStyleContext, nsPresContext* aPresContext, bool aUseProvidedRootEmSize, // aUseUserFontSet should always be true // except when called from // CalcLengthWithInitialFont. bool aUseUserFontSet, RuleNodeCacheConditions& aConditions) { NS_ASSERTION(aValue.IsLengthUnit() || aValue.IsCalcUnit(), "not a length or calc unit"); NS_ASSERTION(aStyleFont || aStyleContext, "Must have style data"); NS_ASSERTION(aStyleContext || aUseProvidedRootEmSize, "Must have style context or specify aUseProvidedRootEmSize"); NS_ASSERTION(aPresContext, "Must have prescontext"); if (aValue.IsFixedLengthUnit()) { return aValue.GetFixedLength(aPresContext); } if (aValue.IsPixelLengthUnit()) { return aValue.GetPixelLength(); } if (aValue.IsCalcUnit()) { // For properties for which lengths are the *only* units accepted in // calc(), we can handle calc() here and just compute a final // result. We ensure that we don't get to this code for other // properties by not calling CalcLength in those cases: SetCoord // only calls CalcLength for a calc when it is appropriate to do so. CalcLengthCalcOps ops(aFontSize, aStyleFont, aStyleContext, aPresContext, aUseProvidedRootEmSize, aUseUserFontSet, aConditions); return css::ComputeCalc(aValue, ops); } switch (aValue.GetUnit()) { // nsPresContext::SetVisibleArea and // nsPresContext::MediaFeatureValuesChanged handle dynamic changes // of the basis for viewport units by rebuilding the rule tree and // style context tree. Not caching them in the rule tree wouldn't // be sufficient to handle these changes because we also need a way // to get rid of cached values in the style context tree without any // changes in specified style. We can either do this by not caching // in the rule tree and then throwing away the style context tree // for dynamic viewport size changes, or by allowing caching in the // rule tree and using the existing rebuild style data path that // throws away the style context and the rule tree. // Thus we do cache viewport units in the rule tree. This allows us // to benefit from the performance advantages of the rule tree // (e.g., faster dynamic changes on other things, like transforms) // and allows us not to need an additional code path, in exchange // for an increased cost to dynamic changes to the viewport size // when viewport units are in use. case eCSSUnit_ViewportWidth: { nscoord viewportWidth = CalcViewportUnitsScale(aPresContext).width; return ScaleViewportCoordTrunc(aValue, viewportWidth); } case eCSSUnit_ViewportHeight: { nscoord viewportHeight = CalcViewportUnitsScale(aPresContext).height; return ScaleViewportCoordTrunc(aValue, viewportHeight); } case eCSSUnit_ViewportMin: { nsSize vuScale(CalcViewportUnitsScale(aPresContext)); nscoord viewportMin = min(vuScale.width, vuScale.height); return ScaleViewportCoordTrunc(aValue, viewportMin); } case eCSSUnit_ViewportMax: { nsSize vuScale(CalcViewportUnitsScale(aPresContext)); nscoord viewportMax = max(vuScale.width, vuScale.height); return ScaleViewportCoordTrunc(aValue, viewportMax); } // While we could deal with 'rem' units correctly by simply not // caching any data that uses them in the rule tree, it's valuable // to store them in the rule tree (for faster dynamic changes of // other things). And since the font size of the root element // changes rarely, we instead handle dynamic changes to the root // element's font size by rebuilding all style data in // nsCSSFrameConstructor::RestyleElement. case eCSSUnit_RootEM: { aPresContext->SetUsesRootEMUnits(true); nscoord rootFontSize; // NOTE: Be very careful with |styleFont|, since we haven't added any // conditions to aConditions or set it to uncacheable yet, so we don't // want to introduce any dependencies on aStyleContext's data here. const nsStyleFont *styleFont = aStyleFont ? aStyleFont : aStyleContext->StyleFont(); if (aUseProvidedRootEmSize) { // We should use the provided aFontSize as the reference length to // scale. This only happens when we are calculating font-size or // an equivalent (scriptminsize or CalcLengthWithInitialFont) on // the root element, in which case aFontSize is already the // value we want. if (aFontSize == -1) { // XXX Should this be styleFont->mSize instead to avoid taking // minfontsize prefs into account? aFontSize = styleFont->mFont.size; } rootFontSize = aFontSize; } else if (aStyleContext && !aStyleContext->GetParent()) { // This is the root element (XXX we don't really know this, but // nsRuleNode::SetFont makes the same assumption!), so we should // use StyleFont on this context to get the root element's // font size. rootFontSize = styleFont->mFont.size; } else { // This is not the root element or we are calculating something other // than font size, so rem is relative to the root element's font size. // Find the root style context by walking up the style context tree. nsStyleContext* rootStyle = aStyleContext; while (rootStyle->GetParent()) { rootStyle = rootStyle->GetParent(); } const nsStyleFont *rootStyleFont = rootStyle->StyleFont(); rootFontSize = rootStyleFont->mFont.size; } return ScaleCoordRound(aValue, float(rootFontSize)); } default: // Fall through to the code for units that can't be stored in the // rule tree because they depend on font data. break; } // Common code for units that depend on the element's font data and // thus can't be stored in the rule tree: const nsStyleFont *styleFont = aStyleFont ? aStyleFont : aStyleContext->StyleFont(); if (aFontSize == -1) { // XXX Should this be styleFont->mSize instead to avoid taking minfontsize // prefs into account? aFontSize = styleFont->mFont.size; } switch (aValue.GetUnit()) { case eCSSUnit_EM: { if (aValue.GetFloatValue() == 0.0f) { // Don't call SetFontSizeDependency for '0em'. return 0; } // CSS2.1 specifies that this unit scales to the computed font // size, not the em-width in the font metrics, despite the name. aConditions.SetFontSizeDependency(aFontSize); return ScaleCoordRound(aValue, float(aFontSize)); } case eCSSUnit_XHeight: { aPresContext->SetUsesExChUnits(true); RefPtr<nsFontMetrics> fm = GetMetricsFor(aPresContext, aStyleContext, styleFont, aFontSize, aUseUserFontSet); aConditions.SetUncacheable(); return ScaleCoordRound(aValue, float(fm->XHeight())); } case eCSSUnit_Char: { aPresContext->SetUsesExChUnits(true); RefPtr<nsFontMetrics> fm = GetMetricsFor(aPresContext, aStyleContext, styleFont, aFontSize, aUseUserFontSet); gfxFloat zeroWidth = fm->GetThebesFontGroup()->GetFirstValidFont()-> GetMetrics(fm->Orientation()).zeroOrAveCharWidth; aConditions.SetUncacheable(); return ScaleCoordRound(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * zeroWidth)); } default: NS_NOTREACHED("unexpected unit"); break; } return 0; } /* static */ nscoord nsRuleNode::CalcLength(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { NS_ASSERTION(aStyleContext, "Must have style data"); return CalcLengthWith(aValue, -1, nullptr, aStyleContext, aPresContext, false, true, aConditions); } /* Inline helper function to redirect requests to CalcLength. */ static inline nscoord CalcLength(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { return nsRuleNode::CalcLength(aValue, aStyleContext, aPresContext, aConditions); } /* static */ nscoord nsRuleNode::CalcLengthWithInitialFont(nsPresContext* aPresContext, const nsCSSValue& aValue) { nsStyleFont defaultFont(aPresContext); // FIXME: best language? RuleNodeCacheConditions conditions; return CalcLengthWith(aValue, -1, &defaultFont, nullptr, aPresContext, true, false, conditions); } struct LengthPercentPairCalcOps : public css::NumbersAlreadyNormalizedOps { typedef nsRuleNode::ComputedCalc result_type; LengthPercentPairCalcOps(nsStyleContext* aContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) : mContext(aContext), mPresContext(aPresContext), mConditions(aConditions), mHasPercent(false) {} nsStyleContext* mContext; nsPresContext* mPresContext; RuleNodeCacheConditions& mConditions; bool mHasPercent; result_type ComputeLeafValue(const nsCSSValue& aValue) { if (aValue.GetUnit() == eCSSUnit_Percent) { mHasPercent = true; return result_type(0, aValue.GetPercentValue()); } return result_type(CalcLength(aValue, mContext, mPresContext, mConditions), 0.0f); } result_type MergeAdditive(nsCSSUnit aCalcFunction, result_type aValue1, result_type aValue2) { if (aCalcFunction == eCSSUnit_Calc_Plus) { return result_type(NSCoordSaturatingAdd(aValue1.mLength, aValue2.mLength), aValue1.mPercent + aValue2.mPercent); } MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, "min() and max() are not allowed in calc() on transform"); return result_type(NSCoordSaturatingSubtract(aValue1.mLength, aValue2.mLength, 0), aValue1.mPercent - aValue2.mPercent); } result_type MergeMultiplicativeL(nsCSSUnit aCalcFunction, float aValue1, result_type aValue2) { MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, "unexpected unit"); return result_type(NSCoordSaturatingMultiply(aValue2.mLength, aValue1), aValue1 * aValue2.mPercent); } result_type MergeMultiplicativeR(nsCSSUnit aCalcFunction, result_type aValue1, float aValue2) { MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_R || aCalcFunction == eCSSUnit_Calc_Divided, "unexpected unit"); if (aCalcFunction == eCSSUnit_Calc_Divided) { aValue2 = 1.0f / aValue2; } return result_type(NSCoordSaturatingMultiply(aValue1.mLength, aValue2), aValue1.mPercent * aValue2); } }; static void SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleCoord& aCoord, nsStyleContext* aStyleContext, RuleNodeCacheConditions& aConditions) { LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aConditions); nsRuleNode::ComputedCalc vals = ComputeCalc(aValue, ops); nsStyleCoord::Calc* calcObj = new nsStyleCoord::Calc; calcObj->mLength = vals.mLength; calcObj->mPercent = vals.mPercent; calcObj->mHasPercent = ops.mHasPercent; aCoord.SetCalcValue(calcObj); } /* static */ nsRuleNode::ComputedCalc nsRuleNode::SpecifiedCalcToComputedCalc(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { LengthPercentPairCalcOps ops(aStyleContext, aPresContext, aConditions); return ComputeCalc(aValue, ops); } // This is our public API for handling calc() expressions that involve // percentages. /* static */ nscoord nsRuleNode::ComputeComputedCalc(const nsStyleCoord& aValue, nscoord aPercentageBasis) { nsStyleCoord::Calc* calc = aValue.GetCalcValue(); return calc->mLength + NSToCoordFloorClamped(aPercentageBasis * calc->mPercent); } /* static */ nscoord nsRuleNode::ComputeCoordPercentCalc(const nsStyleCoord& aCoord, nscoord aPercentageBasis) { switch (aCoord.GetUnit()) { case eStyleUnit_Coord: return aCoord.GetCoordValue(); case eStyleUnit_Percent: return NSToCoordFloorClamped(aPercentageBasis * aCoord.GetPercentValue()); case eStyleUnit_Calc: return ComputeComputedCalc(aCoord, aPercentageBasis); default: MOZ_ASSERT(false, "unexpected unit"); return 0; } } /* Given an enumerated value that represents a box position, converts it to * a float representing the percentage of the box it corresponds to. For * example, "center" becomes 0.5f. * * @param aEnumValue The enumerated value. * @return The float percent it corresponds to. */ static float GetFloatFromBoxPosition(int32_t aEnumValue) { switch (aEnumValue) { case NS_STYLE_IMAGELAYER_POSITION_LEFT: case NS_STYLE_IMAGELAYER_POSITION_TOP: return 0.0f; case NS_STYLE_IMAGELAYER_POSITION_RIGHT: case NS_STYLE_IMAGELAYER_POSITION_BOTTOM: return 1.0f; default: MOZ_FALLTHROUGH_ASSERT("unexpected box position value"); case NS_STYLE_IMAGELAYER_POSITION_CENTER: return 0.5f; } } #define SETCOORD_NORMAL 0x01 // N #define SETCOORD_AUTO 0x02 // A #define SETCOORD_INHERIT 0x04 // H #define SETCOORD_PERCENT 0x08 // P #define SETCOORD_FACTOR 0x10 // F #define SETCOORD_LENGTH 0x20 // L #define SETCOORD_INTEGER 0x40 // I #define SETCOORD_ENUMERATED 0x80 // E #define SETCOORD_NONE 0x100 // O #define SETCOORD_INITIAL_ZERO 0x200 #define SETCOORD_INITIAL_AUTO 0x400 #define SETCOORD_INITIAL_NONE 0x800 #define SETCOORD_INITIAL_NORMAL 0x1000 #define SETCOORD_INITIAL_HALF 0x2000 #define SETCOORD_INITIAL_HUNDRED_PCT 0x00004000 #define SETCOORD_INITIAL_FACTOR_ONE 0x00008000 #define SETCOORD_INITIAL_FACTOR_ZERO 0x00010000 #define SETCOORD_CALC_LENGTH_ONLY 0x00020000 #define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY #define SETCOORD_STORE_CALC 0x00080000 #define SETCOORD_BOX_POSITION 0x00100000 // exclusive with _ENUMERATED #define SETCOORD_ANGLE 0x00200000 #define SETCOORD_UNSET_INHERIT 0x00400000 #define SETCOORD_UNSET_INITIAL 0x00800000 #define SETCOORD_LP (SETCOORD_LENGTH | SETCOORD_PERCENT) #define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT) #define SETCOORD_AH (SETCOORD_AUTO | SETCOORD_INHERIT) #define SETCOORD_LAH (SETCOORD_AUTO | SETCOORD_LENGTH | SETCOORD_INHERIT) #define SETCOORD_LPH (SETCOORD_LP | SETCOORD_INHERIT) #define SETCOORD_LPAH (SETCOORD_LP | SETCOORD_AH) #define SETCOORD_LPE (SETCOORD_LP | SETCOORD_ENUMERATED) #define SETCOORD_LPEH (SETCOORD_LPE | SETCOORD_INHERIT) #define SETCOORD_LPAEH (SETCOORD_LPAH | SETCOORD_ENUMERATED) #define SETCOORD_LPO (SETCOORD_LP | SETCOORD_NONE) #define SETCOORD_LPOH (SETCOORD_LPH | SETCOORD_NONE) #define SETCOORD_LPOEH (SETCOORD_LPOH | SETCOORD_ENUMERATED) #define SETCOORD_LE (SETCOORD_LENGTH | SETCOORD_ENUMERATED) #define SETCOORD_LEH (SETCOORD_LE | SETCOORD_INHERIT) #define SETCOORD_IA (SETCOORD_INTEGER | SETCOORD_AUTO) #define SETCOORD_LAE (SETCOORD_LENGTH | SETCOORD_AUTO | SETCOORD_ENUMERATED) // changes aCoord iff it returns true static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, const nsStyleCoord& aParentCoord, int32_t aMask, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { bool result = true; if (aValue.GetUnit() == eCSSUnit_Null) { result = false; } else if ((((aMask & SETCOORD_LENGTH) != 0) && aValue.IsLengthUnit()) || (((aMask & SETCOORD_CALC_LENGTH_ONLY) != 0) && aValue.IsCalcUnit())) { nscoord len = CalcLength(aValue, aStyleContext, aPresContext, aConditions); if ((aMask & SETCOORD_CALC_CLAMP_NONNEGATIVE) && len < 0) { NS_ASSERTION(aValue.IsCalcUnit(), "parser should have ensured no nonnegative lengths"); len = 0; } aCoord.SetCoordValue(len); } else if (((aMask & SETCOORD_PERCENT) != 0) && (aValue.GetUnit() == eCSSUnit_Percent)) { aCoord.SetPercentValue(aValue.GetPercentValue()); } else if (((aMask & SETCOORD_INTEGER) != 0) && (aValue.GetUnit() == eCSSUnit_Integer)) { aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Integer); } else if (((aMask & SETCOORD_ENUMERATED) != 0) && (aValue.GetUnit() == eCSSUnit_Enumerated)) { aCoord.SetIntValue(aValue.GetIntValue(), eStyleUnit_Enumerated); } else if (((aMask & SETCOORD_BOX_POSITION) != 0) && (aValue.GetUnit() == eCSSUnit_Enumerated)) { aCoord.SetPercentValue(GetFloatFromBoxPosition(aValue.GetIntValue())); } else if (((aMask & SETCOORD_AUTO) != 0) && (aValue.GetUnit() == eCSSUnit_Auto)) { aCoord.SetAutoValue(); } else if ((((aMask & SETCOORD_INHERIT) != 0) && aValue.GetUnit() == eCSSUnit_Inherit) || (((aMask & SETCOORD_UNSET_INHERIT) != 0) && aValue.GetUnit() == eCSSUnit_Unset)) { aCoord = aParentCoord; // just inherit value from parent aConditions.SetUncacheable(); } else if (((aMask & SETCOORD_NORMAL) != 0) && (aValue.GetUnit() == eCSSUnit_Normal)) { aCoord.SetNormalValue(); } else if (((aMask & SETCOORD_NONE) != 0) && (aValue.GetUnit() == eCSSUnit_None)) { aCoord.SetNoneValue(); } else if (((aMask & SETCOORD_FACTOR) != 0) && (aValue.GetUnit() == eCSSUnit_Number)) { aCoord.SetFactorValue(aValue.GetFloatValue()); } else if (((aMask & SETCOORD_STORE_CALC) != 0) && (aValue.IsCalcUnit())) { SpecifiedCalcToComputedCalc(aValue, aCoord, aStyleContext, aConditions); } else if (aValue.GetUnit() == eCSSUnit_Initial || (aValue.GetUnit() == eCSSUnit_Unset && ((aMask & SETCOORD_UNSET_INITIAL) != 0))) { if ((aMask & SETCOORD_INITIAL_AUTO) != 0) { aCoord.SetAutoValue(); } else if ((aMask & SETCOORD_INITIAL_ZERO) != 0) { aCoord.SetCoordValue(0); } else if ((aMask & SETCOORD_INITIAL_FACTOR_ZERO) != 0) { aCoord.SetFactorValue(0.0f); } else if ((aMask & SETCOORD_INITIAL_NONE) != 0) { aCoord.SetNoneValue(); } else if ((aMask & SETCOORD_INITIAL_NORMAL) != 0) { aCoord.SetNormalValue(); } else if ((aMask & SETCOORD_INITIAL_HALF) != 0) { aCoord.SetPercentValue(0.5f); } else if ((aMask & SETCOORD_INITIAL_HUNDRED_PCT) != 0) { aCoord.SetPercentValue(1.0f); } else if ((aMask & SETCOORD_INITIAL_FACTOR_ONE) != 0) { aCoord.SetFactorValue(1.0f); } else { result = false; // didn't set anything } } else if ((aMask & SETCOORD_ANGLE) != 0 && (aValue.IsAngularUnit())) { nsStyleUnit unit; switch (aValue.GetUnit()) { case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; default: NS_NOTREACHED("unrecognized angular unit"); unit = eStyleUnit_Degree; } aCoord.SetAngleValue(aValue.GetAngleValue(), unit); } else { result = false; // didn't set anything } return result; } // This inline function offers a shortcut for SetCoord() by refusing to accept // SETCOORD_LENGTH, SETCOORD_INHERIT and SETCOORD_UNSET_* masks. static inline bool SetAbsCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, int32_t aMask) { MOZ_ASSERT((aMask & (SETCOORD_LH | SETCOORD_UNSET_INHERIT | SETCOORD_UNSET_INITIAL)) == 0, "does not handle SETCOORD_LENGTH, SETCOORD_INHERIT and " "SETCOORD_UNSET_*"); // The values of the following variables will never be used; so it does not // matter what to set. const nsStyleCoord dummyParentCoord; nsStyleContext* dummyStyleContext = nullptr; nsPresContext* dummyPresContext = nullptr; RuleNodeCacheConditions dummyCacheKey; bool rv = SetCoord(aValue, aCoord, dummyParentCoord, aMask, dummyStyleContext, dummyPresContext, dummyCacheKey); MOZ_ASSERT(dummyCacheKey.CacheableWithoutDependencies(), "SetCoord() should not modify dummyCacheKey."); return rv; } /* Given a specified value that might be a pair value, call SetCoord twice, * either using each member of the pair, or using the unpaired value twice. */ static bool SetPairCoords(const nsCSSValue& aValue, nsStyleCoord& aCoordX, nsStyleCoord& aCoordY, const nsStyleCoord& aParentX, const nsStyleCoord& aParentY, int32_t aMask, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { const nsCSSValue& valX = aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mXValue : aValue; const nsCSSValue& valY = aValue.GetUnit() == eCSSUnit_Pair ? aValue.GetPairValue().mYValue : aValue; bool cX = SetCoord(valX, aCoordX, aParentX, aMask, aStyleContext, aPresContext, aConditions); mozilla::DebugOnly<bool> cY = SetCoord(valY, aCoordY, aParentY, aMask, aStyleContext, aPresContext, aConditions); MOZ_ASSERT(cX == cY, "changed one but not the other"); return cX; } static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor, nsPresContext* aPresContext, nsStyleContext *aContext, nscolor& aResult, RuleNodeCacheConditions& aConditions) { bool result = false; nsCSSUnit unit = aValue.GetUnit(); if (aValue.IsNumericColorUnit()) { aResult = aValue.GetColorValue(); result = true; } else if (eCSSUnit_Ident == unit) { nsAutoString value; aValue.GetStringValue(value); nscolor rgba; if (NS_ColorNameToRGB(value, &rgba)) { aResult = rgba; result = true; } } else if (eCSSUnit_EnumColor == unit) { int32_t intValue = aValue.GetIntValue(); if (0 <= intValue) { LookAndFeel::ColorID colorID = (LookAndFeel::ColorID) intValue; bool useStandinsForNativeColors = aPresContext && !aPresContext->IsChrome(); if (NS_SUCCEEDED(LookAndFeel::GetColor(colorID, useStandinsForNativeColors, &aResult))) { result = true; } } else { aResult = NS_RGB(0, 0, 0); result = false; switch (intValue) { case NS_COLOR_MOZ_HYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultLinkColor(); result = true; } break; case NS_COLOR_MOZ_VISITEDHYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultVisitedLinkColor(); result = true; } break; case NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT: if (aPresContext) { aResult = aPresContext->DefaultActiveLinkColor(); result = true; } break; case NS_COLOR_CURRENTCOLOR: // The data computed from this can't be shared in the rule tree // because they could be used on a node with a different color aConditions.SetUncacheable(); if (aContext) { aResult = aContext->StyleColor()->mColor; result = true; } break; case NS_COLOR_MOZ_DEFAULT_COLOR: if (aPresContext) { aResult = aPresContext->DefaultColor(); result = true; } break; case NS_COLOR_MOZ_DEFAULT_BACKGROUND_COLOR: if (aPresContext) { aResult = aPresContext->DefaultBackgroundColor(); result = true; } break; default: NS_NOTREACHED("Should never have an unknown negative colorID."); break; } } } else if (eCSSUnit_Inherit == unit) { aResult = aParentColor; result = true; aConditions.SetUncacheable(); } else if (eCSSUnit_Enumerated == unit && aValue.GetIntValue() == NS_STYLE_COLOR_INHERIT_FROM_BODY) { NS_ASSERTION(aPresContext->CompatibilityMode() == eCompatibility_NavQuirks, "Should only get this value in quirks mode"); // We just grab the color from the prescontext, and rely on the fact that // if the body color ever changes all its descendants will get new style // contexts (but NOT necessarily new rulenodes). aResult = aPresContext->BodyTextColor(); result = true; aConditions.SetUncacheable(); } return result; } template<UnsetAction UnsetTo> static void SetComplexColor(const nsCSSValue& aValue, const StyleComplexColor& aParentColor, const StyleComplexColor& aInitialColor, nsPresContext* aPresContext, StyleComplexColor& aResult, RuleNodeCacheConditions& aConditions) { nsCSSUnit unit = aValue.GetUnit(); if (unit == eCSSUnit_Null) { return; } if (unit == eCSSUnit_Initial || (UnsetTo == eUnsetInitial && unit == eCSSUnit_Unset)) { aResult = aInitialColor; } else if (unit == eCSSUnit_Inherit || (UnsetTo == eUnsetInherit && unit == eCSSUnit_Unset)) { aConditions.SetUncacheable(); aResult = aParentColor; } else if (unit == eCSSUnit_EnumColor && aValue.GetIntValue() == NS_COLOR_CURRENTCOLOR) { aResult = StyleComplexColor::CurrentColor(); } else if (unit == eCSSUnit_ComplexColor) { aResult = aValue.GetStyleComplexColorValue(); } else { if (!SetColor(aValue, aParentColor.mColor, aPresContext, nullptr, aResult.mColor, aConditions)) { MOZ_ASSERT_UNREACHABLE("Unknown color value"); return; } aResult.mForegroundRatio = 0; } } static void SetGradientCoord(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aContext, nsStyleCoord& aResult, RuleNodeCacheConditions& aConditions) { // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT if (!SetCoord(aValue, aResult, nsStyleCoord(), SETCOORD_LPO | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC, aContext, aPresContext, aConditions)) { NS_NOTREACHED("unexpected unit for gradient anchor point"); aResult.SetNoneValue(); } } static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aContext, nsStyleGradient& aResult, RuleNodeCacheConditions& aConditions) { MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Gradient, "The given data is not a gradient"); const nsCSSValueGradient* gradient = aValue.GetGradientValue(); if (gradient->mIsExplicitSize) { SetCoord(gradient->GetRadiusX(), aResult.mRadiusX, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC, aContext, aPresContext, aConditions); if (gradient->GetRadiusY().GetUnit() != eCSSUnit_None) { SetCoord(gradient->GetRadiusY(), aResult.mRadiusY, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC, aContext, aPresContext, aConditions); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; } else { aResult.mRadiusY = aResult.mRadiusX; aResult.mShape = NS_STYLE_GRADIENT_SHAPE_CIRCULAR; } aResult.mSize = NS_STYLE_GRADIENT_SIZE_EXPLICIT_SIZE; } else if (gradient->mIsRadial) { if (gradient->GetRadialShape().GetUnit() == eCSSUnit_Enumerated) { aResult.mShape = gradient->GetRadialShape().GetIntValue(); } else { NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, "bad unit for radial shape"); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL; } if (gradient->GetRadialSize().GetUnit() == eCSSUnit_Enumerated) { aResult.mSize = gradient->GetRadialSize().GetIntValue(); } else { NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, "bad unit for radial shape"); aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; } } else { NS_ASSERTION(gradient->GetRadialShape().GetUnit() == eCSSUnit_None, "bad unit for linear shape"); NS_ASSERTION(gradient->GetRadialSize().GetUnit() == eCSSUnit_None, "bad unit for linear size"); aResult.mShape = NS_STYLE_GRADIENT_SHAPE_LINEAR; aResult.mSize = NS_STYLE_GRADIENT_SIZE_FARTHEST_CORNER; } aResult.mLegacySyntax = gradient->mIsLegacySyntax; // bg-position SetGradientCoord(gradient->mBgPos.mXValue, aPresContext, aContext, aResult.mBgPosX, aConditions); SetGradientCoord(gradient->mBgPos.mYValue, aPresContext, aContext, aResult.mBgPosY, aConditions); aResult.mRepeating = gradient->mIsRepeating; // angle const nsStyleCoord dummyParentCoord; if (!SetCoord(gradient->mAngle, aResult.mAngle, dummyParentCoord, SETCOORD_ANGLE, aContext, aPresContext, aConditions)) { NS_ASSERTION(gradient->mAngle.GetUnit() == eCSSUnit_None, "bad unit for gradient angle"); aResult.mAngle.SetNoneValue(); } // stops for (uint32_t i = 0; i < gradient->mStops.Length(); i++) { nsStyleGradientStop stop; const nsCSSValueGradientStop &valueStop = gradient->mStops[i]; if (!SetCoord(valueStop.mLocation, stop.mLocation, nsStyleCoord(), SETCOORD_LPO | SETCOORD_STORE_CALC, aContext, aPresContext, aConditions)) { NS_NOTREACHED("unexpected unit for gradient stop location"); } stop.mIsInterpolationHint = valueStop.mIsInterpolationHint; // inherit is not a valid color for stops, so we pass in a dummy // parent color NS_ASSERTION(valueStop.mColor.GetUnit() != eCSSUnit_Inherit, "inherit is not a valid color for gradient stops"); if (!valueStop.mIsInterpolationHint) { SetColor(valueStop.mColor, NS_RGB(0, 0, 0), aPresContext, aContext, stop.mColor, aConditions); } else { // Always initialize to the same color so we don't need to worry // about comparisons. stop.mColor = NS_RGB(0, 0, 0); } aResult.mStops.AppendElement(stop); } } // -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>) static void SetStyleImageToImageRect(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleImage& aResult) { MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Function && aValue.EqualsFunction(eCSSKeyword__moz_image_rect), "the value is not valid -moz-image-rect()"); nsCSSValue::Array* arr = aValue.GetArrayValue(); MOZ_ASSERT(arr && arr->Count() == 6, "invalid number of arguments"); // <uri> if (arr->Item(1).GetUnit() == eCSSUnit_Image) { SetStyleImageRequest([&](nsStyleImageRequest* req) { aResult.SetImageRequest(do_AddRef(req)); }, aStyleContext->PresContext(), arr->Item(1)); } else { NS_WARNING("nsCSSValue::Image::Image() failed?"); } // <top>, <right>, <bottom>, <left> nsStyleSides cropRect; NS_FOR_CSS_SIDES(side) { nsStyleCoord coord; const nsCSSValue& val = arr->Item(2 + side); #ifdef DEBUG bool unitOk = #endif SetAbsCoord(val, coord, SETCOORD_FACTOR | SETCOORD_PERCENT); MOZ_ASSERT(unitOk, "Incorrect data structure created by CSS parser"); cropRect.Set(side, coord); } aResult.SetCropRect(MakeUnique<nsStyleSides>(cropRect)); } static void SetStyleImage(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleImage& aResult, RuleNodeCacheConditions& aConditions) { if (aValue.GetUnit() == eCSSUnit_Null) { return; } aResult.SetNull(); switch (aValue.GetUnit()) { case eCSSUnit_Image: SetStyleImageRequest([&](nsStyleImageRequest* req) { aResult.SetImageRequest(do_AddRef(req)); }, aStyleContext->PresContext(), aValue); break; case eCSSUnit_Function: if (aValue.EqualsFunction(eCSSKeyword__moz_image_rect)) { SetStyleImageToImageRect(aStyleContext, aValue, aResult); } else { NS_NOTREACHED("-moz-image-rect() is the only expected function"); } break; case eCSSUnit_Gradient: { nsStyleGradient* gradient = new nsStyleGradient(); SetGradient(aValue, aStyleContext->PresContext(), aStyleContext, *gradient, aConditions); aResult.SetGradientData(gradient); break; } case eCSSUnit_Element: aResult.SetElementId(aValue.GetStringBufferValue()); break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: break; case eCSSUnit_URL: { #ifdef DEBUG // eCSSUnit_URL is expected only if // 1. we have eCSSUnit_URL values for if-visited style contexts, which // we can safely treat like 'none'. // 2. aValue is a local-ref URL, e.g. url(#foo). // 3. aValue is a not a local-ref URL, but it refers to an element in // the current document. For example, the url of the current document // is "http://foo.html" and aValue is url(http://foo.html#foo). // // We skip image download in TryToStartImageLoadOnValue under #2 and #3, // and that's part of reasons we get eCSSUnit_URL instead of // eCSSUnit_Image here. // Check #2. bool isLocalRef = aValue.GetURLStructValue()->IsLocalRef(); // Check #3. bool isEqualExceptRef = false; if (!isLocalRef) { nsIDocument* currentDoc = aStyleContext->PresContext()->Document(); nsIURI* docURI = currentDoc->GetDocumentURI(); nsIURI* imageURI = aValue.GetURLValue(); imageURI->EqualsExceptRef(docURI, &isEqualExceptRef); } MOZ_ASSERT(aStyleContext->IsStyleIfVisited() || isEqualExceptRef || isLocalRef, "unexpected unit; maybe nsCSSValue::Image::Image() failed?"); #endif break; } default: MOZ_ASSERT_UNREACHABLE("Unexpected Unit type."); break; } } struct SetEnumValueHelper { template<typename FieldT> static void SetIntegerValue(FieldT&, const nsCSSValue&) { // FIXME Is it possible to turn this assertion into a compilation error? MOZ_ASSERT_UNREACHABLE("inappropriate unit"); } #define DEFINE_ENUM_CLASS_SETTER(type_, min_, max_) \ static void SetEnumeratedValue(type_& aField, const nsCSSValue& aValue) \ { \ auto value = aValue.GetIntValue(); \ MOZ_ASSERT(value >= static_cast<decltype(value)>(type_::min_) && \ value <= static_cast<decltype(value)>(type_::max_), \ "inappropriate value"); \ aField = static_cast<type_>(value); \ } DEFINE_ENUM_CLASS_SETTER(StyleBoxAlign, Stretch, End) DEFINE_ENUM_CLASS_SETTER(StyleBoxDecorationBreak, Slice, Clone) DEFINE_ENUM_CLASS_SETTER(StyleBoxDirection, Normal, Reverse) DEFINE_ENUM_CLASS_SETTER(StyleBoxOrient, Horizontal, Vertical) DEFINE_ENUM_CLASS_SETTER(StyleBoxPack, Start, Justify) DEFINE_ENUM_CLASS_SETTER(StyleBoxSizing, Content, Border) DEFINE_ENUM_CLASS_SETTER(StyleClear, None, Both) DEFINE_ENUM_CLASS_SETTER(StyleFillRule, Nonzero, Evenodd) DEFINE_ENUM_CLASS_SETTER(StyleFloat, None, InlineEnd) DEFINE_ENUM_CLASS_SETTER(StyleFloatEdge, ContentBox, MarginBox) DEFINE_ENUM_CLASS_SETTER(StyleTextJustify, None, InterCharacter) DEFINE_ENUM_CLASS_SETTER(StyleUserFocus, None, SelectMenu) DEFINE_ENUM_CLASS_SETTER(StyleUserSelect, None, MozText) DEFINE_ENUM_CLASS_SETTER(StyleUserInput, None, Auto) DEFINE_ENUM_CLASS_SETTER(StyleUserModify, ReadOnly, WriteOnly) DEFINE_ENUM_CLASS_SETTER(StyleWindowDragging, Default, NoDrag) DEFINE_ENUM_CLASS_SETTER(StyleOrient, Inline, Vertical) #ifdef MOZ_XUL DEFINE_ENUM_CLASS_SETTER(StyleDisplay, None, Popup) #else DEFINE_ENUM_CLASS_SETTER(StyleDisplay, None, InlineBox) #endif #undef DEF_SET_ENUMERATED_VALUE }; template<typename FieldT> struct SetIntegerValueHelper { static void SetIntegerValue(FieldT& aField, const nsCSSValue& aValue) { aField = aValue.GetIntValue(); } static void SetEnumeratedValue(FieldT& aField, const nsCSSValue& aValue) { aField = aValue.GetIntValue(); } }; template<typename FieldT> struct SetValueHelper : Conditional<IsEnum<FieldT>::value, SetEnumValueHelper, SetIntegerValueHelper<FieldT>>::Type { template<typename ValueT> static void SetValue(FieldT& aField, const ValueT& aValue) { aField = aValue; } static void SetValue(FieldT&, unused_t) { // FIXME Is it possible to turn this assertion into a compilation error? MOZ_ASSERT_UNREACHABLE("inappropriate unit"); } }; // flags for SetValue - align values with SETCOORD_* constants // where possible #define SETVAL_INTEGER 0x40 // I #define SETVAL_ENUMERATED 0x80 // E #define SETVAL_UNSET_INHERIT 0x00400000 #define SETVAL_UNSET_INITIAL 0x00800000 // no caller cares whether aField was changed or not template<typename FieldT, typename InitialT, typename AutoT, typename NoneT, typename NormalT, typename SysFontT> static void SetValue(const nsCSSValue& aValue, FieldT& aField, RuleNodeCacheConditions& aConditions, uint32_t aMask, FieldT aParentValue, InitialT aInitialValue, AutoT aAutoValue, NoneT aNoneValue, NormalT aNormalValue, SysFontT aSystemFontValue) { typedef SetValueHelper<FieldT> Helper; switch (aValue.GetUnit()) { case eCSSUnit_Null: return; // every caller of SetValue provides inherit and initial // alternatives, so we don't require them to say so in the mask case eCSSUnit_Inherit: aConditions.SetUncacheable(); aField = aParentValue; return; case eCSSUnit_Initial: Helper::SetValue(aField, aInitialValue); return; // every caller provides one or other of these alternatives, // but they have to say which case eCSSUnit_Enumerated: if (aMask & SETVAL_ENUMERATED) { Helper::SetEnumeratedValue(aField, aValue); return; } break; case eCSSUnit_Integer: if (aMask & SETVAL_INTEGER) { Helper::SetIntegerValue(aField, aValue); return; } break; // remaining possibilities in descending order of frequency of use case eCSSUnit_Auto: Helper::SetValue(aField, aAutoValue); return; case eCSSUnit_None: Helper::SetValue(aField, aNoneValue); return; case eCSSUnit_Normal: Helper::SetValue(aField, aNormalValue); return; case eCSSUnit_System_Font: Helper::SetValue(aField, aSystemFontValue); return; case eCSSUnit_Unset: if (aMask & SETVAL_UNSET_INHERIT) { aConditions.SetUncacheable(); aField = aParentValue; return; } if (aMask & SETVAL_UNSET_INITIAL) { Helper::SetValue(aField, aInitialValue); return; } break; default: break; } NS_NOTREACHED("SetValue: inappropriate unit"); } template <typename FieldT, typename T1> static void SetValue(const nsCSSValue& aValue, FieldT& aField, RuleNodeCacheConditions& aConditions, uint32_t aMask, FieldT aParentValue, T1 aInitialValue) { SetValue(aValue, aField, aConditions, aMask, aParentValue, aInitialValue, Unused, Unused, Unused, Unused); } // flags for SetFactor #define SETFCT_POSITIVE 0x01 // assert value is >= 0.0f #define SETFCT_OPACITY 0x02 // clamp value to [0.0f .. 1.0f] #define SETFCT_NONE 0x04 // allow _None (uses aInitialValue). #define SETFCT_UNSET_INHERIT 0x00400000 #define SETFCT_UNSET_INITIAL 0x00800000 static void SetFactor(const nsCSSValue& aValue, float& aField, RuleNodeCacheConditions& aConditions, float aParentValue, float aInitialValue, uint32_t aFlags = 0) { switch (aValue.GetUnit()) { case eCSSUnit_Null: return; case eCSSUnit_Number: aField = aValue.GetFloatValue(); if (aFlags & SETFCT_POSITIVE) { NS_ASSERTION(aField >= 0.0f, "negative value for positive-only property"); if (aField < 0.0f) aField = 0.0f; } if (aFlags & SETFCT_OPACITY) { if (aField < 0.0f) aField = 0.0f; if (aField > 1.0f) aField = 1.0f; } return; case eCSSUnit_Inherit: aConditions.SetUncacheable(); aField = aParentValue; return; case eCSSUnit_Initial: aField = aInitialValue; return; case eCSSUnit_None: if (aFlags & SETFCT_NONE) { aField = aInitialValue; return; } break; case eCSSUnit_Unset: if (aFlags & SETFCT_UNSET_INHERIT) { aConditions.SetUncacheable(); aField = aParentValue; return; } if (aFlags & SETFCT_UNSET_INITIAL) { aField = aInitialValue; return; } break; default: break; } NS_NOTREACHED("SetFactor: inappropriate unit"); } void* nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) { // Check the recycle list first. return aPresContext->PresShell()->AllocateByObjectID(eArenaObjectID_nsRuleNode, sz); } // Overridden to prevent the global delete from being called, since the memory // came out of an nsIArena instead of the global delete operator's heap. void nsRuleNode::Destroy() { // Destroy ourselves. this->~nsRuleNode(); // Don't let the memory be freed, since it will be recycled // instead. Don't call the global operator delete. mPresContext->PresShell()->FreeByObjectID(eArenaObjectID_nsRuleNode, this); } already_AddRefed<nsRuleNode> nsRuleNode::CreateRootNode(nsPresContext* aPresContext) { return do_AddRef(new (aPresContext) nsRuleNode(aPresContext, nullptr, nullptr, SheetType::Unknown, false)); } nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, nsIStyleRule* aRule, SheetType aLevel, bool aIsImportant) : mPresContext(aContext), mParent(aParent), mRule(aRule), mNextSibling(nullptr), mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) | (aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)), mNoneBits(aParent ? aParent->mNoneBits & NS_RULE_NODE_HAS_ANIMATION_DATA : 0), mRefCnt(0) { MOZ_ASSERT(aContext); MOZ_ASSERT(IsRoot() == !aRule, "non-root rule nodes must have a rule"); mChildren.asVoid = nullptr; MOZ_COUNT_CTOR(nsRuleNode); NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits"); NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes"); MOZ_ASSERT(aContext->StyleSet()->IsGecko(), "ServoStyleSets should not have rule nodes"); aContext->StyleSet()->AsGecko()->RuleNodeUnused(this, /* aMayGC = */ false); // nsStyleSet::GetContext depends on there being only one animation // rule. MOZ_ASSERT(IsRoot() || GetLevel() != SheetType::Animation || mParent->IsRoot() || mParent->GetLevel() != SheetType::Animation, "must be only one rule at animation level"); } nsRuleNode::~nsRuleNode() { MOZ_ASSERT(!HaveChildren()); MOZ_COUNT_DTOR(nsRuleNode); if (mParent) { mParent->RemoveChild(this); } if (mStyleData.mResetData || mStyleData.mInheritedData) mStyleData.Destroy(mDependentBits, mPresContext); } nsRuleNode* nsRuleNode::Transition(nsIStyleRule* aRule, SheetType aLevel, bool aIsImportantRule) { #ifdef DEBUG { RefPtr<css::Declaration> declaration(do_QueryObject(aRule)); MOZ_ASSERT(!declaration || !declaration->IsMutable(), "caller must call Declaration::SetImmutable first"); } #endif nsRuleNode* next = nullptr; nsRuleNode::Key key(aRule, aLevel, aIsImportantRule); if (HaveChildren() && !ChildrenAreHashed()) { int32_t numKids = 0; nsRuleNode* curr = ChildrenList(); while (curr && curr->GetKey() != key) { curr = curr->mNextSibling; ++numKids; } if (curr) next = curr; else if (numKids >= kMaxChildrenInList) ConvertChildrenToHash(numKids); } if (ChildrenAreHashed()) { auto entry = static_cast<ChildrenHashEntry*>(ChildrenHash()->Add(&key, fallible)); if (!entry) { NS_WARNING("out of memory"); return this; } if (entry->mRuleNode) next = entry->mRuleNode; else { next = entry->mRuleNode = new (mPresContext) nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); } } else if (!next) { // Create the new entry in our list. next = new (mPresContext) nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule); next->mNextSibling = ChildrenList(); SetChildrenList(next); } return next; } nsRuleNode* nsRuleNode::RuleTree() { nsRuleNode* n = this; while (n->mParent) { n = n->mParent; } return n; } void nsRuleNode::SetUsedDirectly() { mDependentBits |= NS_RULE_NODE_USED_DIRECTLY; // Maintain the invariant that any rule node that is used directly has // all structs that live in the rule tree cached (which // nsRuleNode::GetStyleData depends on for speed). if (mDependentBits & NS_STYLE_INHERIT_MASK) { for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) { uint32_t bit = nsCachedStyleData::GetBitForSID(sid); if (mDependentBits & bit) { nsRuleNode *source = mParent; while ((source->mDependentBits & bit) && !source->IsUsedDirectly()) { source = source->mParent; } void *data = source->mStyleData.GetStyleData(sid); NS_ASSERTION(data, "unexpected null struct"); mStyleData.SetStyleData(sid, mPresContext, data); } } } } void nsRuleNode::ConvertChildrenToHash(int32_t aNumKids) { NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(), "must have a non-empty list of children"); PLDHashTable *hash = new PLDHashTable(&ChildrenHashOps, sizeof(ChildrenHashEntry), aNumKids); for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) { Key key = curr->GetKey(); // This will never fail because of the initial size we gave the table. auto entry = static_cast<ChildrenHashEntry*>(hash->Add(&key)); NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list"); entry->mRuleNode = curr; } SetChildrenHash(hash); } void nsRuleNode::RemoveChild(nsRuleNode* aNode) { MOZ_ASSERT(HaveChildren()); if (ChildrenAreHashed()) { PLDHashTable* children = ChildrenHash(); Key key = aNode->GetKey(); MOZ_ASSERT(children->Search(&key)); children->Remove(&key); if (children->EntryCount() == 0) { delete children; mChildren.asVoid = nullptr; } } else { // This linear traversal is unfortunate, but we do the same thing when // adding nodes. The traversal is bounded by kMaxChildrenInList. nsRuleNode** curr = &mChildren.asList; while (*curr != aNode) { curr = &((*curr)->mNextSibling); MOZ_ASSERT(*curr); } *curr = (*curr)->mNextSibling; // If there was one element in the list, this sets mChildren.asList // to 0, and HaveChildren() will return false. } } inline void nsRuleNode::PropagateNoneBit(uint32_t aBit, nsRuleNode* aHighestNode) { nsRuleNode* curr = this; for (;;) { NS_ASSERTION(!(curr->mNoneBits & aBit), "propagating too far"); curr->mNoneBits |= aBit; if (curr == aHighestNode) break; curr = curr->mParent; } } inline void nsRuleNode::PropagateDependentBit(nsStyleStructID aSID, nsRuleNode* aHighestNode, void* aStruct) { NS_ASSERTION(aStruct, "expected struct"); uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); for (nsRuleNode* curr = this; curr != aHighestNode; curr = curr->mParent) { if (curr->mDependentBits & bit) { #ifdef DEBUG while (curr != aHighestNode) { NS_ASSERTION(curr->mDependentBits & bit, "bit not set"); curr = curr->mParent; } #endif break; } curr->mDependentBits |= bit; if (curr->IsUsedDirectly()) { curr->mStyleData.SetStyleData(aSID, mPresContext, aStruct); } } } /* static */ void nsRuleNode::PropagateGrandancestorBit(nsStyleContext* aContext, nsStyleContext* aContextInheritedFrom) { MOZ_ASSERT(aContext); MOZ_ASSERT(aContextInheritedFrom && aContextInheritedFrom != aContext, "aContextInheritedFrom must be an ancestor of aContext"); for (nsStyleContext* context = aContext->GetParent(); context != aContextInheritedFrom; context = context->GetParent()) { if (!context) { MOZ_ASSERT(false, "aContextInheritedFrom must be an ancestor of " "aContext's parent"); break; } context->AddStyleBit(NS_STYLE_CHILD_USES_GRANDANCESTOR_STYLE); } } /* * The following "Check" functions are used for determining what type of * sharing can be used for the data on this rule node. MORE HERE... */ /* * a callback function that that can revise the result of * CheckSpecifiedProperties before finishing; aResult is the current * result, and it returns the revised one. */ typedef nsRuleNode::RuleDetail (* CheckCallbackFn)(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult); /** * @param aValue the value being examined * @param aSpecifiedCount to be incremented by one if the value is specified * @param aInheritedCount to be incremented by one if the value is set to inherit * @param aUnsetCount to be incremented by one if the value is set to unset */ inline void ExamineCSSValue(const nsCSSValue& aValue, uint32_t& aSpecifiedCount, uint32_t& aInheritedCount, uint32_t& aUnsetCount) { if (aValue.GetUnit() != eCSSUnit_Null) { ++aSpecifiedCount; if (aValue.GetUnit() == eCSSUnit_Inherit) { ++aInheritedCount; } else if (aValue.GetUnit() == eCSSUnit_Unset) { ++aUnsetCount; } } } static nsRuleNode::RuleDetail CheckFontCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // em, ex, percent, 'larger', and 'smaller' values on font-size depend // on the parent context's font-size // Likewise, 'lighter' and 'bolder' values of 'font-weight', and 'wider' // and 'narrower' values of 'font-stretch' depend on the parent. const nsCSSValue& size = *aRuleData->ValueForFontSize(); const nsCSSValue& weight = *aRuleData->ValueForFontWeight(); if ((size.IsRelativeLengthUnit() && size.GetUnit() != eCSSUnit_RootEM) || size.GetUnit() == eCSSUnit_Percent || (size.GetUnit() == eCSSUnit_Enumerated && (size.GetIntValue() == NS_STYLE_FONT_SIZE_SMALLER || size.GetIntValue() == NS_STYLE_FONT_SIZE_LARGER)) || aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Integer || (weight.GetUnit() == eCSSUnit_Enumerated && (weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_BOLDER || weight.GetIntValue() == NS_STYLE_FONT_WEIGHT_LIGHTER))) { NS_ASSERTION(aResult == nsRuleNode::eRulePartialReset || aResult == nsRuleNode::eRuleFullReset || aResult == nsRuleNode::eRulePartialMixed || aResult == nsRuleNode::eRuleFullMixed, "we know we already have a reset-counted property"); // Promote reset to mixed since we have something that depends on // the parent. But never promote to inherited since that could // cause inheritance of the exact value. if (aResult == nsRuleNode::eRulePartialReset) aResult = nsRuleNode::eRulePartialMixed; else if (aResult == nsRuleNode::eRuleFullReset) aResult = nsRuleNode::eRuleFullMixed; } return aResult; } static nsRuleNode::RuleDetail CheckColorCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // currentColor values for color require inheritance const nsCSSValue* colorValue = aRuleData->ValueForColor(); if (colorValue->GetUnit() == eCSSUnit_EnumColor && colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) { NS_ASSERTION(aResult == nsRuleNode::eRuleFullReset, "we should already be counted as full-reset"); aResult = nsRuleNode::eRuleFullInherited; } return aResult; } static nsRuleNode::RuleDetail CheckTextCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); if (textAlignValue->GetUnit() == eCSSUnit_Enumerated && (textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT || textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_MATCH_PARENT)) { // Promote reset to mixed since we have something that depends on // the parent. if (aResult == nsRuleNode::eRulePartialReset) aResult = nsRuleNode::eRulePartialMixed; else if (aResult == nsRuleNode::eRuleFullReset) aResult = nsRuleNode::eRuleFullMixed; } return aResult; } static nsRuleNode::RuleDetail CheckVariablesCallback(const nsRuleData* aRuleData, nsRuleNode::RuleDetail aResult) { // We don't actually have any properties on nsStyleVariables, so we do // all of the RuleDetail calculation in here. if (aRuleData->mVariables) { return nsRuleNode::eRulePartialMixed; } return nsRuleNode::eRuleNone; } #define FLAG_DATA_FOR_PROPERTY(name_, id_, method_, flags_, pref_, \ parsevariant_, kwtable_, stylestructoffset_, \ animtype_) \ flags_, // The order here must match the enums in *CheckCounter in nsCSSProps.cpp. static const uint32_t gFontFlags[] = { #define CSS_PROP_FONT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_FONT }; static const uint32_t gDisplayFlags[] = { #define CSS_PROP_DISPLAY FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_DISPLAY }; static const uint32_t gVisibilityFlags[] = { #define CSS_PROP_VISIBILITY FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_VISIBILITY }; static const uint32_t gMarginFlags[] = { #define CSS_PROP_MARGIN FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_MARGIN }; static const uint32_t gBorderFlags[] = { #define CSS_PROP_BORDER FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_BORDER }; static const uint32_t gPaddingFlags[] = { #define CSS_PROP_PADDING FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_PADDING }; static const uint32_t gOutlineFlags[] = { #define CSS_PROP_OUTLINE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_OUTLINE }; static const uint32_t gListFlags[] = { #define CSS_PROP_LIST FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_LIST }; static const uint32_t gColorFlags[] = { #define CSS_PROP_COLOR FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_COLOR }; static const uint32_t gBackgroundFlags[] = { #define CSS_PROP_BACKGROUND FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_BACKGROUND }; static const uint32_t gPositionFlags[] = { #define CSS_PROP_POSITION FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_POSITION }; static const uint32_t gTableFlags[] = { #define CSS_PROP_TABLE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TABLE }; static const uint32_t gTableBorderFlags[] = { #define CSS_PROP_TABLEBORDER FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TABLEBORDER }; static const uint32_t gContentFlags[] = { #define CSS_PROP_CONTENT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_CONTENT }; static const uint32_t gTextFlags[] = { #define CSS_PROP_TEXT FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TEXT }; static const uint32_t gTextResetFlags[] = { #define CSS_PROP_TEXTRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_TEXTRESET }; static const uint32_t gUserInterfaceFlags[] = { #define CSS_PROP_USERINTERFACE FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_USERINTERFACE }; static const uint32_t gUIResetFlags[] = { #define CSS_PROP_UIRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_UIRESET }; static const uint32_t gXULFlags[] = { #define CSS_PROP_XUL FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_XUL }; static const uint32_t gSVGFlags[] = { #define CSS_PROP_SVG FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_SVG }; static const uint32_t gSVGResetFlags[] = { #define CSS_PROP_SVGRESET FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_SVGRESET }; static const uint32_t gColumnFlags[] = { #define CSS_PROP_COLUMN FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_COLUMN }; // There are no properties in nsStyleVariables, but we can't have a // zero length array. static const uint32_t gVariablesFlags[] = { 0, #define CSS_PROP_VARIABLES FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_VARIABLES }; static_assert(sizeof(gVariablesFlags) == sizeof(uint32_t), "if nsStyleVariables has properties now you can remove the dummy " "gVariablesFlags entry"); static const uint32_t gEffectsFlags[] = { #define CSS_PROP_EFFECTS FLAG_DATA_FOR_PROPERTY #include "nsCSSPropList.h" #undef CSS_PROP_EFFECTS }; #undef FLAG_DATA_FOR_PROPERTY static const uint32_t* gFlagsByStruct[] = { #define STYLE_STRUCT(name, checkdata_cb) \ g##name##Flags, #include "nsStyleStructList.h" #undef STYLE_STRUCT }; static const CheckCallbackFn gCheckCallbacks[] = { #define STYLE_STRUCT(name, checkdata_cb) \ checkdata_cb, #include "nsStyleStructList.h" #undef STYLE_STRUCT }; #ifdef DEBUG static bool AreAllMathMLPropertiesUndefined(const nsRuleData* aRuleData) { return aRuleData->ValueForScriptLevel()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForScriptSizeMultiplier()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForScriptMinSize()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForMathVariant()->GetUnit() == eCSSUnit_Null && aRuleData->ValueForMathDisplay()->GetUnit() == eCSSUnit_Null; } #endif inline nsRuleNode::RuleDetail nsRuleNode::CheckSpecifiedProperties(const nsStyleStructID aSID, const nsRuleData* aRuleData) { // Build a count of the: uint32_t total = 0, // total number of props in the struct specified = 0, // number that were specified for this node inherited = 0, // number that were 'inherit' (and not // eCSSUnit_Inherit) for this node unset = 0; // number that were 'unset' // See comment in nsRuleData.h above mValueOffsets. MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0, "we assume the value offset is zero instead of adding it"); for (nsCSSValue *values = aRuleData->mValueStorage, *values_end = values + nsCSSProps::PropertyCountInStruct(aSID); values != values_end; ++values) { ++total; ExamineCSSValue(*values, specified, inherited, unset); } if (!nsCachedStyleData::IsReset(aSID)) { // For inherited properties, 'unset' means the same as 'inherit'. inherited += unset; unset = 0; } #if 0 printf("CheckSpecifiedProperties: SID=%d total=%d spec=%d inh=%d.\n", aSID, total, specified, inherited); #endif NS_ASSERTION(aSID != eStyleStruct_Font || mPresContext->Document()->GetMathMLEnabled() || AreAllMathMLPropertiesUndefined(aRuleData), "MathML style property was defined even though MathML is disabled"); /* * Return the most specific information we can: prefer None or Full * over Partial, and Reset or Inherited over Mixed, since we can * optimize based on the edge cases and not the in-between cases. */ nsRuleNode::RuleDetail result; if (inherited == total) result = eRuleFullInherited; else if (specified == total // MathML defines 5 properties in Font that will never be set when // MathML is not in use. Therefore if all but five // properties have been set, and MathML is not enabled, we can treat // this as fully specified. Code in nsMathMLElementFactory will // rebuild the rule tree and style data when MathML is first enabled // (see nsMathMLElement::BindToTree). || (aSID == eStyleStruct_Font && specified + 5 == total && !mPresContext->Document()->GetMathMLEnabled()) ) { if (inherited == 0) result = eRuleFullReset; else result = eRuleFullMixed; } else if (specified == 0) result = eRuleNone; else if (specified == inherited) result = eRulePartialInherited; else if (inherited == 0) result = eRulePartialReset; else result = eRulePartialMixed; CheckCallbackFn cb = gCheckCallbacks[aSID]; if (cb) { result = (*cb)(aRuleData, result); } return result; } // If we need to restrict which properties apply to the style context, // return the bit to check in nsCSSProp's flags table. Otherwise, // return 0. inline uint32_t GetPseudoRestriction(nsStyleContext *aContext) { // This needs to match nsStyleSet::WalkRestrictionRule. uint32_t pseudoRestriction = 0; nsIAtom *pseudoType = aContext->GetPseudo(); if (pseudoType) { if (pseudoType == nsCSSPseudoElements::firstLetter) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LETTER; } else if (pseudoType == nsCSSPseudoElements::firstLine) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_FIRST_LINE; } else if (pseudoType == nsCSSPseudoElements::placeholder) { pseudoRestriction = CSS_PROPERTY_APPLIES_TO_PLACEHOLDER; } } return pseudoRestriction; } static void UnsetPropertiesWithoutFlags(const nsStyleStructID aSID, nsRuleData* aRuleData, uint32_t aFlags) { NS_ASSERTION(aFlags != 0, "aFlags must be nonzero"); const uint32_t *flagData = gFlagsByStruct[aSID]; // See comment in nsRuleData.h above mValueOffsets. MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0, "we assume the value offset is zero instead of adding it"); nsCSSValue *values = aRuleData->mValueStorage; for (size_t i = 0, i_end = nsCSSProps::PropertyCountInStruct(aSID); i != i_end; ++i) { if ((flagData[i] & aFlags) != aFlags) values[i].Reset(); } } /** * We allocate arrays of CSS values with alloca. (These arrays are a * fixed size per style struct, but we don't want to waste the * allocation and construction/destruction costs of the big structs when * we're handling much smaller ones.) Since the lifetime of an alloca * allocation is the life of the calling function, the caller must call * alloca. However, to ensure that constructors and destructors are * balanced, we do the constructor and destructor calling from this RAII * class, AutoCSSValueArray. */ struct AutoCSSValueArray { /** * aStorage must be the result of alloca(aCount * sizeof(nsCSSValue)) */ AutoCSSValueArray(void* aStorage, size_t aCount) { MOZ_ASSERT(size_t(aStorage) % NS_ALIGNMENT_OF(nsCSSValue) == 0, "bad alignment from alloca"); mCount = aCount; // Don't use placement new[], since it might store extra data // for the count (on Windows!). mArray = static_cast<nsCSSValue*>(aStorage); for (size_t i = 0; i < mCount; ++i) { new (KnownNotNull, mArray + i) nsCSSValue(); } } ~AutoCSSValueArray() { for (size_t i = 0; i < mCount; ++i) { mArray[i].~nsCSSValue(); } } nsCSSValue* get() { return mArray; } private: nsCSSValue *mArray; size_t mCount; }; /* static */ bool nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID, nsRuleData* aRuleData, nsStyleContext* aContext) { MOZ_ASSERT(aSID != eStyleStruct_Variables); MOZ_ASSERT(aRuleData->mSIDs & nsCachedStyleData::GetBitForSID(aSID)); MOZ_ASSERT(aRuleData->mValueOffsets[aSID] == 0); nsCSSParser parser; bool anyTokenStreams = false; // Look at each property in the nsRuleData for the given style struct. size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); for (nsCSSValue* value = aRuleData->mValueStorage, *values_end = aRuleData->mValueStorage + nprops; value != values_end; value++) { if (value->GetUnit() != eCSSUnit_TokenStream) { continue; } const CSSVariableValues* variables = &aContext->StyleVariables()->mVariables; nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue(); MOZ_ASSERT(tokenStream->mLevel != SheetType::Count, "Token stream should have a defined level"); AutoRestore<SheetType> saveLevel(aRuleData->mLevel); aRuleData->mLevel = tokenStream->mLevel; // Note that ParsePropertyWithVariableReferences relies on the fact // that the nsCSSValue in aRuleData for the property we are re-parsing // is still the token stream value. When // ParsePropertyWithVariableReferences calls // nsCSSExpandedDataBlock::MapRuleInfoInto, that function will add // the ImageValue that is created into the token stream object's // mImageValues table; see the comment above mImageValues for why. // XXX Should pass in sheet here (see bug 952338). parser.ParsePropertyWithVariableReferences( tokenStream->mPropertyID, tokenStream->mShorthandPropertyID, tokenStream->mTokenStream, variables, aRuleData, tokenStream->mSheetURI, tokenStream->mBaseURI, tokenStream->mSheetPrincipal, nullptr, tokenStream->mLineNumber, tokenStream->mLineOffset); aRuleData->mConditions.SetUncacheable(); anyTokenStreams = true; } return anyTokenStreams; } const void* nsRuleNode::WalkRuleTree(const nsStyleStructID aSID, nsStyleContext* aContext) { // use placement new[] on the result of alloca() to allocate a // variable-sized stack array, including execution of constructors, // and use an RAII class to run the destructors too. size_t nprops = nsCSSProps::PropertyCountInStruct(aSID); void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); AutoCSSValueArray dataArray(dataStorage, nprops); nsRuleData ruleData(nsCachedStyleData::GetBitForSID(aSID), dataArray.get(), mPresContext, aContext); ruleData.mValueOffsets[aSID] = 0; // We start at the most specific rule in the tree. void* startStruct = nullptr; nsRuleNode* ruleNode = this; nsRuleNode* highestNode = nullptr; // The highest node in the rule tree // that has the same properties // specified for struct |aSID| as // |this| does. nsRuleNode* rootNode = this; // After the loop below, this will be the // highest node that we've walked without // finding cached data on the rule tree. // If we don't find any cached data, it // will be the root. (XXX misnamed) RuleDetail detail = eRuleNone; uint32_t bit = nsCachedStyleData::GetBitForSID(aSID); while (ruleNode) { // See if this rule node has cached the fact that the remaining // nodes along this path specify no data whatsoever. if (ruleNode->mNoneBits & bit) break; // If the dependent bit is set on a rule node for this struct, that // means its rule won't have any information to add, so skip it. // NOTE: If we exit the loop because of the !IsUsedDirectly() check, // then we're guaranteed to break immediately afterwards due to a // non-null startStruct. while ((ruleNode->mDependentBits & bit) && !ruleNode->IsUsedDirectly()) { NS_ASSERTION(ruleNode->mStyleData.GetStyleData(aSID) == nullptr, "dependent bit with cached data makes no sense"); // Climb up to the next rule in the tree (a less specific rule). rootNode = ruleNode; ruleNode = ruleNode->mParent; NS_ASSERTION(!(ruleNode->mNoneBits & bit), "can't have both bits set"); } // Check for cached data after the inner loop above -- otherwise // we'll miss it. startStruct = ruleNode->mStyleData.GetStyleData(aSID); if (startStruct) break; // We found a rule with fully specified data. We don't // need to go up the tree any further, since the remainder // of this branch has already been computed. // Ask the rule to fill in the properties that it specifies. nsIStyleRule *rule = ruleNode->mRule; if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); } // Now we check to see how many properties have been specified by // the rules we've examined so far. RuleDetail oldDetail = detail; detail = CheckSpecifiedProperties(aSID, &ruleData); if (oldDetail == eRuleNone && detail != eRuleNone) highestNode = ruleNode; if (detail == eRuleFullReset || detail == eRuleFullMixed || detail == eRuleFullInherited) break; // We don't need to examine any more rules. All properties // have been fully specified. // Climb up to the next rule in the tree (a less specific rule). rootNode = ruleNode; ruleNode = ruleNode->mParent; } bool recomputeDetail = false; // If we are computing a style struct other than nsStyleVariables, and // ruleData has any properties with variable references (nsCSSValues of // type eCSSUnit_TokenStream), then we need to resolve these. if (aSID != eStyleStruct_Variables) { // A property's value might have became 'inherit' after resolving // variable references. (This happens when an inherited property // fails to parse its resolved value.) We need to recompute // |detail| in case this happened. recomputeDetail = ResolveVariableReferences(aSID, &ruleData, aContext); } // If needed, unset the properties that don't have a flag that allows // them to be set for this style context. (For example, only some // properties apply to :first-line and :first-letter.) uint32_t pseudoRestriction = GetPseudoRestriction(aContext); if (pseudoRestriction) { UnsetPropertiesWithoutFlags(aSID, &ruleData, pseudoRestriction); // We need to recompute |detail| based on the restrictions we just applied. // We can adjust |detail| arbitrarily because of the restriction // rule added in nsStyleSet::WalkRestrictionRule. recomputeDetail = true; } if (recomputeDetail) { detail = CheckSpecifiedProperties(aSID, &ruleData); } NS_ASSERTION(!startStruct || (detail != eRuleFullReset && detail != eRuleFullMixed && detail != eRuleFullInherited), "can't have start struct and be fully specified"); bool isReset = nsCachedStyleData::IsReset(aSID); if (!highestNode) highestNode = rootNode; MOZ_ASSERT(!(aSID == eStyleStruct_Variables && startStruct), "if we start caching Variables structs in the rule tree, then " "not forcing detail to eRulePartialMixed just below is no " "longer valid"); if (detail == eRuleNone && isReset) { // We specified absolutely no rule information for a reset struct, and we // may or may not have found a parent rule in the tree that specified all // the rule information. Regardless, we don't need to use any cache // conditions if we cache this struct in the rule tree. // // Normally ruleData.mConditions would already indicate that the struct // is cacheable without conditions if detail is eRuleNone, but because // of the UnsetPropertiesWithoutFlags call above, we may have encountered // some rules with dependencies, which we then cleared out of ruleData. // // ruleData.mConditions could also indicate we are not cacheable at all, // such as when AnimValuesStyleRule prevents us from caching structs // when attempting to apply animations to pseudos. // // So if we we are uncacheable, we leave it, but if we are cacheable // with dependencies, we convert that to cacheable without dependencies. if (ruleData.mConditions.CacheableWithDependencies()) { MOZ_ASSERT(pseudoRestriction, "should only be cacheable with dependencies if we had a " "pseudo restriction"); ruleData.mConditions.Clear(); } else { // XXXheycam We shouldn't have `|| GetLevel() == SheetType::Transition` // in the assertion condition, but rule nodes created by // ResolveStyleByAddingRules don't call SetIsAnimationRule(). MOZ_ASSERT(ruleData.mConditions.CacheableWithoutDependencies() || ((HasAnimationData() || GetLevel() == SheetType::Transition) && aContext->GetParent() && aContext->GetParent()->HasPseudoElementData()), "should only be uncacheable if we had an animation rule " "and we're inside a pseudo"); } } if (!ruleData.mConditions.CacheableWithoutDependencies() && aSID != eStyleStruct_Variables) { // Treat as though some data is specified to avoid the optimizations and // force data computation. // // We don't need to do this for Variables structs since we know those are // never cached in the rule tree, and it avoids wasteful computation of a // new Variables struct when we have no additional variable declarations, // which otherwise could happen when there is an AnimValuesStyleRule // (which calls SetUncacheable for style contexts with pseudo data). detail = eRulePartialMixed; } if (detail == eRuleNone && startStruct) { // We specified absolutely no rule information, but a parent rule in the tree // specified all the rule information. We set a bit along the branch from our // node in the tree to the node that specified the data that tells nodes on that // branch that they never need to examine their rules for this particular struct type // ever again. PropagateDependentBit(aSID, ruleNode, startStruct); // For inherited structs, mark the struct (which will be set on // the context by our caller) as not being owned by the context. if (!isReset) { aContext->AddStyleBit(nsCachedStyleData::GetBitForSID(aSID)); } else if (HasAnimationData()) { // If we have animation data, the struct should be cached on the style // context so that we can peek the struct. // See comment in AnimValuesStyleRule::MapRuleInfoInto. StoreStyleOnContext(aContext, aSID, startStruct); } return startStruct; } if ((!startStruct && !isReset && (detail == eRuleNone || detail == eRulePartialInherited)) || detail == eRuleFullInherited) { // We specified no non-inherited information and neither did any of // our parent rules. // We set a bit along the branch from the highest node (ruleNode) // down to our node (this) indicating that no non-inherited data was // specified. This bit is guaranteed to be set already on the path // from the highest node to the root node in the case where // (detail == eRuleNone), which is the most common case here. // We must check |!isReset| because the Compute*Data functions for // reset structs wouldn't handle none bits correctly. if (highestNode != this && !isReset) PropagateNoneBit(bit, highestNode); // All information must necessarily be inherited from our parent style context. // In the absence of any computed data in the rule tree and with // no rules specified that didn't have values of 'inherit', we should check our parent. nsStyleContext* parentContext = aContext->GetParent(); if (isReset) { /* Reset structs don't inherit from first-line. */ /* See similar code in COMPUTE_START_RESET */ while (parentContext && parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { parentContext = parentContext->GetParent(); } if (parentContext && parentContext != aContext->GetParent()) { PropagateGrandancestorBit(aContext, parentContext); } } if (parentContext) { // We have a parent, and so we should just inherit from the parent. // Set the inherit bits on our context. These bits tell the style context that // it never has to go back to the rule tree for data. Instead the style context tree // should be walked to find the data. const void* parentStruct = parentContext->StyleData(aSID); aContext->AddStyleBit(bit); // makes const_cast OK. aContext->SetStyle(aSID, const_cast<void*>(parentStruct)); if (isReset) { parentContext->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE); } return parentStruct; } else // We are the root. In the case of fonts, the default values just // come from the pres context. return SetDefaultOnRoot(aSID, aContext); } typedef const void* (nsRuleNode::*ComputeFunc)(void*, const nsRuleData*, nsStyleContext*, nsRuleNode*, RuleDetail, const RuleNodeCacheConditions); static const ComputeFunc sComputeFuncs[] = { #define STYLE_STRUCT(name, checkdata_cb) &nsRuleNode::Compute##name##Data, #include "nsStyleStructList.h" #undef STYLE_STRUCT }; // We need to compute the data from the information that the rules specified. return (this->*sComputeFuncs[aSID])(startStruct, &ruleData, aContext, highestNode, detail, ruleData.mConditions); } const void* nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContext) { switch (aSID) { case eStyleStruct_Font: { nsStyleFont* fontData = new (mPresContext) nsStyleFont(mPresContext); nscoord minimumFontSize = mPresContext->MinFontSize(fontData->mLanguage); if (minimumFontSize > 0 && !mPresContext->IsChrome()) { fontData->mFont.size = std::max(fontData->mSize, minimumFontSize); } else { fontData->mFont.size = fontData->mSize; } aContext->SetStyle(eStyleStruct_Font, fontData); return fontData; } case eStyleStruct_Display: { nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay(mPresContext); aContext->SetStyle(eStyleStruct_Display, disp); return disp; } case eStyleStruct_Visibility: { nsStyleVisibility* vis = new (mPresContext) nsStyleVisibility(mPresContext); aContext->SetStyle(eStyleStruct_Visibility, vis); return vis; } case eStyleStruct_Text: { nsStyleText* text = new (mPresContext) nsStyleText(mPresContext); aContext->SetStyle(eStyleStruct_Text, text); return text; } case eStyleStruct_TextReset: { nsStyleTextReset* text = new (mPresContext) nsStyleTextReset(mPresContext); aContext->SetStyle(eStyleStruct_TextReset, text); return text; } case eStyleStruct_Color: { nsStyleColor* color = new (mPresContext) nsStyleColor(mPresContext); aContext->SetStyle(eStyleStruct_Color, color); return color; } case eStyleStruct_Background: { nsStyleBackground* bg = new (mPresContext) nsStyleBackground(mPresContext); aContext->SetStyle(eStyleStruct_Background, bg); return bg; } case eStyleStruct_Margin: { nsStyleMargin* margin = new (mPresContext) nsStyleMargin(mPresContext); aContext->SetStyle(eStyleStruct_Margin, margin); return margin; } case eStyleStruct_Border: { nsStyleBorder* border = new (mPresContext) nsStyleBorder(mPresContext); aContext->SetStyle(eStyleStruct_Border, border); return border; } case eStyleStruct_Padding: { nsStylePadding* padding = new (mPresContext) nsStylePadding(mPresContext); aContext->SetStyle(eStyleStruct_Padding, padding); return padding; } case eStyleStruct_Outline: { nsStyleOutline* outline = new (mPresContext) nsStyleOutline(mPresContext); aContext->SetStyle(eStyleStruct_Outline, outline); return outline; } case eStyleStruct_List: { nsStyleList* list = new (mPresContext) nsStyleList(mPresContext); aContext->SetStyle(eStyleStruct_List, list); return list; } case eStyleStruct_Position: { nsStylePosition* pos = new (mPresContext) nsStylePosition(mPresContext); aContext->SetStyle(eStyleStruct_Position, pos); return pos; } case eStyleStruct_Table: { nsStyleTable* table = new (mPresContext) nsStyleTable(mPresContext); aContext->SetStyle(eStyleStruct_Table, table); return table; } case eStyleStruct_TableBorder: { nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(mPresContext); aContext->SetStyle(eStyleStruct_TableBorder, table); return table; } case eStyleStruct_Content: { nsStyleContent* content = new (mPresContext) nsStyleContent(mPresContext); aContext->SetStyle(eStyleStruct_Content, content); return content; } case eStyleStruct_UserInterface: { nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface(mPresContext); aContext->SetStyle(eStyleStruct_UserInterface, ui); return ui; } case eStyleStruct_UIReset: { nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset(mPresContext); aContext->SetStyle(eStyleStruct_UIReset, ui); return ui; } case eStyleStruct_XUL: { nsStyleXUL* xul = new (mPresContext) nsStyleXUL(mPresContext); aContext->SetStyle(eStyleStruct_XUL, xul); return xul; } case eStyleStruct_Column: { nsStyleColumn* column = new (mPresContext) nsStyleColumn(mPresContext); aContext->SetStyle(eStyleStruct_Column, column); return column; } case eStyleStruct_SVG: { nsStyleSVG* svg = new (mPresContext) nsStyleSVG(mPresContext); aContext->SetStyle(eStyleStruct_SVG, svg); return svg; } case eStyleStruct_SVGReset: { nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset(mPresContext); aContext->SetStyle(eStyleStruct_SVGReset, svgReset); return svgReset; } case eStyleStruct_Variables: { nsStyleVariables* vars = new (mPresContext) nsStyleVariables(mPresContext); aContext->SetStyle(eStyleStruct_Variables, vars); return vars; } case eStyleStruct_Effects: { nsStyleEffects* effects = new (mPresContext) nsStyleEffects(mPresContext); aContext->SetStyle(eStyleStruct_Effects, effects); return effects; } default: /* * unhandled case: nsStyleStructID_Length. * last item of nsStyleStructID, to know its length. */ MOZ_ASSERT(false, "unexpected SID"); return nullptr; } return nullptr; } /** * Begin an nsRuleNode::Compute*Data function for an inherited struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ #define COMPUTE_START_INHERITED(type_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ nsStyleContext* parentContext = aContext->GetParent(); \ \ nsStyle##type_* data_ = nullptr; \ mozilla::Maybe<nsStyle##type_> maybeFakeParentData; \ const nsStyle##type_* parentdata_ = nullptr; \ RuleNodeCacheConditions conditions = aConditions; \ \ /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ if (aRuleDetail != eRuleFullReset && \ (!aStartStruct || (aRuleDetail != eRulePartialReset && \ aRuleDetail != eRuleNone))) { \ if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ maybeFakeParentData.emplace(mPresContext); \ parentdata_ = maybeFakeParentData.ptr(); \ } \ } \ if (eStyleStruct_##type_ == eStyleStruct_Variables) \ /* no need to copy construct an nsStyleVariables, as we will copy */ \ /* inherited variables (and call SetUncacheable()) in */ \ /* ComputeVariablesData */ \ data_ = new (mPresContext) nsStyle##type_(mPresContext); \ else if (aStartStruct) \ /* We only need to compute the delta between this computed data and */ \ /* our computed data. */ \ data_ = new (mPresContext) \ nsStyle##type_(*static_cast<nsStyle##type_*>(aStartStruct)); \ else { \ if (aRuleDetail != eRuleFullMixed && aRuleDetail != eRuleFullReset) { \ /* No question. We will have to inherit. Go ahead and init */ \ /* with inherited vals from parent. */ \ conditions.SetUncacheable(); \ if (parentdata_) \ data_ = new (mPresContext) nsStyle##type_(*parentdata_); \ else \ data_ = new (mPresContext) nsStyle##type_(mPresContext); \ } \ else \ data_ = new (mPresContext) nsStyle##type_(mPresContext); \ } \ \ if (!parentdata_) \ parentdata_ = data_; /** * Begin an nsRuleNode::Compute*Data function for a reset struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable (declared here) holding the result of this * function. * @param parentdata_ Variable (declared here) holding the parent style * context's data for this struct. */ #define COMPUTE_START_RESET(type_, data_, parentdata_) \ NS_ASSERTION(aRuleDetail != eRuleFullInherited, \ "should not have bothered calling Compute*Data"); \ \ nsStyleContext* parentContext = aContext->GetParent(); \ /* Reset structs don't inherit from first-line */ \ /* See similar code in WalkRuleTree */ \ while (parentContext && \ parentContext->GetPseudo() == nsCSSPseudoElements::firstLine) { \ parentContext = parentContext->GetParent(); \ } \ \ nsStyle##type_* data_; \ if (aStartStruct) \ /* We only need to compute the delta between this computed data and */ \ /* our computed data. */ \ data_ = new (mPresContext) \ nsStyle##type_(*static_cast<nsStyle##type_*>(aStartStruct)); \ else \ data_ = new (mPresContext) nsStyle##type_(mPresContext); \ \ /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ mozilla::Maybe<nsStyle##type_> maybeFakeParentData; \ const nsStyle##type_* parentdata_ = data_; \ if (aRuleDetail != eRuleFullReset && \ aRuleDetail != eRulePartialReset && \ aRuleDetail != eRuleNone) { \ if (parentContext) { \ parentdata_ = parentContext->Style##type_(); \ } else { \ maybeFakeParentData.emplace(mPresContext); \ parentdata_ = maybeFakeParentData.ptr(); \ } \ } \ RuleNodeCacheConditions conditions = aConditions; /** * End an nsRuleNode::Compute*Data function for an inherited struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_INHERITED(type_, data_) \ NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ aRuleDetail == eRuleFullReset || \ (aStartStruct && aRuleDetail == eRulePartialReset), \ "conditions.CacheableWithoutDependencies() must be false " \ "for inherited structs unless all properties have been " \ "specified with values other than inherit"); \ if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mInheritedData) { \ aHighestNode->mStyleData.mInheritedData = \ new (mPresContext) nsInheritedStyleData; \ } \ NS_ASSERTION(!aHighestNode->mStyleData.mInheritedData-> \ mStyleStructs[eStyleStruct_##type_], \ "Going to leak style data"); \ aHighestNode->mStyleData.mInheritedData-> \ mStyleStructs[eStyleStruct_##type_] = data_; \ /* Propagate the bit down. */ \ PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ /* Tell the style context that it doesn't own the data */ \ aContext->AddStyleBit(NS_STYLE_INHERIT_BIT(type_)); \ } \ /* For inherited structs, our caller will cache the data on the */ \ /* style context */ \ \ return data_; /** * End an nsRuleNode::Compute*Data function for a reset struct. * * @param type_ The nsStyle* type this function computes. * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_RESET(type_, data_) \ NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ aRuleDetail == eRuleNone || \ aRuleDetail == eRulePartialReset || \ aRuleDetail == eRuleFullReset, \ "conditions.CacheableWithoutDependencies() must be false " \ "for reset structs if any properties were specified as " \ "inherit"); \ if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mResetData) { \ aHighestNode->mStyleData.mResetData = \ new (mPresContext) nsConditionalResetStyleData; \ } \ NS_ASSERTION(!aHighestNode->mStyleData.mResetData-> \ GetStyleData(eStyleStruct_##type_), \ "Going to leak style data"); \ aHighestNode->mStyleData.mResetData-> \ SetStyleData(eStyleStruct_##type_, data_); \ /* Propagate the bit down. */ \ PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ if (HasAnimationData()) { \ /* If we have animation data, the struct should be cached on the */ \ /* style context so that we can peek the struct. */ \ /* See comment in AnimValuesStyleRule::MapRuleInfoInto. */ \ StoreStyleOnContext(aContext, eStyleStruct_##type_, data_); \ } \ } else if (conditions.Cacheable()) { \ if (!mStyleData.mResetData) { \ mStyleData.mResetData = new (mPresContext) nsConditionalResetStyleData; \ } \ mStyleData.mResetData-> \ SetStyleData(eStyleStruct_##type_, mPresContext, data_, conditions); \ /* Tell the style context that it doesn't own the data */ \ aContext->AddStyleBit(NS_STYLE_INHERIT_BIT(type_)); \ aContext->SetStyle(eStyleStruct_##type_, data_); \ } else { \ /* We can't be cached in the rule node. We have to be put right */ \ /* on the style context. */ \ aContext->SetStyle(eStyleStruct_##type_, data_); \ if (aContext->GetParent()) { \ /* This is pessimistic; we could be uncacheable because we had a */ \ /* relative font-weight, for example, which does not need to defeat */ \ /* the restyle optimizations in RestyleManager.cpp that look at */ \ /* NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE. */ \ aContext->GetParent()-> \ AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE); \ } \ } \ \ return data_; // This function figures out how much scaling should be suppressed to // satisfy scriptminsize. This is our attempt to implement // http://www.w3.org/TR/MathML2/chapter3.html#id.3.3.4.2.2 // This is called after mScriptLevel, mScriptMinSize and mScriptSizeMultiplier // have been set in aFont. // // Here are the invariants we enforce: // 1) A decrease in size must not reduce the size below minscriptsize. // 2) An increase in size must not increase the size above the size we would // have if minscriptsize had not been applied anywhere. // 3) The scriptlevel-induced size change must between 1.0 and the parent's // scriptsizemultiplier^(new script level - old script level), as close to the // latter as possible subject to constraints 1 and 2. static nscoord ComputeScriptLevelSize(const nsStyleFont* aFont, const nsStyleFont* aParentFont, nsPresContext* aPresContext, nscoord* aUnconstrainedSize) { int32_t scriptLevelChange = aFont->mScriptLevel - aParentFont->mScriptLevel; if (scriptLevelChange == 0) { *aUnconstrainedSize = aParentFont->mScriptUnconstrainedSize; // Constraint #3 says that we cannot change size, and #1 and #2 are always // satisfied with no change. It's important this be fast because it covers // all non-MathML content. return aParentFont->mSize; } // Compute actual value of minScriptSize nscoord minScriptSize = aParentFont->mScriptMinSize; if (aFont->mAllowZoom) { minScriptSize = nsStyleFont::ZoomText(aPresContext, minScriptSize); } double scriptLevelScale = pow(aParentFont->mScriptSizeMultiplier, scriptLevelChange); // Compute the size we would have had if minscriptsize had never been // applied, also prevent overflow (bug 413274) *aUnconstrainedSize = NSToCoordRoundWithClamp(aParentFont->mScriptUnconstrainedSize*scriptLevelScale); // Compute the size we could get via scriptlevel change nscoord scriptLevelSize = NSToCoordRoundWithClamp(aParentFont->mSize*scriptLevelScale); if (scriptLevelScale <= 1.0) { if (aParentFont->mSize <= minScriptSize) { // We can't decrease the font size at all, so just stick to no change // (authors are allowed to explicitly set the font size smaller than // minscriptsize) return aParentFont->mSize; } // We can decrease, so apply constraint #1 return std::max(minScriptSize, scriptLevelSize); } else { // scriptminsize can only make sizes larger than the unconstrained size NS_ASSERTION(*aUnconstrainedSize <= scriptLevelSize, "How can this ever happen?"); // Apply constraint #2 return std::min(scriptLevelSize, std::max(*aUnconstrainedSize, minScriptSize)); } } /* static */ nscoord nsRuleNode::CalcFontPointSize(int32_t aHTMLSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { #define sFontSizeTableMin 9 #define sFontSizeTableMax 16 // This table seems to be the one used by MacIE5. We hope its adoption in Mozilla // and eventually in WinIE5.5 will help to establish a standard rendering across // platforms and browsers. For now, it is used only in Strict mode. More can be read // in the document written by Todd Farhner at: // http://style.verso.com/font_size_intervals/altintervals.html // static int32_t sStrictFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = { { 9, 9, 9, 9, 11, 14, 18, 27}, { 9, 9, 9, 10, 12, 15, 20, 30}, { 9, 9, 10, 11, 13, 17, 22, 33}, { 9, 9, 10, 12, 14, 18, 24, 36}, { 9, 10, 12, 13, 16, 20, 26, 39}, { 9, 10, 12, 14, 17, 21, 28, 42}, { 9, 10, 13, 15, 18, 23, 30, 45}, { 9, 10, 13, 16, 18, 24, 32, 48} }; // HTML 1 2 3 4 5 6 7 // CSS xxs xs s m l xl xxl // | // user pref // //------------------------------------------------------------ // // This table gives us compatibility with WinNav4 for the default fonts only. // In WinNav4, the default fonts were: // // Times/12pt == Times/16px at 96ppi // Courier/10pt == Courier/13px at 96ppi // // The 2 lines below marked "anchored" have the exact pixel sizes used by // WinNav4 for Times/12pt and Courier/10pt at 96ppi. As you can see, the // HTML size 3 (user pref) for those 2 anchored lines is 13px and 16px. // // All values other than the anchored values were filled in by hand, never // going below 9px, and maintaining a "diagonal" relationship. See for // example the 13s -- they follow a diagonal line through the table. // static int32_t sQuirksFontSizeTable[sFontSizeTableMax - sFontSizeTableMin + 1][8] = { { 9, 9, 9, 9, 11, 14, 18, 28 }, { 9, 9, 9, 10, 12, 15, 20, 31 }, { 9, 9, 9, 11, 13, 17, 22, 34 }, { 9, 9, 10, 12, 14, 18, 24, 37 }, { 9, 9, 10, 13, 16, 20, 26, 40 }, // anchored (13) { 9, 9, 11, 14, 17, 21, 28, 42 }, { 9, 10, 12, 15, 17, 23, 30, 45 }, { 9, 10, 13, 16, 18, 24, 32, 48 } // anchored (16) }; // HTML 1 2 3 4 5 6 7 // CSS xxs xs s m l xl xxl // | // user pref #if 0 // // These are the exact pixel values used by WinIE5 at 96ppi. // { ?, 8, 11, 12, 13, 16, 21, 32 }, // smallest { ?, 9, 12, 13, 16, 21, 27, 40 }, // smaller { ?, 10, 13, 16, 18, 24, 32, 48 }, // medium { ?, 13, 16, 19, 21, 27, 37, ?? }, // larger { ?, 16, 19, 21, 24, 32, 43, ?? } // largest // // HTML 1 2 3 4 5 6 7 // CSS ? ? ? ? ? ? ? ? // // (CSS not tested yet.) // #endif static int32_t sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 }; static int32_t sCSSColumns[7] = {0, 1, 2, 3, 4, 5, 6}; // xxs...xxl static int32_t sHTMLColumns[7] = {1, 2, 3, 4, 5, 6, 7}; // 1...7 double dFontSize; if (aFontSizeType == eFontSize_HTML) { aHTMLSize--; // input as 1-7 } if (aHTMLSize < 0) aHTMLSize = 0; else if (aHTMLSize > 6) aHTMLSize = 6; int32_t* column; switch (aFontSizeType) { case eFontSize_HTML: column = sHTMLColumns; break; case eFontSize_CSS: column = sCSSColumns; break; } // Make special call specifically for fonts (needed PrintPreview) int32_t fontSize = nsPresContext::AppUnitsToIntCSSPixels(aBasePointSize); if ((fontSize >= sFontSizeTableMin) && (fontSize <= sFontSizeTableMax)) { int32_t row = fontSize - sFontSizeTableMin; if (aPresContext->CompatibilityMode() == eCompatibility_NavQuirks) { dFontSize = nsPresContext::CSSPixelsToAppUnits(sQuirksFontSizeTable[row][column[aHTMLSize]]); } else { dFontSize = nsPresContext::CSSPixelsToAppUnits(sStrictFontSizeTable[row][column[aHTMLSize]]); } } else { int32_t factor = sFontSizeFactors[column[aHTMLSize]]; dFontSize = (factor * aBasePointSize) / 100; } if (1.0 < dFontSize) { return (nscoord)dFontSize; } return (nscoord)1; } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ /* static */ nscoord nsRuleNode::FindNextSmallerFontSize(nscoord aFontSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { int32_t index; int32_t indexMin; int32_t indexMax; float relativePosition; nscoord smallerSize; nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning nscoord smallestIndexFontSize; nscoord largestIndexFontSize; nscoord smallerIndexFontSize; nscoord largerIndexFontSize; nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); if (aFontSizeType == eFontSize_HTML) { indexMin = 1; indexMax = 7; } else { indexMin = 0; indexMax = 6; } smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); if (aFontSize > smallestIndexFontSize) { if (aFontSize < NSToCoordRound(float(largestIndexFontSize) * 1.5)) { // smaller will be in HTML table // find largest index smaller than current for (index = indexMax; index >= indexMin; index--) { indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); if (indexFontSize < aFontSize) break; } // set up points beyond table for interpolation purposes if (indexFontSize == smallestIndexFontSize) { smallerIndexFontSize = indexFontSize - onePx; largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } else if (indexFontSize == largestIndexFontSize) { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = NSToCoordRound(float(largestIndexFontSize) * 1.5); } else { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } // compute the relative position of the parent size between the two closest indexed sizes relativePosition = float(aFontSize - indexFontSize) / float(largerIndexFontSize - indexFontSize); // set the new size to have the same relative position between the next smallest two indexed sizes smallerSize = smallerIndexFontSize + NSToCoordRound(relativePosition * (indexFontSize - smallerIndexFontSize)); } else { // larger than HTML table, drop by 33% smallerSize = NSToCoordRound(float(aFontSize) / 1.5); } } else { // smaller than HTML table, drop by 1px smallerSize = std::max(aFontSize - onePx, onePx); } return smallerSize; } //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ /* static */ nscoord nsRuleNode::FindNextLargerFontSize(nscoord aFontSize, int32_t aBasePointSize, nsPresContext* aPresContext, nsFontSizeType aFontSizeType) { int32_t index; int32_t indexMin; int32_t indexMax; float relativePosition; nscoord adjustment; nscoord largerSize; nscoord indexFontSize = aFontSize; // XXX initialize to quell a spurious gcc3.2 warning nscoord smallestIndexFontSize; nscoord largestIndexFontSize; nscoord smallerIndexFontSize; nscoord largerIndexFontSize; nscoord onePx = nsPresContext::CSSPixelsToAppUnits(1); if (aFontSizeType == eFontSize_HTML) { indexMin = 1; indexMax = 7; } else { indexMin = 0; indexMax = 6; } smallestIndexFontSize = CalcFontPointSize(indexMin, aBasePointSize, aPresContext, aFontSizeType); largestIndexFontSize = CalcFontPointSize(indexMax, aBasePointSize, aPresContext, aFontSizeType); if (aFontSize > (smallestIndexFontSize - onePx)) { if (aFontSize < largestIndexFontSize) { // larger will be in HTML table // find smallest index larger than current for (index = indexMin; index <= indexMax; index++) { indexFontSize = CalcFontPointSize(index, aBasePointSize, aPresContext, aFontSizeType); if (indexFontSize > aFontSize) break; } // set up points beyond table for interpolation purposes if (indexFontSize == smallestIndexFontSize) { smallerIndexFontSize = indexFontSize - onePx; largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } else if (indexFontSize == largestIndexFontSize) { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = NSCoordSaturatingMultiply(largestIndexFontSize, 1.5); } else { smallerIndexFontSize = CalcFontPointSize(index-1, aBasePointSize, aPresContext, aFontSizeType); largerIndexFontSize = CalcFontPointSize(index+1, aBasePointSize, aPresContext, aFontSizeType); } // compute the relative position of the parent size between the two closest indexed sizes relativePosition = float(aFontSize - smallerIndexFontSize) / float(indexFontSize - smallerIndexFontSize); // set the new size to have the same relative position between the next largest two indexed sizes adjustment = NSCoordSaturatingNonnegativeMultiply(largerIndexFontSize - indexFontSize, relativePosition); largerSize = NSCoordSaturatingAdd(indexFontSize, adjustment); } else { // larger than HTML table, increase by 50% largerSize = NSCoordSaturatingMultiply(aFontSize, 1.5); } } else { // smaller than HTML table, increase by 1px largerSize = NSCoordSaturatingAdd(aFontSize, onePx); } return largerSize; } struct SetFontSizeCalcOps : public css::BasicCoordCalcOps, public css::NumbersAlreadyNormalizedOps { // The parameters beyond aValue that we need for CalcLengthWith. const nscoord mParentSize; const nsStyleFont* const mParentFont; nsPresContext* const mPresContext; nsStyleContext* const mStyleContext; const bool mAtRoot; RuleNodeCacheConditions& mConditions; SetFontSizeCalcOps(nscoord aParentSize, const nsStyleFont* aParentFont, nsPresContext* aPresContext, nsStyleContext* aStyleContext, bool aAtRoot, RuleNodeCacheConditions& aConditions) : mParentSize(aParentSize), mParentFont(aParentFont), mPresContext(aPresContext), mStyleContext(aStyleContext), mAtRoot(aAtRoot), mConditions(aConditions) { } result_type ComputeLeafValue(const nsCSSValue& aValue) { nscoord size; if (aValue.IsLengthUnit()) { // Note that font-based length units use the parent's size // unadjusted for scriptlevel changes. A scriptlevel change // between us and the parent is simply ignored. size = CalcLengthWith(aValue, mParentSize, mParentFont, mStyleContext, mPresContext, mAtRoot, true, mConditions); if (!aValue.IsRelativeLengthUnit() && mParentFont->mAllowZoom) { size = nsStyleFont::ZoomText(mPresContext, size); } } else if (eCSSUnit_Percent == aValue.GetUnit()) { mConditions.SetUncacheable(); // Note that % units use the parent's size unadjusted for scriptlevel // changes. A scriptlevel change between us and the parent is simply // ignored. // aValue.GetPercentValue() may be negative for, e.g., calc(-50%) size = NSCoordSaturatingMultiply(mParentSize, aValue.GetPercentValue()); } else { MOZ_ASSERT(false, "unexpected value"); size = mParentSize; } return size; } }; /* static */ void nsRuleNode::SetFontSize(nsPresContext* aPresContext, nsStyleContext* aContext, const nsRuleData* aRuleData, const nsStyleFont* aFont, const nsStyleFont* aParentFont, nscoord* aSize, const nsFont& aSystemFont, nscoord aParentSize, nscoord aScriptLevelAdjustedParentSize, bool aUsedStartStruct, bool aAtRoot, RuleNodeCacheConditions& aConditions) { // If false, means that *aSize has not been zoomed. If true, means that // *aSize has been zoomed iff aParentFont->mAllowZoom is true. bool sizeIsZoomedAccordingToParent = false; int32_t baseSize = (int32_t) aPresContext-> GetDefaultFont(aFont->mGenericID, aFont->mLanguage)->size; const nsCSSValue* sizeValue = aRuleData->ValueForFontSize(); if (eCSSUnit_Enumerated == sizeValue->GetUnit()) { int32_t value = sizeValue->GetIntValue(); if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) && (value <= NS_STYLE_FONT_SIZE_XXLARGE)) { *aSize = CalcFontPointSize(value, baseSize, aPresContext, eFontSize_CSS); } else if (NS_STYLE_FONT_SIZE_XXXLARGE == value) { // <font size="7"> is not specified in CSS, so we don't use eFontSize_CSS. *aSize = CalcFontPointSize(value, baseSize, aPresContext); } else if (NS_STYLE_FONT_SIZE_LARGER == value || NS_STYLE_FONT_SIZE_SMALLER == value) { aConditions.SetUncacheable(); // Un-zoom so we use the tables correctly. We'll then rezoom due // to the |zoom = true| above. // Note that relative units here use the parent's size unadjusted // for scriptlevel changes. A scriptlevel change between us and the parent // is simply ignored. nscoord parentSize = aParentSize; if (aParentFont->mAllowZoom) { parentSize = nsStyleFont::UnZoomText(aPresContext, parentSize); } if (NS_STYLE_FONT_SIZE_LARGER == value) { *aSize = FindNextLargerFontSize(parentSize, baseSize, aPresContext, eFontSize_CSS); NS_ASSERTION(*aSize >= parentSize, "FindNextLargerFontSize failed"); } else { *aSize = FindNextSmallerFontSize(parentSize, baseSize, aPresContext, eFontSize_CSS); NS_ASSERTION(*aSize < parentSize || parentSize <= nsPresContext::CSSPixelsToAppUnits(1), "FindNextSmallerFontSize failed"); } } else { NS_NOTREACHED("unexpected value"); } } else if (sizeValue->IsLengthUnit() || sizeValue->GetUnit() == eCSSUnit_Percent || sizeValue->IsCalcUnit()) { SetFontSizeCalcOps ops(aParentSize, aParentFont, aPresContext, aContext, aAtRoot, aConditions); *aSize = css::ComputeCalc(*sizeValue, ops); if (*aSize < 0) { MOZ_ASSERT(sizeValue->IsCalcUnit(), "negative lengths and percents should be rejected by parser"); *aSize = 0; } // The calc ops will always zoom its result according to the value // of aParentFont->mAllowZoom. sizeIsZoomedAccordingToParent = true; } else if (eCSSUnit_System_Font == sizeValue->GetUnit()) { // this becomes our cascading size *aSize = aSystemFont.size; } else if (eCSSUnit_Inherit == sizeValue->GetUnit() || eCSSUnit_Unset == sizeValue->GetUnit()) { aConditions.SetUncacheable(); // We apply scriptlevel change for this case, because the default is // to inherit and we don't want explicit "inherit" to differ from the // default. *aSize = aScriptLevelAdjustedParentSize; sizeIsZoomedAccordingToParent = true; } else if (eCSSUnit_Initial == sizeValue->GetUnit()) { // The initial value is 'medium', which has magical sizing based on // the generic font family, so do that here too. *aSize = baseSize; } else { NS_ASSERTION(eCSSUnit_Null == sizeValue->GetUnit(), "What kind of font-size value is this?"); // if aUsedStartStruct is true, then every single property in the // font struct is being set all at once. This means scriptlevel is not // going to have any influence on the font size; there is no need to // do anything here. if (!aUsedStartStruct && aParentSize != aScriptLevelAdjustedParentSize) { // There was no rule affecting the size but the size has been // affected by the parent's size via scriptlevel change. So we cannot // store the data in the rule tree. aConditions.SetUncacheable(); *aSize = aScriptLevelAdjustedParentSize; sizeIsZoomedAccordingToParent = true; } else { return; } } // We want to zoom the cascaded size so that em-based measurements, // line-heights, etc., work. bool currentlyZoomed = sizeIsZoomedAccordingToParent && aParentFont->mAllowZoom; if (!currentlyZoomed && aFont->mAllowZoom) { *aSize = nsStyleFont::ZoomText(aPresContext, *aSize); } else if (currentlyZoomed && !aFont->mAllowZoom) { *aSize = nsStyleFont::UnZoomText(aPresContext, *aSize); } } static int8_t ClampTo8Bit(int32_t aValue) { if (aValue < -128) return -128; if (aValue > 127) return 127; return int8_t(aValue); } /* static */ void nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, uint8_t aGenericFontID, const nsRuleData* aRuleData, const nsStyleFont* aParentFont, nsStyleFont* aFont, bool aUsedStartStruct, RuleNodeCacheConditions& aConditions) { bool atRoot = !aContext->GetParent(); // -x-text-zoom: none, inherit, initial bool allowZoom; const nsCSSValue* textZoomValue = aRuleData->ValueForTextZoom(); if (eCSSUnit_Null != textZoomValue->GetUnit()) { if (eCSSUnit_Inherit == textZoomValue->GetUnit()) { allowZoom = aParentFont->mAllowZoom; } else if (eCSSUnit_None == textZoomValue->GetUnit()) { allowZoom = false; } else { MOZ_ASSERT(eCSSUnit_Initial == textZoomValue->GetUnit(), "unexpected unit"); allowZoom = true; } aFont->EnableZoom(aPresContext, allowZoom); } // mLanguage must be set before before any of the CalcLengthWith calls // (direct calls or calls via SetFontSize) for the cases where |aParentFont| // is the same as |aFont|. // // -x-lang: string, inherit // This is not a real CSS property, it is an HTML attribute mapped to CSS. const nsCSSValue* langValue = aRuleData->ValueForLang(); if (eCSSUnit_Ident == langValue->GetUnit()) { nsAutoString lang; langValue->GetStringValue(lang); nsContentUtils::ASCIIToLower(lang); aFont->mLanguage = NS_Atomize(lang); aFont->mExplicitLanguage = true; } const nsFont* defaultVariableFont = aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID, aFont->mLanguage); // -moz-system-font: enum (never inherit!) static_assert( NS_STYLE_FONT_CAPTION == LookAndFeel::eFont_Caption && NS_STYLE_FONT_ICON == LookAndFeel::eFont_Icon && NS_STYLE_FONT_MENU == LookAndFeel::eFont_Menu && NS_STYLE_FONT_MESSAGE_BOX == LookAndFeel::eFont_MessageBox && NS_STYLE_FONT_SMALL_CAPTION == LookAndFeel::eFont_SmallCaption && NS_STYLE_FONT_STATUS_BAR == LookAndFeel::eFont_StatusBar && NS_STYLE_FONT_WINDOW == LookAndFeel::eFont_Window && NS_STYLE_FONT_DOCUMENT == LookAndFeel::eFont_Document && NS_STYLE_FONT_WORKSPACE == LookAndFeel::eFont_Workspace && NS_STYLE_FONT_DESKTOP == LookAndFeel::eFont_Desktop && NS_STYLE_FONT_INFO == LookAndFeel::eFont_Info && NS_STYLE_FONT_DIALOG == LookAndFeel::eFont_Dialog && NS_STYLE_FONT_BUTTON == LookAndFeel::eFont_Button && NS_STYLE_FONT_PULL_DOWN_MENU == LookAndFeel::eFont_PullDownMenu && NS_STYLE_FONT_LIST == LookAndFeel::eFont_List && NS_STYLE_FONT_FIELD == LookAndFeel::eFont_Field, "LookAndFeel.h system-font constants out of sync with nsStyleConsts.h"); // Fall back to defaultVariableFont. nsFont systemFont = *defaultVariableFont; const nsCSSValue* systemFontValue = aRuleData->ValueForSystemFont(); if (eCSSUnit_Enumerated == systemFontValue->GetUnit()) { gfxFontStyle fontStyle; LookAndFeel::FontID fontID = (LookAndFeel::FontID)systemFontValue->GetIntValue(); float devPerCSS = (float)nsPresContext::AppUnitsPerCSSPixel() / aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom(); nsAutoString systemFontName; if (LookAndFeel::GetFont(fontID, systemFontName, fontStyle, devPerCSS)) { systemFontName.Trim("\"'"); systemFont.fontlist = FontFamilyList(systemFontName, eUnquotedName); systemFont.fontlist.SetDefaultFontType(eFamily_none); systemFont.style = fontStyle.style; systemFont.systemFont = fontStyle.systemFont; systemFont.weight = fontStyle.weight; systemFont.stretch = fontStyle.stretch; systemFont.size = NSFloatPixelsToAppUnits(fontStyle.size, aPresContext->DeviceContext()-> AppUnitsPerDevPixelAtUnitFullZoom()); //systemFont.langGroup = fontStyle.langGroup; systemFont.sizeAdjust = fontStyle.sizeAdjust; #ifdef XP_WIN // XXXldb This platform-specific stuff should be in the // LookAndFeel implementation, not here. // XXXzw Should we even still *have* this code? It looks to be making // old, probably obsolete assumptions. if (fontID == LookAndFeel::eFont_Field || fontID == LookAndFeel::eFont_Button || fontID == LookAndFeel::eFont_List) { // As far as I can tell the system default fonts and sizes // on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are // all pre-determined and cannot be changed by either the control panel // or programmatically. // Fields (text fields) // Button and Selects (listboxes/comboboxes) // We use whatever font is defined by the system. Which it appears // (and the assumption is) it is always a proportional font. Then we // always use 2 points smaller than what the browser has defined as // the default proportional font. // Assumption: system defined font is proportional systemFont.size = std::max(defaultVariableFont->size - nsPresContext::CSSPointsToAppUnits(2), 0); } #endif } } // font-family: font family list, enum, inherit const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); NS_ASSERTION(eCSSUnit_Enumerated != familyValue->GetUnit(), "system fonts should not be in mFamily anymore"); if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) { // set the correct font if we are using DocumentFonts OR we are overriding for XUL // MJA: bug 31816 bool useDocumentFonts = aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts); if (aGenericFontID == kGenericFont_NONE || (!useDocumentFonts && (aGenericFontID == kGenericFont_cursive || aGenericFontID == kGenericFont_fantasy))) { FontFamilyType defaultGeneric = defaultVariableFont->fontlist.FirstGeneric(); MOZ_ASSERT(defaultVariableFont->fontlist.Length() == 1 && (defaultGeneric == eFamily_serif || defaultGeneric == eFamily_sans_serif)); if (defaultGeneric != eFamily_none) { if (useDocumentFonts) { aFont->mFont.fontlist.SetDefaultFontType(defaultGeneric); } else { // Either prioritize the first generic in the list, // or (if there isn't one) prepend the default variable font. if (!aFont->mFont.fontlist.PrioritizeFirstGeneric()) { aFont->mFont.fontlist.PrependGeneric(defaultGeneric); } } } } else { aFont->mFont.fontlist.SetDefaultFontType(eFamily_none); } aFont->mFont.systemFont = false; // Technically this is redundant with the code below, but it's good // to have since we'll still want it once we get rid of // SetGenericFont (bug 380915). aFont->mGenericID = aGenericFontID; } else if (eCSSUnit_System_Font == familyValue->GetUnit()) { aFont->mFont.fontlist = systemFont.fontlist; aFont->mFont.systemFont = true; aFont->mGenericID = kGenericFont_NONE; } else if (eCSSUnit_Inherit == familyValue->GetUnit() || eCSSUnit_Unset == familyValue->GetUnit()) { aConditions.SetUncacheable(); aFont->mFont.fontlist = aParentFont->mFont.fontlist; aFont->mFont.systemFont = aParentFont->mFont.systemFont; aFont->mGenericID = aParentFont->mGenericID; } else if (eCSSUnit_Initial == familyValue->GetUnit()) { aFont->mFont.fontlist = defaultVariableFont->fontlist; aFont->mFont.systemFont = defaultVariableFont->systemFont; aFont->mGenericID = kGenericFont_NONE; } // When we're in the loop in SetGenericFont, we must ensure that we // always keep aFont->mFlags set to the correct generic. But we have // to be careful not to touch it when we're called directly from // ComputeFontData, because we could have a start struct. if (aGenericFontID != kGenericFont_NONE) { aFont->mGenericID = aGenericFontID; } // -moz-math-variant: enum, inherit, initial SetValue(*aRuleData->ValueForMathVariant(), aFont->mMathVariant, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mMathVariant, NS_MATHML_MATHVARIANT_NONE); // -moz-math-display: enum, inherit, initial SetValue(*aRuleData->ValueForMathDisplay(), aFont->mMathDisplay, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mMathDisplay, NS_MATHML_DISPLAYSTYLE_INLINE); // font-smoothing: enum, inherit, initial SetValue(*aRuleData->ValueForOsxFontSmoothing(), aFont->mFont.smoothing, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.smoothing, defaultVariableFont->smoothing); // font-style: enum, inherit, initial, -moz-system-font if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { // -moz-math-variant overrides font-style aFont->mFont.style = NS_FONT_STYLE_NORMAL; } else { SetValue(*aRuleData->ValueForFontStyle(), aFont->mFont.style, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.style, defaultVariableFont->style, Unused, Unused, Unused, systemFont.style); } // font-weight: int, enum, inherit, initial, -moz-system-font // special handling for enum const nsCSSValue* weightValue = aRuleData->ValueForFontWeight(); if (aFont->mMathVariant != NS_MATHML_MATHVARIANT_NONE) { // -moz-math-variant overrides font-weight aFont->mFont.weight = NS_FONT_WEIGHT_NORMAL; } else if (eCSSUnit_Enumerated == weightValue->GetUnit()) { int32_t value = weightValue->GetIntValue(); switch (value) { case NS_STYLE_FONT_WEIGHT_NORMAL: case NS_STYLE_FONT_WEIGHT_BOLD: aFont->mFont.weight = value; break; case NS_STYLE_FONT_WEIGHT_BOLDER: { aConditions.SetUncacheable(); int32_t inheritedValue = aParentFont->mFont.weight; if (inheritedValue <= 300) { aFont->mFont.weight = 400; } else if (inheritedValue <= 500) { aFont->mFont.weight = 700; } else { aFont->mFont.weight = 900; } break; } case NS_STYLE_FONT_WEIGHT_LIGHTER: { aConditions.SetUncacheable(); int32_t inheritedValue = aParentFont->mFont.weight; if (inheritedValue < 600) { aFont->mFont.weight = 100; } else if (inheritedValue < 800) { aFont->mFont.weight = 400; } else { aFont->mFont.weight = 700; } break; } } } else SetValue(*weightValue, aFont->mFont.weight, aConditions, SETVAL_INTEGER | SETVAL_UNSET_INHERIT, aParentFont->mFont.weight, defaultVariableFont->weight, Unused, Unused, Unused, systemFont.weight); // font-stretch: enum, inherit, initial, -moz-system-font SetValue(*aRuleData->ValueForFontStretch(), aFont->mFont.stretch, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.stretch, defaultVariableFont->stretch, Unused, Unused, Unused, systemFont.stretch); // Compute scriptlevel, scriptminsize and scriptsizemultiplier now so // they're available for font-size computation. // -moz-script-min-size: length const nsCSSValue* scriptMinSizeValue = aRuleData->ValueForScriptMinSize(); if (scriptMinSizeValue->IsLengthUnit()) { // scriptminsize in font units (em, ex) has to be interpreted relative // to the parent font, or the size definitions are circular and we // aFont->mScriptMinSize = CalcLengthWith(*scriptMinSizeValue, aParentFont->mSize, aParentFont, aContext, aPresContext, atRoot, true /* aUseUserFontSet */, aConditions); } // -moz-script-size-multiplier: factor, inherit, initial SetFactor(*aRuleData->ValueForScriptSizeMultiplier(), aFont->mScriptSizeMultiplier, aConditions, aParentFont->mScriptSizeMultiplier, NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER, SETFCT_POSITIVE | SETFCT_UNSET_INHERIT); // -moz-script-level: integer, number, inherit const nsCSSValue* scriptLevelValue = aRuleData->ValueForScriptLevel(); if (eCSSUnit_Integer == scriptLevelValue->GetUnit()) { // "relative" aConditions.SetUncacheable(); aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + scriptLevelValue->GetIntValue()); } else if (eCSSUnit_Number == scriptLevelValue->GetUnit()) { // "absolute" aFont->mScriptLevel = ClampTo8Bit(int32_t(scriptLevelValue->GetFloatValue())); } else if (eCSSUnit_Auto == scriptLevelValue->GetUnit()) { // auto aConditions.SetUncacheable(); aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + (aParentFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE ? 1 : 0)); } else if (eCSSUnit_Inherit == scriptLevelValue->GetUnit() || eCSSUnit_Unset == scriptLevelValue->GetUnit()) { aConditions.SetUncacheable(); aFont->mScriptLevel = aParentFont->mScriptLevel; } else if (eCSSUnit_Initial == scriptLevelValue->GetUnit()) { aFont->mScriptLevel = 0; } // font-kerning: none, enum, inherit, initial, -moz-system-font SetValue(*aRuleData->ValueForFontKerning(), aFont->mFont.kerning, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.kerning, defaultVariableFont->kerning, Unused, Unused, Unused, systemFont.kerning); // font-synthesis: none, enum (bit field), inherit, initial, -moz-system-font SetValue(*aRuleData->ValueForFontSynthesis(), aFont->mFont.synthesis, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.synthesis, defaultVariableFont->synthesis, Unused, /* none */ 0, Unused, systemFont.synthesis); // font-variant-alternates: normal, enum (bit field) + functions, inherit, // initial, -moz-system-font const nsCSSValue* variantAlternatesValue = aRuleData->ValueForFontVariantAlternates(); int32_t variantAlternates = 0; switch (variantAlternatesValue->GetUnit()) { case eCSSUnit_Inherit: case eCSSUnit_Unset: aFont->mFont.CopyAlternates(aParentFont->mFont); aConditions.SetUncacheable(); break; case eCSSUnit_Initial: case eCSSUnit_Normal: aFont->mFont.variantAlternates = 0; aFont->mFont.alternateValues.Clear(); aFont->mFont.featureValueLookup = nullptr; break; case eCSSUnit_Pair: NS_ASSERTION(variantAlternatesValue->GetPairValue().mXValue.GetUnit() == eCSSUnit_Enumerated, "strange unit for variantAlternates"); variantAlternates = variantAlternatesValue->GetPairValue().mXValue.GetIntValue(); aFont->mFont.variantAlternates = variantAlternates; if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { // fetch the feature lookup object from the styleset MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(), "ServoStyleSets should not have rule nodes"); aFont->mFont.featureValueLookup = aPresContext->StyleSet()->AsGecko()->GetFontFeatureValuesLookup(); NS_ASSERTION(variantAlternatesValue->GetPairValue().mYValue.GetUnit() == eCSSUnit_List, "function list not a list value"); nsStyleUtil::ComputeFunctionalAlternates( variantAlternatesValue->GetPairValue().mYValue.GetListValue(), aFont->mFont.alternateValues); } break; default: break; } // font-variant-caps: normal, enum, inherit, initial, -moz-system-font SetValue(*aRuleData->ValueForFontVariantCaps(), aFont->mFont.variantCaps, aConditions, SETVAL_ENUMERATED |SETVAL_UNSET_INHERIT, aParentFont->mFont.variantCaps, defaultVariableFont->variantCaps, Unused, Unused, /* normal */ 0, systemFont.variantCaps); // font-variant-east-asian: normal, enum (bit field), inherit, initial, // -moz-system-font SetValue(*aRuleData->ValueForFontVariantEastAsian(), aFont->mFont.variantEastAsian, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.variantEastAsian, defaultVariableFont->variantEastAsian, Unused, Unused, /* normal */ 0, systemFont.variantEastAsian); // font-variant-ligatures: normal, none, enum (bit field), inherit, initial, // -moz-system-font SetValue(*aRuleData->ValueForFontVariantLigatures(), aFont->mFont.variantLigatures, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.variantLigatures, defaultVariableFont->variantLigatures, Unused, NS_FONT_VARIANT_LIGATURES_NONE, /* normal */ 0, systemFont.variantLigatures); // font-variant-numeric: normal, enum (bit field), inherit, initial, // -moz-system-font SetValue(*aRuleData->ValueForFontVariantNumeric(), aFont->mFont.variantNumeric, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.variantNumeric, defaultVariableFont->variantNumeric, Unused, Unused, /* normal */ 0, systemFont.variantNumeric); // font-variant-position: normal, enum, inherit, initial, // -moz-system-font SetValue(*aRuleData->ValueForFontVariantPosition(), aFont->mFont.variantPosition, aConditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, aParentFont->mFont.variantPosition, defaultVariableFont->variantPosition, Unused, Unused, /* normal */ 0, systemFont.variantPosition); // font-feature-settings const nsCSSValue* featureSettingsValue = aRuleData->ValueForFontFeatureSettings(); switch (featureSettingsValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Normal: case eCSSUnit_Initial: aFont->mFont.fontFeatureSettings.Clear(); break; case eCSSUnit_Inherit: case eCSSUnit_Unset: aConditions.SetUncacheable(); aFont->mFont.fontFeatureSettings = aParentFont->mFont.fontFeatureSettings; break; case eCSSUnit_System_Font: aFont->mFont.fontFeatureSettings = systemFont.fontFeatureSettings; break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: ComputeFontFeatures(featureSettingsValue->GetPairListValue(), aFont->mFont.fontFeatureSettings); break; default: MOZ_ASSERT(false, "unexpected value unit"); break; } // font-language-override const nsCSSValue* languageOverrideValue = aRuleData->ValueForFontLanguageOverride(); if (eCSSUnit_Inherit == languageOverrideValue->GetUnit() || eCSSUnit_Unset == languageOverrideValue->GetUnit()) { aConditions.SetUncacheable(); aFont->mFont.languageOverride = aParentFont->mFont.languageOverride; } else if (eCSSUnit_Normal == languageOverrideValue->GetUnit() || eCSSUnit_Initial == languageOverrideValue->GetUnit()) { aFont->mFont.languageOverride.Truncate(); } else if (eCSSUnit_System_Font == languageOverrideValue->GetUnit()) { aFont->mFont.languageOverride = systemFont.languageOverride; } else if (eCSSUnit_String == languageOverrideValue->GetUnit()) { languageOverrideValue->GetStringValue(aFont->mFont.languageOverride); } // -moz-min-font-size-ratio: percent, inherit const nsCSSValue* minFontSizeRatio = aRuleData->ValueForMinFontSizeRatio(); switch (minFontSizeRatio->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Unset: case eCSSUnit_Inherit: aFont->mMinFontSizeRatio = aParentFont->mMinFontSizeRatio; aConditions.SetUncacheable(); break; case eCSSUnit_Initial: aFont->mMinFontSizeRatio = 100; // 100% break; case eCSSUnit_Percent: { // While percentages are parsed as floating point numbers, we // only store an integer in the range [0, 255] since that's all // we need for now. float percent = minFontSizeRatio->GetPercentValue() * 100; if (percent < 0) { percent = 0; } else if (percent > 255) { percent = 255; } aFont->mMinFontSizeRatio = uint8_t(percent); break; } default: MOZ_ASSERT_UNREACHABLE("Unknown unit for -moz-min-font-size-ratio"); } nscoord scriptLevelAdjustedUnconstrainedParentSize; // font-size: enum, length, percent, inherit nscoord scriptLevelAdjustedParentSize = ComputeScriptLevelSize(aFont, aParentFont, aPresContext, &scriptLevelAdjustedUnconstrainedParentSize); NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize, "If we have a start struct, we should have reset everything coming in here"); // Compute whether we're affected by scriptMinSize *before* calling // SetFontSize, since aParentFont might be the same as aFont. If it // is, calling SetFontSize might throw off our calculation. bool affectedByScriptMinSize = aParentFont->mSize != aParentFont->mScriptUnconstrainedSize || scriptLevelAdjustedParentSize != scriptLevelAdjustedUnconstrainedParentSize; SetFontSize(aPresContext, aContext, aRuleData, aFont, aParentFont, &aFont->mSize, systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize, aUsedStartStruct, atRoot, aConditions); if (!aPresContext->Document()->GetMathMLEnabled()) { MOZ_ASSERT(!affectedByScriptMinSize); // If MathML is not enabled, we don't need to mark this node as // uncacheable. If it becomes enabled, code in // nsMathMLElementFactory will rebuild the rule tree and style data // when MathML is first enabled (see nsMathMLElement::BindToTree). aFont->mScriptUnconstrainedSize = aFont->mSize; } else if (!affectedByScriptMinSize) { // Fast path: we have not been affected by scriptminsize so we don't // need to call SetFontSize again to compute the // scriptminsize-unconstrained size. This is OK even if we have a // start struct, because if we have a start struct then 'font-size' // was specified and so scriptminsize has no effect. aFont->mScriptUnconstrainedSize = aFont->mSize; // It's possible we could, in the future, have a different parent, // which would lead to a different affectedByScriptMinSize. aConditions.SetUncacheable(); } else { // see previous else-if aConditions.SetUncacheable(); // Use a separate conditions object because it might get a // *different* font-size dependency. We can ignore it because we've // already called SetUncacheable. RuleNodeCacheConditions unconstrainedConditions; SetFontSize(aPresContext, aContext, aRuleData, aFont, aParentFont, &aFont->mScriptUnconstrainedSize, systemFont, aParentFont->mScriptUnconstrainedSize, scriptLevelAdjustedUnconstrainedParentSize, aUsedStartStruct, atRoot, unconstrainedConditions); } NS_ASSERTION(aFont->mScriptUnconstrainedSize <= aFont->mSize, "scriptminsize should never be making things bigger"); nscoord fontSize = aFont->mSize; // enforce the user' specified minimum font-size on the value that we expose // (but don't change font-size:0, since that would unhide hidden text) if (fontSize > 0) { nscoord minFontSize = aPresContext->MinFontSize(aFont->mLanguage); if (minFontSize < 0) { minFontSize = 0; } else { minFontSize = (minFontSize * aFont->mMinFontSizeRatio) / 100; } if (fontSize < minFontSize && !aPresContext->IsChrome()) { // override the minimum font-size constraint fontSize = minFontSize; } } aFont->mFont.size = fontSize; // font-size-adjust: number, none, inherit, initial, -moz-system-font const nsCSSValue* sizeAdjustValue = aRuleData->ValueForFontSizeAdjust(); if (eCSSUnit_System_Font == sizeAdjustValue->GetUnit()) { aFont->mFont.sizeAdjust = systemFont.sizeAdjust; } else SetFactor(*sizeAdjustValue, aFont->mFont.sizeAdjust, aConditions, aParentFont->mFont.sizeAdjust, -1.0f, SETFCT_NONE | SETFCT_UNSET_INHERIT); } /* static */ void nsRuleNode::ComputeFontFeatures(const nsCSSValuePairList *aFeaturesList, nsTArray<gfxFontFeature>& aFeatureSettings) { aFeatureSettings.Clear(); for (const nsCSSValuePairList* p = aFeaturesList; p; p = p->mNext) { gfxFontFeature feat = {0, 0}; MOZ_ASSERT(aFeaturesList->mXValue.GetUnit() == eCSSUnit_String, "unexpected value unit"); // tag is a 4-byte ASCII sequence nsAutoString tag; p->mXValue.GetStringValue(tag); if (tag.Length() != 4) { continue; } // parsing validates that these are ASCII chars // tags are always big-endian feat.mTag = (tag[0] << 24) | (tag[1] << 16) | (tag[2] << 8) | tag[3]; // value NS_ASSERTION(p->mYValue.GetUnit() == eCSSUnit_Integer, "should have found an integer unit"); feat.mValue = p->mYValue.GetIntValue(); aFeatureSettings.AppendElement(feat); } } // This should die (bug 380915). // // SetGenericFont: // - backtrack to an ancestor with the same generic font name (possibly // up to the root where default values come from the presentation context) // - re-apply cascading rules from there without caching intermediate values /* static */ void nsRuleNode::SetGenericFont(nsPresContext* aPresContext, nsStyleContext* aContext, uint8_t aGenericFontID, nsStyleFont* aFont) { // walk up the contexts until a context with the desired generic font AutoTArray<nsStyleContext*, 8> contextPath; contextPath.AppendElement(aContext); nsStyleContext* higherContext = aContext->GetParent(); while (higherContext) { if (higherContext->StyleFont()->mGenericID == aGenericFontID) { // done walking up the higher contexts break; } contextPath.AppendElement(higherContext); higherContext = higherContext->GetParent(); } // re-apply the cascading rules, starting from the higher context // If we stopped earlier because we reached the root of the style tree, // we will start with the default generic font from the presentation // context. Otherwise we start with the higher context. const nsFont* defaultFont = aPresContext->GetDefaultFont(aGenericFontID, aFont->mLanguage); nsStyleFont parentFont(*defaultFont, aPresContext); if (higherContext) { const nsStyleFont* tmpFont = higherContext->StyleFont(); parentFont = *tmpFont; } *aFont = parentFont; uint32_t fontBit = nsCachedStyleData::GetBitForSID(eStyleStruct_Font); // use placement new[] on the result of alloca() to allocate a // variable-sized stack array, including execution of constructors, // and use an RAII class to run the destructors too. size_t nprops = nsCSSProps::PropertyCountInStruct(eStyleStruct_Font); void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); for (int32_t i = contextPath.Length() - 1; i >= 0; --i) { nsStyleContext* context = contextPath[i]; AutoCSSValueArray dataArray(dataStorage, nprops); nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Font), dataArray.get(), aPresContext, context); ruleData.mValueOffsets[eStyleStruct_Font] = 0; // Trimmed down version of ::WalkRuleTree() to re-apply the style rules // Note that we *do* need to do this for our own data, since what is // in |fontData| in ComputeFontData is only for the rules below // aStartStruct. for (nsRuleNode* ruleNode = context->RuleNode(); ruleNode; ruleNode = ruleNode->GetParent()) { if (ruleNode->mNoneBits & fontBit) // no more font rules on this branch, get out break; nsIStyleRule *rule = ruleNode->GetRule(); if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); } } // Compute the delta from the information that the rules specified // Avoid unnecessary operations in SetFont(). But we care if it's // the final value that we're computing. if (i != 0) ruleData.ValueForFontFamily()->Reset(); ResolveVariableReferences(eStyleStruct_Font, &ruleData, aContext); RuleNodeCacheConditions dummy; nsRuleNode::SetFont(aPresContext, context, aGenericFontID, &ruleData, &parentFont, aFont, false, dummy); parentFont = *aFont; } if (higherContext && contextPath.Length() > 1) { // contextPath is a list of all ancestor style contexts, so it must have // at least two elements for it to result in a dependency on grandancestor // styles. PropagateGrandancestorBit(aContext, higherContext); } } const void* nsRuleNode::ComputeFontData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(Font, font, parentFont) // NOTE: The |aRuleDetail| passed in is a little bit conservative due // to the -moz-system-font property. We really don't need to consider // it here in determining whether to cache in the rule tree. However, // we do need to consider it in WalkRuleTree when deciding whether to // walk further up the tree. So this means that when the font struct // is fully specified using *longhand* properties (excluding // -moz-system-font), we won't cache in the rule tree even though we // could. However, it's pretty unlikely authors will do that // (although there is a pretty good chance they'll fully specify it // using the 'font' shorthand). // Figure out if we are a generic font uint8_t generic = kGenericFont_NONE; // XXXldb What if we would have had a string if we hadn't been doing // the optimization with a non-null aStartStruct? const nsCSSValue* familyValue = aRuleData->ValueForFontFamily(); if (eCSSUnit_FontFamilyList == familyValue->GetUnit()) { const FontFamilyList* fontlist = familyValue->GetFontFamilyListValue(); FontFamilyList& fl = font->mFont.fontlist; fl = *fontlist; // extract the first generic in the fontlist, if exists FontFamilyType fontType = fontlist->FirstGeneric(); // if only a single generic, set the generic type if (fontlist->Length() == 1) { switch (fontType) { case eFamily_serif: generic = kGenericFont_serif; break; case eFamily_sans_serif: generic = kGenericFont_sans_serif; break; case eFamily_monospace: generic = kGenericFont_monospace; break; case eFamily_cursive: generic = kGenericFont_cursive; break; case eFamily_fantasy: generic = kGenericFont_fantasy; break; case eFamily_moz_fixed: generic = kGenericFont_moz_fixed; break; default: break; } } } // Now compute our font struct if (generic == kGenericFont_NONE) { // continue the normal processing nsRuleNode::SetFont(mPresContext, aContext, generic, aRuleData, parentFont, font, aStartStruct != nullptr, conditions); } else { // re-calculate the font as a generic font conditions.SetUncacheable(); nsRuleNode::SetGenericFont(mPresContext, aContext, generic, font); } COMPUTE_END_INHERITED(Font, font) } template <typename T> inline uint32_t ListLength(const T* aList) { uint32_t len = 0; while (aList) { len++; aList = aList->mNext; } return len; } static already_AddRefed<nsCSSShadowArray> GetShadowData(const nsCSSValueList* aList, nsStyleContext* aContext, bool aIsBoxShadow, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { uint32_t arrayLength = ListLength(aList); MOZ_ASSERT(arrayLength > 0, "Non-null text-shadow list, yet we counted 0 items."); RefPtr<nsCSSShadowArray> shadowList = new(arrayLength) nsCSSShadowArray(arrayLength); if (!shadowList) return nullptr; nsStyleCoord tempCoord; DebugOnly<bool> unitOK; for (nsCSSShadowItem* item = shadowList->ShadowAt(0); aList; aList = aList->mNext, ++item) { MOZ_ASSERT(aList->mValue.GetUnit() == eCSSUnit_Array, "expecting a plain array value"); nsCSSValue::Array *arr = aList->mValue.GetArrayValue(); // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT unitOK = SetCoord(arr->Item(0), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, aPresContext, aConditions); NS_ASSERTION(unitOK, "unexpected unit"); item->mXOffset = tempCoord.GetCoordValue(); unitOK = SetCoord(arr->Item(1), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, aPresContext, aConditions); NS_ASSERTION(unitOK, "unexpected unit"); item->mYOffset = tempCoord.GetCoordValue(); // Blur radius is optional in the current box-shadow spec if (arr->Item(2).GetUnit() != eCSSUnit_Null) { unitOK = SetCoord(arr->Item(2), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE, aContext, aPresContext, aConditions); NS_ASSERTION(unitOK, "unexpected unit"); item->mRadius = tempCoord.GetCoordValue(); } else { item->mRadius = 0; } // Find the spread radius if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) { unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, aPresContext, aConditions); NS_ASSERTION(unitOK, "unexpected unit"); item->mSpread = tempCoord.GetCoordValue(); } else { item->mSpread = 0; } if (arr->Item(4).GetUnit() != eCSSUnit_Null) { item->mHasColor = true; // 2nd argument can be bogus since inherit is not a valid color unitOK = SetColor(arr->Item(4), 0, aPresContext, aContext, item->mColor, aConditions); NS_ASSERTION(unitOK, "unexpected unit"); } if (aIsBoxShadow && arr->Item(5).GetUnit() == eCSSUnit_Enumerated) { NS_ASSERTION(arr->Item(5).GetIntValue() == uint8_t(StyleBoxShadowType::Inset), "invalid keyword type for box shadow"); item->mInset = true; } else { item->mInset = false; } } return shadowList.forget(); } struct TextEmphasisChars { const char16_t* mFilled; const char16_t* mOpen; }; #define TEXT_EMPHASIS_CHARS_LIST() \ TEXT_EMPHASIS_CHARS_ITEM(u"", u"", NONE) \ TEXT_EMPHASIS_CHARS_ITEM(u"\u2022", u"\u25e6", DOT) \ TEXT_EMPHASIS_CHARS_ITEM(u"\u25cf", u"\u25cb", CIRCLE) \ TEXT_EMPHASIS_CHARS_ITEM(u"\u25c9", u"\u25ce", DOUBLE_CIRCLE) \ TEXT_EMPHASIS_CHARS_ITEM(u"\u25b2", u"\u25b3", TRIANGLE) \ TEXT_EMPHASIS_CHARS_ITEM(u"\ufe45", u"\ufe46", SESAME) static constexpr TextEmphasisChars kTextEmphasisChars[] = { #define TEXT_EMPHASIS_CHARS_ITEM(filled_, open_, type_) \ { filled_, open_ }, // type_ TEXT_EMPHASIS_CHARS_LIST() #undef TEXT_EMPHASIS_CHARS_ITEM }; #define TEXT_EMPHASIS_CHARS_ITEM(filled_, open_, type_) \ static_assert(ArrayLength(filled_) <= 2 && \ ArrayLength(open_) <= 2, \ "emphasis marks should have no more than one char"); \ static_assert( \ *kTextEmphasisChars[NS_STYLE_TEXT_EMPHASIS_STYLE_##type_].mFilled == \ *filled_, "filled " #type_ " should be " #filled_); \ static_assert( \ *kTextEmphasisChars[NS_STYLE_TEXT_EMPHASIS_STYLE_##type_].mOpen == \ *open_, "open " #type_ " should be " #open_); TEXT_EMPHASIS_CHARS_LIST() #undef TEXT_EMPHASIS_CHARS_ITEM #undef TEXT_EMPHASIS_CHARS_LIST static void TruncateStringToSingleGrapheme(nsAString& aStr) { unicode::ClusterIterator iter(aStr.Data(), aStr.Length()); if (!iter.AtEnd()) { iter.Next(); if (!iter.AtEnd()) { // Not mutating the string for common cases helps memory use // since we share the buffer from the specified style into the // computed style. aStr.Truncate(iter - aStr.Data()); } } } struct LineHeightCalcObj { float mLineHeight; bool mIsNumber; }; struct SetLineHeightCalcOps : public css::NumbersAlreadyNormalizedOps { typedef LineHeightCalcObj result_type; nsStyleContext* const mStyleContext; nsPresContext* const mPresContext; RuleNodeCacheConditions& mConditions; SetLineHeightCalcOps(nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) : mStyleContext(aStyleContext), mPresContext(aPresContext), mConditions(aConditions) { } result_type MergeAdditive(nsCSSUnit aCalcFunction, result_type aValue1, result_type aValue2) { MOZ_ASSERT(aValue1.mIsNumber == aValue2.mIsNumber); LineHeightCalcObj result; result.mIsNumber = aValue1.mIsNumber; if (aCalcFunction == eCSSUnit_Calc_Plus) { result.mLineHeight = aValue1.mLineHeight + aValue2.mLineHeight; return result; } MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Minus, "unexpected unit"); result.mLineHeight = aValue1.mLineHeight - aValue2.mLineHeight; return result; } result_type MergeMultiplicativeL(nsCSSUnit aCalcFunction, float aValue1, result_type aValue2) { MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Times_L, "unexpected unit"); LineHeightCalcObj result; result.mIsNumber = aValue2.mIsNumber; result.mLineHeight = aValue1 * aValue2.mLineHeight; return result; } result_type MergeMultiplicativeR(nsCSSUnit aCalcFunction, result_type aValue1, float aValue2) { LineHeightCalcObj result; result.mIsNumber = aValue1.mIsNumber; if (aCalcFunction == eCSSUnit_Calc_Times_R) { result.mLineHeight = aValue1.mLineHeight * aValue2; return result; } MOZ_ASSERT(aCalcFunction == eCSSUnit_Calc_Divided, "unexpected unit"); result.mLineHeight = aValue1.mLineHeight / aValue2; return result; } result_type ComputeLeafValue(const nsCSSValue& aValue) { LineHeightCalcObj result; if (aValue.IsLengthUnit()) { result.mIsNumber = false; result.mLineHeight = CalcLength(aValue, mStyleContext, mPresContext, mConditions); } else if (eCSSUnit_Percent == aValue.GetUnit()) { mConditions.SetUncacheable(); result.mIsNumber = false; nscoord fontSize = mStyleContext->StyleFont()->mFont.size; result.mLineHeight = fontSize * aValue.GetPercentValue(); } else if (eCSSUnit_Number == aValue.GetUnit()) { result.mIsNumber = true; result.mLineHeight = aValue.GetFloatValue(); } else { MOZ_ASSERT(false, "unexpected value"); result.mIsNumber = true; result.mLineHeight = 1.0f; } return result; } }; const void* nsRuleNode::ComputeTextData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(Text, text, parentText) auto setComplexColor = [&](const nsCSSValue* aValue, StyleComplexColor nsStyleText::* aField) { SetComplexColor<eUnsetInherit>(*aValue, parentText->*aField, StyleComplexColor::CurrentColor(), mPresContext, text->*aField, conditions); }; // tab-size: integer, inherit SetValue(*aRuleData->ValueForTabSize(), text->mTabSize, conditions, SETVAL_INTEGER | SETVAL_UNSET_INHERIT, parentText->mTabSize, NS_STYLE_TABSIZE_INITIAL); // letter-spacing: normal, length, inherit SetCoord(*aRuleData->ValueForLetterSpacing(), text->mLetterSpacing, parentText->mLetterSpacing, SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); // text-shadow: none, list, inherit, initial const nsCSSValue* textShadowValue = aRuleData->ValueForTextShadow(); if (textShadowValue->GetUnit() != eCSSUnit_Null) { text->mTextShadow = nullptr; // Don't need to handle none/initial explicitly: The above assignment // takes care of that if (textShadowValue->GetUnit() == eCSSUnit_Inherit || textShadowValue->GetUnit() == eCSSUnit_Unset) { conditions.SetUncacheable(); text->mTextShadow = parentText->mTextShadow; } else if (textShadowValue->GetUnit() == eCSSUnit_List || textShadowValue->GetUnit() == eCSSUnit_ListDep) { // List of arrays text->mTextShadow = GetShadowData(textShadowValue->GetListValue(), aContext, false, mPresContext, conditions); } } // line-height: normal, number, length, percent, calc, inherit const nsCSSValue* lineHeightValue = aRuleData->ValueForLineHeight(); if (eCSSUnit_Percent == lineHeightValue->GetUnit()) { conditions.SetUncacheable(); // Use |mFont.size| to pick up minimum font size. text->mLineHeight.SetCoordValue( NSToCoordRound(float(aContext->StyleFont()->mFont.size) * lineHeightValue->GetPercentValue())); } else if (eCSSUnit_Initial == lineHeightValue->GetUnit() || eCSSUnit_System_Font == lineHeightValue->GetUnit()) { text->mLineHeight.SetNormalValue(); } else if (eCSSUnit_Calc == lineHeightValue->GetUnit()) { SetLineHeightCalcOps ops(aContext, mPresContext, conditions); LineHeightCalcObj obj = css::ComputeCalc(*lineHeightValue, ops); if (obj.mIsNumber) { text->mLineHeight.SetFactorValue(obj.mLineHeight); } else { text->mLineHeight.SetCoordValue( NSToCoordRoundWithClamp(obj.mLineHeight)); } } else { SetCoord(*lineHeightValue, text->mLineHeight, parentText->mLineHeight, SETCOORD_LEH | SETCOORD_FACTOR | SETCOORD_NORMAL | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); if (lineHeightValue->IsLengthUnit() && !lineHeightValue->IsRelativeLengthUnit()) { nscoord lh = nsStyleFont::ZoomText(mPresContext, text->mLineHeight.GetCoordValue()); conditions.SetUncacheable(); const nsStyleFont *font = aContext->StyleFont(); nscoord minimumFontSize = mPresContext->MinFontSize(font->mLanguage); if (minimumFontSize > 0 && !mPresContext->IsChrome()) { if (font->mSize != 0) { lh = nscoord(float(lh) * float(font->mFont.size) / float(font->mSize)); } else { // Never shrink line heights as a result of minFontSize lh = std::max(lh, minimumFontSize); } } text->mLineHeight.SetCoordValue(lh); } } // text-align: enum, string, pair(enum|string), inherit, initial // NOTE: string is not implemented yet. const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign(); text->mTextAlignTrue = false; if (eCSSUnit_String == textAlignValue->GetUnit()) { NS_NOTYETIMPLEMENTED("align string"); } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() && NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT == textAlignValue->GetIntValue()) { conditions.SetUncacheable(); uint8_t parentAlign = parentText->mTextAlign; text->mTextAlign = (NS_STYLE_TEXT_ALIGN_START == parentAlign) ? NS_STYLE_TEXT_ALIGN_CENTER : parentAlign; } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() && NS_STYLE_TEXT_ALIGN_MATCH_PARENT == textAlignValue->GetIntValue()) { conditions.SetUncacheable(); nsStyleContext* parent = aContext->GetParent(); if (parent) { uint8_t parentAlign = parentText->mTextAlign; uint8_t parentDirection = parent->StyleVisibility()->mDirection; switch (parentAlign) { case NS_STYLE_TEXT_ALIGN_START: text->mTextAlign = parentDirection == NS_STYLE_DIRECTION_RTL ? NS_STYLE_TEXT_ALIGN_RIGHT : NS_STYLE_TEXT_ALIGN_LEFT; break; case NS_STYLE_TEXT_ALIGN_END: text->mTextAlign = parentDirection == NS_STYLE_DIRECTION_RTL ? NS_STYLE_TEXT_ALIGN_LEFT : NS_STYLE_TEXT_ALIGN_RIGHT; break; default: text->mTextAlign = parentAlign; } } } else { if (eCSSUnit_Pair == textAlignValue->GetUnit()) { // Two values were specified, one must be 'true'. text->mTextAlignTrue = true; const nsCSSValuePair& textAlignValuePair = textAlignValue->GetPairValue(); textAlignValue = &textAlignValuePair.mXValue; if (eCSSUnit_Enumerated == textAlignValue->GetUnit()) { if (textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) { textAlignValue = &textAlignValuePair.mYValue; } } else if (eCSSUnit_String == textAlignValue->GetUnit()) { NS_NOTYETIMPLEMENTED("align string"); } } else if (eCSSUnit_Inherit == textAlignValue->GetUnit() || eCSSUnit_Unset == textAlignValue->GetUnit()) { text->mTextAlignTrue = parentText->mTextAlignTrue; } SetValue(*textAlignValue, text->mTextAlign, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextAlign, NS_STYLE_TEXT_ALIGN_START); } // text-align-last: enum, pair(enum), inherit, initial const nsCSSValue* textAlignLastValue = aRuleData->ValueForTextAlignLast(); text->mTextAlignLastTrue = false; if (eCSSUnit_Pair == textAlignLastValue->GetUnit()) { // Two values were specified, one must be 'true'. text->mTextAlignLastTrue = true; const nsCSSValuePair& textAlignLastValuePair = textAlignLastValue->GetPairValue(); textAlignLastValue = &textAlignLastValuePair.mXValue; if (eCSSUnit_Enumerated == textAlignLastValue->GetUnit()) { if (textAlignLastValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) { textAlignLastValue = &textAlignLastValuePair.mYValue; } } } else if (eCSSUnit_Inherit == textAlignLastValue->GetUnit() || eCSSUnit_Unset == textAlignLastValue->GetUnit()) { text->mTextAlignLastTrue = parentText->mTextAlignLastTrue; } SetValue(*textAlignLastValue, text->mTextAlignLast, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextAlignLast, NS_STYLE_TEXT_ALIGN_AUTO); // text-indent: length, percent, calc, inherit, initial SetCoord(*aRuleData->ValueForTextIndent(), text->mTextIndent, parentText->mTextIndent, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); // text-justify: enum, inherit, initial SetValue(*aRuleData->ValueForTextJustify(), text->mTextJustify, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextJustify, StyleTextJustify::Auto); // text-transform: enum, inherit, initial SetValue(*aRuleData->ValueForTextTransform(), text->mTextTransform, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextTransform, NS_STYLE_TEXT_TRANSFORM_NONE); // white-space: enum, inherit, initial SetValue(*aRuleData->ValueForWhiteSpace(), text->mWhiteSpace, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mWhiteSpace, NS_STYLE_WHITESPACE_NORMAL); // word-break: enum, inherit, initial SetValue(*aRuleData->ValueForWordBreak(), text->mWordBreak, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mWordBreak, NS_STYLE_WORDBREAK_NORMAL); // word-spacing: normal, length, percent, inherit const nsCSSValue* wordSpacingValue = aRuleData->ValueForWordSpacing(); if (wordSpacingValue->GetUnit() == eCSSUnit_Normal) { // Do this so that "normal" computes to 0px, as the CSS 2.1 spec requires. text->mWordSpacing.SetCoordValue(0); } else { SetCoord(*aRuleData->ValueForWordSpacing(), text->mWordSpacing, parentText->mWordSpacing, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); } // overflow-wrap: enum, inherit, initial SetValue(*aRuleData->ValueForOverflowWrap(), text->mOverflowWrap, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mOverflowWrap, NS_STYLE_OVERFLOWWRAP_NORMAL); // hyphens: enum, inherit, initial SetValue(*aRuleData->ValueForHyphens(), text->mHyphens, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mHyphens, NS_STYLE_HYPHENS_MANUAL); // ruby-align: enum, inherit, initial SetValue(*aRuleData->ValueForRubyAlign(), text->mRubyAlign, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mRubyAlign, NS_STYLE_RUBY_ALIGN_SPACE_AROUND); // ruby-position: enum, inherit, initial SetValue(*aRuleData->ValueForRubyPosition(), text->mRubyPosition, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mRubyPosition, NS_STYLE_RUBY_POSITION_OVER); // text-size-adjust: none, auto, inherit, initial SetValue(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust, conditions, SETVAL_UNSET_INHERIT, parentText->mTextSizeAdjust, /* initial */ NS_STYLE_TEXT_SIZE_ADJUST_AUTO, /* auto */ NS_STYLE_TEXT_SIZE_ADJUST_AUTO, /* none */ NS_STYLE_TEXT_SIZE_ADJUST_NONE, Unused, Unused); // text-combine-upright: enum, inherit, initial SetValue(*aRuleData->ValueForTextCombineUpright(), text->mTextCombineUpright, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextCombineUpright, NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE); // text-emphasis-color: color, string, inherit, initial setComplexColor(aRuleData->ValueForTextEmphasisColor(), &nsStyleText::mTextEmphasisColor); // text-emphasis-position: enum, inherit, initial SetValue(*aRuleData->ValueForTextEmphasisPosition(), text->mTextEmphasisPosition, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextEmphasisPosition, NS_STYLE_TEXT_EMPHASIS_POSITION_OVER | NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT); // text-emphasis-style: string, enum, inherit, initial const nsCSSValue* textEmphasisStyleValue = aRuleData->ValueForTextEmphasisStyle(); switch (textEmphasisStyleValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_None: { text->mTextEmphasisStyle = NS_STYLE_TEXT_EMPHASIS_STYLE_NONE; text->mTextEmphasisStyleString = u""; break; } case eCSSUnit_Inherit: case eCSSUnit_Unset: { conditions.SetUncacheable(); text->mTextEmphasisStyle = parentText->mTextEmphasisStyle; text->mTextEmphasisStyleString = parentText->mTextEmphasisStyleString; break; } case eCSSUnit_Enumerated: { auto style = textEmphasisStyleValue->GetIntValue(); // If shape part is not specified, compute it according to the // writing-mode. Note that, if the fill part (filled/open) is not // specified, we compute it to filled per spec. Since that value // is zero, no additional computation is needed. See the assertion // in CSSParserImpl::ParseTextEmphasisStyle(). if (!(style & NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK)) { conditions.SetUncacheable(); if (WritingMode(aContext).IsVertical()) { style |= NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME; } else { style |= NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE; } } text->mTextEmphasisStyle = style; size_t shape = style & NS_STYLE_TEXT_EMPHASIS_STYLE_SHAPE_MASK; MOZ_ASSERT(shape > 0 && shape < ArrayLength(kTextEmphasisChars)); const TextEmphasisChars& chars = kTextEmphasisChars[shape]; text->mTextEmphasisStyleString = (style & NS_STYLE_TEXT_EMPHASIS_STYLE_FILL_MASK) == NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED ? chars.mFilled : chars.mOpen; break; } case eCSSUnit_String: { text->mTextEmphasisStyle = NS_STYLE_TEXT_EMPHASIS_STYLE_STRING; nsString strValue; textEmphasisStyleValue->GetStringValue(strValue); TruncateStringToSingleGrapheme(strValue); text->mTextEmphasisStyleString = strValue; break; } default: MOZ_ASSERT_UNREACHABLE("Unknown value unit type"); } // text-rendering: enum, inherit, initial SetValue(*aRuleData->ValueForTextRendering(), text->mTextRendering, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mTextRendering, NS_STYLE_TEXT_RENDERING_AUTO); // -webkit-text-fill-color: color, string, inherit, initial setComplexColor(aRuleData->ValueForWebkitTextFillColor(), &nsStyleText::mWebkitTextFillColor); // -webkit-text-stroke-color: color, string, inherit, initial setComplexColor(aRuleData->ValueForWebkitTextStrokeColor(), &nsStyleText::mWebkitTextStrokeColor); // -webkit-text-stroke-width: length, inherit, initial, enum const nsCSSValue* webkitTextStrokeWidthValue = aRuleData->ValueForWebkitTextStrokeWidth(); if (webkitTextStrokeWidthValue->GetUnit() == eCSSUnit_Enumerated) { NS_ASSERTION(webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || webkitTextStrokeWidthValue->GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, "Unexpected enum value"); text->mWebkitTextStrokeWidth.SetCoordValue( mPresContext->GetBorderWidthTable()[webkitTextStrokeWidthValue->GetIntValue()]); } else { SetCoord(*webkitTextStrokeWidthValue, text->mWebkitTextStrokeWidth, parentText->mWebkitTextStrokeWidth, SETCOORD_LH | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_INITIAL_ZERO | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); } // -moz-control-character-visibility: enum, inherit, initial SetValue(*aRuleData->ValueForControlCharacterVisibility(), text->mControlCharacterVisibility, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentText->mControlCharacterVisibility, nsCSSParser::ControlCharVisibilityDefault()); COMPUTE_END_INHERITED(Text, text) } const void* nsRuleNode::ComputeTextResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(TextReset, text, parentText) // text-decoration-line: enum (bit field), inherit, initial const nsCSSValue* decorationLineValue = aRuleData->ValueForTextDecorationLine(); if (eCSSUnit_Enumerated == decorationLineValue->GetUnit()) { int32_t td = decorationLineValue->GetIntValue(); text->mTextDecorationLine = td; if (td & NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS) { bool underlineLinks = mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks); if (underlineLinks) { text->mTextDecorationLine |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; } else { text->mTextDecorationLine &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; } } } else if (eCSSUnit_Inherit == decorationLineValue->GetUnit()) { conditions.SetUncacheable(); text->mTextDecorationLine = parentText->mTextDecorationLine; } else if (eCSSUnit_Initial == decorationLineValue->GetUnit() || eCSSUnit_Unset == decorationLineValue->GetUnit()) { text->mTextDecorationLine = NS_STYLE_TEXT_DECORATION_LINE_NONE; } // text-decoration-color: color, string, enum, inherit, initial SetComplexColor<eUnsetInitial>(*aRuleData->ValueForTextDecorationColor(), parentText->mTextDecorationColor, StyleComplexColor::CurrentColor(), mPresContext, text->mTextDecorationColor, conditions); // text-decoration-style: enum, inherit, initial const nsCSSValue* decorationStyleValue = aRuleData->ValueForTextDecorationStyle(); if (eCSSUnit_Enumerated == decorationStyleValue->GetUnit()) { text->mTextDecorationStyle = decorationStyleValue->GetIntValue(); } else if (eCSSUnit_Inherit == decorationStyleValue->GetUnit()) { text->mTextDecorationStyle = parentText->mTextDecorationStyle; conditions.SetUncacheable(); } else if (eCSSUnit_Initial == decorationStyleValue->GetUnit() || eCSSUnit_Unset == decorationStyleValue->GetUnit()) { text->mTextDecorationStyle = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; } // text-overflow: enum, string, pair(enum|string), inherit, initial const nsCSSValue* textOverflowValue = aRuleData->ValueForTextOverflow(); if (eCSSUnit_Initial == textOverflowValue->GetUnit() || eCSSUnit_Unset == textOverflowValue->GetUnit()) { text->mTextOverflow = nsStyleTextOverflow(); } else if (eCSSUnit_Inherit == textOverflowValue->GetUnit()) { conditions.SetUncacheable(); text->mTextOverflow = parentText->mTextOverflow; } else if (eCSSUnit_Enumerated == textOverflowValue->GetUnit()) { // A single enumerated value. SetValue(*textOverflowValue, text->mTextOverflow.mRight.mType, conditions, SETVAL_ENUMERATED, parentText->mTextOverflow.mRight.mType, NS_STYLE_TEXT_OVERFLOW_CLIP); text->mTextOverflow.mRight.mString.Truncate(); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; text->mTextOverflow.mLeft.mString.Truncate(); text->mTextOverflow.mLogicalDirections = true; } else if (eCSSUnit_String == textOverflowValue->GetUnit()) { // A single string value. text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; textOverflowValue->GetStringValue(text->mTextOverflow.mRight.mString); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_CLIP; text->mTextOverflow.mLeft.mString.Truncate(); text->mTextOverflow.mLogicalDirections = true; } else if (eCSSUnit_Pair == textOverflowValue->GetUnit()) { // Two values were specified. text->mTextOverflow.mLogicalDirections = false; const nsCSSValuePair& textOverflowValuePair = textOverflowValue->GetPairValue(); const nsCSSValue *textOverflowLeftValue = &textOverflowValuePair.mXValue; if (eCSSUnit_Enumerated == textOverflowLeftValue->GetUnit()) { SetValue(*textOverflowLeftValue, text->mTextOverflow.mLeft.mType, conditions, SETVAL_ENUMERATED, parentText->mTextOverflow.mLeft.mType, NS_STYLE_TEXT_OVERFLOW_CLIP); text->mTextOverflow.mLeft.mString.Truncate(); } else if (eCSSUnit_String == textOverflowLeftValue->GetUnit()) { textOverflowLeftValue->GetStringValue(text->mTextOverflow.mLeft.mString); text->mTextOverflow.mLeft.mType = NS_STYLE_TEXT_OVERFLOW_STRING; } const nsCSSValue *textOverflowRightValue = &textOverflowValuePair.mYValue; if (eCSSUnit_Enumerated == textOverflowRightValue->GetUnit()) { SetValue(*textOverflowRightValue, text->mTextOverflow.mRight.mType, conditions, SETVAL_ENUMERATED, parentText->mTextOverflow.mRight.mType, NS_STYLE_TEXT_OVERFLOW_CLIP); text->mTextOverflow.mRight.mString.Truncate(); } else if (eCSSUnit_String == textOverflowRightValue->GetUnit()) { textOverflowRightValue->GetStringValue(text->mTextOverflow.mRight.mString); text->mTextOverflow.mRight.mType = NS_STYLE_TEXT_OVERFLOW_STRING; } } // unicode-bidi: enum, inherit, initial SetValue(*aRuleData->ValueForUnicodeBidi(), text->mUnicodeBidi, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentText->mUnicodeBidi, NS_STYLE_UNICODE_BIDI_NORMAL); // initial-letter: normal, number, array(number, integer?), initial const nsCSSValue* initialLetterValue = aRuleData->ValueForInitialLetter(); if (initialLetterValue->GetUnit() == eCSSUnit_Null) { // We don't want to change anything in this case. } else if (initialLetterValue->GetUnit() == eCSSUnit_Inherit) { conditions.SetUncacheable(); text->mInitialLetterSink = parentText->mInitialLetterSink; text->mInitialLetterSize = parentText->mInitialLetterSize; } else if (initialLetterValue->GetUnit() == eCSSUnit_Initial || initialLetterValue->GetUnit() == eCSSUnit_Unset || initialLetterValue->GetUnit() == eCSSUnit_Normal) { // Use invalid values in initial-letter property to mean normal. So we can // determine whether it is normal by checking mInitialLetterSink == 0. text->mInitialLetterSink = 0; text->mInitialLetterSize = 0.0f; } else if (initialLetterValue->GetUnit() == eCSSUnit_Array) { const nsCSSValue& firstValue = initialLetterValue->GetArrayValue()->Item(0); const nsCSSValue& secondValue = initialLetterValue->GetArrayValue()->Item(1); MOZ_ASSERT(firstValue.GetUnit() == eCSSUnit_Number && secondValue.GetUnit() == eCSSUnit_Integer, "unexpected value unit"); text->mInitialLetterSize = firstValue.GetFloatValue(); text->mInitialLetterSink = secondValue.GetIntValue(); } else if (initialLetterValue->GetUnit() == eCSSUnit_Number) { text->mInitialLetterSize = initialLetterValue->GetFloatValue(); text->mInitialLetterSink = NSToCoordFloorClamped(text->mInitialLetterSize); } else { MOZ_ASSERT_UNREACHABLE("unknown unit for initial-letter"); } COMPUTE_END_RESET(TextReset, text) } const void* nsRuleNode::ComputeUserInterfaceData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(UserInterface, ui, parentUI) // cursor: enum, url, inherit const nsCSSValue* cursorValue = aRuleData->ValueForCursor(); nsCSSUnit cursorUnit = cursorValue->GetUnit(); if (cursorUnit != eCSSUnit_Null) { ui->mCursorImages.Clear(); if (cursorUnit == eCSSUnit_Inherit || cursorUnit == eCSSUnit_Unset) { conditions.SetUncacheable(); ui->mCursor = parentUI->mCursor; ui->mCursorImages = parentUI->mCursorImages; } else if (cursorUnit == eCSSUnit_Initial) { ui->mCursor = NS_STYLE_CURSOR_AUTO; } else { // The parser will never create a list that is *all* URL values -- // that's invalid. MOZ_ASSERT(cursorUnit == eCSSUnit_List || cursorUnit == eCSSUnit_ListDep, "unrecognized cursor unit"); const nsCSSValueList* list = cursorValue->GetListValue(); for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext) { nsCSSValue::Array* arr = list->mValue.GetArrayValue(); imgRequestProxy* req = GetImageRequest(aContext->PresContext(), arr->Item(0)); if (req) { nsCursorImage* item = ui->mCursorImages.AppendElement(); item->SetImage(req); if (arr->Item(1).GetUnit() != eCSSUnit_Null) { item->mHaveHotspot = true; item->mHotspotX = arr->Item(1).GetFloatValue(); item->mHotspotY = arr->Item(2).GetFloatValue(); } } } NS_ASSERTION(list, "Must have non-array value at the end"); NS_ASSERTION(list->mValue.GetUnit() == eCSSUnit_Enumerated, "Unexpected fallback value at end of cursor list"); ui->mCursor = list->mValue.GetIntValue(); } } // user-input: enum, inherit, initial SetValue(*aRuleData->ValueForUserInput(), ui->mUserInput, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentUI->mUserInput, StyleUserInput::Auto); // user-modify: enum, inherit, initial SetValue(*aRuleData->ValueForUserModify(), ui->mUserModify, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentUI->mUserModify, StyleUserModify::ReadOnly); // user-focus: enum, inherit, initial SetValue(*aRuleData->ValueForUserFocus(), ui->mUserFocus, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentUI->mUserFocus, StyleUserFocus::None); // pointer-events: enum, inherit, initial SetValue(*aRuleData->ValueForPointerEvents(), ui->mPointerEvents, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentUI->mPointerEvents, NS_STYLE_POINTER_EVENTS_AUTO); COMPUTE_END_INHERITED(UserInterface, ui) } const void* nsRuleNode::ComputeUIResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(UIReset, ui, parentUI) // user-select: enum, inherit, initial SetValue(*aRuleData->ValueForUserSelect(), ui->mUserSelect, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentUI->mUserSelect, StyleUserSelect::Auto); // ime-mode: enum, inherit, initial SetValue(*aRuleData->ValueForImeMode(), ui->mIMEMode, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentUI->mIMEMode, NS_STYLE_IME_MODE_AUTO); // force-broken-image-icons: integer, inherit, initial SetValue(*aRuleData->ValueForForceBrokenImageIcon(), ui->mForceBrokenImageIcon, conditions, SETVAL_INTEGER | SETVAL_UNSET_INITIAL, parentUI->mForceBrokenImageIcon, 0); // -moz-window-dragging: enum, inherit, initial SetValue(*aRuleData->ValueForWindowDragging(), ui->mWindowDragging, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentUI->mWindowDragging, StyleWindowDragging::Default); // -moz-window-shadow: enum, inherit, initial SetValue(*aRuleData->ValueForWindowShadow(), ui->mWindowShadow, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentUI->mWindowShadow, NS_STYLE_WINDOW_SHADOW_DEFAULT); COMPUTE_END_RESET(UIReset, ui) } // Information about each transition or animation property that is // constant. struct TransitionPropInfo { nsCSSPropertyID property; // Location of the count of the property's computed value. uint32_t nsStyleDisplay::* sdCount; }; // Each property's index in this array must match its index in the // mutable array |transitionPropData| below. static const TransitionPropInfo transitionPropInfo[4] = { { eCSSProperty_transition_delay, &nsStyleDisplay::mTransitionDelayCount }, { eCSSProperty_transition_duration, &nsStyleDisplay::mTransitionDurationCount }, { eCSSProperty_transition_property, &nsStyleDisplay::mTransitionPropertyCount }, { eCSSProperty_transition_timing_function, &nsStyleDisplay::mTransitionTimingFunctionCount }, }; // Each property's index in this array must match its index in the // mutable array |animationPropData| below. static const TransitionPropInfo animationPropInfo[8] = { { eCSSProperty_animation_delay, &nsStyleDisplay::mAnimationDelayCount }, { eCSSProperty_animation_duration, &nsStyleDisplay::mAnimationDurationCount }, { eCSSProperty_animation_name, &nsStyleDisplay::mAnimationNameCount }, { eCSSProperty_animation_timing_function, &nsStyleDisplay::mAnimationTimingFunctionCount }, { eCSSProperty_animation_direction, &nsStyleDisplay::mAnimationDirectionCount }, { eCSSProperty_animation_fill_mode, &nsStyleDisplay::mAnimationFillModeCount }, { eCSSProperty_animation_play_state, &nsStyleDisplay::mAnimationPlayStateCount }, { eCSSProperty_animation_iteration_count, &nsStyleDisplay::mAnimationIterationCountCount }, }; // Information about each transition or animation property that changes // during ComputeDisplayData. struct TransitionPropData { const nsCSSValueList *list; nsCSSUnit unit; uint32_t num; }; static uint32_t CountTransitionProps(const TransitionPropInfo* aInfo, TransitionPropData* aData, size_t aLength, nsStyleDisplay* aDisplay, const nsStyleDisplay* aParentDisplay, const nsRuleData* aRuleData, RuleNodeCacheConditions& aConditions) { // The four transition properties or eight animation properties are // stored in nsCSSDisplay in a single array for all properties. The // number of transitions is equal to the number of items in the // longest property's value. Properties that have fewer values than // the longest are filled in by repeating the list. However, this // repetition does not extend the computed value of that particular // property (for purposes of inheritance, or, in our code, for when // other properties are overridden by a more specific rule). // But actually, since the spec isn't clear yet, we'll fully compute // all of them (so we can switch easily later), but only care about // the ones up to the number of items for 'transition-property', per // http://lists.w3.org/Archives/Public/www-style/2009Aug/0109.html . // Transitions are difficult to handle correctly because of this. For // example, we need to handle scenarios such as: // * a more general rule specifies transition-property: a, b, c; // * a more specific rule overrides as transition-property: d; // // If only the general rule applied, we would fill in the extra // properties (duration, delay, etc) with initial values to create 3 // fully-specified transitions. But when the more specific rule // applies, we should only create a single transition. In order to do // this we need to remember which properties were explicitly specified // and which ones were just filled in with initial values to get a // fully-specified transition, which we do by remembering the number // of values for each property. uint32_t numTransitions = 0; for (size_t i = 0; i < aLength; ++i) { const TransitionPropInfo& info = aInfo[i]; TransitionPropData& data = aData[i]; // cache whether any of the properties are specified as 'inherit' so // we can use it below const nsCSSValue& value = *aRuleData->ValueFor(info.property); data.unit = value.GetUnit(); data.list = (value.GetUnit() == eCSSUnit_List || value.GetUnit() == eCSSUnit_ListDep) ? value.GetListValue() : nullptr; // General algorithm to determine how many total transitions we need // to build. For each property: // - if there is no value specified in for the property in // displayData, use the values from the start struct, but only if // they were explicitly specified // - if there is a value specified for the property in displayData: // - if the value is 'inherit', count the number of values for // that property are specified by the parent, but only those // that were explicitly specified // - otherwise, count the number of values specified in displayData // calculate number of elements if (data.unit == eCSSUnit_Inherit) { data.num = aParentDisplay->*(info.sdCount); aConditions.SetUncacheable(); } else if (data.list) { data.num = ListLength(data.list); } else { data.num = aDisplay->*(info.sdCount); } if (data.num > numTransitions) numTransitions = data.num; } return numTransitions; } /* static */ void nsRuleNode::ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult) { switch (aValue.GetUnit()) { case eCSSUnit_Enumerated: aResult = nsTimingFunction(aValue.GetIntValue()); break; case eCSSUnit_Cubic_Bezier: { nsCSSValue::Array* array = aValue.GetArrayValue(); NS_ASSERTION(array && array->Count() == 4, "Need 4 control points"); aResult = nsTimingFunction(array->Item(0).GetFloatValue(), array->Item(1).GetFloatValue(), array->Item(2).GetFloatValue(), array->Item(3).GetFloatValue()); } break; case eCSSUnit_Steps: { nsCSSValue::Array* array = aValue.GetArrayValue(); NS_ASSERTION(array && array->Count() == 2, "Need 2 items"); NS_ASSERTION(array->Item(0).GetUnit() == eCSSUnit_Integer, "unexpected first value"); NS_ASSERTION(array->Item(1).GetUnit() == eCSSUnit_Enumerated && (array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START || array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END || array->Item(1).GetIntValue() == -1), "unexpected second value"); nsTimingFunction::Type type = (array->Item(1).GetIntValue() == NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START) ? nsTimingFunction::Type::StepStart : nsTimingFunction::Type::StepEnd; aResult = nsTimingFunction(type, array->Item(0).GetIntValue()); } break; default: NS_NOTREACHED("Invalid transition property unit"); } } static uint8_t GetWillChangeBitFieldFromPropFlags(const nsCSSPropertyID& aProp) { uint8_t willChangeBitField = 0; if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_CREATES_STACKING_CONTEXT)) { willChangeBitField |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; } if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_FIXPOS_CB)) { willChangeBitField |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; } if (nsCSSProps::PropHasFlags(aProp, CSS_PROPERTY_ABSPOS_CB)) { willChangeBitField |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; } return willChangeBitField; } const void* nsRuleNode::ComputeDisplayData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Display, display, parentDisplay) // We may have ended up with aStartStruct's values of mDisplay and // mFloat, but those may not be correct if our style data overrides // its position or float properties. Reset to mOriginalDisplay and // mOriginalFloat; if it turns out we still need the display/floats // adjustments, we'll do them below. display->mDisplay = display->mOriginalDisplay; display->mFloat = display->mOriginalFloat; // Each property's index in this array must match its index in the // const array |transitionPropInfo| above. TransitionPropData transitionPropData[4]; TransitionPropData& delay = transitionPropData[0]; TransitionPropData& duration = transitionPropData[1]; TransitionPropData& property = transitionPropData[2]; TransitionPropData& timingFunction = transitionPropData[3]; #define FOR_ALL_TRANSITION_PROPS(var_) \ for (uint32_t var_ = 0; var_ < 4; ++var_) // CSS Transitions uint32_t numTransitions = CountTransitionProps(transitionPropInfo, transitionPropData, ArrayLength(transitionPropData), display, parentDisplay, aRuleData, conditions); display->mTransitions.SetLengthNonZero(numTransitions); FOR_ALL_TRANSITION_PROPS(p) { const TransitionPropInfo& i = transitionPropInfo[p]; TransitionPropData& d = transitionPropData[p]; display->*(i.sdCount) = d.num; } // Fill in the transitions we just allocated with the appropriate values. for (uint32_t i = 0; i < numTransitions; ++i) { StyleTransition *transition = &display->mTransitions[i]; if (i >= delay.num) { MOZ_ASSERT(delay.num, "delay.num must be greater than 0"); transition->SetDelay(display->mTransitions[i % delay.num].GetDelay()); } else if (delay.unit == eCSSUnit_Inherit) { // FIXME (Bug 522599) (for all transition properties): write a test that // detects when this was wrong for i >= delay.num if parent had // count for this property not equal to length MOZ_ASSERT(i < parentDisplay->mTransitionDelayCount, "delay.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); transition->SetDelay(parentDisplay->mTransitions[i].GetDelay()); } else if (delay.unit == eCSSUnit_Initial || delay.unit == eCSSUnit_Unset) { transition->SetDelay(0.0); } else if (delay.list) { switch (delay.list->mValue.GetUnit()) { case eCSSUnit_Seconds: transition->SetDelay(PR_MSEC_PER_SEC * delay.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: transition->SetDelay(delay.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid delay unit"); } } if (i >= duration.num) { MOZ_ASSERT(duration.num, "duration.num must be greater than 0"); transition->SetDuration( display->mTransitions[i % duration.num].GetDuration()); } else if (duration.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mTransitionDurationCount, "duration.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); transition->SetDuration(parentDisplay->mTransitions[i].GetDuration()); } else if (duration.unit == eCSSUnit_Initial || duration.unit == eCSSUnit_Unset) { transition->SetDuration(0.0); } else if (duration.list) { switch (duration.list->mValue.GetUnit()) { case eCSSUnit_Seconds: transition->SetDuration(PR_MSEC_PER_SEC * duration.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: transition->SetDuration(duration.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid duration unit"); } } if (i >= property.num) { MOZ_ASSERT(property.num, "property.num must be greater than 0"); transition->CopyPropertyFrom(display->mTransitions[i % property.num]); } else if (property.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mTransitionPropertyCount, "property.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); transition->CopyPropertyFrom(parentDisplay->mTransitions[i]); } else if (property.unit == eCSSUnit_Initial || property.unit == eCSSUnit_Unset) { transition->SetProperty(eCSSPropertyExtra_all_properties); } else if (property.unit == eCSSUnit_None) { transition->SetProperty(eCSSPropertyExtra_no_properties); } else if (property.list) { const nsCSSValue &val = property.list->mValue; if (val.GetUnit() == eCSSUnit_Ident) { nsDependentString propertyStr(property.list->mValue.GetStringBufferValue()); nsCSSPropertyID prop = nsCSSProps::LookupProperty(propertyStr, CSSEnabledState::eForAllContent); if (prop == eCSSProperty_UNKNOWN || prop == eCSSPropertyExtra_variable) { transition->SetUnknownProperty(prop, propertyStr); } else { transition->SetProperty(prop); } } else { MOZ_ASSERT(val.GetUnit() == eCSSUnit_All, "Invalid transition property unit"); transition->SetProperty(eCSSPropertyExtra_all_properties); } } if (i >= timingFunction.num) { MOZ_ASSERT(timingFunction.num, "timingFunction.num must be greater than 0"); transition->SetTimingFunction( display->mTransitions[i % timingFunction.num].GetTimingFunction()); } else if (timingFunction.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mTransitionTimingFunctionCount, "timingFunction.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); transition->SetTimingFunction( parentDisplay->mTransitions[i].GetTimingFunction()); } else if (timingFunction.unit == eCSSUnit_Initial || timingFunction.unit == eCSSUnit_Unset) { transition->SetTimingFunction( nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); } else if (timingFunction.list) { ComputeTimingFunction(timingFunction.list->mValue, transition->TimingFunctionSlot()); } FOR_ALL_TRANSITION_PROPS(p) { const TransitionPropInfo& info = transitionPropInfo[p]; TransitionPropData& d = transitionPropData[p]; // if we're at the end of the list, start at the beginning and repeat // until we're out of transitions to populate if (d.list) { d.list = d.list->mNext ? d.list->mNext : aRuleData->ValueFor(info.property)->GetListValue(); } } } // Each property's index in this array must match its index in the // const array |animationPropInfo| above. TransitionPropData animationPropData[8]; TransitionPropData& animDelay = animationPropData[0]; TransitionPropData& animDuration = animationPropData[1]; TransitionPropData& animName = animationPropData[2]; TransitionPropData& animTimingFunction = animationPropData[3]; TransitionPropData& animDirection = animationPropData[4]; TransitionPropData& animFillMode = animationPropData[5]; TransitionPropData& animPlayState = animationPropData[6]; TransitionPropData& animIterationCount = animationPropData[7]; #define FOR_ALL_ANIMATION_PROPS(var_) \ for (uint32_t var_ = 0; var_ < 8; ++var_) // CSS Animations. uint32_t numAnimations = CountTransitionProps(animationPropInfo, animationPropData, ArrayLength(animationPropData), display, parentDisplay, aRuleData, conditions); display->mAnimations.SetLengthNonZero(numAnimations); FOR_ALL_ANIMATION_PROPS(p) { const TransitionPropInfo& i = animationPropInfo[p]; TransitionPropData& d = animationPropData[p]; display->*(i.sdCount) = d.num; } // Fill in the animations we just allocated with the appropriate values. for (uint32_t i = 0; i < numAnimations; ++i) { StyleAnimation *animation = &display->mAnimations[i]; if (i >= animDelay.num) { MOZ_ASSERT(animDelay.num, "animDelay.num must be greater than 0"); animation->SetDelay(display->mAnimations[i % animDelay.num].GetDelay()); } else if (animDelay.unit == eCSSUnit_Inherit) { // FIXME (Bug 522599) (for all animation properties): write a test that // detects when this was wrong for i >= animDelay.num if parent had // count for this property not equal to length MOZ_ASSERT(i < parentDisplay->mAnimationDelayCount, "animDelay.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetDelay(parentDisplay->mAnimations[i].GetDelay()); } else if (animDelay.unit == eCSSUnit_Initial || animDelay.unit == eCSSUnit_Unset) { animation->SetDelay(0.0); } else if (animDelay.list) { switch (animDelay.list->mValue.GetUnit()) { case eCSSUnit_Seconds: animation->SetDelay(PR_MSEC_PER_SEC * animDelay.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: animation->SetDelay(animDelay.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid delay unit"); } } if (i >= animDuration.num) { MOZ_ASSERT(animDuration.num, "animDuration.num must be greater than 0"); animation->SetDuration( display->mAnimations[i % animDuration.num].GetDuration()); } else if (animDuration.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationDurationCount, "animDuration.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetDuration(parentDisplay->mAnimations[i].GetDuration()); } else if (animDuration.unit == eCSSUnit_Initial || animDuration.unit == eCSSUnit_Unset) { animation->SetDuration(0.0); } else if (animDuration.list) { switch (animDuration.list->mValue.GetUnit()) { case eCSSUnit_Seconds: animation->SetDuration(PR_MSEC_PER_SEC * animDuration.list->mValue.GetFloatValue()); break; case eCSSUnit_Milliseconds: animation->SetDuration(animDuration.list->mValue.GetFloatValue()); break; default: NS_NOTREACHED("Invalid duration unit"); } } if (i >= animName.num) { MOZ_ASSERT(animName.num, "animName.num must be greater than 0"); animation->SetName(display->mAnimations[i % animName.num].GetName()); } else if (animName.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationNameCount, "animName.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetName(parentDisplay->mAnimations[i].GetName()); } else if (animName.unit == eCSSUnit_Initial || animName.unit == eCSSUnit_Unset) { animation->SetName(EmptyString()); } else if (animName.list) { switch (animName.list->mValue.GetUnit()) { case eCSSUnit_Ident: { nsDependentString nameStr(animName.list->mValue.GetStringBufferValue()); animation->SetName(nameStr); break; } case eCSSUnit_None: { animation->SetName(EmptyString()); break; } default: MOZ_ASSERT(false, "Invalid animation-name unit"); } } if (i >= animTimingFunction.num) { MOZ_ASSERT(animTimingFunction.num, "animTimingFunction.num must be greater than 0"); animation->SetTimingFunction( display->mAnimations[i % animTimingFunction.num].GetTimingFunction()); } else if (animTimingFunction.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationTimingFunctionCount, "animTimingFunction.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetTimingFunction( parentDisplay->mAnimations[i].GetTimingFunction()); } else if (animTimingFunction.unit == eCSSUnit_Initial || animTimingFunction.unit == eCSSUnit_Unset) { animation->SetTimingFunction( nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); } else if (animTimingFunction.list) { ComputeTimingFunction(animTimingFunction.list->mValue, animation->TimingFunctionSlot()); } if (i >= animDirection.num) { MOZ_ASSERT(animDirection.num, "animDirection.num must be greater than 0"); animation->SetDirection(display->mAnimations[i % animDirection.num].GetDirection()); } else if (animDirection.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationDirectionCount, "animDirection.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetDirection(parentDisplay->mAnimations[i].GetDirection()); } else if (animDirection.unit == eCSSUnit_Initial || animDirection.unit == eCSSUnit_Unset) { animation->SetDirection(dom::PlaybackDirection::Normal); } else if (animDirection.list) { MOZ_ASSERT(animDirection.list->mValue.GetUnit() == eCSSUnit_Enumerated, "Invalid animation-direction unit"); animation->SetDirection( static_cast<dom::PlaybackDirection>(animDirection.list->mValue.GetIntValue())); } if (i >= animFillMode.num) { MOZ_ASSERT(animFillMode.num, "animFillMode.num must be greater than 0"); animation->SetFillMode(display->mAnimations[i % animFillMode.num].GetFillMode()); } else if (animFillMode.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationFillModeCount, "animFillMode.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetFillMode(parentDisplay->mAnimations[i].GetFillMode()); } else if (animFillMode.unit == eCSSUnit_Initial || animFillMode.unit == eCSSUnit_Unset) { animation->SetFillMode(dom::FillMode::None); } else if (animFillMode.list) { MOZ_ASSERT(animFillMode.list->mValue.GetUnit() == eCSSUnit_Enumerated, "Invalid animation-fill-mode unit"); animation->SetFillMode( static_cast<dom::FillMode>(animFillMode.list->mValue.GetIntValue())); } if (i >= animPlayState.num) { MOZ_ASSERT(animPlayState.num, "animPlayState.num must be greater than 0"); animation->SetPlayState(display->mAnimations[i % animPlayState.num].GetPlayState()); } else if (animPlayState.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationPlayStateCount, "animPlayState.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetPlayState(parentDisplay->mAnimations[i].GetPlayState()); } else if (animPlayState.unit == eCSSUnit_Initial || animPlayState.unit == eCSSUnit_Unset) { animation->SetPlayState(NS_STYLE_ANIMATION_PLAY_STATE_RUNNING); } else if (animPlayState.list) { MOZ_ASSERT(animPlayState.list->mValue.GetUnit() == eCSSUnit_Enumerated, "Invalid animation-play-state unit"); animation->SetPlayState(animPlayState.list->mValue.GetIntValue()); } if (i >= animIterationCount.num) { MOZ_ASSERT(animIterationCount.num, "animIterationCount.num must be greater than 0"); animation->SetIterationCount(display->mAnimations[i % animIterationCount.num].GetIterationCount()); } else if (animIterationCount.unit == eCSSUnit_Inherit) { MOZ_ASSERT(i < parentDisplay->mAnimationIterationCountCount, "animIterationCount.num computed incorrectly"); MOZ_ASSERT(!conditions.Cacheable(), "should have made conditions.Cacheable() false above"); animation->SetIterationCount(parentDisplay->mAnimations[i].GetIterationCount()); } else if (animIterationCount.unit == eCSSUnit_Initial || animIterationCount.unit == eCSSUnit_Unset) { animation->SetIterationCount(1.0f); } else if (animIterationCount.list) { switch (animIterationCount.list->mValue.GetUnit()) { case eCSSUnit_Enumerated: MOZ_ASSERT(animIterationCount.list->mValue.GetIntValue() == NS_STYLE_ANIMATION_ITERATION_COUNT_INFINITE, "unexpected value"); animation->SetIterationCount(NS_IEEEPositiveInfinity()); break; case eCSSUnit_Number: animation->SetIterationCount( animIterationCount.list->mValue.GetFloatValue()); break; default: MOZ_ASSERT(false, "unexpected animation-iteration-count unit"); } } FOR_ALL_ANIMATION_PROPS(p) { const TransitionPropInfo& info = animationPropInfo[p]; TransitionPropData& d = animationPropData[p]; // if we're at the end of the list, start at the beginning and repeat // until we're out of animations to populate if (d.list) { d.list = d.list->mNext ? d.list->mNext : aRuleData->ValueFor(info.property)->GetListValue(); } } } // display: enum, inherit, initial SetValue(*aRuleData->ValueForDisplay(), display->mDisplay, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mDisplay, StyleDisplay::Inline); // contain: none, enum, inherit, initial SetValue(*aRuleData->ValueForContain(), display->mContain, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mContain, NS_STYLE_CONTAIN_NONE, Unused, NS_STYLE_CONTAIN_NONE, Unused, Unused); // scroll-behavior: enum, inherit, initial SetValue(*aRuleData->ValueForScrollBehavior(), display->mScrollBehavior, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mScrollBehavior, NS_STYLE_SCROLL_BEHAVIOR_AUTO); // scroll-snap-type-x: none, enum, inherit, initial SetValue(*aRuleData->ValueForScrollSnapTypeX(), display->mScrollSnapTypeX, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mScrollSnapTypeX, NS_STYLE_SCROLL_SNAP_TYPE_NONE); // scroll-snap-type-y: none, enum, inherit, initial SetValue(*aRuleData->ValueForScrollSnapTypeY(), display->mScrollSnapTypeY, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mScrollSnapTypeY, NS_STYLE_SCROLL_SNAP_TYPE_NONE); // scroll-snap-points-x: none, inherit, initial const nsCSSValue& scrollSnapPointsX = *aRuleData->ValueForScrollSnapPointsX(); switch (scrollSnapPointsX.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: display->mScrollSnapPointsX.SetNoneValue(); break; case eCSSUnit_Inherit: display->mScrollSnapPointsX = parentDisplay->mScrollSnapPointsX; conditions.SetUncacheable(); break; case eCSSUnit_Function: { nsCSSValue::Array* func = scrollSnapPointsX.GetArrayValue(); NS_ASSERTION(func->Item(0).GetKeywordValue() == eCSSKeyword_repeat, "Expected repeat(), got another function name"); nsStyleCoord coord; if (SetCoord(func->Item(1), coord, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC | SETCOORD_CALC_CLAMP_NONNEGATIVE, aContext, mPresContext, conditions)) { NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord || coord.GetUnit() == eStyleUnit_Percent || coord.GetUnit() == eStyleUnit_Calc, "unexpected unit"); display->mScrollSnapPointsX = coord; } break; } default: NS_NOTREACHED("unexpected unit"); } // scroll-snap-points-y: none, inherit, initial const nsCSSValue& scrollSnapPointsY = *aRuleData->ValueForScrollSnapPointsY(); switch (scrollSnapPointsY.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: display->mScrollSnapPointsY.SetNoneValue(); break; case eCSSUnit_Inherit: display->mScrollSnapPointsY = parentDisplay->mScrollSnapPointsY; conditions.SetUncacheable(); break; case eCSSUnit_Function: { nsCSSValue::Array* func = scrollSnapPointsY.GetArrayValue(); NS_ASSERTION(func->Item(0).GetKeywordValue() == eCSSKeyword_repeat, "Expected repeat(), got another function name"); nsStyleCoord coord; if (SetCoord(func->Item(1), coord, nsStyleCoord(), SETCOORD_LP | SETCOORD_STORE_CALC | SETCOORD_CALC_CLAMP_NONNEGATIVE, aContext, mPresContext, conditions)) { NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord || coord.GetUnit() == eStyleUnit_Percent || coord.GetUnit() == eStyleUnit_Calc, "unexpected unit"); display->mScrollSnapPointsY = coord; } break; } default: NS_NOTREACHED("unexpected unit"); } // scroll-snap-destination: inherit, initial const nsCSSValue& snapDestination = *aRuleData->ValueForScrollSnapDestination(); switch (snapDestination.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: display->mScrollSnapDestination.SetInitialZeroValues(); break; case eCSSUnit_Inherit: display->mScrollSnapDestination = parentDisplay->mScrollSnapDestination; conditions.SetUncacheable(); break; default: { ComputePositionValue(aContext, snapDestination, display->mScrollSnapDestination, conditions); } } // scroll-snap-coordinate: none, inherit, initial const nsCSSValue& snapCoordinate = *aRuleData->ValueForScrollSnapCoordinate(); switch (snapCoordinate.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: // Unset and Initial is none, indicated by an empty array display->mScrollSnapCoordinate.Clear(); break; case eCSSUnit_Inherit: display->mScrollSnapCoordinate = parentDisplay->mScrollSnapCoordinate; conditions.SetUncacheable(); break; case eCSSUnit_List: { display->mScrollSnapCoordinate.Clear(); const nsCSSValueList* item = snapCoordinate.GetListValue(); do { NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null && item->mValue.GetUnit() != eCSSUnit_Inherit && item->mValue.GetUnit() != eCSSUnit_Initial && item->mValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); Position* pos = display->mScrollSnapCoordinate.AppendElement(); ComputePositionValue(aContext, item->mValue, *pos, conditions); item = item->mNext; } while(item); break; } default: NS_NOTREACHED("unexpected unit"); } // isolation: enum, inherit, initial SetValue(*aRuleData->ValueForIsolation(), display->mIsolation, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mIsolation, NS_STYLE_ISOLATION_AUTO); // -moz-top-layer: enum, inherit, initial SetValue(*aRuleData->ValueForTopLayer(), display->mTopLayer, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mTopLayer, NS_STYLE_TOP_LAYER_NONE); // Backup original display value for calculation of a hypothetical // box (CSS2 10.6.4/10.6.5), in addition to getting our style data right later. // See ReflowInput::CalculateHypotheticalBox display->mOriginalDisplay = display->mDisplay; // appearance: enum, inherit, initial SetValue(*aRuleData->ValueForAppearance(), display->mAppearance, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mAppearance, NS_THEME_NONE); // binding: url, none, inherit const nsCSSValue* bindingValue = aRuleData->ValueForBinding(); if (eCSSUnit_URL == bindingValue->GetUnit()) { mozilla::css::URLValue* url = bindingValue->GetURLStructValue(); NS_ASSERTION(url, "What's going on here?"); if (MOZ_LIKELY(url->GetURI())) { display->mBinding = url; } else { display->mBinding = nullptr; } } else if (eCSSUnit_None == bindingValue->GetUnit() || eCSSUnit_Initial == bindingValue->GetUnit() || eCSSUnit_Unset == bindingValue->GetUnit()) { display->mBinding = nullptr; } else if (eCSSUnit_Inherit == bindingValue->GetUnit()) { conditions.SetUncacheable(); display->mBinding = parentDisplay->mBinding; } // position: enum, inherit, initial SetValue(*aRuleData->ValueForPosition(), display->mPosition, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mPosition, NS_STYLE_POSITION_STATIC); // If an element is put in the top layer, while it is not absolutely // positioned, the position value should be computed to 'absolute' per // the Fullscreen API spec. if (display->mTopLayer != NS_STYLE_TOP_LAYER_NONE && !display->IsAbsolutelyPositionedStyle()) { display->mPosition = NS_STYLE_POSITION_ABSOLUTE; // We cannot cache this struct because otherwise it may be used as // an aStartStruct for some other elements. conditions.SetUncacheable(); } // clear: enum, inherit, initial SetValue(*aRuleData->ValueForClear(), display->mBreakType, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mBreakType, StyleClear::None); // temp fix for bug 24000 // Map 'auto' and 'avoid' to false, and 'always', 'left', and // 'right' to true. // "A conforming user agent may interpret the values 'left' and // 'right' as 'always'." - CSS2.1, section 13.3.1 const nsCSSValue* breakBeforeValue = aRuleData->ValueForPageBreakBefore(); if (eCSSUnit_Enumerated == breakBeforeValue->GetUnit()) { display->mBreakBefore = (NS_STYLE_PAGE_BREAK_AVOID != breakBeforeValue->GetIntValue() && NS_STYLE_PAGE_BREAK_AUTO != breakBeforeValue->GetIntValue()); } else if (eCSSUnit_Initial == breakBeforeValue->GetUnit() || eCSSUnit_Unset == breakBeforeValue->GetUnit()) { display->mBreakBefore = false; } else if (eCSSUnit_Inherit == breakBeforeValue->GetUnit()) { conditions.SetUncacheable(); display->mBreakBefore = parentDisplay->mBreakBefore; } const nsCSSValue* breakAfterValue = aRuleData->ValueForPageBreakAfter(); if (eCSSUnit_Enumerated == breakAfterValue->GetUnit()) { display->mBreakAfter = (NS_STYLE_PAGE_BREAK_AVOID != breakAfterValue->GetIntValue() && NS_STYLE_PAGE_BREAK_AUTO != breakAfterValue->GetIntValue()); } else if (eCSSUnit_Initial == breakAfterValue->GetUnit() || eCSSUnit_Unset == breakAfterValue->GetUnit()) { display->mBreakAfter = false; } else if (eCSSUnit_Inherit == breakAfterValue->GetUnit()) { conditions.SetUncacheable(); display->mBreakAfter = parentDisplay->mBreakAfter; } // end temp fix // page-break-inside: enum, inherit, initial SetValue(*aRuleData->ValueForPageBreakInside(), display->mBreakInside, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mBreakInside, NS_STYLE_PAGE_BREAK_AUTO); // touch-action: none, auto, enum, inherit, initial SetValue(*aRuleData->ValueForTouchAction(), display->mTouchAction, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mTouchAction, /* initial */ NS_STYLE_TOUCH_ACTION_AUTO, /* auto */ NS_STYLE_TOUCH_ACTION_AUTO, /* none */ NS_STYLE_TOUCH_ACTION_NONE, Unused, Unused); // float: enum, inherit, initial SetValue(*aRuleData->ValueForFloat(), display->mFloat, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mFloat, StyleFloat::None); // Save mFloat in mOriginalFloat in case we need it later display->mOriginalFloat = display->mFloat; // overflow-x: enum, inherit, initial SetValue(*aRuleData->ValueForOverflowX(), display->mOverflowX, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mOverflowX, NS_STYLE_OVERFLOW_VISIBLE); // overflow-y: enum, inherit, initial SetValue(*aRuleData->ValueForOverflowY(), display->mOverflowY, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mOverflowY, NS_STYLE_OVERFLOW_VISIBLE); // CSS3 overflow-x and overflow-y require some fixup as well in some // cases. NS_STYLE_OVERFLOW_VISIBLE and NS_STYLE_OVERFLOW_CLIP are // meaningful only when used in both dimensions. if (display->mOverflowX != display->mOverflowY && (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE || display->mOverflowX == NS_STYLE_OVERFLOW_CLIP || display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE || display->mOverflowY == NS_STYLE_OVERFLOW_CLIP)) { // We can't store in the rule tree since a more specific rule might // change these conditions. conditions.SetUncacheable(); // NS_STYLE_OVERFLOW_CLIP is a deprecated value, so if it's specified // in only one dimension, convert it to NS_STYLE_OVERFLOW_HIDDEN. if (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP) display->mOverflowX = NS_STYLE_OVERFLOW_HIDDEN; if (display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) display->mOverflowY = NS_STYLE_OVERFLOW_HIDDEN; // If 'visible' is specified but doesn't match the other dimension, it // turns into 'auto'. if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) display->mOverflowX = NS_STYLE_OVERFLOW_AUTO; if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) display->mOverflowY = NS_STYLE_OVERFLOW_AUTO; } // When 'contain: paint', update overflow from 'visible' to 'clip'. if (display->IsContainPaint()) { // XXX This actually sets overflow-[x|y] to -moz-hidden-unscrollable. if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) { // This uncacheability (and the one below) could be fixed by adding // mOriginalOverflowX and mOriginalOverflowY fields, if necessary. display->mOverflowX = NS_STYLE_OVERFLOW_CLIP; conditions.SetUncacheable(); } if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { display->mOverflowY = NS_STYLE_OVERFLOW_CLIP; conditions.SetUncacheable(); } } SetValue(*aRuleData->ValueForOverflowClipBox(), display->mOverflowClipBox, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mOverflowClipBox, NS_STYLE_OVERFLOW_CLIP_BOX_PADDING_BOX); SetValue(*aRuleData->ValueForResize(), display->mResize, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mResize, NS_STYLE_RESIZE_NONE); if (display->mDisplay != StyleDisplay::None) { // CSS2 9.7 specifies display type corrections dealing with 'float' // and 'position'. Since generated content can't be floated or // positioned, we can deal with it here. nsIAtom* pseudo = aContext->GetPseudo(); if (pseudo && display->mDisplay == StyleDisplay::Contents) { // We don't want to create frames for anonymous content using a parent // frame that is for content above the root of the anon tree. // (XXX what we really should check here is not GetPseudo() but if there's // a 'content' property value that implies anon content but we can't // check that here since that's a different struct(?)) // We might get display:contents to work for CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS // pseudos (:first-letter etc) in the future, but those have a lot of // special handling in frame construction so they are also unsupported // for now. display->mOriginalDisplay = display->mDisplay = StyleDisplay::Inline; conditions.SetUncacheable(); } // Inherit a <fieldset> grid/flex display type into its anon content frame. if (pseudo == nsCSSAnonBoxes::fieldsetContent) { MOZ_ASSERT(display->mDisplay == StyleDisplay::Block, "forms.css should have set 'display:block'"); switch (parentDisplay->mDisplay) { case StyleDisplay::Grid: case StyleDisplay::InlineGrid: display->mDisplay = StyleDisplay::Grid; conditions.SetUncacheable(); break; case StyleDisplay::Flex: case StyleDisplay::InlineFlex: display->mDisplay = StyleDisplay::Flex; conditions.SetUncacheable(); break; default: break; // Do nothing } } if (nsCSSPseudoElements::firstLetter == pseudo) { // a non-floating first-letter must be inline // XXX this fix can go away once bug 103189 is fixed correctly // Note that we reset mOriginalDisplay to enforce the invariant that it equals mDisplay if we're not positioned or floating. display->mOriginalDisplay = display->mDisplay = StyleDisplay::Inline; // We can't cache the data in the rule tree since if a more specific // rule has 'float: left' we'll end up with the wrong 'display' // property. conditions.SetUncacheable(); } if (display->IsAbsolutelyPositionedStyle()) { // 1) if position is 'absolute' or 'fixed' then display must be // block-level and float must be 'none' EnsureBlockDisplay(display->mDisplay); display->mFloat = StyleFloat::None; // Note that it's OK to cache this struct in the ruletree // because it's fine as-is for any style context that points to // it directly, and any use of it as aStartStruct (e.g. if a // more specific rule sets "position: static") will use // mOriginalDisplay and mOriginalFloat, which we have carefully // not changed. } else if (display->mFloat != StyleFloat::None) { // 2) if float is not none, and display is not none, then we must // set a block-level 'display' type per CSS2.1 section 9.7. EnsureBlockDisplay(display->mDisplay); // Note that it's OK to cache this struct in the ruletree // because it's fine as-is for any style context that points to // it directly, and any use of it as aStartStruct (e.g. if a // more specific rule sets "float: none") will use // mOriginalDisplay, which we have carefully not changed. } if (display->IsContainPaint()) { // An element with contain:paint or contain:layout needs to "be a // formatting context". For the purposes of the "display" property, that // just means we need to promote "display:inline" to "inline-block". // XXX We may also need to promote ruby display vals; see bug 1179349. // It's okay to cache this change in the rule tree for the same // reasons as floats in the previous condition. if (display->mDisplay == StyleDisplay::Inline) { display->mDisplay = StyleDisplay::InlineBlock; } } } /* Convert the nsCSSValueList into an nsTArray<nsTransformFunction *>. */ const nsCSSValue* transformValue = aRuleData->ValueForTransform(); switch (transformValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: display->mSpecifiedTransform = nullptr; break; case eCSSUnit_Inherit: display->mSpecifiedTransform = parentDisplay->mSpecifiedTransform; conditions.SetUncacheable(); break; case eCSSUnit_SharedList: { nsCSSValueSharedList* list = transformValue->GetSharedListValue(); nsCSSValueList* head = list->mHead; MOZ_ASSERT(head, "transform list must have at least one item"); // can get a _None in here from transform animation if (head->mValue.GetUnit() == eCSSUnit_None) { MOZ_ASSERT(head->mNext == nullptr, "none must be alone"); display->mSpecifiedTransform = nullptr; } else { display->mSpecifiedTransform = list; } break; } default: MOZ_ASSERT(false, "unrecognized transform unit"); } /* Convert the nsCSSValueList into a will-change bitfield for fast lookup */ const nsCSSValue* willChangeValue = aRuleData->ValueForWillChange(); switch (willChangeValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_List: case eCSSUnit_ListDep: { display->mWillChange.Clear(); display->mWillChangeBitField = 0; for (const nsCSSValueList* item = willChangeValue->GetListValue(); item; item = item->mNext) { if (item->mValue.UnitHasStringValue()) { nsAutoString buffer; item->mValue.GetStringValue(buffer); display->mWillChange.AppendElement(buffer); if (buffer.EqualsLiteral("transform")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM; } if (buffer.EqualsLiteral("opacity")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY; } if (buffer.EqualsLiteral("scroll-position")) { display->mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL; } nsCSSPropertyID prop = nsCSSProps::LookupProperty(buffer, CSSEnabledState::eForAllContent); if (prop != eCSSProperty_UNKNOWN && prop != eCSSPropertyExtra_variable) { // If the property given is a shorthand, it indicates the expectation // for all the longhands the shorthand expands to. if (nsCSSProps::IsShorthand(prop)) { for (const nsCSSPropertyID* shorthands = nsCSSProps::SubpropertyEntryFor(prop); *shorthands != eCSSProperty_UNKNOWN; ++shorthands) { display->mWillChangeBitField |= GetWillChangeBitFieldFromPropFlags(*shorthands); } } else { display->mWillChangeBitField |= GetWillChangeBitFieldFromPropFlags(prop); } } } } break; } case eCSSUnit_Inherit: display->mWillChange = parentDisplay->mWillChange; display->mWillChangeBitField = parentDisplay->mWillChangeBitField; conditions.SetUncacheable(); break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_Auto: display->mWillChange.Clear(); display->mWillChangeBitField = 0; break; default: MOZ_ASSERT(false, "unrecognized will-change unit"); } // vertical-align: enum, length, percent, calc, inherit const nsCSSValue* verticalAlignValue = aRuleData->ValueForVerticalAlign(); if (!SetCoord(*verticalAlignValue, display->mVerticalAlign, parentDisplay->mVerticalAlign, SETCOORD_LPH | SETCOORD_ENUMERATED | SETCOORD_STORE_CALC, aContext, mPresContext, conditions)) { if (eCSSUnit_Initial == verticalAlignValue->GetUnit() || eCSSUnit_Unset == verticalAlignValue->GetUnit()) { display->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE, eStyleUnit_Enumerated); } } /* Convert -moz-transform-origin. */ const nsCSSValue* transformOriginValue = aRuleData->ValueForTransformOrigin(); if (transformOriginValue->GetUnit() != eCSSUnit_Null) { const nsCSSValue& valX = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mXValue : *transformOriginValue; const nsCSSValue& valY = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mYValue : *transformOriginValue; const nsCSSValue& valZ = transformOriginValue->GetUnit() == eCSSUnit_Triplet ? transformOriginValue->GetTripletValue().mZValue : *transformOriginValue; mozilla::DebugOnly<bool> cX = SetCoord(valX, display->mTransformOrigin[0], parentDisplay->mTransformOrigin[0], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); mozilla::DebugOnly<bool> cY = SetCoord(valY, display->mTransformOrigin[1], parentDisplay->mTransformOrigin[1], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); if (valZ.GetUnit() == eCSSUnit_Null) { // Null for the z component means a 0 translation, not // unspecified, as we have already checked the triplet // value for Null. display->mTransformOrigin[2].SetCoordValue(0); } else { mozilla::DebugOnly<bool> cZ = SetCoord(valZ, display->mTransformOrigin[2], parentDisplay->mTransformOrigin[2], SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); MOZ_ASSERT(cY == cZ, "changed one but not the other"); } MOZ_ASSERT(cX == cY, "changed one but not the other"); NS_ASSERTION(cX, "Malformed -moz-transform-origin parse!"); } const nsCSSValue* perspectiveOriginValue = aRuleData->ValueForPerspectiveOrigin(); if (perspectiveOriginValue->GetUnit() != eCSSUnit_Null) { mozilla::DebugOnly<bool> result = SetPairCoords(*perspectiveOriginValue, display->mPerspectiveOrigin[0], display->mPerspectiveOrigin[1], parentDisplay->mPerspectiveOrigin[0], parentDisplay->mPerspectiveOrigin[1], SETCOORD_LPH | SETCOORD_INITIAL_HALF | SETCOORD_BOX_POSITION | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); NS_ASSERTION(result, "Malformed -moz-perspective-origin parse!"); } SetCoord(*aRuleData->ValueForPerspective(), display->mChildPerspective, parentDisplay->mChildPerspective, SETCOORD_LAH | SETCOORD_INITIAL_NONE | SETCOORD_NONE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); SetValue(*aRuleData->ValueForBackfaceVisibility(), display->mBackfaceVisibility, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mBackfaceVisibility, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE); // transform-style: enum, inherit, initial SetValue(*aRuleData->ValueForTransformStyle(), display->mTransformStyle, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mTransformStyle, NS_STYLE_TRANSFORM_STYLE_FLAT); // transform-box: enum, inherit, initial SetValue(*aRuleData->ValueForTransformBox(), display->mTransformBox, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mTransformBox, NS_STYLE_TRANSFORM_BOX_BORDER_BOX); // orient: enum, inherit, initial SetValue(*aRuleData->ValueForOrient(), display->mOrient, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentDisplay->mOrient, StyleOrient::Inline); // shape-outside: none | [ <basic-shape> || <shape-box> ] | <image> const nsCSSValue* shapeOutsideValue = aRuleData->ValueForShapeOutside(); switch (shapeOutsideValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: display->mShapeOutside = StyleShapeOutside(); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); display->mShapeOutside = parentDisplay->mShapeOutside; break; case eCSSUnit_URL: { display->mShapeOutside = StyleShapeOutside(); display->mShapeOutside.SetURL(shapeOutsideValue->GetURLStructValue()); break; } case eCSSUnit_Array: { display->mShapeOutside = StyleShapeOutside(); SetStyleShapeSourceToCSSValue(&display->mShapeOutside, shapeOutsideValue, aContext, mPresContext, conditions); break; } default: MOZ_ASSERT_UNREACHABLE("Unrecognized shape-outside unit!"); } COMPUTE_END_RESET(Display, display) } const void* nsRuleNode::ComputeVisibilityData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(Visibility, visibility, parentVisibility) // IMPORTANT: No properties in this struct have lengths in them. We // depend on this since CalcLengthWith can call StyleVisibility() // to get the language for resolving fonts! // direction: enum, inherit, initial SetValue(*aRuleData->ValueForDirection(), visibility->mDirection, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mDirection, (GET_BIDI_OPTION_DIRECTION(mPresContext->GetBidi()) == IBMBIDI_TEXTDIRECTION_RTL) ? NS_STYLE_DIRECTION_RTL : NS_STYLE_DIRECTION_LTR); // visibility: enum, inherit, initial SetValue(*aRuleData->ValueForVisibility(), visibility->mVisible, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mVisible, NS_STYLE_VISIBILITY_VISIBLE); // image-rendering: enum, inherit SetValue(*aRuleData->ValueForImageRendering(), visibility->mImageRendering, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mImageRendering, NS_STYLE_IMAGE_RENDERING_AUTO); // writing-mode: enum, inherit, initial SetValue(*aRuleData->ValueForWritingMode(), visibility->mWritingMode, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mWritingMode, NS_STYLE_WRITING_MODE_HORIZONTAL_TB); // text-orientation: enum, inherit, initial SetValue(*aRuleData->ValueForTextOrientation(), visibility->mTextOrientation, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mTextOrientation, NS_STYLE_TEXT_ORIENTATION_MIXED); // image-orientation: enum, inherit, initial const nsCSSValue* orientation = aRuleData->ValueForImageOrientation(); if (orientation->GetUnit() == eCSSUnit_Inherit || orientation->GetUnit() == eCSSUnit_Unset) { conditions.SetUncacheable(); visibility->mImageOrientation = parentVisibility->mImageOrientation; } else if (orientation->GetUnit() == eCSSUnit_Initial) { visibility->mImageOrientation = nsStyleImageOrientation(); } else if (orientation->IsAngularUnit()) { double angle = orientation->GetAngleValueInRadians(); visibility->mImageOrientation = nsStyleImageOrientation::CreateAsAngleAndFlip(angle, false); } else if (orientation->GetUnit() == eCSSUnit_Array) { const nsCSSValue::Array* array = orientation->GetArrayValue(); MOZ_ASSERT(array->Item(0).IsAngularUnit(), "First image-orientation value is not an angle"); MOZ_ASSERT(array->Item(1).GetUnit() == eCSSUnit_Enumerated && array->Item(1).GetIntValue() == NS_STYLE_IMAGE_ORIENTATION_FLIP, "Second image-orientation value is not 'flip'"); double angle = array->Item(0).GetAngleValueInRadians(); visibility->mImageOrientation = nsStyleImageOrientation::CreateAsAngleAndFlip(angle, true); } else if (orientation->GetUnit() == eCSSUnit_Enumerated) { switch (orientation->GetIntValue()) { case NS_STYLE_IMAGE_ORIENTATION_FLIP: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFlip(); break; case NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE: visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFromImage(); break; default: NS_NOTREACHED("Invalid image-orientation enumerated value"); } } else { MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); } SetValue(*aRuleData->ValueForColorAdjust(), visibility->mColorAdjust, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentVisibility->mColorAdjust, NS_STYLE_COLOR_ADJUST_ECONOMY); COMPUTE_END_INHERITED(Visibility, visibility) } const void* nsRuleNode::ComputeColorData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(Color, color, parentColor) // color: color, string, inherit // Special case for currentColor. According to CSS3, setting color to 'currentColor' // should behave as if it is inherited const nsCSSValue* colorValue = aRuleData->ValueForColor(); if ((colorValue->GetUnit() == eCSSUnit_EnumColor && colorValue->GetIntValue() == NS_COLOR_CURRENTCOLOR) || colorValue->GetUnit() == eCSSUnit_Unset) { color->mColor = parentColor->mColor; conditions.SetUncacheable(); } else if (colorValue->GetUnit() == eCSSUnit_Initial) { color->mColor = mPresContext->DefaultColor(); } else { SetColor(*colorValue, parentColor->mColor, mPresContext, aContext, color->mColor, conditions); } COMPUTE_END_INHERITED(Color, color) } // information about how to compute values for background-* properties template <class SpecifiedValueItem, class ComputedValueItem> struct BackgroundItemComputer { }; template <> struct BackgroundItemComputer<nsCSSValueList, uint8_t> { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, uint8_t& aComputedValue, RuleNodeCacheConditions& aConditions) { SetValue(aSpecifiedValue->mValue, aComputedValue, aConditions, SETVAL_ENUMERATED, uint8_t(0), 0); } }; template <> struct BackgroundItemComputer<nsCSSValuePairList, nsStyleImageLayers::Repeat> { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValuePairList* aSpecifiedValue, nsStyleImageLayers::Repeat& aComputedValue, RuleNodeCacheConditions& aConditions) { NS_ASSERTION(aSpecifiedValue->mXValue.GetUnit() == eCSSUnit_Enumerated && (aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Enumerated || aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null), "Invalid unit"); bool hasContraction = true; uint8_t value = aSpecifiedValue->mXValue.GetIntValue(); switch (value) { case NS_STYLE_IMAGELAYER_REPEAT_REPEAT_X: aComputedValue.mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; aComputedValue.mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT; break; case NS_STYLE_IMAGELAYER_REPEAT_REPEAT_Y: aComputedValue.mXRepeat = NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT; aComputedValue.mYRepeat = NS_STYLE_IMAGELAYER_REPEAT_REPEAT; break; default: NS_ASSERTION(value == NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT || value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT || value == NS_STYLE_IMAGELAYER_REPEAT_SPACE || value == NS_STYLE_IMAGELAYER_REPEAT_ROUND, "Unexpected value"); aComputedValue.mXRepeat = value; hasContraction = false; break; } if (hasContraction) { NS_ASSERTION(aSpecifiedValue->mYValue.GetUnit() == eCSSUnit_Null, "Invalid unit."); return; } switch (aSpecifiedValue->mYValue.GetUnit()) { case eCSSUnit_Null: aComputedValue.mYRepeat = aComputedValue.mXRepeat; break; case eCSSUnit_Enumerated: value = aSpecifiedValue->mYValue.GetIntValue(); NS_ASSERTION(value == NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT || value == NS_STYLE_IMAGELAYER_REPEAT_REPEAT || value == NS_STYLE_IMAGELAYER_REPEAT_SPACE || value == NS_STYLE_IMAGELAYER_REPEAT_ROUND, "Unexpected value"); aComputedValue.mYRepeat = value; break; default: NS_NOTREACHED("Unexpected CSS value"); break; } } }; template <> struct BackgroundItemComputer<nsCSSValueList, nsStyleImage> { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, nsStyleImage& aComputedValue, RuleNodeCacheConditions& aConditions) { SetStyleImage(aStyleContext, aSpecifiedValue->mValue, aComputedValue, aConditions); } }; template <> struct BackgroundItemComputer<nsCSSValueList, RefPtr<css::URLValueData>> { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, RefPtr<css::URLValueData>& aComputedValue, RuleNodeCacheConditions& aConditions) { switch (aSpecifiedValue->mValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_URL: aComputedValue = aSpecifiedValue->mValue.GetURLStructValue(); break; case eCSSUnit_Image: aComputedValue = aSpecifiedValue->mValue.GetImageStructValue(); break; default: aComputedValue = nullptr; break; } } }; template <typename T> struct BackgroundItemComputer<nsCSSValueList, T> { typedef typename EnableIf<IsEnum<T>::value, T>::Type ComputedType; static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValueList* aSpecifiedValue, ComputedType& aComputedValue, RuleNodeCacheConditions& aConditions) { aComputedValue = static_cast<T>(aSpecifiedValue->mValue.GetIntValue()); } }; /* Helper function for ComputePositionValue. * This function computes a single PositionCoord from two nsCSSValue objects, * which represent an edge and an offset from that edge. */ static void ComputePositionCoord(nsStyleContext* aStyleContext, const nsCSSValue& aEdge, const nsCSSValue& aOffset, Position::Coord* aResult, RuleNodeCacheConditions& aConditions) { if (eCSSUnit_Percent == aOffset.GetUnit()) { aResult->mLength = 0; aResult->mPercent = aOffset.GetPercentValue(); aResult->mHasPercent = true; } else if (aOffset.IsLengthUnit()) { aResult->mLength = CalcLength(aOffset, aStyleContext, aStyleContext->PresContext(), aConditions); aResult->mPercent = 0.0f; aResult->mHasPercent = false; } else if (aOffset.IsCalcUnit()) { LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aConditions); nsRuleNode::ComputedCalc vals = ComputeCalc(aOffset, ops); aResult->mLength = vals.mLength; aResult->mPercent = vals.mPercent; aResult->mHasPercent = ops.mHasPercent; } else { aResult->mLength = 0; aResult->mPercent = 0.0f; aResult->mHasPercent = false; NS_ASSERTION(aOffset.GetUnit() == eCSSUnit_Null, "unexpected unit"); } if (eCSSUnit_Enumerated == aEdge.GetUnit()) { int sign; if (aEdge.GetIntValue() & (NS_STYLE_IMAGELAYER_POSITION_BOTTOM | NS_STYLE_IMAGELAYER_POSITION_RIGHT)) { sign = -1; } else { sign = 1; } aResult->mPercent = GetFloatFromBoxPosition(aEdge.GetIntValue()) + sign * aResult->mPercent; aResult->mLength = sign * aResult->mLength; aResult->mHasPercent = true; } else { NS_ASSERTION(eCSSUnit_Null == aEdge.GetUnit(), "unexpected unit"); } } /* Helper function to convert a CSS <position> specified value into its * computed-style form. */ static void ComputePositionValue(nsStyleContext* aStyleContext, const nsCSSValue& aValue, Position& aComputedValue, RuleNodeCacheConditions& aConditions) { NS_ASSERTION(aValue.GetUnit() == eCSSUnit_Array, "unexpected unit for CSS <position> value"); RefPtr<nsCSSValue::Array> positionArray = aValue.GetArrayValue(); NS_ASSERTION(positionArray->Count() == 4, "unexpected number of values in CSS <position> value"); const nsCSSValue &xEdge = positionArray->Item(0); const nsCSSValue &xOffset = positionArray->Item(1); const nsCSSValue &yEdge = positionArray->Item(2); const nsCSSValue &yOffset = positionArray->Item(3); NS_ASSERTION((eCSSUnit_Enumerated == xEdge.GetUnit() || eCSSUnit_Null == xEdge.GetUnit()) && (eCSSUnit_Enumerated == yEdge.GetUnit() || eCSSUnit_Null == yEdge.GetUnit()) && eCSSUnit_Enumerated != xOffset.GetUnit() && eCSSUnit_Enumerated != yOffset.GetUnit(), "Invalid background position"); ComputePositionCoord(aStyleContext, xEdge, xOffset, &aComputedValue.mXPosition, aConditions); ComputePositionCoord(aStyleContext, yEdge, yOffset, &aComputedValue.mYPosition, aConditions); } /* Helper function to convert the -x or -y part of a CSS <position> specified * value into its computed-style form. */ static void ComputePositionCoordValue(nsStyleContext* aStyleContext, const nsCSSValue& aValue, Position::Coord& aComputedValue, RuleNodeCacheConditions& aConditions) { NS_ASSERTION(aValue.GetUnit() == eCSSUnit_Array, "unexpected unit for position coord value"); RefPtr<nsCSSValue::Array> positionArray = aValue.GetArrayValue(); NS_ASSERTION(positionArray->Count() == 2, "unexpected number of values, expecting one edge and one offset"); const nsCSSValue &edge = positionArray->Item(0); const nsCSSValue &offset = positionArray->Item(1); NS_ASSERTION((eCSSUnit_Enumerated == edge.GetUnit() || eCSSUnit_Null == edge.GetUnit()) && eCSSUnit_Enumerated != offset.GetUnit(), "Invalid background position"); ComputePositionCoord(aStyleContext, edge, offset, &aComputedValue, aConditions); } struct BackgroundSizeAxis { nsCSSValue nsCSSValuePairList::* specified; nsStyleImageLayers::Size::Dimension nsStyleImageLayers::Size::* result; uint8_t nsStyleImageLayers::Size::* type; }; static const BackgroundSizeAxis gBGSizeAxes[] = { { &nsCSSValuePairList::mXValue, &nsStyleImageLayers::Size::mWidth, &nsStyleImageLayers::Size::mWidthType }, { &nsCSSValuePairList::mYValue, &nsStyleImageLayers::Size::mHeight, &nsStyleImageLayers::Size::mHeightType } }; template <> struct BackgroundItemComputer<nsCSSValuePairList, nsStyleImageLayers::Size> { static void ComputeValue(nsStyleContext* aStyleContext, const nsCSSValuePairList* aSpecifiedValue, nsStyleImageLayers::Size& aComputedValue, RuleNodeCacheConditions& aConditions) { nsStyleImageLayers::Size &size = aComputedValue; for (const BackgroundSizeAxis *axis = gBGSizeAxes, *axis_end = ArrayEnd(gBGSizeAxes); axis < axis_end; ++axis) { const nsCSSValue &specified = aSpecifiedValue->*(axis->specified); if (eCSSUnit_Auto == specified.GetUnit()) { size.*(axis->type) = nsStyleImageLayers::Size::eAuto; } else if (eCSSUnit_Enumerated == specified.GetUnit()) { static_assert(nsStyleImageLayers::Size::eContain == NS_STYLE_IMAGELAYER_SIZE_CONTAIN && nsStyleImageLayers::Size::eCover == NS_STYLE_IMAGELAYER_SIZE_COVER, "background size constants out of sync"); MOZ_ASSERT(specified.GetIntValue() == NS_STYLE_IMAGELAYER_SIZE_CONTAIN || specified.GetIntValue() == NS_STYLE_IMAGELAYER_SIZE_COVER, "invalid enumerated value for size coordinate"); size.*(axis->type) = specified.GetIntValue(); } else if (eCSSUnit_Null == specified.GetUnit()) { MOZ_ASSERT(axis == gBGSizeAxes + 1, "null allowed only as height value, and only " "for contain/cover/initial/inherit"); #ifdef DEBUG { const nsCSSValue &widthValue = aSpecifiedValue->mXValue; MOZ_ASSERT(widthValue.GetUnit() != eCSSUnit_Inherit && widthValue.GetUnit() != eCSSUnit_Initial && widthValue.GetUnit() != eCSSUnit_Unset, "initial/inherit/unset should already have been handled"); MOZ_ASSERT(widthValue.GetUnit() == eCSSUnit_Enumerated && (widthValue.GetIntValue() == NS_STYLE_IMAGELAYER_SIZE_CONTAIN || widthValue.GetIntValue() == NS_STYLE_IMAGELAYER_SIZE_COVER), "null height value not corresponding to allowable " "non-null width value"); } #endif size.*(axis->type) = size.mWidthType; } else if (eCSSUnit_Percent == specified.GetUnit()) { (size.*(axis->result)).mLength = 0; (size.*(axis->result)).mPercent = specified.GetPercentValue(); (size.*(axis->result)).mHasPercent = true; size.*(axis->type) = nsStyleImageLayers::Size::eLengthPercentage; } else if (specified.IsLengthUnit()) { (size.*(axis->result)).mLength = CalcLength(specified, aStyleContext, aStyleContext->PresContext(), aConditions); (size.*(axis->result)).mPercent = 0.0f; (size.*(axis->result)).mHasPercent = false; size.*(axis->type) = nsStyleImageLayers::Size::eLengthPercentage; } else { MOZ_ASSERT(specified.IsCalcUnit(), "unexpected unit"); LengthPercentPairCalcOps ops(aStyleContext, aStyleContext->PresContext(), aConditions); nsRuleNode::ComputedCalc vals = ComputeCalc(specified, ops); (size.*(axis->result)).mLength = vals.mLength; (size.*(axis->result)).mPercent = vals.mPercent; (size.*(axis->result)).mHasPercent = ops.mHasPercent; size.*(axis->type) = nsStyleImageLayers::Size::eLengthPercentage; } } MOZ_ASSERT(size.mWidthType < nsStyleImageLayers::Size::eDimensionType_COUNT, "bad width type"); MOZ_ASSERT(size.mHeightType < nsStyleImageLayers::Size::eDimensionType_COUNT, "bad height type"); MOZ_ASSERT((size.mWidthType != nsStyleImageLayers::Size::eContain && size.mWidthType != nsStyleImageLayers::Size::eCover) || size.mWidthType == size.mHeightType, "contain/cover apply to both dimensions or to neither"); } }; template <class ComputedValueItem> static void SetImageLayerList(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, const nsStyleAutoArray<nsStyleImageLayers::Layer>& aParentLayers, ComputedValueItem nsStyleImageLayers::Layer::* aResultLocation, ComputedValueItem aInitialValue, uint32_t aParentItemCount, uint32_t& aItemCount, uint32_t& aMaxItemCount, bool& aRebuild, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aRebuild = true; aConditions.SetUncacheable(); aLayers.EnsureLengthAtLeast(aParentItemCount); aItemCount = aParentItemCount; for (uint32_t i = 0; i < aParentItemCount; ++i) { aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; } break; case eCSSUnit_Initial: case eCSSUnit_Unset: aRebuild = true; aItemCount = 1; aLayers[0].*aResultLocation = aInitialValue; break; case eCSSUnit_List: case eCSSUnit_ListDep: { aRebuild = true; aItemCount = 0; const nsCSSValueList* item = aValue.GetListValue(); do { NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null && item->mValue.GetUnit() != eCSSUnit_Inherit && item->mValue.GetUnit() != eCSSUnit_Initial && item->mValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); ++aItemCount; aLayers.EnsureLengthAtLeast(aItemCount); BackgroundItemComputer<nsCSSValueList, ComputedValueItem> ::ComputeValue(aStyleContext, item, aLayers[aItemCount-1].*aResultLocation, aConditions); item = item->mNext; } while (item); break; } default: MOZ_ASSERT(false, "unexpected unit"); } if (aItemCount > aMaxItemCount) aMaxItemCount = aItemCount; } // The same as SetImageLayerList, but for values stored in // layer.mPosition.*aResultLocation instead of layer.*aResultLocation. // This code is duplicated because it would be annoying to make // SetImageLayerList generic enough to handle both cases. static void SetImageLayerPositionCoordList( nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, const nsStyleAutoArray<nsStyleImageLayers::Layer>& aParentLayers, Position::Coord Position::* aResultLocation, Position::Coord aInitialValue, uint32_t aParentItemCount, uint32_t& aItemCount, uint32_t& aMaxItemCount, bool& aRebuild, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aRebuild = true; aConditions.SetUncacheable(); aLayers.EnsureLengthAtLeast(aParentItemCount); aItemCount = aParentItemCount; for (uint32_t i = 0; i < aParentItemCount; ++i) { aLayers[i].mPosition.*aResultLocation = aParentLayers[i].mPosition.*aResultLocation; } break; case eCSSUnit_Initial: case eCSSUnit_Unset: aRebuild = true; aItemCount = 1; aLayers[0].mPosition.*aResultLocation = aInitialValue; break; case eCSSUnit_List: case eCSSUnit_ListDep: { aRebuild = true; aItemCount = 0; const nsCSSValueList* item = aValue.GetListValue(); do { NS_ASSERTION(item->mValue.GetUnit() != eCSSUnit_Null && item->mValue.GetUnit() != eCSSUnit_Inherit && item->mValue.GetUnit() != eCSSUnit_Initial && item->mValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); ++aItemCount; aLayers.EnsureLengthAtLeast(aItemCount); ComputePositionCoordValue(aStyleContext, item->mValue, aLayers[aItemCount-1].mPosition.*aResultLocation, aConditions); item = item->mNext; } while (item); break; } default: MOZ_ASSERT(false, "unexpected unit"); } if (aItemCount > aMaxItemCount) aMaxItemCount = aItemCount; } template <class ComputedValueItem> static void SetImageLayerPairList(nsStyleContext* aStyleContext, const nsCSSValue& aValue, nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, const nsStyleAutoArray<nsStyleImageLayers::Layer>& aParentLayers, ComputedValueItem nsStyleImageLayers::Layer::* aResultLocation, ComputedValueItem aInitialValue, uint32_t aParentItemCount, uint32_t& aItemCount, uint32_t& aMaxItemCount, bool& aRebuild, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aRebuild = true; aConditions.SetUncacheable(); aLayers.EnsureLengthAtLeast(aParentItemCount); aItemCount = aParentItemCount; for (uint32_t i = 0; i < aParentItemCount; ++i) { aLayers[i].*aResultLocation = aParentLayers[i].*aResultLocation; } break; case eCSSUnit_Initial: case eCSSUnit_Unset: aRebuild = true; aItemCount = 1; aLayers[0].*aResultLocation = aInitialValue; break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { aRebuild = true; aItemCount = 0; const nsCSSValuePairList* item = aValue.GetPairListValue(); do { NS_ASSERTION(item->mXValue.GetUnit() != eCSSUnit_Inherit && item->mXValue.GetUnit() != eCSSUnit_Initial && item->mXValue.GetUnit() != eCSSUnit_Unset && item->mYValue.GetUnit() != eCSSUnit_Inherit && item->mYValue.GetUnit() != eCSSUnit_Initial && item->mYValue.GetUnit() != eCSSUnit_Unset, "unexpected unit"); ++aItemCount; aLayers.EnsureLengthAtLeast(aItemCount); BackgroundItemComputer<nsCSSValuePairList, ComputedValueItem> ::ComputeValue(aStyleContext, item, aLayers[aItemCount-1].*aResultLocation, aConditions); item = item->mNext; } while (item); break; } default: MOZ_ASSERT(false, "unexpected unit"); } if (aItemCount > aMaxItemCount) aMaxItemCount = aItemCount; } template <class ComputedValueItem> static void FillImageLayerList( nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, ComputedValueItem nsStyleImageLayers::Layer::* aResultLocation, uint32_t aItemCount, uint32_t aFillCount) { NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length"); for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount; ++sourceLayer, ++destLayer) { aLayers[destLayer].*aResultLocation = aLayers[sourceLayer].*aResultLocation; } } // The same as FillImageLayerList, but for values stored in // layer.mPosition.*aResultLocation instead of layer.*aResultLocation. static void FillImageLayerPositionCoordList( nsStyleAutoArray<nsStyleImageLayers::Layer>& aLayers, Position::Coord Position::* aResultLocation, uint32_t aItemCount, uint32_t aFillCount) { NS_PRECONDITION(aFillCount <= aLayers.Length(), "unexpected array length"); for (uint32_t sourceLayer = 0, destLayer = aItemCount; destLayer < aFillCount; ++sourceLayer, ++destLayer) { aLayers[destLayer].mPosition.*aResultLocation = aLayers[sourceLayer].mPosition.*aResultLocation; } } /* static */ void nsRuleNode::FillAllBackgroundLists(nsStyleImageLayers& aImage, uint32_t aMaxItemCount) { // Delete any extra items. We need to keep layers in which any // property was specified. aImage.mLayers.TruncateLengthNonZero(aMaxItemCount); uint32_t fillCount = aImage.mImageCount; FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mImage, aImage.mImageCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mRepeat, aImage.mRepeatCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mAttachment, aImage.mAttachmentCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mClip, aImage.mClipCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mBlendMode, aImage.mBlendModeCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mOrigin, aImage.mOriginCount, fillCount); FillImageLayerPositionCoordList(aImage.mLayers, &Position::mXPosition, aImage.mPositionXCount, fillCount); FillImageLayerPositionCoordList(aImage.mLayers, &Position::mYPosition, aImage.mPositionYCount, fillCount); FillImageLayerList(aImage.mLayers, &nsStyleImageLayers::Layer::mSize, aImage.mSizeCount, fillCount); } const void* nsRuleNode::ComputeBackgroundData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Background, bg, parentBG) // background-color: color, string, inherit const nsCSSValue* backColorValue = aRuleData->ValueForBackgroundColor(); if (eCSSUnit_Initial == backColorValue->GetUnit() || eCSSUnit_Unset == backColorValue->GetUnit()) { bg->mBackgroundColor = NS_RGBA(0, 0, 0, 0); } else if (!SetColor(*backColorValue, parentBG->mBackgroundColor, mPresContext, aContext, bg->mBackgroundColor, conditions)) { NS_ASSERTION(eCSSUnit_Null == backColorValue->GetUnit(), "unexpected color unit"); } uint32_t maxItemCount = 1; bool rebuild = false; // background-image: url (stored as image), none, inherit [list] nsStyleImage initialImage; SetImageLayerList(aContext, *aRuleData->ValueForBackgroundImage(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mImage, initialImage, parentBG->mImage.mImageCount, bg->mImage.mImageCount, maxItemCount, rebuild, conditions); // background-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; initialRepeat.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundRepeat(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mRepeat, initialRepeat, parentBG->mImage.mRepeatCount, bg->mImage.mRepeatCount, maxItemCount, rebuild, conditions); // background-attachment: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundAttachment(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mAttachment, uint8_t(NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL), parentBG->mImage.mAttachmentCount, bg->mImage.mAttachmentCount, maxItemCount, rebuild, conditions); // background-clip: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundClip(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mClip, StyleGeometryBox::Border, parentBG->mImage.mClipCount, bg->mImage.mClipCount, maxItemCount, rebuild, conditions); // background-blend-mode: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundBlendMode(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mBlendMode, uint8_t(NS_STYLE_BLEND_NORMAL), parentBG->mImage.mBlendModeCount, bg->mImage.mBlendModeCount, maxItemCount, rebuild, conditions); // background-origin: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForBackgroundOrigin(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mOrigin, StyleGeometryBox::Padding, parentBG->mImage.mOriginCount, bg->mImage.mOriginCount, maxItemCount, rebuild, conditions); // background-position-x/y: enum, length, percent (flags), inherit [list] Position::Coord initialPositionCoord; initialPositionCoord.mPercent = 0.0f; initialPositionCoord.mLength = 0; initialPositionCoord.mHasPercent = true; SetImageLayerPositionCoordList( aContext, *aRuleData->ValueForBackgroundPositionX(), bg->mImage.mLayers, parentBG->mImage.mLayers, &Position::mXPosition, initialPositionCoord, parentBG->mImage.mPositionXCount, bg->mImage.mPositionXCount, maxItemCount, rebuild, conditions); SetImageLayerPositionCoordList( aContext, *aRuleData->ValueForBackgroundPositionY(), bg->mImage.mLayers, parentBG->mImage.mLayers, &Position::mYPosition, initialPositionCoord, parentBG->mImage.mPositionYCount, bg->mImage.mPositionYCount, maxItemCount, rebuild, conditions); // background-size: enum, length, auto, inherit, initial [pair list] nsStyleImageLayers::Size initialSize; initialSize.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForBackgroundSize(), bg->mImage.mLayers, parentBG->mImage.mLayers, &nsStyleImageLayers::Layer::mSize, initialSize, parentBG->mImage.mSizeCount, bg->mImage.mSizeCount, maxItemCount, rebuild, conditions); if (rebuild) { FillAllBackgroundLists(bg->mImage, maxItemCount); } COMPUTE_END_RESET(Background, bg) } const void* nsRuleNode::ComputeMarginData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Margin, margin, parentMargin) // margin: length, percent, calc, inherit const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_margin); nsStyleCoord coord; NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentMargin->mMargin.Get(side); if (SetCoord(*aRuleData->ValueFor(subprops[side]), coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { margin->mMargin.Set(side, coord); } } COMPUTE_END_RESET(Margin, margin) } static void SetBorderImageRect(const nsCSSValue& aValue, /** outparam */ nsCSSRect& aRect) { switch (aValue.GetUnit()) { case eCSSUnit_Null: aRect.Reset(); break; case eCSSUnit_Rect: aRect = aValue.GetRectValue(); break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aRect.SetAllSidesTo(aValue); break; default: NS_ASSERTION(false, "Unexpected border image value for rect."); } } static void SetBorderImagePair(const nsCSSValue& aValue, /** outparam */ nsCSSValuePair& aPair) { switch (aValue.GetUnit()) { case eCSSUnit_Null: aPair.Reset(); break; case eCSSUnit_Pair: aPair = aValue.GetPairValue(); break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aPair.SetBothValuesTo(aValue); break; default: NS_ASSERTION(false, "Unexpected border image value for pair."); } } static void SetBorderImageSlice(const nsCSSValue& aValue, /** outparam */ nsCSSValue& aSlice, /** outparam */ nsCSSValue& aFill) { const nsCSSValueList* valueList; switch (aValue.GetUnit()) { case eCSSUnit_Null: aSlice.Reset(); aFill.Reset(); break; case eCSSUnit_List: // Get slice dimensions. valueList = aValue.GetListValue(); aSlice = valueList->mValue; // Get "fill" keyword. valueList = valueList->mNext; if (valueList) { aFill = valueList->mValue; } else { aFill.SetInitialValue(); } break; case eCSSUnit_Inherit: case eCSSUnit_Initial: case eCSSUnit_Unset: aSlice = aValue; aFill = aValue; break; default: NS_ASSERTION(false, "Unexpected border image value for pair."); } } const void* nsRuleNode::ComputeBorderData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Border, border, parentBorder) // box-decoration-break: enum, inherit, initial SetValue(*aRuleData->ValueForBoxDecorationBreak(), border->mBoxDecorationBreak, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentBorder->mBoxDecorationBreak, StyleBoxDecorationBreak::Slice); // border-width, border-*-width: length, enum, inherit nsStyleCoord coord; { const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_width); NS_FOR_CSS_SIDES(side) { const nsCSSValue& value = *aRuleData->ValueFor(subprops[side]); NS_ASSERTION(eCSSUnit_Percent != value.GetUnit(), "Percentage borders not implemented yet " "If implementing, make sure to fix all consumers of " "nsStyleBorder, the IsPercentageAwareChild method, " "the nsAbsoluteContainingBlock::FrameDependsOnContainer " "method, the " "nsLineLayout::IsPercentageAwareReplacedElement method " "and probably some other places"); if (eCSSUnit_Enumerated == value.GetUnit()) { NS_ASSERTION(value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || value.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, "Unexpected enum value"); border->SetBorderWidth(side, (mPresContext->GetBorderWidthTable())[value.GetIntValue()]); } // OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT else if (SetCoord(value, coord, nsStyleCoord(), SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, conditions)) { NS_ASSERTION(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit"); // clamp negative calc() to 0. border->SetBorderWidth(side, std::max(coord.GetCoordValue(), 0)); } else if (eCSSUnit_Inherit == value.GetUnit()) { conditions.SetUncacheable(); border->SetBorderWidth(side, parentBorder->GetComputedBorder().Side(side)); } else if (eCSSUnit_Initial == value.GetUnit() || eCSSUnit_Unset == value.GetUnit()) { border->SetBorderWidth(side, (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); } else { NS_ASSERTION(eCSSUnit_Null == value.GetUnit(), "missing case handling border width"); } } } // border-style, border-*-style: enum, inherit { const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_style); NS_FOR_CSS_SIDES(side) { const nsCSSValue& value = *aRuleData->ValueFor(subprops[side]); nsCSSUnit unit = value.GetUnit(); MOZ_ASSERT(eCSSUnit_None != unit, "'none' should be handled as enumerated value"); if (eCSSUnit_Enumerated == unit) { border->SetBorderStyle(side, value.GetIntValue()); } else if (eCSSUnit_Initial == unit || eCSSUnit_Unset == unit) { border->SetBorderStyle(side, NS_STYLE_BORDER_STYLE_NONE); } else if (eCSSUnit_Inherit == unit) { conditions.SetUncacheable(); border->SetBorderStyle(side, parentBorder->GetBorderStyle(side)); } } } // -moz-border-*-colors: color, string, enum, none, inherit/initial nscolor borderColor; nscolor unused = NS_RGB(0,0,0); static const nsCSSPropertyID borderColorsProps[] = { eCSSProperty_border_top_colors, eCSSProperty_border_right_colors, eCSSProperty_border_bottom_colors, eCSSProperty_border_left_colors }; NS_FOR_CSS_SIDES(side) { const nsCSSValue& value = *aRuleData->ValueFor(borderColorsProps[side]); switch (value.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: border->ClearBorderColors(side); break; case eCSSUnit_Inherit: { conditions.SetUncacheable(); border->ClearBorderColors(side); if (parentContext) { nsBorderColors *parentColors; parentBorder->GetCompositeColors(side, &parentColors); if (parentColors) { border->EnsureBorderColors(); border->mBorderColors[side] = parentColors->Clone(); } } break; } case eCSSUnit_List: case eCSSUnit_ListDep: { // Some composite border color information has been specified for this // border side. border->EnsureBorderColors(); border->ClearBorderColors(side); const nsCSSValueList* list = value.GetListValue(); while (list) { if (SetColor(list->mValue, unused, mPresContext, aContext, borderColor, conditions)) border->AppendBorderColor(side, borderColor); else { NS_NOTREACHED("unexpected item in -moz-border-*-colors list"); } list = list->mNext; } break; } default: MOZ_ASSERT(false, "unrecognized border color unit"); } } // border-color, border-*-color: color, string, enum, inherit { const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color); NS_FOR_CSS_SIDES(side) { SetComplexColor<eUnsetInitial>(*aRuleData->ValueFor(subprops[side]), parentBorder->mBorderColor[side], StyleComplexColor::CurrentColor(), mPresContext, border->mBorderColor[side], conditions); } } // border-radius: length, percent, inherit { const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_radius); NS_FOR_CSS_FULL_CORNERS(corner) { int cx = NS_FULL_TO_HALF_CORNER(corner, false); int cy = NS_FULL_TO_HALF_CORNER(corner, true); const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); nsStyleCoord parentX = parentBorder->mBorderRadius.Get(cx); nsStyleCoord parentY = parentBorder->mBorderRadius.Get(cy); nsStyleCoord coordX, coordY; if (SetPairCoords(radius, coordX, coordY, parentX, parentY, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { border->mBorderRadius.Set(cx, coordX); border->mBorderRadius.Set(cy, coordY); } } } // float-edge: enum, inherit, initial SetValue(*aRuleData->ValueForFloatEdge(), border->mFloatEdge, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentBorder->mFloatEdge, StyleFloatEdge::ContentBox); // border-image-source const nsCSSValue* borderImageSource = aRuleData->ValueForBorderImageSource(); if (borderImageSource->GetUnit() == eCSSUnit_Inherit) { conditions.SetUncacheable(); border->mBorderImageSource = parentBorder->mBorderImageSource; } else { SetStyleImage(aContext, *borderImageSource, border->mBorderImageSource, conditions); } nsCSSValue borderImageSliceValue; nsCSSValue borderImageSliceFill; SetBorderImageSlice(*aRuleData->ValueForBorderImageSlice(), borderImageSliceValue, borderImageSliceFill); // border-image-slice: fill SetValue(borderImageSliceFill, border->mBorderImageFill, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentBorder->mBorderImageFill, NS_STYLE_BORDER_IMAGE_SLICE_NOFILL); nsCSSRect borderImageSlice; SetBorderImageRect(borderImageSliceValue, borderImageSlice); nsCSSRect borderImageWidth; SetBorderImageRect(*aRuleData->ValueForBorderImageWidth(), borderImageWidth); nsCSSRect borderImageOutset; SetBorderImageRect(*aRuleData->ValueForBorderImageOutset(), borderImageOutset); NS_FOR_CSS_SIDES (side) { // border-image-slice if (SetCoord(borderImageSlice.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageSlice.Get(side), SETCOORD_FACTOR | SETCOORD_PERCENT | SETCOORD_INHERIT | SETCOORD_INITIAL_HUNDRED_PCT | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { border->mBorderImageSlice.Set(side, coord); } // border-image-width // 'auto' here means "same as slice" if (SetCoord(borderImageWidth.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageWidth.Get(side), SETCOORD_LPAH | SETCOORD_FACTOR | SETCOORD_INITIAL_FACTOR_ONE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { border->mBorderImageWidth.Set(side, coord); } // border-image-outset if (SetCoord(borderImageOutset.*(nsCSSRect::sides[side]), coord, parentBorder->mBorderImageOutset.Get(side), SETCOORD_LENGTH | SETCOORD_FACTOR | SETCOORD_INHERIT | SETCOORD_INITIAL_FACTOR_ZERO | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { border->mBorderImageOutset.Set(side, coord); } } // border-image-repeat nsCSSValuePair borderImageRepeat; SetBorderImagePair(*aRuleData->ValueForBorderImageRepeat(), borderImageRepeat); SetValue(borderImageRepeat.mXValue, border->mBorderImageRepeatH, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentBorder->mBorderImageRepeatH, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH); SetValue(borderImageRepeat.mYValue, border->mBorderImageRepeatV, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentBorder->mBorderImageRepeatV, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH); COMPUTE_END_RESET(Border, border) } const void* nsRuleNode::ComputePaddingData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Padding, padding, parentPadding) // padding: length, percent, calc, inherit const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_padding); nsStyleCoord coord; NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentPadding->mPadding.Get(side); if (SetCoord(*aRuleData->ValueFor(subprops[side]), coord, parentCoord, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { padding->mPadding.Set(side, coord); } } COMPUTE_END_RESET(Padding, padding) } const void* nsRuleNode::ComputeOutlineData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Outline, outline, parentOutline) // outline-width: length, enum, inherit const nsCSSValue* outlineWidthValue = aRuleData->ValueForOutlineWidth(); if (eCSSUnit_Initial == outlineWidthValue->GetUnit() || eCSSUnit_Unset == outlineWidthValue->GetUnit()) { outline->mOutlineWidth = nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated); } else { SetCoord(*outlineWidthValue, outline->mOutlineWidth, parentOutline->mOutlineWidth, SETCOORD_LEH | SETCOORD_CALC_LENGTH_ONLY, aContext, mPresContext, conditions); } // outline-offset: length, inherit nsStyleCoord tempCoord; const nsCSSValue* outlineOffsetValue = aRuleData->ValueForOutlineOffset(); if (SetCoord(*outlineOffsetValue, tempCoord, nsStyleCoord(parentOutline->mOutlineOffset, nsStyleCoord::CoordConstructor), SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { outline->mOutlineOffset = tempCoord.GetCoordValue(); } else { NS_ASSERTION(outlineOffsetValue->GetUnit() == eCSSUnit_Null, "unexpected unit"); } // outline-color: color, string, enum, inherit SetComplexColor<eUnsetInitial>(*aRuleData->ValueForOutlineColor(), parentOutline->mOutlineColor, StyleComplexColor::CurrentColor(), mPresContext, outline->mOutlineColor, conditions); // -moz-outline-radius: length, percent, inherit { const nsCSSPropertyID* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty__moz_outline_radius); NS_FOR_CSS_FULL_CORNERS(corner) { int cx = NS_FULL_TO_HALF_CORNER(corner, false); int cy = NS_FULL_TO_HALF_CORNER(corner, true); const nsCSSValue& radius = *aRuleData->ValueFor(subprops[corner]); nsStyleCoord parentX = parentOutline->mOutlineRadius.Get(cx); nsStyleCoord parentY = parentOutline->mOutlineRadius.Get(cy); nsStyleCoord coordX, coordY; if (SetPairCoords(radius, coordX, coordY, parentX, parentY, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { outline->mOutlineRadius.Set(cx, coordX); outline->mOutlineRadius.Set(cy, coordY); } } } // outline-style: enum, inherit, initial // cannot use SetValue because of SetOutlineStyle const nsCSSValue* outlineStyleValue = aRuleData->ValueForOutlineStyle(); nsCSSUnit unit = outlineStyleValue->GetUnit(); MOZ_ASSERT(eCSSUnit_None != unit && eCSSUnit_Auto != unit, "'none' and 'auto' should be handled as enumerated values"); if (eCSSUnit_Enumerated == unit) { outline->mOutlineStyle = outlineStyleValue->GetIntValue(); } else if (eCSSUnit_Initial == unit || eCSSUnit_Unset == unit) { outline->mOutlineStyle = NS_STYLE_BORDER_STYLE_NONE; } else if (eCSSUnit_Inherit == unit) { conditions.SetUncacheable(); outline->mOutlineStyle = parentOutline->mOutlineStyle; } outline->RecalcData(); COMPUTE_END_RESET(Outline, outline) } const void* nsRuleNode::ComputeListData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(List, list, parentList) // quotes: inherit, initial, none, [string string]+ const nsCSSValue* quotesValue = aRuleData->ValueForQuotes(); switch (quotesValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: case eCSSUnit_Unset: conditions.SetUncacheable(); list->SetQuotesInherit(parentList); break; case eCSSUnit_Initial: list->SetQuotesInitial(); break; case eCSSUnit_None: list->SetQuotesNone(); break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourQuotes = quotesValue->GetPairListValue(); nsStyleQuoteValues::QuotePairArray quotePairs; quotePairs.SetLength(ListLength(ourQuotes)); size_t index = 0; nsAutoString buffer; while (ourQuotes) { MOZ_ASSERT(ourQuotes->mXValue.GetUnit() == eCSSUnit_String && ourQuotes->mYValue.GetUnit() == eCSSUnit_String, "improper list contents for quotes"); quotePairs[index].first = ourQuotes->mXValue.GetStringValue(buffer); quotePairs[index].second = ourQuotes->mYValue.GetStringValue(buffer); ++index; ourQuotes = ourQuotes->mNext; } list->SetQuotes(Move(quotePairs)); break; } default: MOZ_ASSERT(false, "unexpected value unit"); } // list-style-type: string, none, inherit, initial const nsCSSValue* typeValue = aRuleData->ValueForListStyleType(); switch (typeValue->GetUnit()) { case eCSSUnit_Unset: case eCSSUnit_Inherit: { conditions.SetUncacheable(); list->SetCounterStyle(parentList->GetCounterStyle()); break; } case eCSSUnit_Initial: list->SetListStyleType(NS_LITERAL_STRING("disc"), mPresContext); break; case eCSSUnit_Ident: { nsString typeIdent; typeValue->GetStringValue(typeIdent); list->SetListStyleType(typeIdent, mPresContext); break; } case eCSSUnit_String: { nsString str; typeValue->GetStringValue(str); list->SetCounterStyle(new AnonymousCounterStyle(str)); break; } case eCSSUnit_Enumerated: { // For compatibility with html attribute map. // This branch should never be called for value from CSS. int32_t intValue = typeValue->GetIntValue(); nsAutoString name; switch (intValue) { case NS_STYLE_LIST_STYLE_LOWER_ROMAN: name.AssignLiteral(u"lower-roman"); break; case NS_STYLE_LIST_STYLE_UPPER_ROMAN: name.AssignLiteral(u"upper-roman"); break; case NS_STYLE_LIST_STYLE_LOWER_ALPHA: name.AssignLiteral(u"lower-alpha"); break; case NS_STYLE_LIST_STYLE_UPPER_ALPHA: name.AssignLiteral(u"upper-alpha"); break; default: CopyASCIItoUTF16(nsCSSProps::ValueToKeyword( intValue, nsCSSProps::kListStyleKTable), name); break; } list->SetListStyleType(name, mPresContext); break; } case eCSSUnit_Symbols: list->SetCounterStyle(new AnonymousCounterStyle(typeValue->GetArrayValue())); break; case eCSSUnit_Null: break; default: NS_NOTREACHED("Unexpected value unit"); } // list-style-image: url, none, inherit const nsCSSValue* imageValue = aRuleData->ValueForListStyleImage(); if (eCSSUnit_Image == imageValue->GetUnit()) { SetStyleImageRequest([&](nsStyleImageRequest* req) { list->mListStyleImage = req; }, mPresContext, *imageValue, nsStyleImageRequest::Mode(0)); } else if (eCSSUnit_None == imageValue->GetUnit() || eCSSUnit_Initial == imageValue->GetUnit()) { list->mListStyleImage = nullptr; } else if (eCSSUnit_Inherit == imageValue->GetUnit() || eCSSUnit_Unset == imageValue->GetUnit()) { conditions.SetUncacheable(); list->mListStyleImage = parentList->mListStyleImage; } // list-style-position: enum, inherit, initial SetValue(*aRuleData->ValueForListStylePosition(), list->mListStylePosition, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentList->mListStylePosition, NS_STYLE_LIST_STYLE_POSITION_OUTSIDE); // image region property: length, auto, inherit const nsCSSValue* imageRegionValue = aRuleData->ValueForImageRegion(); switch (imageRegionValue->GetUnit()) { case eCSSUnit_Inherit: case eCSSUnit_Unset: conditions.SetUncacheable(); list->mImageRegion = parentList->mImageRegion; break; case eCSSUnit_Initial: case eCSSUnit_Auto: list->mImageRegion.SetRect(0,0,0,0); break; case eCSSUnit_Null: break; case eCSSUnit_Rect: { const nsCSSRect& rgnRect = imageRegionValue->GetRectValue(); if (rgnRect.mTop.GetUnit() == eCSSUnit_Auto) list->mImageRegion.y = 0; else if (rgnRect.mTop.IsLengthUnit()) list->mImageRegion.y = CalcLength(rgnRect.mTop, aContext, mPresContext, conditions); if (rgnRect.mBottom.GetUnit() == eCSSUnit_Auto) list->mImageRegion.height = 0; else if (rgnRect.mBottom.IsLengthUnit()) list->mImageRegion.height = CalcLength(rgnRect.mBottom, aContext, mPresContext, conditions) - list->mImageRegion.y; if (rgnRect.mLeft.GetUnit() == eCSSUnit_Auto) list->mImageRegion.x = 0; else if (rgnRect.mLeft.IsLengthUnit()) list->mImageRegion.x = CalcLength(rgnRect.mLeft, aContext, mPresContext, conditions); if (rgnRect.mRight.GetUnit() == eCSSUnit_Auto) list->mImageRegion.width = 0; else if (rgnRect.mRight.IsLengthUnit()) list->mImageRegion.width = CalcLength(rgnRect.mRight, aContext, mPresContext, conditions) - list->mImageRegion.x; break; } default: MOZ_ASSERT(false, "unrecognized image-region unit"); } COMPUTE_END_INHERITED(List, list) } static void SetGridTrackBreadth(const nsCSSValue& aValue, nsStyleCoord& aResult, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { nsCSSUnit unit = aValue.GetUnit(); if (unit == eCSSUnit_FlexFraction) { aResult.SetFlexFractionValue(aValue.GetFloatValue()); } else if (unit == eCSSUnit_Auto) { aResult.SetAutoValue(); } else if (unit == eCSSUnit_None) { // For fit-content(). aResult.SetNoneValue(); } else { MOZ_ASSERT(unit != eCSSUnit_Inherit && unit != eCSSUnit_Unset, "Unexpected value that would use dummyParentCoord"); const nsStyleCoord dummyParentCoord; DebugOnly<bool> stored = SetCoord(aValue, aResult, dummyParentCoord, SETCOORD_LPE | SETCOORD_STORE_CALC, aStyleContext, aPresContext, aConditions); MOZ_ASSERT(stored, "invalid <track-size> value"); } } static void SetGridTrackSize(const nsCSSValue& aValue, nsStyleCoord& aResultMin, nsStyleCoord& aResultMax, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { if (aValue.GetUnit() == eCSSUnit_Function) { nsCSSValue::Array* func = aValue.GetArrayValue(); auto funcName = func->Item(0).GetKeywordValue(); if (funcName == eCSSKeyword_minmax) { SetGridTrackBreadth(func->Item(1), aResultMin, aStyleContext, aPresContext, aConditions); SetGridTrackBreadth(func->Item(2), aResultMax, aStyleContext, aPresContext, aConditions); } else if (funcName == eCSSKeyword_fit_content) { // We represent fit-content(L) as 'none' min-sizing and L max-sizing. SetGridTrackBreadth(nsCSSValue(eCSSUnit_None), aResultMin, aStyleContext, aPresContext, aConditions); SetGridTrackBreadth(func->Item(1), aResultMax, aStyleContext, aPresContext, aConditions); } else { NS_ERROR("Expected minmax() or fit-content(), got another function name"); } } else { // A single <track-breadth>, // specifies identical min and max sizing functions. SetGridTrackBreadth(aValue, aResultMin, aStyleContext, aPresContext, aConditions); aResultMax = aResultMin; } } static void SetGridAutoColumnsRows(const nsCSSValue& aValue, nsStyleCoord& aResultMin, nsStyleCoord& aResultMax, const nsStyleCoord& aParentValueMin, const nsStyleCoord& aParentValueMax, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aConditions.SetUncacheable(); aResultMin = aParentValueMin; aResultMax = aParentValueMax; break; case eCSSUnit_Initial: case eCSSUnit_Unset: // The initial value is 'auto', // which computes to 'minmax(auto, auto)'. // (Explicitly-specified 'auto' values are handled in SetGridTrackSize.) aResultMin.SetAutoValue(); aResultMax.SetAutoValue(); break; default: SetGridTrackSize(aValue, aResultMin, aResultMax, aStyleContext, aPresContext, aConditions); } } static void AppendGridLineNames(const nsCSSValue& aValue, nsTArray<nsString>& aNameList) { // Compute a <line-names> value // Null unit means empty list, nothing more to do. if (aValue.GetUnit() != eCSSUnit_Null) { const nsCSSValueList* item = aValue.GetListValue(); do { nsString* name = aNameList.AppendElement(); item->mValue.GetStringValue(*name); item = item->mNext; } while (item); } } static void SetGridTrackList(const nsCSSValue& aValue, nsStyleGridTemplate& aResult, const nsStyleGridTemplate& aParentValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aConditions.SetUncacheable(); aResult.mIsSubgrid = aParentValue.mIsSubgrid; aResult.mLineNameLists = aParentValue.mLineNameLists; aResult.mMinTrackSizingFunctions = aParentValue.mMinTrackSizingFunctions; aResult.mMaxTrackSizingFunctions = aParentValue.mMaxTrackSizingFunctions; aResult.mRepeatAutoLineNameListBefore = aParentValue.mRepeatAutoLineNameListBefore; aResult.mRepeatAutoLineNameListAfter = aParentValue.mRepeatAutoLineNameListAfter; aResult.mRepeatAutoIndex = aParentValue.mRepeatAutoIndex; aResult.mIsAutoFill = aParentValue.mIsAutoFill; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: aResult.mIsSubgrid = false; aResult.mLineNameLists.Clear(); aResult.mMinTrackSizingFunctions.Clear(); aResult.mMaxTrackSizingFunctions.Clear(); aResult.mRepeatAutoLineNameListBefore.Clear(); aResult.mRepeatAutoLineNameListAfter.Clear(); aResult.mRepeatAutoIndex = -1; aResult.mIsAutoFill = false; break; default: aResult.mLineNameLists.Clear(); aResult.mMinTrackSizingFunctions.Clear(); aResult.mMaxTrackSizingFunctions.Clear(); aResult.mRepeatAutoLineNameListBefore.Clear(); aResult.mRepeatAutoLineNameListAfter.Clear(); aResult.mRepeatAutoIndex = -1; aResult.mIsAutoFill = false; const nsCSSValueList* item = aValue.GetListValue(); if (item->mValue.GetUnit() == eCSSUnit_Enumerated && item->mValue.GetIntValue() == NS_STYLE_GRID_TEMPLATE_SUBGRID) { // subgrid <line-name-list>? aResult.mIsSubgrid = true; item = item->mNext; for (int32_t i = 0; item && i < nsStyleGridLine::kMaxLine; ++i) { if (item->mValue.GetUnit() == eCSSUnit_Pair) { // This is a 'auto-fill' <name-repeat> expression. const nsCSSValuePair& pair = item->mValue.GetPairValue(); MOZ_ASSERT(aResult.mRepeatAutoIndex == -1, "can only have one <name-repeat> with auto-fill"); aResult.mRepeatAutoIndex = i; aResult.mIsAutoFill = true; MOZ_ASSERT(pair.mXValue.GetIntValue() == NS_STYLE_GRID_REPEAT_AUTO_FILL, "unexpected repeat() enum value for subgrid"); const nsCSSValueList* list = pair.mYValue.GetListValue(); AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListBefore); } else { AppendGridLineNames(item->mValue, *aResult.mLineNameLists.AppendElement()); } item = item->mNext; } } else { // <track-list> // The list is expected to have odd number of items, at least 3 // starting with a <line-names> (sub list of identifiers), // and alternating between that and <track-size>. aResult.mIsSubgrid = false; for (int32_t line = 1; ; ++line) { AppendGridLineNames(item->mValue, *aResult.mLineNameLists.AppendElement()); item = item->mNext; if (!item || line == nsStyleGridLine::kMaxLine) { break; } if (item->mValue.GetUnit() == eCSSUnit_Pair) { // This is a 'auto-fill' / 'auto-fit' <auto-repeat> expression. const nsCSSValuePair& pair = item->mValue.GetPairValue(); MOZ_ASSERT(aResult.mRepeatAutoIndex == -1, "can only have one <auto-repeat>"); aResult.mRepeatAutoIndex = line - 1; switch (pair.mXValue.GetIntValue()) { case NS_STYLE_GRID_REPEAT_AUTO_FILL: aResult.mIsAutoFill = true; break; case NS_STYLE_GRID_REPEAT_AUTO_FIT: aResult.mIsAutoFill = false; break; default: MOZ_ASSERT_UNREACHABLE("unexpected repeat() enum value"); } const nsCSSValueList* list = pair.mYValue.GetListValue(); AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListBefore); list = list->mNext; nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement(); nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement(); SetGridTrackSize(list->mValue, min, max, aStyleContext, aPresContext, aConditions); list = list->mNext; AppendGridLineNames(list->mValue, aResult.mRepeatAutoLineNameListAfter); } else { nsStyleCoord& min = *aResult.mMinTrackSizingFunctions.AppendElement(); nsStyleCoord& max = *aResult.mMaxTrackSizingFunctions.AppendElement(); SetGridTrackSize(item->mValue, min, max, aStyleContext, aPresContext, aConditions); } item = item->mNext; MOZ_ASSERT(item, "Expected a eCSSUnit_List of odd length"); } MOZ_ASSERT(!aResult.mMinTrackSizingFunctions.IsEmpty() && aResult.mMinTrackSizingFunctions.Length() == aResult.mMaxTrackSizingFunctions.Length() && aResult.mMinTrackSizingFunctions.Length() + 1 == aResult.mLineNameLists.Length(), "Inconstistent array lengths for nsStyleGridTemplate"); } } } static void SetGridTemplateAreas(const nsCSSValue& aValue, RefPtr<css::GridTemplateAreasValue>* aResult, css::GridTemplateAreasValue* aParentValue, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aConditions.SetUncacheable(); *aResult = aParentValue; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: *aResult = nullptr; break; default: *aResult = aValue.GetGridTemplateAreas(); } } static void SetGridLine(const nsCSSValue& aValue, nsStyleGridLine& aResult, const nsStyleGridLine& aParentValue, RuleNodeCacheConditions& aConditions) { switch (aValue.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: aConditions.SetUncacheable(); aResult = aParentValue; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_Auto: aResult.SetAuto(); break; default: aResult.SetAuto(); // Reset any existing value. const nsCSSValueList* item = aValue.GetListValue(); do { if (item->mValue.GetUnit() == eCSSUnit_Enumerated) { aResult.mHasSpan = true; } else if (item->mValue.GetUnit() == eCSSUnit_Integer) { aResult.mInteger = clamped(item->mValue.GetIntValue(), nsStyleGridLine::kMinLine, nsStyleGridLine::kMaxLine); } else if (item->mValue.GetUnit() == eCSSUnit_Ident) { item->mValue.GetStringValue(aResult.mLineName); } else { NS_ASSERTION(false, "Unexpected unit"); } item = item->mNext; } while (item); MOZ_ASSERT(!aResult.IsAuto(), "should have set something away from default value"); } } const void* nsRuleNode::ComputePositionData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Position, pos, parentPos) // box offsets: length, percent, calc, auto, inherit static const nsCSSPropertyID offsetProps[] = { eCSSProperty_top, eCSSProperty_right, eCSSProperty_bottom, eCSSProperty_left }; nsStyleCoord coord; NS_FOR_CSS_SIDES(side) { nsStyleCoord parentCoord = parentPos->mOffset.Get(side); if (SetCoord(*aRuleData->ValueFor(offsetProps[side]), coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { pos->mOffset.Set(side, coord); } } // We allow the enumerated box size property values -moz-min-content, etc. to // be specified on both the {,min-,max-}width properties and the // {,min-,max-}height properties, regardless of the writing mode. This is // because the writing mode is not determined until here, at computed value // time. Since we do not support layout behavior of these keywords on the // block-axis properties, we turn them into unset if we find them in // that case. WritingMode wm(aContext); bool vertical = wm.IsVertical(); const nsCSSValue* width = aRuleData->ValueForWidth(); if (width->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(width->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *width, pos->mWidth, parentPos->mWidth, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); const nsCSSValue* minWidth = aRuleData->ValueForMinWidth(); if (minWidth->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(minWidth->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *minWidth, pos->mMinWidth, parentPos->mMinWidth, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); const nsCSSValue* maxWidth = aRuleData->ValueForMaxWidth(); if (maxWidth->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(maxWidth->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *maxWidth, pos->mMaxWidth, parentPos->mMaxWidth, SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); const nsCSSValue* height = aRuleData->ValueForHeight(); if (height->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(height->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *height, pos->mHeight, parentPos->mHeight, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); const nsCSSValue* minHeight = aRuleData->ValueForMinHeight(); if (minHeight->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(minHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *minHeight, pos->mMinHeight, parentPos->mMinHeight, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); const nsCSSValue* maxHeight = aRuleData->ValueForMaxHeight(); if (maxHeight->GetUnit() == eCSSUnit_Enumerated) { conditions.SetWritingModeDependency(wm.GetBits()); } SetCoord(maxHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *maxHeight, pos->mMaxHeight, parentPos->mMaxHeight, SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); // box-sizing: enum, inherit, initial SetValue(*aRuleData->ValueForBoxSizing(), pos->mBoxSizing, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mBoxSizing, StyleBoxSizing::Content); // align-content: enum, inherit, initial SetValue(*aRuleData->ValueForAlignContent(), pos->mAlignContent, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mAlignContent, NS_STYLE_ALIGN_NORMAL); // align-items: enum, inherit, initial SetValue(*aRuleData->ValueForAlignItems(), pos->mAlignItems, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mAlignItems, NS_STYLE_ALIGN_NORMAL); // align-self: enum, inherit, initial SetValue(*aRuleData->ValueForAlignSelf(), pos->mAlignSelf, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mAlignSelf, NS_STYLE_ALIGN_AUTO); // justify-content: enum, inherit, initial SetValue(*aRuleData->ValueForJustifyContent(), pos->mJustifyContent, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mJustifyContent, NS_STYLE_JUSTIFY_NORMAL); // justify-items: enum, inherit, initial const auto& justifyItemsValue = *aRuleData->ValueForJustifyItems(); if (MOZ_UNLIKELY(justifyItemsValue.GetUnit() == eCSSUnit_Inherit)) { if (MOZ_LIKELY(parentContext)) { pos->mJustifyItems = parentPos->ComputedJustifyItems(parentContext->GetParent()); } else { pos->mJustifyItems = NS_STYLE_JUSTIFY_NORMAL; } conditions.SetUncacheable(); } else { SetValue(justifyItemsValue, pos->mJustifyItems, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mJustifyItems, // unused, we handle 'inherit' above NS_STYLE_JUSTIFY_AUTO); } // justify-self: enum, inherit, initial SetValue(*aRuleData->ValueForJustifySelf(), pos->mJustifySelf, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mJustifySelf, NS_STYLE_JUSTIFY_AUTO); // flex-basis: auto, length, percent, enum, calc, inherit, initial // (Note: The flags here should match those used for 'width' property above.) SetCoord(*aRuleData->ValueForFlexBasis(), pos->mFlexBasis, parentPos->mFlexBasis, SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); // flex-direction: enum, inherit, initial SetValue(*aRuleData->ValueForFlexDirection(), pos->mFlexDirection, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mFlexDirection, NS_STYLE_FLEX_DIRECTION_ROW); // flex-grow: float, inherit, initial SetFactor(*aRuleData->ValueForFlexGrow(), pos->mFlexGrow, conditions, parentPos->mFlexGrow, 0.0f, SETFCT_UNSET_INITIAL); // flex-shrink: float, inherit, initial SetFactor(*aRuleData->ValueForFlexShrink(), pos->mFlexShrink, conditions, parentPos->mFlexShrink, 1.0f, SETFCT_UNSET_INITIAL); // flex-wrap: enum, inherit, initial SetValue(*aRuleData->ValueForFlexWrap(), pos->mFlexWrap, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mFlexWrap, NS_STYLE_FLEX_WRAP_NOWRAP); // order: integer, inherit, initial SetValue(*aRuleData->ValueForOrder(), pos->mOrder, conditions, SETVAL_INTEGER | SETVAL_UNSET_INITIAL, parentPos->mOrder, NS_STYLE_ORDER_INITIAL); // object-fit: enum, inherit, initial SetValue(*aRuleData->ValueForObjectFit(), pos->mObjectFit, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentPos->mObjectFit, NS_STYLE_OBJECT_FIT_FILL); // object-position const nsCSSValue& objectPosition = *aRuleData->ValueForObjectPosition(); switch (objectPosition.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: conditions.SetUncacheable(); pos->mObjectPosition = parentPos->mObjectPosition; break; case eCSSUnit_Initial: case eCSSUnit_Unset: pos->mObjectPosition.SetInitialPercentValues(0.5f); break; default: ComputePositionValue(aContext, objectPosition, pos->mObjectPosition, conditions); } // grid-auto-flow const nsCSSValue& gridAutoFlow = *aRuleData->ValueForGridAutoFlow(); switch (gridAutoFlow.GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: conditions.SetUncacheable(); pos->mGridAutoFlow = parentPos->mGridAutoFlow; break; case eCSSUnit_Initial: case eCSSUnit_Unset: pos->mGridAutoFlow = NS_STYLE_GRID_AUTO_FLOW_ROW; break; default: NS_ASSERTION(gridAutoFlow.GetUnit() == eCSSUnit_Enumerated, "Unexpected unit"); pos->mGridAutoFlow = gridAutoFlow.GetIntValue(); } // grid-auto-columns SetGridAutoColumnsRows(*aRuleData->ValueForGridAutoColumns(), pos->mGridAutoColumnsMin, pos->mGridAutoColumnsMax, parentPos->mGridAutoColumnsMin, parentPos->mGridAutoColumnsMax, aContext, mPresContext, conditions); // grid-auto-rows SetGridAutoColumnsRows(*aRuleData->ValueForGridAutoRows(), pos->mGridAutoRowsMin, pos->mGridAutoRowsMax, parentPos->mGridAutoRowsMin, parentPos->mGridAutoRowsMax, aContext, mPresContext, conditions); // grid-template-columns SetGridTrackList(*aRuleData->ValueForGridTemplateColumns(), pos->mGridTemplateColumns, parentPos->mGridTemplateColumns, aContext, mPresContext, conditions); // grid-template-rows SetGridTrackList(*aRuleData->ValueForGridTemplateRows(), pos->mGridTemplateRows, parentPos->mGridTemplateRows, aContext, mPresContext, conditions); // grid-tempate-areas SetGridTemplateAreas(*aRuleData->ValueForGridTemplateAreas(), &pos->mGridTemplateAreas, parentPos->mGridTemplateAreas, conditions); // grid-column-start SetGridLine(*aRuleData->ValueForGridColumnStart(), pos->mGridColumnStart, parentPos->mGridColumnStart, conditions); // grid-column-end SetGridLine(*aRuleData->ValueForGridColumnEnd(), pos->mGridColumnEnd, parentPos->mGridColumnEnd, conditions); // grid-row-start SetGridLine(*aRuleData->ValueForGridRowStart(), pos->mGridRowStart, parentPos->mGridRowStart, conditions); // grid-row-end SetGridLine(*aRuleData->ValueForGridRowEnd(), pos->mGridRowEnd, parentPos->mGridRowEnd, conditions); // grid-column-gap if (SetCoord(*aRuleData->ValueForGridColumnGap(), pos->mGridColumnGap, parentPos->mGridColumnGap, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { } else { MOZ_ASSERT(aRuleData->ValueForGridColumnGap()->GetUnit() == eCSSUnit_Null, "unexpected unit"); } // grid-row-gap if (SetCoord(*aRuleData->ValueForGridRowGap(), pos->mGridRowGap, parentPos->mGridRowGap, SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions)) { } else { MOZ_ASSERT(aRuleData->ValueForGridRowGap()->GetUnit() == eCSSUnit_Null, "unexpected unit"); } // z-index const nsCSSValue* zIndexValue = aRuleData->ValueForZIndex(); if (! SetCoord(*zIndexValue, pos->mZIndex, parentPos->mZIndex, SETCOORD_IA | SETCOORD_INITIAL_AUTO | SETCOORD_UNSET_INITIAL, aContext, nullptr, conditions)) { if (eCSSUnit_Inherit == zIndexValue->GetUnit()) { // handle inherit, because it's ok to inherit 'auto' here conditions.SetUncacheable(); pos->mZIndex = parentPos->mZIndex; } } COMPUTE_END_RESET(Position, pos) } const void* nsRuleNode::ComputeTableData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Table, table, parentTable) // table-layout: enum, inherit, initial SetValue(*aRuleData->ValueForTableLayout(), table->mLayoutStrategy, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentTable->mLayoutStrategy, NS_STYLE_TABLE_LAYOUT_AUTO); // span: pixels (not a real CSS prop) const nsCSSValue* spanValue = aRuleData->ValueForSpan(); if (eCSSUnit_Enumerated == spanValue->GetUnit() || eCSSUnit_Integer == spanValue->GetUnit()) table->mSpan = spanValue->GetIntValue(); COMPUTE_END_RESET(Table, table) } const void* nsRuleNode::ComputeTableBorderData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(TableBorder, table, parentTable) // border-collapse: enum, inherit, initial SetValue(*aRuleData->ValueForBorderCollapse(), table->mBorderCollapse, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentTable->mBorderCollapse, NS_STYLE_BORDER_SEPARATE); const nsCSSValue* borderSpacingValue = aRuleData->ValueForBorderSpacing(); // border-spacing: pair(length), inherit if (borderSpacingValue->GetUnit() != eCSSUnit_Null) { nsStyleCoord parentCol(parentTable->mBorderSpacingCol, nsStyleCoord::CoordConstructor); nsStyleCoord parentRow(parentTable->mBorderSpacingRow, nsStyleCoord::CoordConstructor); nsStyleCoord coordCol, coordRow; #ifdef DEBUG bool result = #endif SetPairCoords(*borderSpacingValue, coordCol, coordRow, parentCol, parentRow, SETCOORD_LH | SETCOORD_INITIAL_ZERO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); NS_ASSERTION(result, "malformed table border value"); table->mBorderSpacingCol = coordCol.GetCoordValue(); table->mBorderSpacingRow = coordRow.GetCoordValue(); } // caption-side: enum, inherit, initial SetValue(*aRuleData->ValueForCaptionSide(), table->mCaptionSide, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentTable->mCaptionSide, NS_STYLE_CAPTION_SIDE_TOP); // empty-cells: enum, inherit, initial SetValue(*aRuleData->ValueForEmptyCells(), table->mEmptyCells, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentTable->mEmptyCells, NS_STYLE_TABLE_EMPTY_CELLS_SHOW); COMPUTE_END_INHERITED(TableBorder, table) } const void* nsRuleNode::ComputeContentData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { uint32_t count; nsAutoString buffer; COMPUTE_START_RESET(Content, content, parentContent) // content: [string, url, counter, attr, enum]+, normal, none, inherit const nsCSSValue* contentValue = aRuleData->ValueForContent(); switch (contentValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Normal: case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: // "normal", "none", "initial" and "unset" all mean no content content->AllocateContents(0); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); count = parentContent->ContentCount(); content->AllocateContents(count); while (0 < count--) { content->ContentAt(count) = parentContent->ContentAt(count); } break; case eCSSUnit_Enumerated: { MOZ_ASSERT(contentValue->GetIntValue() == NS_STYLE_CONTENT_ALT_CONTENT, "unrecognized solitary content keyword"); content->AllocateContents(1); nsStyleContentData& data = content->ContentAt(0); data.mType = eStyleContentType_AltContent; data.mContent.mString = nullptr; break; } case eCSSUnit_List: case eCSSUnit_ListDep: { const nsCSSValueList* contentValueList = contentValue->GetListValue(); count = 0; while (contentValueList) { count++; contentValueList = contentValueList->mNext; } content->AllocateContents(count); const nsAutoString nullStr; count = 0; contentValueList = contentValue->GetListValue(); while (contentValueList) { const nsCSSValue& value = contentValueList->mValue; nsCSSUnit unit = value.GetUnit(); nsStyleContentType type; nsStyleContentData &data = content->ContentAt(count++); switch (unit) { case eCSSUnit_String: type = eStyleContentType_String; break; case eCSSUnit_Image: type = eStyleContentType_Image; break; case eCSSUnit_Attr: type = eStyleContentType_Attr; break; case eCSSUnit_Counter: type = eStyleContentType_Counter; break; case eCSSUnit_Counters: type = eStyleContentType_Counters; break; case eCSSUnit_Enumerated: switch (value.GetIntValue()) { case NS_STYLE_CONTENT_OPEN_QUOTE: type = eStyleContentType_OpenQuote; break; case NS_STYLE_CONTENT_CLOSE_QUOTE: type = eStyleContentType_CloseQuote; break; case NS_STYLE_CONTENT_NO_OPEN_QUOTE: type = eStyleContentType_NoOpenQuote; break; case NS_STYLE_CONTENT_NO_CLOSE_QUOTE: type = eStyleContentType_NoCloseQuote; break; default: NS_ERROR("bad content value"); type = eStyleContentType_Uninitialized; } break; default: NS_ERROR("bad content type"); type = eStyleContentType_Uninitialized; } data.mType = type; if (type == eStyleContentType_Image) { SetImageRequest([&](imgRequestProxy* req) { data.SetImage(req); }, mPresContext, value); } else if (type <= eStyleContentType_Attr) { value.GetStringValue(buffer); data.mContent.mString = NS_strdup(buffer.get()); } else if (type <= eStyleContentType_Counters) { data.mContent.mCounters = value.GetArrayValue(); data.mContent.mCounters->AddRef(); } else { data.mContent.mString = nullptr; } contentValueList = contentValueList->mNext; } break; } default: MOZ_ASSERT(false, "unrecognized content unit"); } // counter-increment: [string [int]]+, none, inherit const nsCSSValue* counterIncrementValue = aRuleData->ValueForCounterIncrement(); switch (counterIncrementValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: content->AllocateCounterIncrements(0); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); count = parentContent->CounterIncrementCount(); content->AllocateCounterIncrements(count); while (count--) { const nsStyleCounterData& data = parentContent->CounterIncrementAt(count); content->SetCounterIncrementAt(count, data.mCounter, data.mValue); } break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourIncrement = counterIncrementValue->GetPairListValue(); MOZ_ASSERT(ourIncrement->mXValue.GetUnit() == eCSSUnit_Ident, "unexpected value unit"); count = ListLength(ourIncrement); content->AllocateCounterIncrements(count); count = 0; for (const nsCSSValuePairList* p = ourIncrement; p; p = p->mNext, count++) { int32_t increment; if (p->mYValue.GetUnit() == eCSSUnit_Integer) { increment = p->mYValue.GetIntValue(); } else { increment = 1; } p->mXValue.GetStringValue(buffer); content->SetCounterIncrementAt(count, buffer, increment); } break; } default: MOZ_ASSERT(false, "unexpected value unit"); } // counter-reset: [string [int]]+, none, inherit const nsCSSValue* counterResetValue = aRuleData->ValueForCounterReset(); switch (counterResetValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: content->AllocateCounterResets(0); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); count = parentContent->CounterResetCount(); content->AllocateCounterResets(count); while (0 < count--) { const nsStyleCounterData& data = parentContent->CounterResetAt(count); content->SetCounterResetAt(count, data.mCounter, data.mValue); } break; case eCSSUnit_PairList: case eCSSUnit_PairListDep: { const nsCSSValuePairList* ourReset = counterResetValue->GetPairListValue(); MOZ_ASSERT(ourReset->mXValue.GetUnit() == eCSSUnit_Ident, "unexpected value unit"); count = ListLength(ourReset); content->AllocateCounterResets(count); count = 0; for (const nsCSSValuePairList* p = ourReset; p; p = p->mNext, count++) { int32_t reset; if (p->mYValue.GetUnit() == eCSSUnit_Integer) { reset = p->mYValue.GetIntValue(); } else { reset = 0; } p->mXValue.GetStringValue(buffer); content->SetCounterResetAt(count, buffer, reset); } break; } default: MOZ_ASSERT(false, "unexpected value unit"); } // If we ended up with an image, track it. for (uint32_t i = 0; i < content->ContentCount(); ++i) { if ((content->ContentAt(i).mType == eStyleContentType_Image) && content->ContentAt(i).mContent.mImage) { content->ContentAt(i).TrackImage( aContext->PresContext()->Document()->ImageTracker()); } } COMPUTE_END_RESET(Content, content) } const void* nsRuleNode::ComputeXULData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(XUL, xul, parentXUL) // box-align: enum, inherit, initial SetValue(*aRuleData->ValueForBoxAlign(), xul->mBoxAlign, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentXUL->mBoxAlign, StyleBoxAlign::Stretch); // box-direction: enum, inherit, initial SetValue(*aRuleData->ValueForBoxDirection(), xul->mBoxDirection, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentXUL->mBoxDirection, StyleBoxDirection::Normal); // box-flex: factor, inherit SetFactor(*aRuleData->ValueForBoxFlex(), xul->mBoxFlex, conditions, parentXUL->mBoxFlex, 0.0f, SETFCT_UNSET_INITIAL); // box-orient: enum, inherit, initial SetValue(*aRuleData->ValueForBoxOrient(), xul->mBoxOrient, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentXUL->mBoxOrient, StyleBoxOrient::Horizontal); // box-pack: enum, inherit, initial SetValue(*aRuleData->ValueForBoxPack(), xul->mBoxPack, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentXUL->mBoxPack, StyleBoxPack::Start); // box-ordinal-group: integer, inherit, initial SetValue(*aRuleData->ValueForBoxOrdinalGroup(), xul->mBoxOrdinal, conditions, SETVAL_INTEGER | SETVAL_UNSET_INITIAL, parentXUL->mBoxOrdinal, 1); const nsCSSValue* stackSizingValue = aRuleData->ValueForStackSizing(); if (eCSSUnit_Inherit == stackSizingValue->GetUnit()) { conditions.SetUncacheable(); xul->mStretchStack = parentXUL->mStretchStack; } else if (eCSSUnit_Initial == stackSizingValue->GetUnit() || eCSSUnit_Unset == stackSizingValue->GetUnit()) { xul->mStretchStack = true; } else if (eCSSUnit_Enumerated == stackSizingValue->GetUnit()) { xul->mStretchStack = stackSizingValue->GetIntValue() == NS_STYLE_STACK_SIZING_STRETCH_TO_FIT; } COMPUTE_END_RESET(XUL, xul) } const void* nsRuleNode::ComputeColumnData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Column, column, parent) // column-width: length, auto, inherit SetCoord(*aRuleData->ValueForColumnWidth(), column->mColumnWidth, parent->mColumnWidth, SETCOORD_LAH | SETCOORD_INITIAL_AUTO | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); // column-gap: length, inherit, normal SetCoord(*aRuleData->ValueForColumnGap(), column->mColumnGap, parent->mColumnGap, SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INITIAL, aContext, mPresContext, conditions); // clamp negative calc() to 0 if (column->mColumnGap.GetUnit() == eStyleUnit_Coord) { column->mColumnGap.SetCoordValue( std::max(column->mColumnGap.GetCoordValue(), 0)); } // column-count: auto, integer, inherit const nsCSSValue* columnCountValue = aRuleData->ValueForColumnCount(); if (eCSSUnit_Auto == columnCountValue->GetUnit() || eCSSUnit_Initial == columnCountValue->GetUnit() || eCSSUnit_Unset == columnCountValue->GetUnit()) { column->mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO; } else if (eCSSUnit_Integer == columnCountValue->GetUnit()) { column->mColumnCount = columnCountValue->GetIntValue(); // Max kMaxColumnCount columns - wallpaper for bug 345583. column->mColumnCount = std::min(column->mColumnCount, nsStyleColumn::kMaxColumnCount); } else if (eCSSUnit_Inherit == columnCountValue->GetUnit()) { conditions.SetUncacheable(); column->mColumnCount = parent->mColumnCount; } // column-rule-width: length, enum, inherit const nsCSSValue& widthValue = *aRuleData->ValueForColumnRuleWidth(); if (eCSSUnit_Initial == widthValue.GetUnit() || eCSSUnit_Unset == widthValue.GetUnit()) { column->SetColumnRuleWidth( (mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]); } else if (eCSSUnit_Enumerated == widthValue.GetUnit()) { NS_ASSERTION(widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN || widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM || widthValue.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK, "Unexpected enum value"); column->SetColumnRuleWidth( (mPresContext->GetBorderWidthTable())[widthValue.GetIntValue()]); } else if (eCSSUnit_Inherit == widthValue.GetUnit()) { column->SetColumnRuleWidth(parent->GetComputedColumnRuleWidth()); conditions.SetUncacheable(); } else if (widthValue.IsLengthUnit() || widthValue.IsCalcUnit()) { nscoord len = CalcLength(widthValue, aContext, mPresContext, conditions); if (len < 0) { // FIXME: This is untested (by test_value_storage.html) for // column-rule-width since it gets covered up by the border // rounding code. NS_ASSERTION(widthValue.IsCalcUnit(), "parser should have rejected negative length"); len = 0; } column->SetColumnRuleWidth(len); } // column-rule-style: enum, inherit const nsCSSValue& styleValue = *aRuleData->ValueForColumnRuleStyle(); MOZ_ASSERT(eCSSUnit_None != styleValue.GetUnit(), "'none' should be handled as enumerated value"); if (eCSSUnit_Enumerated == styleValue.GetUnit()) { column->mColumnRuleStyle = styleValue.GetIntValue(); } else if (eCSSUnit_Initial == styleValue.GetUnit() || eCSSUnit_Unset == styleValue.GetUnit()) { column->mColumnRuleStyle = NS_STYLE_BORDER_STYLE_NONE; } else if (eCSSUnit_Inherit == styleValue.GetUnit()) { conditions.SetUncacheable(); column->mColumnRuleStyle = parent->mColumnRuleStyle; } // column-rule-color: color, inherit SetComplexColor<eUnsetInitial>(*aRuleData->ValueForColumnRuleColor(), parent->mColumnRuleColor, StyleComplexColor::CurrentColor(), mPresContext, column->mColumnRuleColor, conditions); // column-fill: enum SetValue(*aRuleData->ValueForColumnFill(), column->mColumnFill, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parent->mColumnFill, NS_STYLE_COLUMN_FILL_BALANCE); COMPUTE_END_RESET(Column, column) } static void SetSVGPaint(const nsCSSValue& aValue, const nsStyleSVGPaint& parentPaint, nsPresContext* aPresContext, nsStyleContext *aContext, nsStyleSVGPaint& aResult, nsStyleSVGPaintType aInitialPaintType, RuleNodeCacheConditions& aConditions) { MOZ_ASSERT(aInitialPaintType == eStyleSVGPaintType_None || aInitialPaintType == eStyleSVGPaintType_Color, "SetSVGPaint only supports initial values being either 'black' " "(represented by eStyleSVGPaintType_Color) or none (by " "eStyleSVGPaintType_None)"); nscolor color; if (aValue.GetUnit() == eCSSUnit_Inherit || aValue.GetUnit() == eCSSUnit_Unset) { aResult = parentPaint; aConditions.SetUncacheable(); } else if (aValue.GetUnit() == eCSSUnit_None) { aResult.SetNone(); } else if (aValue.GetUnit() == eCSSUnit_Initial) { if (aInitialPaintType == eStyleSVGPaintType_None) { aResult.SetNone(); } else { aResult.SetColor(NS_RGB(0, 0, 0)); } } else if (SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aContext, color, aConditions)) { aResult.SetColor(color); } else if (aValue.GetUnit() == eCSSUnit_Pair) { const nsCSSValuePair& pair = aValue.GetPairValue(); nscolor fallback; if (pair.mYValue.GetUnit() == eCSSUnit_None) { fallback = NS_RGBA(0, 0, 0, 0); } else { MOZ_ASSERT(pair.mYValue.GetUnit() != eCSSUnit_Inherit, "cannot inherit fallback colour"); SetColor(pair.mYValue, NS_RGB(0, 0, 0), aPresContext, aContext, fallback, aConditions); } if (pair.mXValue.GetUnit() == eCSSUnit_URL) { aResult.SetPaintServer(pair.mXValue.GetURLStructValue(), fallback); } else if (pair.mXValue.GetUnit() == eCSSUnit_Enumerated) { switch (pair.mXValue.GetIntValue()) { case NS_COLOR_CONTEXT_FILL: aResult.SetContextValue(eStyleSVGPaintType_ContextFill, fallback); break; case NS_COLOR_CONTEXT_STROKE: aResult.SetContextValue(eStyleSVGPaintType_ContextStroke, fallback); break; default: NS_NOTREACHED("unknown keyword as paint server value"); } } else { NS_NOTREACHED("malformed paint server value"); } } else { MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Null, "malformed paint server value"); } } static void SetSVGOpacity(const nsCSSValue& aValue, float& aOpacityField, nsStyleSVGOpacitySource& aOpacityTypeField, RuleNodeCacheConditions& aConditions, float aParentOpacity, nsStyleSVGOpacitySource aParentOpacityType) { if (eCSSUnit_Enumerated == aValue.GetUnit()) { switch (aValue.GetIntValue()) { case NS_STYLE_CONTEXT_FILL_OPACITY: aOpacityTypeField = eStyleSVGOpacitySource_ContextFillOpacity; break; case NS_STYLE_CONTEXT_STROKE_OPACITY: aOpacityTypeField = eStyleSVGOpacitySource_ContextStrokeOpacity; break; default: NS_NOTREACHED("SetSVGOpacity: Unknown keyword"); } // Fall back on fully opaque aOpacityField = 1.0f; } else if (eCSSUnit_Inherit == aValue.GetUnit() || eCSSUnit_Unset == aValue.GetUnit()) { aConditions.SetUncacheable(); aOpacityField = aParentOpacity; aOpacityTypeField = aParentOpacityType; } else if (eCSSUnit_Null != aValue.GetUnit()) { SetFactor(aValue, aOpacityField, aConditions, aParentOpacity, 1.0f, SETFCT_OPACITY); aOpacityTypeField = eStyleSVGOpacitySource_Normal; } } /* static */ void nsRuleNode::FillAllMaskLists(nsStyleImageLayers& aMask, uint32_t aMaxItemCount) { // Delete any extra items. We need to keep layers in which any // property was specified. aMask.mLayers.TruncateLengthNonZero(aMaxItemCount); uint32_t fillCount = aMask.mImageCount; FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mImage, aMask.mImageCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mSourceURI, aMask.mImageCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mRepeat, aMask.mRepeatCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mClip, aMask.mClipCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mOrigin, aMask.mOriginCount, fillCount); FillImageLayerPositionCoordList(aMask.mLayers, &Position::mXPosition, aMask.mPositionXCount, fillCount); FillImageLayerPositionCoordList(aMask.mLayers, &Position::mYPosition, aMask.mPositionYCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mSize, aMask.mSizeCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mMaskMode, aMask.mMaskModeCount, fillCount); FillImageLayerList(aMask.mLayers, &nsStyleImageLayers::Layer::mComposite, aMask.mCompositeCount, fillCount); } const void* nsRuleNode::ComputeSVGData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(SVG, svg, parentSVG) // clip-rule: enum, inherit, initial SetValue(*aRuleData->ValueForClipRule(), svg->mClipRule, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mClipRule, StyleFillRule::Nonzero); // color-interpolation: enum, inherit, initial SetValue(*aRuleData->ValueForColorInterpolation(), svg->mColorInterpolation, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mColorInterpolation, NS_STYLE_COLOR_INTERPOLATION_SRGB); // color-interpolation-filters: enum, inherit, initial SetValue(*aRuleData->ValueForColorInterpolationFilters(), svg->mColorInterpolationFilters, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mColorInterpolationFilters, NS_STYLE_COLOR_INTERPOLATION_LINEARRGB); // fill: SetSVGPaint(*aRuleData->ValueForFill(), parentSVG->mFill, mPresContext, aContext, svg->mFill, eStyleSVGPaintType_Color, conditions); // fill-opacity: factor, inherit, initial, // context-fill-opacity, context-stroke-opacity nsStyleSVGOpacitySource contextFillOpacity = svg->FillOpacitySource(); SetSVGOpacity(*aRuleData->ValueForFillOpacity(), svg->mFillOpacity, contextFillOpacity, conditions, parentSVG->mFillOpacity, parentSVG->FillOpacitySource()); svg->SetFillOpacitySource(contextFillOpacity); // fill-rule: enum, inherit, initial SetValue(*aRuleData->ValueForFillRule(), svg->mFillRule, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mFillRule, StyleFillRule::Nonzero); // marker-end: url, none, inherit const nsCSSValue* markerEndValue = aRuleData->ValueForMarkerEnd(); if (eCSSUnit_URL == markerEndValue->GetUnit()) { svg->mMarkerEnd = markerEndValue->GetURLStructValue(); } else if (eCSSUnit_None == markerEndValue->GetUnit() || eCSSUnit_Initial == markerEndValue->GetUnit()) { svg->mMarkerEnd = nullptr; } else if (eCSSUnit_Inherit == markerEndValue->GetUnit() || eCSSUnit_Unset == markerEndValue->GetUnit()) { conditions.SetUncacheable(); svg->mMarkerEnd = parentSVG->mMarkerEnd; } // marker-mid: url, none, inherit const nsCSSValue* markerMidValue = aRuleData->ValueForMarkerMid(); if (eCSSUnit_URL == markerMidValue->GetUnit()) { svg->mMarkerMid = markerMidValue->GetURLStructValue(); } else if (eCSSUnit_None == markerMidValue->GetUnit() || eCSSUnit_Initial == markerMidValue->GetUnit()) { svg->mMarkerMid = nullptr; } else if (eCSSUnit_Inherit == markerMidValue->GetUnit() || eCSSUnit_Unset == markerMidValue->GetUnit()) { conditions.SetUncacheable(); svg->mMarkerMid = parentSVG->mMarkerMid; } // marker-start: url, none, inherit const nsCSSValue* markerStartValue = aRuleData->ValueForMarkerStart(); if (eCSSUnit_URL == markerStartValue->GetUnit()) { svg->mMarkerStart = markerStartValue->GetURLStructValue(); } else if (eCSSUnit_None == markerStartValue->GetUnit() || eCSSUnit_Initial == markerStartValue->GetUnit()) { svg->mMarkerStart = nullptr; } else if (eCSSUnit_Inherit == markerStartValue->GetUnit() || eCSSUnit_Unset == markerStartValue->GetUnit()) { conditions.SetUncacheable(); svg->mMarkerStart = parentSVG->mMarkerStart; } // paint-order: enum (bit field), inherit, initial const nsCSSValue* paintOrderValue = aRuleData->ValueForPaintOrder(); switch (paintOrderValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Enumerated: static_assert (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8, "SVGStyleStruct::mPaintOrder not big enough"); svg->mPaintOrder = static_cast<uint8_t>(paintOrderValue->GetIntValue()); break; case eCSSUnit_Inherit: case eCSSUnit_Unset: conditions.SetUncacheable(); svg->mPaintOrder = parentSVG->mPaintOrder; break; case eCSSUnit_Initial: svg->mPaintOrder = NS_STYLE_PAINT_ORDER_NORMAL; break; default: NS_NOTREACHED("unexpected unit"); } // shape-rendering: enum, inherit SetValue(*aRuleData->ValueForShapeRendering(), svg->mShapeRendering, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mShapeRendering, NS_STYLE_SHAPE_RENDERING_AUTO); // stroke: SetSVGPaint(*aRuleData->ValueForStroke(), parentSVG->mStroke, mPresContext, aContext, svg->mStroke, eStyleSVGPaintType_None, conditions); // stroke-dasharray: <dasharray>, none, inherit, context-value const nsCSSValue* strokeDasharrayValue = aRuleData->ValueForStrokeDasharray(); switch (strokeDasharrayValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Inherit: case eCSSUnit_Unset: conditions.SetUncacheable(); svg->SetStrokeDasharrayFromObject(parentSVG->StrokeDasharrayFromObject()); svg->mStrokeDasharray = parentSVG->mStrokeDasharray; break; case eCSSUnit_Enumerated: MOZ_ASSERT(strokeDasharrayValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE, "Unknown keyword for stroke-dasharray"); svg->SetStrokeDasharrayFromObject(true); svg->mStrokeDasharray.Clear(); break; case eCSSUnit_Initial: case eCSSUnit_None: svg->SetStrokeDasharrayFromObject(false); svg->mStrokeDasharray.Clear(); break; case eCSSUnit_List: case eCSSUnit_ListDep: { svg->SetStrokeDasharrayFromObject(false); svg->mStrokeDasharray.Clear(); // count number of values const nsCSSValueList *value = strokeDasharrayValue->GetListValue(); uint32_t strokeDasharrayLength = ListLength(value); MOZ_ASSERT(strokeDasharrayLength != 0, "no dasharray items"); svg->mStrokeDasharray.SetLength(strokeDasharrayLength); uint32_t i = 0; while (nullptr != value) { SetCoord(value->mValue, svg->mStrokeDasharray[i++], nsStyleCoord(), SETCOORD_LP | SETCOORD_FACTOR, aContext, mPresContext, conditions); value = value->mNext; } break; } default: MOZ_ASSERT(false, "unrecognized dasharray unit"); } // stroke-dashoffset: <dashoffset>, inherit const nsCSSValue *strokeDashoffsetValue = aRuleData->ValueForStrokeDashoffset(); svg->SetStrokeDashoffsetFromObject( strokeDashoffsetValue->GetUnit() == eCSSUnit_Enumerated && strokeDashoffsetValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE); if (svg->StrokeDashoffsetFromObject()) { svg->mStrokeDashoffset.SetCoordValue(0); } else { SetCoord(*aRuleData->ValueForStrokeDashoffset(), svg->mStrokeDashoffset, parentSVG->mStrokeDashoffset, SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_INITIAL_ZERO | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); } // stroke-linecap: enum, inherit, initial SetValue(*aRuleData->ValueForStrokeLinecap(), svg->mStrokeLinecap, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mStrokeLinecap, NS_STYLE_STROKE_LINECAP_BUTT); // stroke-linejoin: enum, inherit, initial SetValue(*aRuleData->ValueForStrokeLinejoin(), svg->mStrokeLinejoin, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mStrokeLinejoin, NS_STYLE_STROKE_LINEJOIN_MITER); // stroke-miterlimit: <miterlimit>, inherit SetFactor(*aRuleData->ValueForStrokeMiterlimit(), svg->mStrokeMiterlimit, conditions, parentSVG->mStrokeMiterlimit, 4.0f, SETFCT_UNSET_INHERIT); // stroke-opacity: nsStyleSVGOpacitySource contextStrokeOpacity = svg->StrokeOpacitySource(); SetSVGOpacity(*aRuleData->ValueForStrokeOpacity(), svg->mStrokeOpacity, contextStrokeOpacity, conditions, parentSVG->mStrokeOpacity, parentSVG->StrokeOpacitySource()); svg->SetStrokeOpacitySource(contextStrokeOpacity); // stroke-width: const nsCSSValue* strokeWidthValue = aRuleData->ValueForStrokeWidth(); switch (strokeWidthValue->GetUnit()) { case eCSSUnit_Enumerated: MOZ_ASSERT(strokeWidthValue->GetIntValue() == NS_STYLE_STROKE_PROP_CONTEXT_VALUE, "Unrecognized keyword for stroke-width"); svg->SetStrokeWidthFromObject(true); svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); break; case eCSSUnit_Initial: svg->SetStrokeWidthFromObject(false); svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1)); break; default: svg->SetStrokeWidthFromObject(false); SetCoord(*strokeWidthValue, svg->mStrokeWidth, parentSVG->mStrokeWidth, SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_UNSET_INHERIT, aContext, mPresContext, conditions); } // text-anchor: enum, inherit, initial SetValue(*aRuleData->ValueForTextAnchor(), svg->mTextAnchor, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INHERIT, parentSVG->mTextAnchor, NS_STYLE_TEXT_ANCHOR_START); COMPUTE_END_INHERITED(SVG, svg) } static already_AddRefed<StyleBasicShape> GetStyleBasicShapeFromCSSValue(const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { RefPtr<StyleBasicShape> basicShape; nsCSSValue::Array* shapeFunction = aValue.GetArrayValue(); nsCSSKeyword functionName = (nsCSSKeyword)shapeFunction->Item(0).GetIntValue(); if (functionName == eCSSKeyword_polygon) { MOZ_ASSERT(!basicShape, "did not expect value"); basicShape = new StyleBasicShape(StyleBasicShapeType::Polygon); MOZ_ASSERT(shapeFunction->Count() > 1, "polygon has wrong number of arguments"); size_t j = 1; if (shapeFunction->Item(j).GetUnit() == eCSSUnit_Enumerated) { StyleFillRule rule; SetEnumValueHelper::SetEnumeratedValue(rule, shapeFunction->Item(j)); basicShape->SetFillRule(rule); ++j; } const int32_t mask = SETCOORD_PERCENT | SETCOORD_LENGTH | SETCOORD_STORE_CALC; const nsCSSValuePairList* curPair = shapeFunction->Item(j).GetPairListValue(); nsTArray<nsStyleCoord>& coordinates = basicShape->Coordinates(); while (curPair) { nsStyleCoord xCoord, yCoord; DebugOnly<bool> didSetCoordX = SetCoord(curPair->mXValue, xCoord, nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); coordinates.AppendElement(xCoord); MOZ_ASSERT(didSetCoordX, "unexpected x coordinate unit"); DebugOnly<bool> didSetCoordY = SetCoord(curPair->mYValue, yCoord, nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); coordinates.AppendElement(yCoord); MOZ_ASSERT(didSetCoordY, "unexpected y coordinate unit"); curPair = curPair->mNext; } } else if (functionName == eCSSKeyword_circle || functionName == eCSSKeyword_ellipse) { StyleBasicShapeType type = functionName == eCSSKeyword_circle ? StyleBasicShapeType::Circle : StyleBasicShapeType::Ellipse; MOZ_ASSERT(!basicShape, "did not expect value"); basicShape = new StyleBasicShape(type); const int32_t mask = SETCOORD_PERCENT | SETCOORD_LENGTH | SETCOORD_STORE_CALC | SETCOORD_ENUMERATED; size_t count = type == StyleBasicShapeType::Circle ? 2 : 3; MOZ_ASSERT(shapeFunction->Count() == count + 1, "unexpected arguments count"); MOZ_ASSERT(type == StyleBasicShapeType::Circle || (shapeFunction->Item(1).GetUnit() == eCSSUnit_Null) == (shapeFunction->Item(2).GetUnit() == eCSSUnit_Null), "ellipse should have two radii or none"); for (size_t j = 1; j < count; ++j) { const nsCSSValue& val = shapeFunction->Item(j); nsStyleCoord radius; if (val.GetUnit() != eCSSUnit_Null) { DebugOnly<bool> didSetRadius = SetCoord(val, radius, nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); MOZ_ASSERT(didSetRadius, "unexpected radius unit"); } else { radius.SetIntValue(NS_RADIUS_CLOSEST_SIDE, eStyleUnit_Enumerated); } basicShape->Coordinates().AppendElement(radius); } const nsCSSValue& positionVal = shapeFunction->Item(count); if (positionVal.GetUnit() == eCSSUnit_Array) { ComputePositionValue(aStyleContext, positionVal, basicShape->GetPosition(), aConditions); } else { MOZ_ASSERT(positionVal.GetUnit() == eCSSUnit_Null, "expected no value"); } } else if (functionName == eCSSKeyword_inset) { MOZ_ASSERT(!basicShape, "did not expect value"); basicShape = new StyleBasicShape(StyleBasicShapeType::Inset); MOZ_ASSERT(shapeFunction->Count() == 6, "inset function has wrong number of arguments"); MOZ_ASSERT(shapeFunction->Item(1).GetUnit() != eCSSUnit_Null, "no shape arguments defined"); const int32_t mask = SETCOORD_PERCENT | SETCOORD_LENGTH | SETCOORD_STORE_CALC; nsTArray<nsStyleCoord>& coords = basicShape->Coordinates(); for (size_t j = 1; j <= 4; ++j) { const nsCSSValue& val = shapeFunction->Item(j); nsStyleCoord inset; // Fill missing values to get 4 at the end. if (val.GetUnit() == eCSSUnit_Null) { if (j == 4) { inset = coords[1]; } else { MOZ_ASSERT(j != 1, "first argument not specified"); inset = coords[0]; } } else { DebugOnly<bool> didSetInset = SetCoord(val, inset, nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); MOZ_ASSERT(didSetInset, "unexpected inset unit"); } coords.AppendElement(inset); } nsStyleCorners& insetRadius = basicShape->GetRadius(); if (shapeFunction->Item(5).GetUnit() == eCSSUnit_Array) { nsCSSValue::Array* radiiArray = shapeFunction->Item(5).GetArrayValue(); NS_FOR_CSS_FULL_CORNERS(corner) { int cx = NS_FULL_TO_HALF_CORNER(corner, false); int cy = NS_FULL_TO_HALF_CORNER(corner, true); const nsCSSValue& radius = radiiArray->Item(corner); nsStyleCoord coordX, coordY; DebugOnly<bool> didSetRadii = SetPairCoords(radius, coordX, coordY, nsStyleCoord(), nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); MOZ_ASSERT(didSetRadii, "unexpected radius unit"); insetRadius.Set(cx, coordX); insetRadius.Set(cy, coordY); } } else { MOZ_ASSERT(shapeFunction->Item(5).GetUnit() == eCSSUnit_Null, "unexpected value"); // Initialize border-radius nsStyleCoord zero; zero.SetCoordValue(0); NS_FOR_CSS_HALF_CORNERS(j) { insetRadius.Set(j, zero); } } } else { NS_NOTREACHED("unexpected basic shape function"); } return basicShape.forget(); } template<typename ReferenceBox> static void SetStyleShapeSourceToCSSValue( StyleShapeSource<ReferenceBox>* aShapeSource, const nsCSSValue* aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { MOZ_ASSERT(aValue->GetUnit() == eCSSUnit_Array, "expected a basic shape or reference box"); const nsCSSValue::Array* array = aValue->GetArrayValue(); MOZ_ASSERT(array->Count() == 1 || array->Count() == 2, "Expect one or both of a shape function and a reference box"); ReferenceBox referenceBox = ReferenceBox::NoBox; RefPtr<StyleBasicShape> basicShape; for (size_t i = 0; i < array->Count(); ++i) { const nsCSSValue& item = array->Item(i); if (item.GetUnit() == eCSSUnit_Enumerated) { referenceBox = static_cast<ReferenceBox>(item.GetIntValue()); } else if (item.GetUnit() == eCSSUnit_Function) { basicShape = GetStyleBasicShapeFromCSSValue(item, aStyleContext, aPresContext, aConditions); } else { MOZ_ASSERT_UNREACHABLE("Unexpected unit!"); return; } } if (basicShape) { aShapeSource->SetBasicShape(basicShape, referenceBox); } else { aShapeSource->SetReferenceBox(referenceBox); } } // Returns true if the nsStyleFilter was successfully set using the nsCSSValue. static bool SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, const nsCSSValue& aValue, nsStyleContext* aStyleContext, nsPresContext* aPresContext, RuleNodeCacheConditions& aConditions) { nsCSSUnit unit = aValue.GetUnit(); if (unit == eCSSUnit_URL) { return aStyleFilter->SetURL(aValue.GetURLStructValue()); } MOZ_ASSERT(unit == eCSSUnit_Function, "expected a filter function"); nsCSSValue::Array* filterFunction = aValue.GetArrayValue(); nsCSSKeyword functionName = (nsCSSKeyword)filterFunction->Item(0).GetIntValue(); int32_t type; DebugOnly<bool> foundKeyword = nsCSSProps::FindKeyword(functionName, nsCSSProps::kFilterFunctionKTable, type); MOZ_ASSERT(foundKeyword, "unknown filter type"); if (type == NS_STYLE_FILTER_DROP_SHADOW) { RefPtr<nsCSSShadowArray> shadowArray = GetShadowData( filterFunction->Item(1).GetListValue(), aStyleContext, false, aPresContext, aConditions); aStyleFilter->SetDropShadow(shadowArray); return true; } int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR; if (type == NS_STYLE_FILTER_BLUR) { mask = SETCOORD_LENGTH | SETCOORD_CALC_LENGTH_ONLY | SETCOORD_CALC_CLAMP_NONNEGATIVE; } else if (type == NS_STYLE_FILTER_HUE_ROTATE) { mask = SETCOORD_ANGLE; } MOZ_ASSERT(filterFunction->Count() == 2, "all filter functions should have exactly one argument"); nsCSSValue& arg = filterFunction->Item(1); nsStyleCoord filterParameter; DebugOnly<bool> didSetCoord = SetCoord(arg, filterParameter, nsStyleCoord(), mask, aStyleContext, aPresContext, aConditions); aStyleFilter->SetFilterParameter(filterParameter, type); MOZ_ASSERT(didSetCoord, "unexpected unit"); return true; } const void* nsRuleNode::ComputeSVGResetData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(SVGReset, svgReset, parentSVGReset) // stop-color: const nsCSSValue* stopColorValue = aRuleData->ValueForStopColor(); if (eCSSUnit_Initial == stopColorValue->GetUnit() || eCSSUnit_Unset == stopColorValue->GetUnit()) { svgReset->mStopColor = NS_RGB(0, 0, 0); } else { SetColor(*stopColorValue, parentSVGReset->mStopColor, mPresContext, aContext, svgReset->mStopColor, conditions); } // flood-color: const nsCSSValue* floodColorValue = aRuleData->ValueForFloodColor(); if (eCSSUnit_Initial == floodColorValue->GetUnit() || eCSSUnit_Unset == floodColorValue->GetUnit()) { svgReset->mFloodColor = NS_RGB(0, 0, 0); } else { SetColor(*floodColorValue, parentSVGReset->mFloodColor, mPresContext, aContext, svgReset->mFloodColor, conditions); } // lighting-color: const nsCSSValue* lightingColorValue = aRuleData->ValueForLightingColor(); if (eCSSUnit_Initial == lightingColorValue->GetUnit() || eCSSUnit_Unset == lightingColorValue->GetUnit()) { svgReset->mLightingColor = NS_RGB(255, 255, 255); } else { SetColor(*lightingColorValue, parentSVGReset->mLightingColor, mPresContext, aContext, svgReset->mLightingColor, conditions); } // clip-path: url, <basic-shape> || <geometry-box>, none, inherit const nsCSSValue* clipPathValue = aRuleData->ValueForClipPath(); switch (clipPathValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: svgReset->mClipPath = StyleClipPath(); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); svgReset->mClipPath = parentSVGReset->mClipPath; break; case eCSSUnit_URL: { svgReset->mClipPath = StyleClipPath(); svgReset->mClipPath.SetURL(clipPathValue->GetURLStructValue()); break; } case eCSSUnit_Array: { svgReset->mClipPath = StyleClipPath(); SetStyleShapeSourceToCSSValue(&svgReset->mClipPath, clipPathValue, aContext, mPresContext, conditions); break; } default: NS_NOTREACHED("unexpected unit"); } // stop-opacity: SetFactor(*aRuleData->ValueForStopOpacity(), svgReset->mStopOpacity, conditions, parentSVGReset->mStopOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // flood-opacity: SetFactor(*aRuleData->ValueForFloodOpacity(), svgReset->mFloodOpacity, conditions, parentSVGReset->mFloodOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // dominant-baseline: enum, inherit, initial SetValue(*aRuleData->ValueForDominantBaseline(), svgReset->mDominantBaseline, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentSVGReset->mDominantBaseline, NS_STYLE_DOMINANT_BASELINE_AUTO); // vector-effect: enum, inherit, initial SetValue(*aRuleData->ValueForVectorEffect(), svgReset->mVectorEffect, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentSVGReset->mVectorEffect, NS_STYLE_VECTOR_EFFECT_NONE); // mask-type: enum, inherit, initial SetValue(*aRuleData->ValueForMaskType(), svgReset->mMaskType, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentSVGReset->mMaskType, NS_STYLE_MASK_TYPE_LUMINANCE); uint32_t maxItemCount = 1; bool rebuild = false; // mask-image: none | <url> | <image-list> | <element-reference> | <gradient> nsStyleImage initialImage; SetImageLayerList(aContext, *aRuleData->ValueForMaskImage(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mImage, initialImage, parentSVGReset->mMask.mImageCount, svgReset->mMask.mImageCount, maxItemCount, rebuild, conditions); SetImageLayerList(aContext, *aRuleData->ValueForMaskImage(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mSourceURI, RefPtr<css::URLValueData>(), parentSVGReset->mMask.mImageCount, svgReset->mMask.mImageCount, maxItemCount, rebuild, conditions); // mask-repeat: enum, inherit, initial [pair list] nsStyleImageLayers::Repeat initialRepeat; initialRepeat.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForMaskRepeat(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mRepeat, initialRepeat, parentSVGReset->mMask.mRepeatCount, svgReset->mMask.mRepeatCount, maxItemCount, rebuild, conditions); // mask-clip: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForMaskClip(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mClip, StyleGeometryBox::Border, parentSVGReset->mMask.mClipCount, svgReset->mMask.mClipCount, maxItemCount, rebuild, conditions); // mask-origin: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForMaskOrigin(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mOrigin, StyleGeometryBox::Border, parentSVGReset->mMask.mOriginCount, svgReset->mMask.mOriginCount, maxItemCount, rebuild, conditions); // mask-position-x/y: enum, length, percent (flags), inherit [list] Position::Coord initialPositionCoord; initialPositionCoord.mPercent = 0.0f; initialPositionCoord.mLength = 0; initialPositionCoord.mHasPercent = true; SetImageLayerPositionCoordList( aContext, *aRuleData->ValueForMaskPositionX(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &Position::mXPosition, initialPositionCoord, parentSVGReset->mMask.mPositionXCount, svgReset->mMask.mPositionXCount, maxItemCount, rebuild, conditions); SetImageLayerPositionCoordList( aContext, *aRuleData->ValueForMaskPositionY(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &Position::mYPosition, initialPositionCoord, parentSVGReset->mMask.mPositionYCount, svgReset->mMask.mPositionYCount, maxItemCount, rebuild, conditions); // mask-size: enum, length, auto, inherit, initial [pair list] nsStyleImageLayers::Size initialSize; initialSize.SetInitialValues(); SetImageLayerPairList(aContext, *aRuleData->ValueForMaskSize(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mSize, initialSize, parentSVGReset->mMask.mSizeCount, svgReset->mMask.mSizeCount, maxItemCount, rebuild, conditions); // mask-mode: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForMaskMode(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mMaskMode, uint8_t(NS_STYLE_MASK_MODE_MATCH_SOURCE), parentSVGReset->mMask.mMaskModeCount, svgReset->mMask.mMaskModeCount, maxItemCount, rebuild, conditions); // mask-composite: enum, inherit, initial [list] SetImageLayerList(aContext, *aRuleData->ValueForMaskComposite(), svgReset->mMask.mLayers, parentSVGReset->mMask.mLayers, &nsStyleImageLayers::Layer::mComposite, uint8_t(NS_STYLE_MASK_COMPOSITE_ADD), parentSVGReset->mMask.mCompositeCount, svgReset->mMask.mCompositeCount, maxItemCount, rebuild, conditions); if (rebuild) { FillAllBackgroundLists(svgReset->mMask, maxItemCount); } COMPUTE_END_RESET(SVGReset, svgReset) } const void* nsRuleNode::ComputeVariablesData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_INHERITED(Variables, variables, parentVariables) MOZ_ASSERT(aRuleData->mVariables, "shouldn't be in ComputeVariablesData if there were no variable " "declarations specified"); CSSVariableResolver resolver(&variables->mVariables); resolver.Resolve(&parentVariables->mVariables, aRuleData->mVariables); conditions.SetUncacheable(); COMPUTE_END_INHERITED(Variables, variables) } const void* nsRuleNode::ComputeEffectsData(void* aStartStruct, const nsRuleData* aRuleData, nsStyleContext* aContext, nsRuleNode* aHighestNode, const RuleDetail aRuleDetail, const RuleNodeCacheConditions aConditions) { COMPUTE_START_RESET(Effects, effects, parentEffects) // filter: url, none, inherit const nsCSSValue* filterValue = aRuleData->ValueForFilter(); switch (filterValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_None: case eCSSUnit_Initial: case eCSSUnit_Unset: effects->mFilters.Clear(); break; case eCSSUnit_Inherit: conditions.SetUncacheable(); effects->mFilters = parentEffects->mFilters; break; case eCSSUnit_List: case eCSSUnit_ListDep: { effects->mFilters.Clear(); const nsCSSValueList* cur = filterValue->GetListValue(); while (cur) { nsStyleFilter styleFilter; if (!SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, mPresContext, conditions)) { effects->mFilters.Clear(); break; } MOZ_ASSERT(styleFilter.GetType() != NS_STYLE_FILTER_NONE, "filter should be set"); effects->mFilters.AppendElement(styleFilter); cur = cur->mNext; } break; } default: NS_NOTREACHED("unexpected unit"); } // box-shadow: none, list, inherit, initial const nsCSSValue* boxShadowValue = aRuleData->ValueForBoxShadow(); switch (boxShadowValue->GetUnit()) { case eCSSUnit_Null: break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_None: effects->mBoxShadow = nullptr; break; case eCSSUnit_Inherit: effects->mBoxShadow = parentEffects->mBoxShadow; conditions.SetUncacheable(); break; case eCSSUnit_List: case eCSSUnit_ListDep: effects->mBoxShadow = GetShadowData(boxShadowValue->GetListValue(), aContext, true, mPresContext, conditions); break; default: MOZ_ASSERT(false, "unrecognized shadow unit"); } // clip property: length, auto, inherit const nsCSSValue* clipValue = aRuleData->ValueForClip(); switch (clipValue->GetUnit()) { case eCSSUnit_Inherit: conditions.SetUncacheable(); effects->mClipFlags = parentEffects->mClipFlags; effects->mClip = parentEffects->mClip; break; case eCSSUnit_Initial: case eCSSUnit_Unset: case eCSSUnit_Auto: effects->mClipFlags = NS_STYLE_CLIP_AUTO; effects->mClip.SetRect(0,0,0,0); break; case eCSSUnit_Null: break; case eCSSUnit_Rect: { const nsCSSRect& clipRect = clipValue->GetRectValue(); effects->mClipFlags = NS_STYLE_CLIP_RECT; if (clipRect.mTop.GetUnit() == eCSSUnit_Auto) { effects->mClip.y = 0; effects->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO; } else if (clipRect.mTop.IsLengthUnit()) { effects->mClip.y = CalcLength(clipRect.mTop, aContext, mPresContext, conditions); } if (clipRect.mBottom.GetUnit() == eCSSUnit_Auto) { // Setting to NS_MAXSIZE for the 'auto' case ensures that // the clip rect is nonempty. It is important that mClip be // nonempty if the actual clip rect could be nonempty. effects->mClip.height = NS_MAXSIZE; effects->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO; } else if (clipRect.mBottom.IsLengthUnit()) { effects->mClip.height = CalcLength(clipRect.mBottom, aContext, mPresContext, conditions) - effects->mClip.y; } if (clipRect.mLeft.GetUnit() == eCSSUnit_Auto) { effects->mClip.x = 0; effects->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO; } else if (clipRect.mLeft.IsLengthUnit()) { effects->mClip.x = CalcLength(clipRect.mLeft, aContext, mPresContext, conditions); } if (clipRect.mRight.GetUnit() == eCSSUnit_Auto) { // Setting to NS_MAXSIZE for the 'auto' case ensures that // the clip rect is nonempty. It is important that mClip be // nonempty if the actual clip rect could be nonempty. effects->mClip.width = NS_MAXSIZE; effects->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO; } else if (clipRect.mRight.IsLengthUnit()) { effects->mClip.width = CalcLength(clipRect.mRight, aContext, mPresContext, conditions) - effects->mClip.x; } break; } default: MOZ_ASSERT(false, "unrecognized clip unit"); } // opacity: factor, inherit, initial SetFactor(*aRuleData->ValueForOpacity(), effects->mOpacity, conditions, parentEffects->mOpacity, 1.0f, SETFCT_OPACITY | SETFCT_UNSET_INITIAL); // mix-blend-mode: enum, inherit, initial SetValue(*aRuleData->ValueForMixBlendMode(), effects->mMixBlendMode, conditions, SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL, parentEffects->mMixBlendMode, NS_STYLE_BLEND_NORMAL); COMPUTE_END_RESET(Effects, effects) } const void* nsRuleNode::GetStyleData(nsStyleStructID aSID, nsStyleContext* aContext, bool aComputeData) { NS_ASSERTION(IsUsedDirectly(), "if we ever call this on rule nodes that aren't used " "directly, we should adjust handling of mDependentBits " "in some way."); MOZ_ASSERT(!aContext->GetCachedStyleData(aSID), "style context should not have cached data for struct"); const void *data; // Never use cached data for animated style inside a pseudo-element; // see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto. if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { data = mStyleData.GetStyleData(aSID, aContext, aComputeData); if (MOZ_LIKELY(data != nullptr)) { // For inherited structs, mark the struct (which will be set on // the context by our caller) as not being owned by the context. if (!nsCachedStyleData::IsReset(aSID)) { aContext->AddStyleBit(nsCachedStyleData::GetBitForSID(aSID)); } else if (HasAnimationData()) { // If we have animation data, the struct should be cached on the style // context so that we can peek the struct. // See comment in AnimValuesStyleRule::MapRuleInfoInto. StoreStyleOnContext(aContext, aSID, const_cast<void*>(data)); } return data; // We have a fully specified struct. Just return it. } } if (MOZ_UNLIKELY(!aComputeData)) return nullptr; // Nothing is cached. We'll have to delve further and examine our rules. data = WalkRuleTree(aSID, aContext); MOZ_ASSERT(data, "should have aborted on out-of-memory"); return data; } void nsRuleNode::GetDiscretelyAnimatedCSSValue(nsCSSPropertyID aProperty, nsCSSValue* aValue) { for (nsRuleNode* node = this; node; node = node->GetParent()) { nsIStyleRule* rule = node->GetRule(); if (!rule) { continue; } if (rule->GetDiscretelyAnimatedCSSValue(aProperty, aValue)) { return; } } } /* static */ bool nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, uint32_t ruleTypeMask, bool aAuthorColorsAllowed) { #ifdef MOZ_STYLO if (aStyleContext->StyleSource().IsServoComputedValues()) { NS_WARNING("stylo: nsRuleNode::HasAuthorSpecifiedRules not implemented"); return true; } #endif uint32_t inheritBits = 0; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) inheritBits |= NS_STYLE_INHERIT_BIT(Background); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) inheritBits |= NS_STYLE_INHERIT_BIT(Border); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) inheritBits |= NS_STYLE_INHERIT_BIT(Padding); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) inheritBits |= NS_STYLE_INHERIT_BIT(Text); // properties in the SIDS, whether or not we care about them size_t nprops = 0, backgroundOffset, borderOffset, paddingOffset, textShadowOffset; // We put the reset properties the start of the nsCSSValue array.... if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { backgroundOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Background); } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { borderOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Border); } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { paddingOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Padding); } // ...and the inherited properties at the end of the array. size_t inheritedOffset = nprops; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { textShadowOffset = nprops; nprops += nsCSSProps::PropertyCountInStruct(eStyleStruct_Text); } void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); AutoCSSValueArray dataArray(dataStorage, nprops); /* We're relying on the use of |aStyleContext| not mutating it! */ nsRuleData ruleData(inheritBits, dataArray.get(), aStyleContext->PresContext(), aStyleContext); if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { ruleData.mValueOffsets[eStyleStruct_Background] = backgroundOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { ruleData.mValueOffsets[eStyleStruct_Border] = borderOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { ruleData.mValueOffsets[eStyleStruct_Padding] = paddingOffset; } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { ruleData.mValueOffsets[eStyleStruct_Text] = textShadowOffset; } static const nsCSSPropertyID backgroundValues[] = { eCSSProperty_background_color, eCSSProperty_background_image, }; static const nsCSSPropertyID borderValues[] = { eCSSProperty_border_top_color, eCSSProperty_border_top_style, eCSSProperty_border_top_width, eCSSProperty_border_right_color, eCSSProperty_border_right_style, eCSSProperty_border_right_width, eCSSProperty_border_bottom_color, eCSSProperty_border_bottom_style, eCSSProperty_border_bottom_width, eCSSProperty_border_left_color, eCSSProperty_border_left_style, eCSSProperty_border_left_width, eCSSProperty_border_top_left_radius, eCSSProperty_border_top_right_radius, eCSSProperty_border_bottom_right_radius, eCSSProperty_border_bottom_left_radius, }; static const nsCSSPropertyID paddingValues[] = { eCSSProperty_padding_top, eCSSProperty_padding_right, eCSSProperty_padding_bottom, eCSSProperty_padding_left, }; static const nsCSSPropertyID textShadowValues[] = { eCSSProperty_text_shadow }; // Number of properties we care about size_t nValues = 0; nsCSSValue* values[MOZ_ARRAY_LENGTH(backgroundValues) + MOZ_ARRAY_LENGTH(borderValues) + MOZ_ARRAY_LENGTH(paddingValues) + MOZ_ARRAY_LENGTH(textShadowValues)]; nsCSSPropertyID properties[MOZ_ARRAY_LENGTH(backgroundValues) + MOZ_ARRAY_LENGTH(borderValues) + MOZ_ARRAY_LENGTH(paddingValues) + MOZ_ARRAY_LENGTH(textShadowValues)]; if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BACKGROUND) { for (uint32_t i = 0, i_end = ArrayLength(backgroundValues); i < i_end; ++i) { properties[nValues] = backgroundValues[i]; values[nValues++] = ruleData.ValueFor(backgroundValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_BORDER) { for (uint32_t i = 0, i_end = ArrayLength(borderValues); i < i_end; ++i) { properties[nValues] = borderValues[i]; values[nValues++] = ruleData.ValueFor(borderValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_PADDING) { for (uint32_t i = 0, i_end = ArrayLength(paddingValues); i < i_end; ++i) { properties[nValues] = paddingValues[i]; values[nValues++] = ruleData.ValueFor(paddingValues[i]); } } if (ruleTypeMask & NS_AUTHOR_SPECIFIED_TEXT_SHADOW) { for (uint32_t i = 0, i_end = ArrayLength(textShadowValues); i < i_end; ++i) { properties[nValues] = textShadowValues[i]; values[nValues++] = ruleData.ValueFor(textShadowValues[i]); } } nsStyleContext* styleContext = aStyleContext; // We need to be careful not to count styles covered up by user-important or // UA-important declarations. But we do want to catch explicit inherit // styling in those and check our parent style context to see whether we have // user styling for those properties. Note that we don't care here about // inheritance due to lack of a specified value, since all the properties we // care about are reset properties. bool haveExplicitUAInherit; do { haveExplicitUAInherit = false; for (nsRuleNode* ruleNode = styleContext->RuleNode(); ruleNode; ruleNode = ruleNode->GetParent()) { nsIStyleRule *rule = ruleNode->GetRule(); if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); rule->MapRuleInfoInto(&ruleData); if (ruleData.mLevel == SheetType::Agent || ruleData.mLevel == SheetType::User) { // This is a rule whose effect we want to ignore, so if any of // the properties we care about were set, set them to the dummy // value that they'll never otherwise get. for (uint32_t i = 0; i < nValues; ++i) { nsCSSUnit unit = values[i]->GetUnit(); if (unit != eCSSUnit_Null && unit != eCSSUnit_Dummy && unit != eCSSUnit_DummyInherit) { if (unit == eCSSUnit_Inherit || (i >= inheritedOffset && unit == eCSSUnit_Unset)) { haveExplicitUAInherit = true; values[i]->SetDummyInheritValue(); } else { values[i]->SetDummyValue(); } } } } else { // If any of the values we care about was set by the above rule, // we have author style. for (uint32_t i = 0; i < nValues; ++i) { if (values[i]->GetUnit() != eCSSUnit_Null && values[i]->GetUnit() != eCSSUnit_Dummy && // see above values[i]->GetUnit() != eCSSUnit_DummyInherit) { // If author colors are not allowed, only claim to have // author-specified rules if we're looking at a non-color // property or if we're looking at the background color and it's // set to transparent. Anything else should get set to a dummy // value instead. if (aAuthorColorsAllowed || !nsCSSProps::PropHasFlags(properties[i], CSS_PROPERTY_IGNORED_WHEN_COLORS_DISABLED) || (properties[i] == eCSSProperty_background_color && !values[i]->IsNonTransparentColor())) { return true; } values[i]->SetDummyValue(); } } } } } if (haveExplicitUAInherit) { // reset all the eCSSUnit_Null values to eCSSUnit_Dummy (since they're // not styled by the author, or by anyone else), and then reset all the // eCSSUnit_DummyInherit values to eCSSUnit_Null (so we will be able to // detect them being styled by the author) and move up to our parent // style context. for (uint32_t i = 0; i < nValues; ++i) if (values[i]->GetUnit() == eCSSUnit_Null) values[i]->SetDummyValue(); for (uint32_t i = 0; i < nValues; ++i) if (values[i]->GetUnit() == eCSSUnit_DummyInherit) values[i]->Reset(); styleContext = styleContext->GetParent(); } } while (haveExplicitUAInherit && styleContext); return false; } /* static */ void nsRuleNode::ComputePropertiesOverridingAnimation( const nsTArray<nsCSSPropertyID>& aProperties, nsStyleContext* aStyleContext, nsCSSPropertyIDSet& aPropertiesOverridden) { /* * Set up an nsRuleData with all the structs needed for all of the * properties in aProperties. */ uint32_t structBits = 0; size_t nprops = 0; size_t offsets[nsStyleStructID_Length]; for (size_t propIdx = 0, propEnd = aProperties.Length(); propIdx < propEnd; ++propIdx) { nsCSSPropertyID prop = aProperties[propIdx]; nsStyleStructID sid = nsCSSProps::kSIDTable[prop]; uint32_t bit = nsCachedStyleData::GetBitForSID(sid); if (!(structBits & bit)) { structBits |= bit; offsets[sid] = nprops; nprops += nsCSSProps::PropertyCountInStruct(sid); } } void* dataStorage = alloca(nprops * sizeof(nsCSSValue)); AutoCSSValueArray dataArray(dataStorage, nprops); // We're relying on the use of |aStyleContext| not mutating it! nsRuleData ruleData(structBits, dataArray.get(), aStyleContext->PresContext(), aStyleContext); for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) { if (structBits & nsCachedStyleData::GetBitForSID(sid)) { ruleData.mValueOffsets[sid] = offsets[sid]; } } /* * Actually walk up the rule tree until we're someplace less * specific than animations. */ for (nsRuleNode* ruleNode = aStyleContext->RuleNode(); ruleNode; ruleNode = ruleNode->GetParent()) { nsIStyleRule *rule = ruleNode->GetRule(); if (rule) { ruleData.mLevel = ruleNode->GetLevel(); ruleData.mIsImportantRule = ruleNode->IsImportantRule(); // Transitions are the only non-!important level overriding // animations in the cascade ordering. They also don't actually // override animations, since transitions are suppressed when both // are present. And since we might not have called // UpdateCascadeResults (which updates when they are suppressed // due to the presence of animations for the same element and // property) for transitions yet (which will make their // MapRuleInfoInto skip the properties that are currently // animating), we should skip them explicitly. if (ruleData.mLevel == SheetType::Transition) { continue; } if (!ruleData.mIsImportantRule) { // We're now equal to or less than the animation level; stop. break; } rule->MapRuleInfoInto(&ruleData); } } /* * Fill in which properties were overridden. */ for (size_t propIdx = 0, propEnd = aProperties.Length(); propIdx < propEnd; ++propIdx) { nsCSSPropertyID prop = aProperties[propIdx]; if (ruleData.ValueFor(prop)->GetUnit() != eCSSUnit_Null) { aPropertiesOverridden.AddProperty(prop); } } } /* static */ bool nsRuleNode::ComputeColor(const nsCSSValue& aValue, nsPresContext* aPresContext, nsStyleContext* aStyleContext, nscolor& aResult) { MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Inherit, "aValue shouldn't have eCSSUnit_Inherit"); MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Initial, "aValue shouldn't have eCSSUnit_Initial"); MOZ_ASSERT(aValue.GetUnit() != eCSSUnit_Unset, "aValue shouldn't have eCSSUnit_Unset"); RuleNodeCacheConditions conditions; bool ok = SetColor(aValue, NS_RGB(0, 0, 0), aPresContext, aStyleContext, aResult, conditions); MOZ_ASSERT(ok || !(aPresContext && aStyleContext)); return ok; } /* static */ bool nsRuleNode::ParentHasPseudoElementData(nsStyleContext* aContext) { nsStyleContext* parent = aContext->GetParent(); return parent && parent->HasPseudoElementData(); } /* static */ void nsRuleNode::StoreStyleOnContext(nsStyleContext* aContext, nsStyleStructID aSID, void* aStruct) { aContext->AddStyleBit(nsCachedStyleData::GetBitForSID(aSID)); aContext->SetStyle(aSID, aStruct); } #ifdef DEBUG bool nsRuleNode::ContextHasCachedData(nsStyleContext* aContext, nsStyleStructID aSID) { return !!aContext->GetCachedStyleData(aSID); } #endif