gecko-dev/layout/style/nsRuleNode.cpp

8831 lines
343 KiB
C++

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