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