gecko/layout/style/nsRuleNode.cpp
vladimir@pobox.com 5b4f07a7fa backing out
2008-04-20 01:24:42 -07:00

5220 lines
200 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Original Author: David W. Hyatt (hyatt@netscape.com)
* Daniel Glazman <glazman@netscape.com>
* Roger B. Sidje <rbs@maths.uq.edu.au>
* Mats Palmgren <mats.palmgren@bredband.net>
* L. David Baron <dbaron@dbaron.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* 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 "nscore.h"
#include "nsIServiceManager.h"
#include "nsIDeviceContext.h"
#include "nsILookAndFeel.h"
#include "nsIPresShell.h"
#include "nsIFontMetrics.h"
#include "nsStyleUtil.h"
#include "nsCSSPseudoElements.h"
#include "nsThemeConstants.h"
#include "nsITheme.h"
#include "pldhash.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
#include "nsSize.h"
#include "imgIRequest.h"
#include "nsRuleData.h"
#include "nsILanguageAtomService.h"
#include "nsIStyleRule.h"
#include "nsBidiUtils.h"
/*
* For storage of an |nsRuleNode|'s children in a linked list.
*/
struct nsRuleList {
nsRuleNode* mRuleNode;
nsRuleList* mNext;
public:
nsRuleList(nsRuleNode* aNode, nsRuleList* aNext= nsnull) {
MOZ_COUNT_CTOR(nsRuleList);
mRuleNode = aNode;
mNext = aNext;
}
~nsRuleList() {
MOZ_COUNT_DTOR(nsRuleList);
mRuleNode->Destroy();
if (mNext)
mNext->Destroy(mNext->mRuleNode->mPresContext);
}
void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW {
return aContext->AllocateFromShell(sz);
}
void operator delete(void* aPtr) {} // Does nothing. The arena will free us up when the rule tree
// dies.
void Destroy(nsPresContext* aContext) {
this->~nsRuleList();
aContext->FreeToShell(sizeof(nsRuleList), this);
}
// Destroy this node, but not its rule node or the rest of the list.
nsRuleList* DestroySelf(nsPresContext* aContext) {
nsRuleList *next = mNext;
MOZ_COUNT_DTOR(nsRuleList); // hack
aContext->FreeToShell(sizeof(nsRuleList), this);
return next;
}
};
/*
* For storage of an |nsRuleNode|'s children in a PLDHashTable.
*/
struct ChildrenHashEntry : public PLDHashEntryHdr {
// key is |mRuleNode->GetKey()|
nsRuleNode *mRuleNode;
};
/* static */ PR_CALLBACK PLDHashNumber
nsRuleNode::ChildrenHashHashKey(PLDHashTable *aTable, const void *aKey)
{
const nsRuleNode::Key *key =
static_cast<const nsRuleNode::Key*>(aKey);
// Disagreement on importance and level for the same rule is extremely
// rare, so hash just on the rule.
return PL_DHashVoidPtrKeyStub(aTable, key->mRule);
}
/* static */ PR_CALLBACK PRBool
nsRuleNode::ChildrenHashMatchEntry(PLDHashTable *aTable,
const PLDHashEntryHdr *aHdr,
const void *aKey)
{
const ChildrenHashEntry *entry =
static_cast<const ChildrenHashEntry*>(aHdr);
const nsRuleNode::Key *key =
static_cast<const nsRuleNode::Key*>(aKey);
return entry->mRuleNode->GetKey() == *key;
}
/* static */ PLDHashTableOps
nsRuleNode::ChildrenHashOps = {
// It's probably better to allocate the table itself using malloc and
// free rather than the pres shell's arena because the table doesn't
// grow very often and the pres shell's arena doesn't recycle very
// large size allocations.
PL_DHashAllocTable,
PL_DHashFreeTable,
ChildrenHashHashKey,
ChildrenHashMatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub,
NULL
};
// EnsureBlockDisplay:
// - if the display value (argument) is not a block-type
// then we set it to a valid block display value
// - For enforcing the floated/positioned element CSS2 rules
static void EnsureBlockDisplay(PRUint8& display)
{
// see if the display value is already a block
switch (display) {
case NS_STYLE_DISPLAY_NONE :
// never change display:none *ever*
case NS_STYLE_DISPLAY_TABLE :
case NS_STYLE_DISPLAY_BLOCK :
case NS_STYLE_DISPLAY_LIST_ITEM :
// do not muck with these at all - already blocks
// This is equivalent to nsStyleDisplay::IsBlockOutside. (XXX Maybe we
// should just call that?)
break;
case NS_STYLE_DISPLAY_INLINE_TABLE :
// make inline tables into tables
display = NS_STYLE_DISPLAY_TABLE;
break;
default :
// make it a block
display = NS_STYLE_DISPLAY_BLOCK;
}
}
// XXX This should really be done in the CSS parser.
static nsString& Unquote(nsString& aString)
{
PRUnichar start = aString.First();
PRUnichar end = aString.Last();
if ((start == end) &&
((start == PRUnichar('\"')) ||
(start == PRUnichar('\'')))) {
PRInt32 length = aString.Length();
aString.Truncate(length - 1);
aString.Cut(0, 1);
}
return aString;
}
static nscoord CalcLengthWith(const nsCSSValue& aValue,
nscoord aFontSize,
const nsStyleFont* aStyleFont,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
PRBool& aInherited)
{
NS_ASSERTION(aValue.IsLengthUnit(), "not a length unit");
NS_ASSERTION(aStyleFont || aStyleContext, "Must have style data");
NS_ASSERTION(aPresContext, "Must have prescontext");
if (aValue.IsFixedLengthUnit()) {
return aPresContext->TwipsToAppUnits(aValue.GetLengthTwips());
}
nsCSSUnit unit = aValue.GetUnit();
if (unit == eCSSUnit_Pixel) {
return nsPresContext::CSSPixelsToAppUnits(aValue.GetFloatValue());
}
// Common code for all units other than pixels:
aInherited = PR_TRUE;
if (!aStyleFont) {
aStyleFont = aStyleContext->GetStyleFont();
}
if (aFontSize == -1) {
// XXX Should this be aStyleFont->mSize instead to avoid taking minfontsize
// prefs into account?
aFontSize = aStyleFont->mFont.size;
}
switch (unit) {
case eCSSUnit_EM:
case eCSSUnit_Char: {
return NSToCoordRound(aValue.GetFloatValue() * float(aFontSize));
// XXX scale against font metrics height instead?
}
case eCSSUnit_EN: {
return NSToCoordRound((aValue.GetFloatValue() * float(aFontSize)) / 2.0f);
}
case eCSSUnit_XHeight: {
nsFont font = aStyleFont->mFont;
font.size = aFontSize;
nsCOMPtr<nsIFontMetrics> fm = aPresContext->GetMetricsFor(font);
nscoord xHeight;
fm->GetXHeight(xHeight);
return NSToCoordRound(aValue.GetFloatValue() * float(xHeight));
}
case eCSSUnit_CapHeight: {
NS_NOTYETIMPLEMENTED("cap height unit");
nscoord capHeight = ((aFontSize / 3) * 2); // XXX HACK!
return NSToCoordRound(aValue.GetFloatValue() * float(capHeight));
}
default:
NS_NOTREACHED("unexpected unit");
break;
}
return 0;
}
static nscoord CalcLength(const nsCSSValue& aValue,
nsStyleContext* aStyleContext,
nsPresContext* aPresContext,
PRBool& aInherited)
{
NS_ASSERTION(aStyleContext, "Must have style data");
return CalcLengthWith(aValue, -1, nsnull, aStyleContext, aPresContext, aInherited);
}
#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_LP (SETCOORD_LENGTH | SETCOORD_PERCENT)
#define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT)
#define SETCOORD_AH (SETCOORD_AUTO | SETCOORD_INHERIT)
#define SETCOORD_LAH (SETCOORD_AUTO | SETCOORD_LENGTH | SETCOORD_INHERIT)
#define SETCOORD_LPH (SETCOORD_LP | SETCOORD_INHERIT)
#define SETCOORD_LPAH (SETCOORD_LP | SETCOORD_AH)
#define SETCOORD_LPEH (SETCOORD_LP | SETCOORD_ENUMERATED | SETCOORD_INHERIT)
#define SETCOORD_LPAEH (SETCOORD_LPAH | SETCOORD_ENUMERATED)
#define SETCOORD_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 PR_TRUE
static PRBool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord,
const nsStyleCoord& aParentCoord,
PRInt32 aMask, nsStyleContext* aStyleContext,
nsPresContext* aPresContext, PRBool& aInherited)
{
PRBool result = PR_TRUE;
if (aValue.GetUnit() == eCSSUnit_Null) {
result = PR_FALSE;
}
else if (((aMask & SETCOORD_LENGTH) != 0) &&
(aValue.GetUnit() == eCSSUnit_Char)) {
aCoord.SetIntValue(NSToIntFloor(aValue.GetFloatValue()), eStyleUnit_Chars);
}
else if (((aMask & SETCOORD_LENGTH) != 0) &&
aValue.IsLengthUnit()) {
aCoord.SetCoordValue(CalcLength(aValue, aStyleContext, aPresContext, aInherited));
}
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_AUTO) != 0) &&
(aValue.GetUnit() == eCSSUnit_Auto)) {
aCoord.SetAutoValue();
}
else if (((aMask & SETCOORD_INHERIT) != 0) &&
(aValue.GetUnit() == eCSSUnit_Inherit)) {
aCoord = aParentCoord; // just inherit value from parent
aInherited = PR_TRUE;
}
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_INITIAL_AUTO) != 0) &&
(aValue.GetUnit() == eCSSUnit_Initial)) {
aCoord.SetAutoValue();
}
else if (((aMask & SETCOORD_INITIAL_ZERO) != 0) &&
(aValue.GetUnit() == eCSSUnit_Initial)) {
aCoord.SetCoordValue(0);
}
else if (((aMask & SETCOORD_INITIAL_NONE) != 0) &&
(aValue.GetUnit() == eCSSUnit_Initial)) {
aCoord.SetNoneValue();
}
else if (((aMask & SETCOORD_INITIAL_NORMAL) != 0) &&
(aValue.GetUnit() == eCSSUnit_Initial)) {
aCoord.SetNormalValue();
}
else {
result = PR_FALSE; // didn't set anything
}
return result;
}
static PRBool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
nsPresContext* aPresContext, nsStyleContext *aContext,
nscolor& aResult, PRBool& aInherited)
{
PRBool result = PR_FALSE;
nsCSSUnit unit = aValue.GetUnit();
if (eCSSUnit_Color == unit) {
aResult = aValue.GetColorValue();
result = PR_TRUE;
}
else if (eCSSUnit_String == unit) {
nsAutoString value;
aValue.GetStringValue(value);
nscolor rgba;
if (NS_ColorNameToRGB(value, &rgba)) {
aResult = rgba;
result = PR_TRUE;
}
}
else if (eCSSUnit_EnumColor == unit) {
PRInt32 intValue = aValue.GetIntValue();
if (0 <= intValue) {
nsILookAndFeel* look = aPresContext->LookAndFeel();
nsILookAndFeel::nsColorID colorID = (nsILookAndFeel::nsColorID) intValue;
if (NS_SUCCEEDED(look->GetColor(colorID, aResult))) {
result = PR_TRUE;
}
}
else {
switch (intValue) {
case NS_COLOR_MOZ_HYPERLINKTEXT:
aResult = aPresContext->DefaultLinkColor();
break;
case NS_COLOR_MOZ_VISITEDHYPERLINKTEXT:
aResult = aPresContext->DefaultVisitedLinkColor();
break;
case NS_COLOR_MOZ_ACTIVEHYPERLINKTEXT:
aResult = aPresContext->DefaultActiveLinkColor();
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
aInherited = PR_TRUE;
aResult = aContext->GetStyleColor()->mColor;
break;
default:
NS_NOTREACHED("Should never have an unknown negative colorID.");
break;
}
result = PR_TRUE;
}
}
else if (eCSSUnit_Inherit == unit) {
aResult = aParentColor;
result = PR_TRUE;
aInherited = PR_TRUE;
}
return result;
}
// Overloaded new operator. Initializes the memory to 0 and relies on an arena
// (which comes from the presShell) to perform the allocation.
void*
nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW
{
// Check the recycle list first.
return aPresContext->AllocateFromShell(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->FreeToShell(sizeof(nsRuleNode), this);
}
nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext)
{
return new (aPresContext)
nsRuleNode(aPresContext, nsnull, nsnull, 0xff, PR_FALSE);
}
nsILanguageAtomService* nsRuleNode::gLangService = nsnull;
nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent,
nsIStyleRule* aRule, PRUint8 aLevel,
PRBool aIsImportant)
: mPresContext(aContext),
mParent(aParent),
mRule(aRule),
mChildrenTaggedPtr(nsnull),
mDependentBits((PRUint32(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) |
(aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)),
mNoneBits(0)
{
MOZ_COUNT_CTOR(nsRuleNode);
NS_IF_ADDREF(mRule);
NS_ASSERTION(IsRoot() || GetLevel() == aLevel, "not enough bits");
NS_ASSERTION(IsRoot() || IsImportantRule() == aIsImportant, "yikes");
}
PR_STATIC_CALLBACK(PLDHashOperator)
DeleteRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(hdr);
entry->mRuleNode->Destroy();
return PL_DHASH_NEXT;
}
nsRuleNode::~nsRuleNode()
{
MOZ_COUNT_DTOR(nsRuleNode);
if (mStyleData.mResetData || mStyleData.mInheritedData)
mStyleData.Destroy(0, mPresContext);
if (ChildrenAreHashed()) {
PLDHashTable *children = ChildrenHash();
PL_DHashTableEnumerate(children, DeleteRuleNodeChildren, nsnull);
PL_DHashTableDestroy(children);
} else if (HaveChildren())
ChildrenList()->Destroy(mPresContext);
NS_IF_RELEASE(mRule);
}
nsRuleNode*
nsRuleNode::Transition(nsIStyleRule* aRule, PRUint8 aLevel,
PRPackedBool aIsImportantRule)
{
nsRuleNode* next = nsnull;
nsRuleNode::Key key(aRule, aLevel, aIsImportantRule);
if (HaveChildren() && !ChildrenAreHashed()) {
PRInt32 numKids = 0;
nsRuleList* curr = ChildrenList();
while (curr && curr->mRuleNode->GetKey() != key) {
curr = curr->mNext;
++numKids;
}
if (curr)
next = curr->mRuleNode;
else if (numKids >= kMaxChildrenInList)
ConvertChildrenToHash();
}
if (ChildrenAreHashed()) {
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>
(PL_DHashTableOperate(ChildrenHash(), &key, PL_DHASH_ADD));
if (!entry) {
return nsnull;
}
if (entry->mRuleNode)
next = entry->mRuleNode;
else {
next = entry->mRuleNode = new (mPresContext)
nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule);
if (!next) {
PL_DHashTableRawRemove(ChildrenHash(), entry);
return nsnull;
}
}
} else if (!next) {
// Create the new entry in our list.
next = new (mPresContext)
nsRuleNode(mPresContext, this, aRule, aLevel, aIsImportantRule);
if (!next) {
return nsnull;
}
nsRuleList* newChildrenList = new (mPresContext) nsRuleList(next, ChildrenList());
if (NS_UNLIKELY(!newChildrenList)) {
next->Destroy();
return nsnull;
}
SetChildrenList(newChildrenList);
}
return next;
}
void
nsRuleNode::ConvertChildrenToHash()
{
NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(),
"must have a non-empty list of children");
PLDHashTable *hash = PL_NewDHashTable(&ChildrenHashOps, nsnull,
sizeof(ChildrenHashEntry),
kMaxChildrenInList * 4);
if (!hash)
return;
for (nsRuleList* curr = ChildrenList(); curr;
curr = curr->DestroySelf(mPresContext)) {
// This will never fail because of the initial size we gave the table.
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>
(PL_DHashTableOperate(hash, curr->mRuleNode->mRule, PL_DHASH_ADD));
NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list");
entry->mRuleNode = curr->mRuleNode;
}
SetChildrenHash(hash);
}
inline void
nsRuleNode::PropagateNoneBit(PRUint32 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(PRUint32 aBit, nsRuleNode* aHighestNode)
{
for (nsRuleNode* curr = this; curr != aHighestNode; curr = curr->mParent) {
if (curr->mDependentBits & aBit) {
#ifdef DEBUG
while (curr != aHighestNode) {
NS_ASSERTION(curr->mDependentBits & aBit, "bit not set");
curr = curr->mParent;
}
#endif
break;
}
curr->mDependentBits |= aBit;
}
}
/*
* The following "Check" functions are used for determining what type of
* sharing can be used for the data on this rule node. MORE HERE...
*/
/* the information for a property (or in some cases, a rect group of
properties) */
struct PropertyCheckData {
size_t offset;
nsCSSType type;
};
/*
* 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
(* PR_CALLBACK CheckCallbackFn)(const nsRuleDataStruct& aData,
nsRuleNode::RuleDetail aResult);
/* the information for all the properties in a style struct */
struct StructCheckData {
const PropertyCheckData* props;
PRInt32 nprops;
CheckCallbackFn callback;
};
/**
* @param aValue the value being examined
* @param aSpecifiedCount to be incremented by one if the value is specified
* @param aInherited to be incremented by one if the value is set to inherit
*/
inline void
ExamineCSSValue(const nsCSSValue& aValue,
PRUint32& aSpecifiedCount, PRUint32& aInheritedCount)
{
if (aValue.GetUnit() != eCSSUnit_Null) {
++aSpecifiedCount;
if (aValue.GetUnit() == eCSSUnit_Inherit) {
++aInheritedCount;
}
}
}
static void
ExamineCSSValuePair(const nsCSSValuePair* aValuePair,
PRUint32& aSpecifiedCount, PRUint32& aInheritedCount)
{
NS_PRECONDITION(aValuePair, "Must have a value pair");
ExamineCSSValue(aValuePair->mXValue, aSpecifiedCount, aInheritedCount);
ExamineCSSValue(aValuePair->mYValue, aSpecifiedCount, aInheritedCount);
}
static void
ExamineCSSRect(const nsCSSRect* aRect,
PRUint32& aSpecifiedCount, PRUint32& aInheritedCount)
{
NS_PRECONDITION(aRect, "Must have a rect");
NS_FOR_CSS_SIDES(side) {
ExamineCSSValue(aRect->*(nsCSSRect::sides[side]),
aSpecifiedCount, aInheritedCount);
}
}
PR_STATIC_CALLBACK(nsRuleNode::RuleDetail)
CheckFontCallback(const nsRuleDataStruct& aData,
nsRuleNode::RuleDetail aResult)
{
const nsRuleDataFont& fontData =
static_cast<const nsRuleDataFont&>(aData);
// 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' depend on
// the parent.
const nsCSSValue& size = fontData.mSize;
const nsCSSValue& weight = fontData.mWeight;
if ((size.IsRelativeLengthUnit() && size.GetUnit() != eCSSUnit_Pixel) ||
size.GetUnit() == eCSSUnit_Percent ||
(size.GetUnit() == eCSSUnit_Enumerated &&
(size.GetIntValue() == NS_STYLE_FONT_SIZE_SMALLER ||
size.GetIntValue() == NS_STYLE_FONT_SIZE_LARGER)) ||
#ifdef MOZ_MATHML
fontData.mScriptLevel.GetUnit() == eCSSUnit_Integer ||
#endif
(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;
}
PR_STATIC_CALLBACK(nsRuleNode::RuleDetail)
CheckColorCallback(const nsRuleDataStruct& aData,
nsRuleNode::RuleDetail aResult)
{
const nsRuleDataColor& colorData =
static_cast<const nsRuleDataColor&>(aData);
// currentColor values for color require inheritance
if (colorData.mColor.GetUnit() == eCSSUnit_EnumColor &&
colorData.mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
NS_ASSERTION(aResult == nsRuleNode::eRuleFullReset,
"we should already be counted as full-reset");
aResult = nsRuleNode::eRuleFullInherited;
}
return aResult;
}
// for nsCSSPropList.h, so we get information on things in the style
// structs but not nsCSS*
#define CSS_PROP_INCLUDE_NOT_CSS
static const PropertyCheckData FontCheckProperties[] = {
#define CSS_PROP_FONT(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_FONT
};
static const PropertyCheckData DisplayCheckProperties[] = {
#define CSS_PROP_DISPLAY(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_DISPLAY
};
static const PropertyCheckData VisibilityCheckProperties[] = {
#define CSS_PROP_VISIBILITY(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_VISIBILITY
};
static const PropertyCheckData MarginCheckProperties[] = {
#define CSS_PROP_MARGIN(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_MARGIN
};
static const PropertyCheckData BorderCheckProperties[] = {
#define CSS_PROP_BORDER(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_BORDER
};
static const PropertyCheckData PaddingCheckProperties[] = {
#define CSS_PROP_PADDING(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_PADDING
};
static const PropertyCheckData OutlineCheckProperties[] = {
#define CSS_PROP_OUTLINE(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_OUTLINE
};
static const PropertyCheckData ListCheckProperties[] = {
#define CSS_PROP_LIST(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_LIST
};
static const PropertyCheckData ColorCheckProperties[] = {
#define CSS_PROP_COLOR(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_COLOR
};
static const PropertyCheckData BackgroundCheckProperties[] = {
#define CSS_PROP_BACKGROUND(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_BACKGROUND
};
static const PropertyCheckData PositionCheckProperties[] = {
#define CSS_PROP_POSITION(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_POSITION
};
static const PropertyCheckData TableCheckProperties[] = {
#define CSS_PROP_TABLE(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_TABLE
};
static const PropertyCheckData TableBorderCheckProperties[] = {
#define CSS_PROP_TABLEBORDER(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_TABLEBORDER
};
static const PropertyCheckData ContentCheckProperties[] = {
#define CSS_PROP_CONTENT(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_CONTENT
};
static const PropertyCheckData QuotesCheckProperties[] = {
#define CSS_PROP_QUOTES(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_QUOTES
};
static const PropertyCheckData TextCheckProperties[] = {
#define CSS_PROP_TEXT(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_TEXT
};
static const PropertyCheckData TextResetCheckProperties[] = {
#define CSS_PROP_TEXTRESET(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_TEXTRESET
};
static const PropertyCheckData UserInterfaceCheckProperties[] = {
#define CSS_PROP_USERINTERFACE(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_USERINTERFACE
};
static const PropertyCheckData UIResetCheckProperties[] = {
#define CSS_PROP_UIRESET(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_UIRESET
};
static const PropertyCheckData XULCheckProperties[] = {
#define CSS_PROP_XUL(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_XUL
};
#ifdef MOZ_SVG
static const PropertyCheckData SVGCheckProperties[] = {
#define CSS_PROP_SVG(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_SVG
};
static const PropertyCheckData SVGResetCheckProperties[] = {
#define CSS_PROP_SVGRESET(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_SVGRESET
};
#endif
static const PropertyCheckData ColumnCheckProperties[] = {
#define CSS_PROP_COLUMN(name_, id_, method_, datastruct_, member_, type_, kwtable_) \
{ offsetof(nsRuleData##datastruct_, member_), type_ },
#include "nsCSSPropList.h"
#undef CSS_PROP_COLUMN
};
#undef CSS_PROP_INCLUDE_NOT_CSS
static const StructCheckData gCheckProperties[] = {
#define STYLE_STRUCT(name, checkdata_cb, ctor_args) \
{name##CheckProperties, \
sizeof(name##CheckProperties)/sizeof(PropertyCheckData), \
checkdata_cb},
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
{nsnull, 0, nsnull}
};
// XXXldb Taking the address of a reference is evil.
inline const nsCSSValue&
ValueAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return * reinterpret_cast<const nsCSSValue*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSRect*
RectAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return reinterpret_cast<const nsCSSRect*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSValuePair*
ValuePairAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return reinterpret_cast<const nsCSSValuePair*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSValueList*
ValueListAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return * reinterpret_cast<const nsCSSValueList*const*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSValueList**
ValueListArrayAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return * reinterpret_cast<const nsCSSValueList**const*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSCounterData*
CounterDataAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return * reinterpret_cast<const nsCSSCounterData*const*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
inline const nsCSSQuotes*
QuotesAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset)
{
return * reinterpret_cast<const nsCSSQuotes*const*>
(reinterpret_cast<const char*>(&aRuleDataStruct) + aOffset);
}
#if defined(MOZ_MATHML) && defined(DEBUG)
static PRBool
AreAllMathMLPropertiesUndefined(const nsCSSFont& aRuleData)
{
return aRuleData.mScriptLevel.GetUnit() == eCSSUnit_Null &&
aRuleData.mScriptSizeMultiplier.GetUnit() == eCSSUnit_Null &&
aRuleData.mScriptMinSize.GetUnit() == eCSSUnit_Null;
}
#endif
inline nsRuleNode::RuleDetail
nsRuleNode::CheckSpecifiedProperties(const nsStyleStructID aSID,
const nsRuleDataStruct& aRuleDataStruct)
{
const StructCheckData *structData = gCheckProperties + aSID;
// Build a count of the:
PRUint32 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
for (const PropertyCheckData *prop = structData->props,
*prop_end = prop + structData->nprops;
prop != prop_end;
++prop)
switch (prop->type) {
case eCSSType_Value:
++total;
ExamineCSSValue(ValueAtOffset(aRuleDataStruct, prop->offset),
specified, inherited);
break;
case eCSSType_Rect:
total += 4;
ExamineCSSRect(RectAtOffset(aRuleDataStruct, prop->offset),
specified, inherited);
break;
case eCSSType_ValuePair:
total += 2;
ExamineCSSValuePair(ValuePairAtOffset(aRuleDataStruct, prop->offset),
specified, inherited);
break;
case eCSSType_ValueList:
{
++total;
const nsCSSValueList* valueList =
ValueListAtOffset(aRuleDataStruct, prop->offset);
if (valueList) {
++specified;
if (eCSSUnit_Inherit == valueList->mValue.GetUnit()) {
++inherited;
}
}
}
break;
case eCSSType_CounterData:
{
++total;
const nsCSSCounterData* counterData =
CounterDataAtOffset(aRuleDataStruct, prop->offset);
if (counterData) {
++specified;
if (eCSSUnit_Inherit == counterData->mCounter.GetUnit()) {
++inherited;
}
}
}
break;
case eCSSType_Quotes:
{
++total;
const nsCSSQuotes* quotes =
QuotesAtOffset(aRuleDataStruct, prop->offset);
if (quotes) {
++specified;
if (eCSSUnit_Inherit == quotes->mOpen.GetUnit()) {
++inherited;
}
}
}
break;
default:
NS_NOTREACHED("unknown type");
break;
}
#if 0
printf("CheckSpecifiedProperties: SID=%d total=%d spec=%d inh=%d.\n",
aSID, total, specified, inherited);
#endif
#ifdef MOZ_MATHML
NS_ASSERTION(aSID != eStyleStruct_Font ||
mPresContext->Document()->GetMathMLEnabled() ||
AreAllMathMLPropertiesUndefined(static_cast<const nsCSSFont&>(aRuleDataStruct)),
"MathML style property was defined even though MathML is disabled");
#endif
/*
* 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
#ifdef MOZ_MATHML
// MathML defines 3 properties in Font that will never be set when
// MathML is not in use. Therefore if all but three
// 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 + 3 == total &&
!mPresContext->Document()->GetMathMLEnabled())
#endif
) {
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;
if (structData->callback) {
result = (*structData->callback)(aRuleDataStruct, result);
}
return result;
}
const void*
nsRuleNode::GetDisplayData(nsStyleContext* aContext)
{
nsRuleDataDisplay displayData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Display), mPresContext, aContext);
ruleData.mDisplayData = &displayData;
return WalkRuleTree(eStyleStruct_Display, aContext, &ruleData, &displayData);
}
const void*
nsRuleNode::GetVisibilityData(nsStyleContext* aContext)
{
nsRuleDataDisplay displayData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Visibility), mPresContext, aContext);
ruleData.mDisplayData = &displayData;
return WalkRuleTree(eStyleStruct_Visibility, aContext, &ruleData, &displayData);
}
const void*
nsRuleNode::GetTextData(nsStyleContext* aContext)
{
nsRuleDataText textData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Text), mPresContext, aContext);
ruleData.mTextData = &textData;
return WalkRuleTree(eStyleStruct_Text, aContext, &ruleData, &textData);
}
const void*
nsRuleNode::GetTextResetData(nsStyleContext* aContext)
{
nsRuleDataText textData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(TextReset), mPresContext, aContext);
ruleData.mTextData = &textData;
const void* res = WalkRuleTree(eStyleStruct_TextReset, aContext, &ruleData, &textData);
textData.mTextShadow = nsnull; // We are sharing with some style rule. It really owns the data.
return res;
}
const void*
nsRuleNode::GetUserInterfaceData(nsStyleContext* aContext)
{
nsRuleDataUserInterface uiData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(UserInterface), mPresContext, aContext);
ruleData.mUserInterfaceData = &uiData;
const void* res = WalkRuleTree(eStyleStruct_UserInterface, aContext, &ruleData, &uiData);
uiData.mCursor = nsnull; // We are sharing with some style rule. It really owns the data.
return res;
}
const void*
nsRuleNode::GetUIResetData(nsStyleContext* aContext)
{
nsRuleDataUserInterface uiData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(UIReset), mPresContext, aContext);
ruleData.mUserInterfaceData = &uiData;
const void* res = WalkRuleTree(eStyleStruct_UIReset, aContext, &ruleData, &uiData);
return res;
}
const void*
nsRuleNode::GetFontData(nsStyleContext* aContext)
{
nsRuleDataFont fontData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Font), mPresContext, aContext);
ruleData.mFontData = &fontData;
return WalkRuleTree(eStyleStruct_Font, aContext, &ruleData, &fontData);
}
const void*
nsRuleNode::GetColorData(nsStyleContext* aContext)
{
nsRuleDataColor colorData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Color), mPresContext, aContext);
ruleData.mColorData = &colorData;
return WalkRuleTree(eStyleStruct_Color, aContext, &ruleData, &colorData);
}
const void*
nsRuleNode::GetBackgroundData(nsStyleContext* aContext)
{
nsRuleDataColor colorData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Background), mPresContext, aContext);
ruleData.mColorData = &colorData;
return WalkRuleTree(eStyleStruct_Background, aContext, &ruleData, &colorData);
}
const void*
nsRuleNode::GetMarginData(nsStyleContext* aContext)
{
nsRuleDataMargin marginData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Margin), mPresContext, aContext);
ruleData.mMarginData = &marginData;
return WalkRuleTree(eStyleStruct_Margin, aContext, &ruleData, &marginData);
}
const void*
nsRuleNode::GetBorderData(nsStyleContext* aContext)
{
nsRuleDataMargin marginData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Border), mPresContext, aContext);
ruleData.mMarginData = &marginData;
return WalkRuleTree(eStyleStruct_Border, aContext, &ruleData, &marginData);
}
const void*
nsRuleNode::GetPaddingData(nsStyleContext* aContext)
{
nsRuleDataMargin marginData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Padding), mPresContext, aContext);
ruleData.mMarginData = &marginData;
return WalkRuleTree(eStyleStruct_Padding, aContext, &ruleData, &marginData);
}
const void*
nsRuleNode::GetOutlineData(nsStyleContext* aContext)
{
nsRuleDataMargin marginData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Outline), mPresContext, aContext);
ruleData.mMarginData = &marginData;
return WalkRuleTree(eStyleStruct_Outline, aContext, &ruleData, &marginData);
}
const void*
nsRuleNode::GetListData(nsStyleContext* aContext)
{
nsRuleDataList listData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(List), mPresContext, aContext);
ruleData.mListData = &listData;
return WalkRuleTree(eStyleStruct_List, aContext, &ruleData, &listData);
}
const void*
nsRuleNode::GetPositionData(nsStyleContext* aContext)
{
nsRuleDataPosition posData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Position), mPresContext, aContext);
ruleData.mPositionData = &posData;
return WalkRuleTree(eStyleStruct_Position, aContext, &ruleData, &posData);
}
const void*
nsRuleNode::GetTableData(nsStyleContext* aContext)
{
nsRuleDataTable tableData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Table), mPresContext, aContext);
ruleData.mTableData = &tableData;
return WalkRuleTree(eStyleStruct_Table, aContext, &ruleData, &tableData);
}
const void*
nsRuleNode::GetTableBorderData(nsStyleContext* aContext)
{
nsRuleDataTable tableData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(TableBorder), mPresContext, aContext);
ruleData.mTableData = &tableData;
return WalkRuleTree(eStyleStruct_TableBorder, aContext, &ruleData, &tableData);
}
const void*
nsRuleNode::GetContentData(nsStyleContext* aContext)
{
nsRuleDataContent contentData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Content), mPresContext, aContext);
ruleData.mContentData = &contentData;
const void* res = WalkRuleTree(eStyleStruct_Content, aContext, &ruleData, &contentData);
contentData.mCounterIncrement = contentData.mCounterReset = nsnull;
contentData.mContent = nsnull; // We are sharing with some style rule. It really owns the data.
return res;
}
const void*
nsRuleNode::GetQuotesData(nsStyleContext* aContext)
{
nsRuleDataContent contentData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Quotes), mPresContext, aContext);
ruleData.mContentData = &contentData;
const void* res = WalkRuleTree(eStyleStruct_Quotes, aContext, &ruleData, &contentData);
contentData.mQuotes = nsnull; // We are sharing with some style rule. It really owns the data.
return res;
}
const void*
nsRuleNode::GetXULData(nsStyleContext* aContext)
{
nsRuleDataXUL xulData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(XUL), mPresContext, aContext);
ruleData.mXULData = &xulData;
return WalkRuleTree(eStyleStruct_XUL, aContext, &ruleData, &xulData);
}
const void*
nsRuleNode::GetColumnData(nsStyleContext* aContext)
{
nsRuleDataColumn columnData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Column), mPresContext, aContext);
ruleData.mColumnData = &columnData;
return WalkRuleTree(eStyleStruct_Column, aContext, &ruleData, &columnData);
}
#ifdef MOZ_SVG
const void*
nsRuleNode::GetSVGData(nsStyleContext* aContext)
{
nsRuleDataSVG svgData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(SVG), mPresContext, aContext);
ruleData.mSVGData = &svgData;
const void *res = WalkRuleTree(eStyleStruct_SVG, aContext, &ruleData, &svgData);
svgData.mStrokeDasharray = nsnull; // We are sharing with some style rule. It really owns the data.
return res;
}
const void*
nsRuleNode::GetSVGResetData(nsStyleContext* aContext)
{
nsRuleDataSVG svgData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(SVGReset), mPresContext, aContext);
ruleData.mSVGData = &svgData;
return WalkRuleTree(eStyleStruct_SVGReset, aContext, &ruleData, &svgData);
}
#endif
const void*
nsRuleNode::WalkRuleTree(const nsStyleStructID aSID,
nsStyleContext* aContext,
nsRuleData* aRuleData,
nsRuleDataStruct* aSpecificData)
{
// We start at the most specific rule in the tree.
void* startStruct = nsnull;
nsRuleNode* ruleNode = this;
nsRuleNode* highestNode = nsnull; // 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;
PRUint32 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.
// XXXldb I don't understand why we need to check |detail| here, but
// we do.
if (detail == eRuleNone)
while (ruleNode->mDependentBits & bit) {
NS_ASSERTION(ruleNode->mStyleData.GetStyleData(aSID) == nsnull,
"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) {
aRuleData->mLevel = ruleNode->GetLevel();
aRuleData->mIsImportantRule = ruleNode->IsImportantRule();
rule->MapRuleInfoInto(aRuleData);
}
// 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, *aSpecificData);
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;
}
NS_ASSERTION(!startStruct || (detail != eRuleFullReset &&
detail != eRuleFullMixed &&
detail != eRuleFullInherited),
"can't have start struct and be fully specified");
PRBool isReset = nsCachedStyleData::IsReset(aSID);
if (!highestNode)
highestNode = rootNode;
if (!aRuleData->mCanStoreInRuleTree)
detail = eRulePartialMixed; // Treat as though some data is specified to avoid
// the optimizations and force data computation.
if (detail == eRuleNone && startStruct && !aRuleData->mPostResolveCallback) {
// 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(bit, ruleNode);
return startStruct;
}
// FIXME Do we need to check for mPostResolveCallback?
if ((!startStruct && !isReset &&
(detail == eRuleNone || detail == eRulePartialInherited)) ||
detail == eRuleFullInherited) {
// We specified no non-inherited information and neither did any of
// our parent rules.
// We set a bit along the branch from the highest node (ruleNode)
// down to our node (this) indicating that no non-inherited data was
// specified. This bit is guaranteed to be set already on the path
// from the highest node to the root node in the case where
// (detail == eRuleNone), which is the most common case here.
// We must check |!isReset| because the Compute*Data functions for
// reset structs wouldn't handle none bits correctly.
if (highestNode != this && !isReset)
PropagateNoneBit(bit, highestNode);
// All information must necessarily be inherited from our parent style context.
// In the absence of any computed data in the rule tree and with
// no rules specified that didn't have values of 'inherit', we should check our parent.
nsStyleContext* parentContext = aContext->GetParent();
if (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->GetStyleData(aSID);
aContext->AddStyleBit(bit); // makes const_cast OK.
aContext->SetStyle(aSID, const_cast<void*>(parentStruct));
return parentStruct;
}
else
// We are the root. In the case of fonts, the default values just
// come from the pres context.
return SetDefaultOnRoot(aSID, aContext);
}
// We need to compute the data from the information that the rules specified.
const void* res;
#define STYLE_STRUCT_TEST aSID
#define STYLE_STRUCT(name, checkdata_cb, ctor_args) \
res = Compute##name##Data(startStruct, *aSpecificData, aContext, \
highestNode, detail, !aRuleData->mCanStoreInRuleTree);
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_TEST
// If we have a post-resolve callback, handle that now.
if (aRuleData->mPostResolveCallback && (NS_LIKELY(res != nsnull)))
(*aRuleData->mPostResolveCallback)(const_cast<void*>(res), aRuleData);
// Now return the result.
return res;
}
const void*
nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContext)
{
switch (aSID) {
case eStyleStruct_Font:
{
nsStyleFont* fontData = new (mPresContext) nsStyleFont(mPresContext);
if (NS_LIKELY(fontData != nsnull)) {
nscoord minimumFontSize =
mPresContext->GetCachedIntPref(kPresContext_MinimumFontSize);
if (minimumFontSize > 0 && !mPresContext->IsChrome()) {
fontData->mFont.size = PR_MAX(fontData->mSize, minimumFontSize);
}
else {
fontData->mFont.size = fontData->mSize;
}
aContext->SetStyle(eStyleStruct_Font, fontData);
}
return fontData;
}
case eStyleStruct_Display:
{
nsStyleDisplay* disp = new (mPresContext) nsStyleDisplay();
if (NS_LIKELY(disp != nsnull)) {
aContext->SetStyle(eStyleStruct_Display, disp);
}
return disp;
}
case eStyleStruct_Visibility:
{
nsStyleVisibility* vis = new (mPresContext) nsStyleVisibility(mPresContext);
if (NS_LIKELY(vis != nsnull)) {
aContext->SetStyle(eStyleStruct_Visibility, vis);
}
return vis;
}
case eStyleStruct_Text:
{
nsStyleText* text = new (mPresContext) nsStyleText();
if (NS_LIKELY(text != nsnull)) {
aContext->SetStyle(eStyleStruct_Text, text);
}
return text;
}
case eStyleStruct_TextReset:
{
nsStyleTextReset* text = new (mPresContext) nsStyleTextReset();
if (NS_LIKELY(text != nsnull)) {
aContext->SetStyle(eStyleStruct_TextReset, text);
}
return text;
}
case eStyleStruct_Color:
{
nsStyleColor* color = new (mPresContext) nsStyleColor(mPresContext);
if (NS_LIKELY(color != nsnull)) {
aContext->SetStyle(eStyleStruct_Color, color);
}
return color;
}
case eStyleStruct_Background:
{
nsStyleBackground* bg = new (mPresContext) nsStyleBackground(mPresContext);
if (NS_LIKELY(bg != nsnull)) {
aContext->SetStyle(eStyleStruct_Background, bg);
}
return bg;
}
case eStyleStruct_Margin:
{
nsStyleMargin* margin = new (mPresContext) nsStyleMargin();
if (NS_LIKELY(margin != nsnull)) {
aContext->SetStyle(eStyleStruct_Margin, margin);
}
return margin;
}
case eStyleStruct_Border:
{
nsStyleBorder* border = new (mPresContext) nsStyleBorder(mPresContext);
if (NS_LIKELY(border != nsnull)) {
aContext->SetStyle(eStyleStruct_Border, border);
}
return border;
}
case eStyleStruct_Padding:
{
nsStylePadding* padding = new (mPresContext) nsStylePadding();
if (NS_LIKELY(padding != nsnull)) {
aContext->SetStyle(eStyleStruct_Padding, padding);
}
return padding;
}
case eStyleStruct_Outline:
{
nsStyleOutline* outline = new (mPresContext) nsStyleOutline(mPresContext);
if (NS_LIKELY(outline != nsnull)) {
aContext->SetStyle(eStyleStruct_Outline, outline);
}
return outline;
}
case eStyleStruct_List:
{
nsStyleList* list = new (mPresContext) nsStyleList();
if (NS_LIKELY(list != nsnull)) {
aContext->SetStyle(eStyleStruct_List, list);
}
return list;
}
case eStyleStruct_Position:
{
nsStylePosition* pos = new (mPresContext) nsStylePosition();
if (NS_LIKELY(pos != nsnull)) {
aContext->SetStyle(eStyleStruct_Position, pos);
}
return pos;
}
case eStyleStruct_Table:
{
nsStyleTable* table = new (mPresContext) nsStyleTable();
if (NS_LIKELY(table != nsnull)) {
aContext->SetStyle(eStyleStruct_Table, table);
}
return table;
}
case eStyleStruct_TableBorder:
{
nsStyleTableBorder* table = new (mPresContext) nsStyleTableBorder(mPresContext);
if (NS_LIKELY(table != nsnull)) {
aContext->SetStyle(eStyleStruct_TableBorder, table);
}
return table;
}
case eStyleStruct_Content:
{
nsStyleContent* content = new (mPresContext) nsStyleContent();
if (NS_LIKELY(content != nsnull)) {
aContext->SetStyle(eStyleStruct_Content, content);
}
return content;
}
case eStyleStruct_Quotes:
{
nsStyleQuotes* quotes = new (mPresContext) nsStyleQuotes();
if (NS_LIKELY(quotes != nsnull)) {
aContext->SetStyle(eStyleStruct_Quotes, quotes);
}
return quotes;
}
case eStyleStruct_UserInterface:
{
nsStyleUserInterface* ui = new (mPresContext) nsStyleUserInterface();
if (NS_LIKELY(ui != nsnull)) {
aContext->SetStyle(eStyleStruct_UserInterface, ui);
}
return ui;
}
case eStyleStruct_UIReset:
{
nsStyleUIReset* ui = new (mPresContext) nsStyleUIReset();
if (NS_LIKELY(ui != nsnull)) {
aContext->SetStyle(eStyleStruct_UIReset, ui);
}
return ui;
}
case eStyleStruct_XUL:
{
nsStyleXUL* xul = new (mPresContext) nsStyleXUL();
if (NS_LIKELY(xul != nsnull)) {
aContext->SetStyle(eStyleStruct_XUL, xul);
}
return xul;
}
case eStyleStruct_Column:
{
nsStyleColumn* column = new (mPresContext) nsStyleColumn();
if (NS_LIKELY(column != nsnull)) {
aContext->SetStyle(eStyleStruct_Column, column);
}
return column;
}
#ifdef MOZ_SVG
case eStyleStruct_SVG:
{
nsStyleSVG* svg = new (mPresContext) nsStyleSVG();
if (NS_LIKELY(svg != nsnull)) {
aContext->SetStyle(eStyleStruct_SVG, svg);
}
return svg;
}
case eStyleStruct_SVGReset:
{
nsStyleSVGReset* svgReset = new (mPresContext) nsStyleSVGReset();
if (NS_LIKELY(svgReset != nsnull)) {
aContext->SetStyle(eStyleStruct_SVGReset, svgReset);
}
return svgReset;
}
#endif
}
return nsnull;
}
/*
* This function handles cascading of *-left or *-right box properties
* against *-start (which is L for LTR and R for RTL) or *-end (which is
* R for LTR and L for RTL).
*
* Cascading these properties correctly is hard because we need to
* cascade two properties as one, but which two properties depends on a
* third property ('direction'). We solve this by treating each of
* these properties (say, 'margin-start') as a shorthand that sets a
* property containing the value of the property specified
* ('margin-start-value') and sets a pair of properties
* ('margin-left-ltr-source' and 'margin-right-rtl-source') saying which
* of the properties we use. Thus, when we want to compute the value of
* 'margin-left' when 'direction' is 'ltr', we look at the value of
* 'margin-left-ltr-source', which tells us whether to use the highest
* 'margin-left' in the cascade or the highest 'margin-start'.
*
* Finally, since we can compute the normal (*-left and *-right)
* properties in a loop, this function works by modifying the data we
* will use in that loop (which the caller must copy from the const
* input).
*/
void
nsRuleNode::AdjustLogicalBoxProp(nsStyleContext* aContext,
const nsCSSValue& aLTRSource,
const nsCSSValue& aRTLSource,
const nsCSSValue& aLTRLogicalValue,
const nsCSSValue& aRTLLogicalValue,
PRUint8 aSide,
nsCSSRect& aValueRect,
PRBool& aInherited)
{
PRBool LTRlogical = aLTRSource.GetUnit() == eCSSUnit_Enumerated &&
aLTRSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL;
PRBool RTLlogical = aRTLSource.GetUnit() == eCSSUnit_Enumerated &&
aRTLSource.GetIntValue() == NS_BOXPROP_SOURCE_LOGICAL;
if (LTRlogical || RTLlogical) {
// We can't cache anything on the rule tree if we use any data from
// the style context, since data cached in the rule tree could be
// used with a style context with a different value.
aInherited = PR_TRUE;
PRUint8 dir = aContext->GetStyleVisibility()->mDirection;
if (dir == NS_STYLE_DIRECTION_LTR) {
if (LTRlogical)
aValueRect.*(nsCSSRect::sides[aSide]) = aLTRLogicalValue;
} else {
if (RTLlogical)
aValueRect.*(nsCSSRect::sides[aSide]) = aRTLLogicalValue;
}
}
}
/**
* Begin an nsRuleNode::Compute*Data function for an inherited struct.
*
* @param type_ The nsStyle* type this function computes.
* @param ctorargs_ The arguments used for the default nsStyle* constructor.
* @param data_ Variable (declared here) holding the result of this
* function.
* @param parentdata_ Variable (declared here) holding the parent style
* context's data for this struct.
* @param rdtype_ The nsCSS* struct type used to compute this struct's data.
* @param rdata_ Variable (declared here) holding the nsCSS* used here.
*/
#define COMPUTE_START_INHERITED(type_, ctorargs_, data_, parentdata_, rdtype_, rdata_) \
NS_ASSERTION(aRuleDetail != eRuleFullInherited, \
"should not have bothered calling Compute*Data"); \
\
nsStyleContext* parentContext = aContext->GetParent(); \
\
const nsRuleData##rdtype_& rdata_ = \
static_cast<const nsRuleData##rdtype_&>(aData); \
nsStyle##type_* data_ = nsnull; \
const nsStyle##type_* parentdata_ = nsnull; \
PRBool inherited = aInherited; \
\
/* If |inherited| might be false by the time we're done, we can't call */ \
/* parentContext->GetStyle##type_() since it could recur into setting */ \
/* the same struct on the same rule node, causing a leak. */ \
if (parentContext && aRuleDetail != eRuleFullReset && \
(!aStartStruct || (aRuleDetail != eRulePartialReset && \
aRuleDetail != eRuleNone))) \
parentdata_ = parentContext->GetStyle##type_(); \
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. */ \
inherited = PR_TRUE; \
if (parentdata_) \
data_ = new (mPresContext) nsStyle##type_(*parentdata_); \
else \
data_ = new (mPresContext) nsStyle##type_ ctorargs_; \
} \
else \
data_ = new (mPresContext) nsStyle##type_ ctorargs_; \
} \
\
if (NS_UNLIKELY(!data_)) \
return nsnull; /* Out Of Memory */ \
if (!parentdata_) \
parentdata_ = data_;
/**
* Begin an nsRuleNode::Compute*Data function for a reset struct.
*
* @param type_ The nsStyle* type this function computes.
* @param ctorargs_ The arguments used for the default nsStyle* constructor.
* @param data_ Variable (declared here) holding the result of this
* function.
* @param parentdata_ Variable (declared here) holding the parent style
* context's data for this struct.
* @param rdtype_ The nsCSS* struct type used to compute this struct's data.
* @param rdata_ Variable (declared here) holding the nsCSS* used here.
*/
#define COMPUTE_START_RESET(type_, ctorargs_, data_, parentdata_, rdtype_, rdata_) \
NS_ASSERTION(aRuleDetail != eRuleFullInherited, \
"should not have bothered calling Compute*Data"); \
\
nsStyleContext* parentContext = aContext->GetParent(); \
if (parentContext && \
parentContext->GetPseudoType() == nsCSSPseudoElements::firstLine) { \
/* Reset structs don't inherit from first-line */ \
parentContext = parentContext->GetParent(); \
} \
\
const nsRuleData##rdtype_& rdata_ = \
static_cast<const nsRuleData##rdtype_&>(aData); \
nsStyle##type_* data_; \
if (aStartStruct) \
/* We only need to compute the delta between this computed data and */ \
/* our computed data. */ \
data_ = new (mPresContext) \
nsStyle##type_(*static_cast<nsStyle##type_*>(aStartStruct)); \
else \
data_ = new (mPresContext) nsStyle##type_ ctorargs_; \
\
if (NS_UNLIKELY(!data_)) \
return nsnull; /* Out Of Memory */ \
\
/* If |inherited| might be false by the time we're done, we can't call */ \
/* parentContext->GetStyle##type_() since it could recur into setting */ \
/* the same struct on the same rule node, causing a leak. */ \
const nsStyle##type_* parentdata_ = data_; \
if (parentContext && \
aRuleDetail != eRuleFullReset && \
aRuleDetail != eRulePartialReset && \
aRuleDetail != eRuleNone) \
parentdata_ = parentContext->GetStyle##type_(); \
PRBool inherited = aInherited;
/**
* Begin 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_) \
if (inherited) \
/* We inherited, and therefore can't be cached in the rule node. We */ \
/* have to be put right on the style context. */ \
aContext->SetStyle(eStyleStruct_##type_, data_); \
else { \
/* We were fully specified and can therefore be cached right on the */ \
/* rule node. */ \
if (!aHighestNode->mStyleData.mInheritedData) { \
aHighestNode->mStyleData.mInheritedData = \
new (mPresContext) nsInheritedStyleData; \
if (NS_UNLIKELY(!aHighestNode->mStyleData.mInheritedData)) { \
data_->Destroy(mPresContext); \
return nsnull; \
} \
} \
aHighestNode->mStyleData.mInheritedData->m##type_##Data = data_; \
/* Propagate the bit down. */ \
PropagateDependentBit(NS_STYLE_INHERIT_BIT(type_), aHighestNode); \
} \
\
return data_;
/**
* Begin 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_) \
if (inherited) \
/* We inherited, and therefore can't be cached in the rule node. We */ \
/* have to be put right on the style context. */ \
aContext->SetStyle(eStyleStruct_##type_, data_); \
else { \
/* We were fully specified and can therefore be cached right on the */ \
/* rule node. */ \
if (!aHighestNode->mStyleData.mResetData) { \
aHighestNode->mStyleData.mResetData = \
new (mPresContext) nsResetStyleData; \
if (NS_UNLIKELY(!aHighestNode->mStyleData.mResetData)) { \
data_->Destroy(mPresContext); \
return nsnull; \
} \
} \
aHighestNode->mStyleData.mResetData->m##type_##Data = data_; \
/* Propagate the bit down. */ \
PropagateDependentBit(NS_STYLE_INHERIT_BIT(type_), aHighestNode); \
} \
\
return data_;
#ifdef MOZ_MATHML
// 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)
{
PRInt32 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 =
nsStyleFont::ZoomText(aPresContext, aParentFont->mScriptMinSize);
double scriptLevelScale =
pow(aParentFont->mScriptSizeMultiplier, scriptLevelChange);
// Compute the size we would have had if minscriptsize had never been
// applied, also prevent overflow (bug 413274)
*aUnconstrainedSize =
NSToCoordRound(PR_MIN(aParentFont->mScriptUnconstrainedSize*scriptLevelScale,
nscoord_MAX));
// Compute the size we could get via scriptlevel change
nscoord scriptLevelSize =
NSToCoordRound(PR_MIN(aParentFont->mSize*scriptLevelScale,
nscoord_MAX));
if (scriptLevelScale <= 1.0) {
if (aParentFont->mSize <= minScriptSize) {
// We can't decrease the font size at all, so just stick to no change
// (authors are allowed to explicitly set the font size smaller than
// minscriptsize)
return aParentFont->mSize;
}
// We can decrease, so apply constraint #1
return PR_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 PR_MIN(scriptLevelSize, PR_MAX(*aUnconstrainedSize, minScriptSize));
}
}
#endif
/* static */ void
nsRuleNode::SetFontSize(nsPresContext* aPresContext,
const nsRuleDataFont& aFontData,
const nsStyleFont* aFont,
const nsStyleFont* aParentFont,
nscoord* aSize,
const nsFont& aSystemFont,
nscoord aParentSize,
nscoord aScriptLevelAdjustedParentSize,
PRBool aUsedStartStruct,
PRBool& aInherited)
{
PRBool zoom = PR_FALSE;
PRInt32 baseSize = (PRInt32) aPresContext->
GetDefaultFont(aFont->mFlags & NS_STYLE_FONT_FACE_MASK)->size;
if (eCSSUnit_Enumerated == aFontData.mSize.GetUnit()) {
PRInt32 value = aFontData.mSize.GetIntValue();
PRInt32 scaler = aPresContext->FontScaler();
float scaleFactor = nsStyleUtil::GetScalingFactor(scaler);
zoom = PR_TRUE;
if ((NS_STYLE_FONT_SIZE_XXSMALL <= value) &&
(value <= NS_STYLE_FONT_SIZE_XXLARGE)) {
*aSize = nsStyleUtil::CalcFontPointSize(value, baseSize,
scaleFactor, 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 = nsStyleUtil::CalcFontPointSize(value, baseSize,
scaleFactor, aPresContext);
}
else if (NS_STYLE_FONT_SIZE_LARGER == value ||
NS_STYLE_FONT_SIZE_SMALLER == value) {
aInherited = PR_TRUE;
// Un-zoom so we use the tables correctly. We'll then rezoom due
// to the |zoom = PR_TRUE| above.
// Note that relative units here use the parent's size unadjusted
// for scriptlevel changes. A scriptlevel change between us and the parent
// is simply ignored.
nscoord parentSize =
nsStyleFont::UnZoomText(aPresContext, aParentSize);
if (NS_STYLE_FONT_SIZE_LARGER == value) {
*aSize = nsStyleUtil::FindNextLargerFontSize(parentSize,
baseSize, scaleFactor, aPresContext, eFontSize_CSS);
NS_ASSERTION(*aSize > parentSize,
"FindNextLargerFontSize failed");
}
else {
*aSize = nsStyleUtil::FindNextSmallerFontSize(parentSize,
baseSize, scaleFactor, aPresContext, eFontSize_CSS);
NS_ASSERTION(*aSize < parentSize ||
parentSize <= nsPresContext::CSSPixelsToAppUnits(1),
"FindNextSmallerFontSize failed");
}
} else {
NS_NOTREACHED("unexpected value");
}
}
else if (aFontData.mSize.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.
*aSize = CalcLengthWith(aFontData.mSize, aParentSize, aParentFont, nsnull,
aPresContext, aInherited);
zoom = aFontData.mSize.IsFixedLengthUnit() ||
aFontData.mSize.GetUnit() == eCSSUnit_Pixel;
}
else if (eCSSUnit_Percent == aFontData.mSize.GetUnit()) {
aInherited = PR_TRUE;
// Note that % units use the parent's size unadjusted for scriptlevel
// changes. A scriptlevel change between us and the parent is simply
// ignored.
*aSize = NSToCoordRound(aParentSize *
aFontData.mSize.GetPercentValue());
zoom = PR_FALSE;
}
else if (eCSSUnit_System_Font == aFontData.mSize.GetUnit()) {
// this becomes our cascading size
*aSize = aSystemFont.size;
zoom = PR_TRUE;
}
else if (eCSSUnit_Inherit == aFontData.mSize.GetUnit()) {
aInherited = PR_TRUE;
// 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;
zoom = PR_FALSE;
}
else if (eCSSUnit_Initial == aFontData.mSize.GetUnit()) {
// The initial value is 'medium', which has magical sizing based on
// the generic font family, so do that here too.
*aSize = baseSize;
zoom = PR_TRUE;
} else {
NS_ASSERTION(eCSSUnit_Null == aFontData.mSize.GetUnit(),
"What kind of font-size value is this?");
#ifdef MOZ_MATHML
// 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 treat
// this as inherited.
aInherited = PR_TRUE;
*aSize = aScriptLevelAdjustedParentSize;
}
#endif
}
// We want to zoom the cascaded size so that em-based measurements,
// line-heights, etc., work.
if (zoom) {
*aSize = nsStyleFont::ZoomText(aPresContext, *aSize);
}
}
static PRInt8 ClampTo8Bit(PRInt32 aValue) {
if (aValue < -128)
return -128;
if (aValue > 127)
return 127;
return PRInt8(aValue);
}
/* static */ void
nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext,
nscoord aMinFontSize,
PRUint8 aGenericFontID, const nsRuleDataFont& aFontData,
const nsStyleFont* aParentFont,
nsStyleFont* aFont, PRBool aUsedStartStruct,
PRBool& aInherited)
{
const nsFont* defaultVariableFont =
aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID);
// -moz-system-font: enum (never inherit!)
nsFont systemFont;
if (eCSSUnit_Enumerated == aFontData.mSystemFont.GetUnit()) {
nsSystemFontID sysID;
switch (aFontData.mSystemFont.GetIntValue()) {
case NS_STYLE_FONT_CAPTION: sysID = eSystemFont_Caption; break; // css2
case NS_STYLE_FONT_ICON: sysID = eSystemFont_Icon; break;
case NS_STYLE_FONT_MENU: sysID = eSystemFont_Menu; break;
case NS_STYLE_FONT_MESSAGE_BOX: sysID = eSystemFont_MessageBox; break;
case NS_STYLE_FONT_SMALL_CAPTION: sysID = eSystemFont_SmallCaption; break;
case NS_STYLE_FONT_STATUS_BAR: sysID = eSystemFont_StatusBar; break;
case NS_STYLE_FONT_WINDOW: sysID = eSystemFont_Window; break; // css3
case NS_STYLE_FONT_DOCUMENT: sysID = eSystemFont_Document; break;
case NS_STYLE_FONT_WORKSPACE: sysID = eSystemFont_Workspace; break;
case NS_STYLE_FONT_DESKTOP: sysID = eSystemFont_Desktop; break;
case NS_STYLE_FONT_INFO: sysID = eSystemFont_Info; break;
case NS_STYLE_FONT_DIALOG: sysID = eSystemFont_Dialog; break;
case NS_STYLE_FONT_BUTTON: sysID = eSystemFont_Button; break;
case NS_STYLE_FONT_PULL_DOWN_MENU:sysID = eSystemFont_PullDownMenu; break;
case NS_STYLE_FONT_LIST: sysID = eSystemFont_List; break;
case NS_STYLE_FONT_FIELD: sysID = eSystemFont_Field; break;
}
// GetSystemFont sets the font face but not necessarily the size
// XXX Or at least it used to -- no longer true for thebes. Maybe
// it should be again, though.
systemFont.size = defaultVariableFont->size;
if (NS_FAILED(aPresContext->DeviceContext()->GetSystemFont(sysID,
&systemFont))) {
systemFont.name = defaultVariableFont->name;
}
// XXXldb All of this platform-specific stuff should be in the
// nsIDeviceContext implementations, not here.
#ifdef XP_WIN
//
// As far as I can tell the system default fonts and sizes for
// on MS-Windows for Buttons, Listboxes/Comboxes and Text Fields are
// all pre-determined and cannot be changed by either the control panel
// or programmtically.
//
switch (sysID) {
// 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.
case eSystemFont_Field:
case eSystemFont_Button:
case eSystemFont_List:
// Assumption: system defined font is proportional
systemFont.size =
PR_MAX(defaultVariableFont->size - aPresContext->PointsToAppUnits(2), 0);
break;
}
#endif
} else {
// In case somebody explicitly used -moz-use-system-font.
systemFont = *defaultVariableFont;
}
// font-family: string list, enum, inherit
NS_ASSERTION(eCSSUnit_Enumerated != aFontData.mFamily.GetUnit(),
"system fonts should not be in mFamily anymore");
if (eCSSUnit_String == aFontData.mFamily.GetUnit()) {
// set the correct font if we are using DocumentFonts OR we are overriding for XUL
// MJA: bug 31816
if (aGenericFontID == kGenericFont_NONE) {
// only bother appending fallback fonts if this isn't a fallback generic font itself
if (!aFont->mFont.name.IsEmpty())
aFont->mFont.name.Append((PRUnichar)',');
// defaultVariableFont.name should always be "serif" or "sans-serif".
aFont->mFont.name.Append(defaultVariableFont->name);
}
aFont->mFont.familyNameQuirks =
(aPresContext->CompatibilityMode() == eCompatibility_NavQuirks &&
aFontData.mFamilyFromHTML);
aFont->mFont.systemFont = PR_FALSE;
aFont->mFlags &= ~NS_STYLE_FONT_FACE_MASK;
// 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->mFlags |= aGenericFontID;
}
else if (eCSSUnit_System_Font == aFontData.mFamily.GetUnit()) {
aFont->mFont.name = systemFont.name;
aFont->mFont.familyNameQuirks = PR_FALSE;
aFont->mFont.systemFont = PR_TRUE;
aFont->mFlags &= ~NS_STYLE_FONT_FACE_MASK;
}
else if (eCSSUnit_Inherit == aFontData.mFamily.GetUnit()) {
aInherited = PR_TRUE;
aFont->mFont.name = aParentFont->mFont.name;
aFont->mFont.familyNameQuirks = aParentFont->mFont.familyNameQuirks;
aFont->mFont.systemFont = aParentFont->mFont.systemFont;
aFont->mFlags &= ~NS_STYLE_FONT_FACE_MASK;
aFont->mFlags |= (aParentFont->mFlags & NS_STYLE_FONT_FACE_MASK);
}
else if (eCSSUnit_Initial == aFontData.mFamily.GetUnit()) {
aFont->mFont.name = defaultVariableFont->name;
aFont->mFont.familyNameQuirks = PR_FALSE;
aFont->mFont.systemFont = defaultVariableFont->systemFont;
aFont->mFlags &= ~NS_STYLE_FONT_FACE_MASK;
}
// 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->mFlags &= ~NS_STYLE_FONT_FACE_MASK;
aFont->mFlags |= aGenericFontID;
}
// font-style: enum, normal, inherit
if (eCSSUnit_Enumerated == aFontData.mStyle.GetUnit()) {
aFont->mFont.style = aFontData.mStyle.GetIntValue();
}
else if (eCSSUnit_Normal == aFontData.mStyle.GetUnit()) {
aFont->mFont.style = NS_STYLE_FONT_STYLE_NORMAL;
}
else if (eCSSUnit_System_Font == aFontData.mStyle.GetUnit()) {
aFont->mFont.style = systemFont.style;
}
else if (eCSSUnit_Inherit == aFontData.mStyle.GetUnit()) {
aInherited = PR_TRUE;
aFont->mFont.style = aParentFont->mFont.style;
}
else if (eCSSUnit_Initial == aFontData.mStyle.GetUnit()) {
aFont->mFont.style = defaultVariableFont->style;
}
// font-variant: enum, normal, inherit
if (eCSSUnit_Enumerated == aFontData.mVariant.GetUnit()) {
aFont->mFont.variant = aFontData.mVariant.GetIntValue();
}
else if (eCSSUnit_Normal == aFontData.mVariant.GetUnit()) {
aFont->mFont.variant = NS_STYLE_FONT_VARIANT_NORMAL;
}
else if (eCSSUnit_System_Font == aFontData.mVariant.GetUnit()) {
aFont->mFont.variant = systemFont.variant;
}
else if (eCSSUnit_Inherit == aFontData.mVariant.GetUnit()) {
aInherited = PR_TRUE;
aFont->mFont.variant = aParentFont->mFont.variant;
}
else if (eCSSUnit_Initial == aFontData.mVariant.GetUnit()) {
aFont->mFont.variant = defaultVariableFont->variant;
}
// font-weight: int, enum, normal, inherit
if (eCSSUnit_Integer == aFontData.mWeight.GetUnit()) {
aFont->mFont.weight = aFontData.mWeight.GetIntValue();
}
else if (eCSSUnit_Enumerated == aFontData.mWeight.GetUnit()) {
PRInt32 value = aFontData.mWeight.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:
case NS_STYLE_FONT_WEIGHT_LIGHTER:
aInherited = PR_TRUE;
aFont->mFont.weight = nsStyleUtil::ConstrainFontWeight(aParentFont->mFont.weight + value);
break;
}
}
else if (eCSSUnit_Normal == aFontData.mWeight.GetUnit()) {
aFont->mFont.weight = NS_STYLE_FONT_WEIGHT_NORMAL;
}
else if (eCSSUnit_System_Font == aFontData.mWeight.GetUnit()) {
aFont->mFont.weight = systemFont.weight;
}
else if (eCSSUnit_Inherit == aFontData.mWeight.GetUnit()) {
aInherited = PR_TRUE;
aFont->mFont.weight = aParentFont->mFont.weight;
}
else if (eCSSUnit_Initial == aFontData.mWeight.GetUnit()) {
aFont->mFont.weight = defaultVariableFont->weight;
}
#ifdef MOZ_MATHML
// Compute scriptlevel, scriptminsize and scriptsizemultiplier now so
// they're available for font-size computation.
// -moz-script-min-size: length
if (aFontData.mScriptMinSize.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(aFontData.mScriptMinSize, aParentFont->mSize, aParentFont, nsnull,
aPresContext, aInherited);
}
// -moz-script-size-multiplier: factor, inherit
if (eCSSUnit_Number == aFontData.mScriptSizeMultiplier.GetUnit()) {
aFont->mScriptSizeMultiplier = aFontData.mScriptSizeMultiplier.GetFloatValue();
NS_ASSERTION(aFont->mScriptSizeMultiplier >= 0.0f, "Cannot have negative script size multiplier");
}
else if (eCSSUnit_Inherit == aFontData.mScriptSizeMultiplier.GetUnit()) {
aInherited = PR_TRUE;
aFont->mScriptSizeMultiplier = aParentFont->mScriptSizeMultiplier;
}
else if (eCSSUnit_Initial == aFontData.mScriptSizeMultiplier.GetUnit()) {
aFont->mScriptSizeMultiplier = NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER;
}
// -moz-script-level: integer, number, inherit
if (eCSSUnit_Integer == aFontData.mScriptLevel.GetUnit()) {
// "relative"
aFont->mScriptLevel = ClampTo8Bit(aParentFont->mScriptLevel + aFontData.mScriptLevel.GetIntValue());
}
else if (eCSSUnit_Number == aFontData.mScriptLevel.GetUnit()) {
// "absolute"
aFont->mScriptLevel = ClampTo8Bit(PRInt32(aFontData.mScriptLevel.GetFloatValue()));
}
else if (eCSSUnit_Inherit == aFontData.mScriptSizeMultiplier.GetUnit()) {
aInherited = PR_TRUE;
aFont->mScriptLevel = aParentFont->mScriptLevel;
}
else if (eCSSUnit_Initial == aFontData.mScriptSizeMultiplier.GetUnit()) {
aFont->mScriptLevel = 0;
}
#endif
// font-size: enum, length, percent, inherit
nscoord scriptLevelAdjustedParentSize = aParentFont->mSize;
#ifdef MOZ_MATHML
nscoord scriptLevelAdjustedUnconstrainedParentSize;
scriptLevelAdjustedParentSize =
ComputeScriptLevelSize(aFont, aParentFont, aPresContext,
&scriptLevelAdjustedUnconstrainedParentSize);
NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize,
"If we have a start struct, we should have reset everything coming in here");
#endif
SetFontSize(aPresContext, aFontData, aFont, aParentFont, &aFont->mSize,
systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize,
aUsedStartStruct, aInherited);
#ifdef MOZ_MATHML
if (aParentFont->mSize == aParentFont->mScriptUnconstrainedSize &&
scriptLevelAdjustedParentSize == scriptLevelAdjustedUnconstrainedParentSize) {
// Fast path: we have not been affected by scriptminsize so we don't
// need to call SetFontSize again to compute the
// scriptminsize-unconstrained size. This is OK even if we have a
// start struct, because if we have a start struct then 'font-size'
// was specified and so scriptminsize has no effect.
aFont->mScriptUnconstrainedSize = aFont->mSize;
} else {
SetFontSize(aPresContext, aFontData, aFont, aParentFont,
&aFont->mScriptUnconstrainedSize, systemFont,
aParentFont->mScriptUnconstrainedSize,
scriptLevelAdjustedUnconstrainedParentSize,
aUsedStartStruct, aInherited);
}
NS_ASSERTION(aFont->mScriptUnconstrainedSize <= aFont->mSize,
"scriptminsize should never be making things bigger");
#endif
// enforce the user' specified minimum font-size on the value that we expose
// (but don't change font-size:0)
if (0 < aFont->mSize && aFont->mSize < aMinFontSize)
aFont->mFont.size = aMinFontSize;
else
aFont->mFont.size = aFont->mSize;
// font-size-adjust: number, none, inherit
if (eCSSUnit_Number == aFontData.mSizeAdjust.GetUnit()) {
aFont->mFont.sizeAdjust = aFontData.mSizeAdjust.GetFloatValue();
}
else if (eCSSUnit_None == aFontData.mSizeAdjust.GetUnit()) {
aFont->mFont.sizeAdjust = 0.0f;
}
else if (eCSSUnit_System_Font == aFontData.mSizeAdjust.GetUnit()) {
aFont->mFont.sizeAdjust = systemFont.sizeAdjust;
}
else if (eCSSUnit_Inherit == aFontData.mSizeAdjust.GetUnit()) {
aInherited = PR_TRUE;
aFont->mFont.sizeAdjust = aParentFont->mFont.sizeAdjust;
}
else if (eCSSUnit_Initial == aFontData.mSizeAdjust.GetUnit()) {
aFont->mFont.sizeAdjust = 0.0f;
}
}
// SetGenericFont:
// - backtrack to an ancestor with the same generic font name (possibly
// up to the root where default values come from the presentation context)
// - re-apply cascading rules from there without caching intermediate values
/* static */ void
nsRuleNode::SetGenericFont(nsPresContext* aPresContext,
nsStyleContext* aContext,
PRUint8 aGenericFontID, nscoord aMinFontSize,
nsStyleFont* aFont)
{
// walk up the contexts until a context with the desired generic font
nsAutoVoidArray contextPath;
contextPath.AppendElement(aContext);
nsStyleContext* higherContext = aContext->GetParent();
while (higherContext) {
if (higherContext->GetStyleFont()->mFlags & 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);
nsStyleFont parentFont(*defaultFont, aPresContext);
if (higherContext) {
const nsStyleFont* tmpFont = higherContext->GetStyleFont();
parentFont = *tmpFont;
}
*aFont = parentFont;
PRBool dummy;
PRUint32 fontBit = nsCachedStyleData::GetBitForSID(eStyleStruct_Font);
for (PRInt32 i = contextPath.Count() - 1; i >= 0; --i) {
nsStyleContext* context = (nsStyleContext*)contextPath[i];
nsRuleDataFont fontData; // Declare a struct with null CSS values.
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Font), aPresContext, context);
ruleData.mFontData = &fontData;
// 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->GetRuleNode(); 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)
fontData.mFamily.Reset();
nsRuleNode::SetFont(aPresContext, context, aMinFontSize,
aGenericFontID, fontData, &parentFont, aFont,
PR_FALSE, dummy);
// XXX Not sure if we need to do this here
// If we have a post-resolve callback, handle that now.
if (ruleData.mPostResolveCallback)
(ruleData.mPostResolveCallback)(aFont, &ruleData);
parentFont = *aFont;
}
}
static PRBool ExtractGeneric(const nsString& aFamily, PRBool aGeneric,
void *aData)
{
nsAutoString *data = static_cast<nsAutoString*>(aData);
if (aGeneric) {
*data = aFamily;
return PR_FALSE; // stop enumeration
}
return PR_TRUE;
}
const void*
nsRuleNode::ComputeFontData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(Font, (mPresContext), font, parentFont,
Font, fontData)
// 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).
// See if there is a minimum font-size constraint to honor
nscoord minimumFontSize =
mPresContext->GetCachedIntPref(kPresContext_MinimumFontSize);
if (minimumFontSize < 0)
minimumFontSize = 0;
PRBool useDocumentFonts =
mPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts);
// See if we are in the chrome
// We only need to know this to determine if we have to use the
// document fonts (overriding the useDocumentFonts flag), or to
// determine if we have to override the minimum font-size constraint.
if ((!useDocumentFonts || minimumFontSize > 0) && mPresContext->IsChrome()) {
// if we are not using document fonts, but this is a XUL document,
// then we use the document fonts anyway
useDocumentFonts = PR_TRUE;
minimumFontSize = 0;
}
// Figure out if we are a generic font
PRUint8 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?
if (eCSSUnit_String == fontData.mFamily.GetUnit()) {
fontData.mFamily.GetStringValue(font->mFont.name);
// XXXldb Do we want to extract the generic for this if it's not only a
// generic?
nsFont::GetGenericID(font->mFont.name, &generic);
// If we aren't allowed to use document fonts, then we are only entitled
// to use the user's default variable-width font and fixed-width font
if (!useDocumentFonts) {
// Extract the generic from the specified font family...
nsAutoString genericName;
if (!font->mFont.EnumerateFamilies(ExtractGeneric, &genericName)) {
// The specified font had a generic family.
font->mFont.name = genericName;
nsFont::GetGenericID(genericName, &generic);
// ... and only use it if it's -moz-fixed or monospace
if (generic != kGenericFont_moz_fixed &&
generic != kGenericFont_monospace) {
font->mFont.name.Truncate();
generic = kGenericFont_NONE;
}
} else {
// The specified font did not have a generic family.
font->mFont.name.Truncate();
generic = kGenericFont_NONE;
}
}
}
// Now compute our font struct
if (generic == kGenericFont_NONE) {
// continue the normal processing
nsRuleNode::SetFont(mPresContext, aContext, minimumFontSize, generic,
fontData, parentFont, font,
aStartStruct != nsnull, inherited);
}
else {
// re-calculate the font as a generic font
inherited = PR_TRUE;
nsRuleNode::SetGenericFont(mPresContext, aContext, generic,
minimumFontSize, font);
}
COMPUTE_END_INHERITED(Font, font)
}
const void*
nsRuleNode::ComputeTextData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(Text, (), text, parentText, Text, textData)
// letter-spacing: normal, length, inherit
SetCoord(textData.mLetterSpacing, text->mLetterSpacing, parentText->mLetterSpacing,
SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL,
aContext, mPresContext, inherited);
// line-height: normal, number, length, percent, inherit
if (eCSSUnit_Percent == textData.mLineHeight.GetUnit()) {
inherited = PR_TRUE;
// Use |mFont.size| to pick up minimum font size.
text->mLineHeight.SetCoordValue(
nscoord(float(aContext->GetStyleFont()->mFont.size) *
textData.mLineHeight.GetPercentValue()));
}
else if (eCSSUnit_Initial == textData.mLineHeight.GetUnit() ||
eCSSUnit_System_Font == textData.mLineHeight.GetUnit()) {
text->mLineHeight.SetNormalValue();
}
else {
SetCoord(textData.mLineHeight, text->mLineHeight, parentText->mLineHeight,
SETCOORD_LH | SETCOORD_FACTOR | SETCOORD_NORMAL,
aContext, mPresContext, inherited);
if (textData.mLineHeight.IsFixedLengthUnit() ||
textData.mLineHeight.GetUnit() == eCSSUnit_Pixel) {
nscoord lh = nsStyleFont::ZoomText(mPresContext,
text->mLineHeight.GetCoordValue());
nscoord minimumFontSize =
mPresContext->GetCachedIntPref(kPresContext_MinimumFontSize);
if (minimumFontSize > 0 && !mPresContext->IsChrome()) {
// If we applied a minimum font size, scale the line height by
// the same ratio. (If we *might* have applied a minimum font
// size, we can't cache in the rule tree.)
inherited = PR_TRUE;
const nsStyleFont *font = aContext->GetStyleFont();
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, inherit
if (eCSSUnit_Enumerated == textData.mTextAlign.GetUnit()) {
text->mTextAlign = textData.mTextAlign.GetIntValue();
}
else if (eCSSUnit_String == textData.mTextAlign.GetUnit()) {
NS_NOTYETIMPLEMENTED("align string");
}
else if (eCSSUnit_Inherit == textData.mTextAlign.GetUnit()) {
inherited = PR_TRUE;
text->mTextAlign = parentText->mTextAlign;
}
else if (eCSSUnit_Initial == textData.mTextAlign.GetUnit())
text->mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
// text-indent: length, percent, inherit
SetCoord(textData.mTextIndent, text->mTextIndent, parentText->mTextIndent,
SETCOORD_LPH | SETCOORD_INITIAL_ZERO, aContext,
mPresContext, inherited);
// text-transform: enum, none, inherit
if (eCSSUnit_Enumerated == textData.mTextTransform.GetUnit()) {
text->mTextTransform = textData.mTextTransform.GetIntValue();
}
else if (eCSSUnit_None == textData.mTextTransform.GetUnit() ||
eCSSUnit_Initial == textData.mTextTransform.GetUnit()) {
text->mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE;
}
else if (eCSSUnit_Inherit == textData.mTextTransform.GetUnit()) {
inherited = PR_TRUE;
text->mTextTransform = parentText->mTextTransform;
}
// white-space: enum, normal, inherit
if (eCSSUnit_Enumerated == textData.mWhiteSpace.GetUnit()) {
text->mWhiteSpace = textData.mWhiteSpace.GetIntValue();
}
else if (eCSSUnit_Normal == textData.mWhiteSpace.GetUnit() ||
eCSSUnit_Initial == textData.mWhiteSpace.GetUnit()) {
text->mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL;
}
else if (eCSSUnit_Inherit == textData.mWhiteSpace.GetUnit()) {
inherited = PR_TRUE;
text->mWhiteSpace = parentText->mWhiteSpace;
}
// word-spacing: normal, length, inherit
SetCoord(textData.mWordSpacing, text->mWordSpacing, parentText->mWordSpacing,
SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL,
aContext, mPresContext, inherited);
COMPUTE_END_INHERITED(Text, text)
}
const void*
nsRuleNode::ComputeTextResetData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(TextReset, (), text, parentText, Text, textData)
// vertical-align: enum, length, percent, inherit
if (!SetCoord(textData.mVerticalAlign, text->mVerticalAlign,
parentText->mVerticalAlign, SETCOORD_LPH | SETCOORD_ENUMERATED,
aContext, mPresContext, inherited)) {
if (eCSSUnit_Initial == textData.mVerticalAlign.GetUnit()) {
text->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_BASELINE,
eStyleUnit_Enumerated);
}
}
// text-decoration: none, enum (bit field), inherit
if (eCSSUnit_Enumerated == textData.mDecoration.GetUnit()) {
PRInt32 td = textData.mDecoration.GetIntValue();
text->mTextDecoration = td;
if (td & NS_STYLE_TEXT_DECORATION_PREF_ANCHORS) {
PRBool underlineLinks =
mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
if (underlineLinks) {
text->mTextDecoration |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
}
else {
text->mTextDecoration &= ~NS_STYLE_TEXT_DECORATION_UNDERLINE;
}
}
}
else if (eCSSUnit_None == textData.mDecoration.GetUnit() ||
eCSSUnit_Initial == textData.mDecoration.GetUnit()) {
text->mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
}
else if (eCSSUnit_Inherit == textData.mDecoration.GetUnit()) {
inherited = PR_TRUE;
text->mTextDecoration = parentText->mTextDecoration;
}
// unicode-bidi: enum, normal, inherit
if (eCSSUnit_Normal == textData.mUnicodeBidi.GetUnit() ||
eCSSUnit_Initial == textData.mUnicodeBidi.GetUnit()) {
text->mUnicodeBidi = NS_STYLE_UNICODE_BIDI_NORMAL;
}
else if (eCSSUnit_Enumerated == textData.mUnicodeBidi.GetUnit() ) {
text->mUnicodeBidi = textData.mUnicodeBidi.GetIntValue();
}
else if (eCSSUnit_Inherit == textData.mUnicodeBidi.GetUnit() ) {
inherited = PR_TRUE;
text->mUnicodeBidi = parentText->mUnicodeBidi;
}
COMPUTE_END_RESET(TextReset, text)
}
const void*
nsRuleNode::ComputeUserInterfaceData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail,
PRBool aInherited)
{
COMPUTE_START_INHERITED(UserInterface, (), ui, parentUI,
UserInterface, uiData)
// cursor: enum, auto, url, inherit
nsCSSValueList* list = uiData.mCursor;
if (nsnull != list) {
delete [] ui->mCursorArray;
ui->mCursorArray = nsnull;
ui->mCursorArrayLength = 0;
if (eCSSUnit_Inherit == list->mValue.GetUnit()) {
inherited = PR_TRUE;
ui->mCursor = parentUI->mCursor;
ui->CopyCursorArrayFrom(*parentUI);
}
else if (eCSSUnit_Initial == list->mValue.GetUnit()) {
ui->mCursor = NS_STYLE_CURSOR_AUTO;
}
else {
// The parser will never create a list that is *all* URL values --
// that's invalid.
PRUint32 arrayLength = 0;
nsCSSValueList* list2 = list;
for ( ; list->mValue.GetUnit() == eCSSUnit_Array; list = list->mNext)
if (list->mValue.GetArrayValue()->Item(0).GetImageValue())
++arrayLength;
if (arrayLength != 0) {
ui->mCursorArray = new nsCursorImage[arrayLength];
if (ui->mCursorArray) {
ui->mCursorArrayLength = arrayLength;
for (nsCursorImage *item = ui->mCursorArray;
list2->mValue.GetUnit() == eCSSUnit_Array;
list2 = list2->mNext) {
nsCSSValue::Array *arr = list2->mValue.GetArrayValue();
imgIRequest *req = arr->Item(0).GetImageValue();
if (req) {
item->mImage = req;
if (arr->Item(1).GetUnit() != eCSSUnit_Null) {
item->mHaveHotspot = PR_TRUE;
item->mHotspotX = arr->Item(1).GetFloatValue(),
item->mHotspotY = arr->Item(2).GetFloatValue();
}
++item;
}
}
}
}
NS_ASSERTION(list, "Must have non-array value at the end");
NS_ASSERTION(list->mValue.GetUnit() == eCSSUnit_Enumerated ||
list->mValue.GetUnit() == eCSSUnit_Auto,
"Unexpected fallback value at end of cursor list");
if (eCSSUnit_Enumerated == list->mValue.GetUnit()) {
ui->mCursor = list->mValue.GetIntValue();
}
else if (eCSSUnit_Auto == list->mValue.GetUnit()) {
ui->mCursor = NS_STYLE_CURSOR_AUTO;
}
}
}
// user-input: auto, none, enum, inherit
if (eCSSUnit_Enumerated == uiData.mUserInput.GetUnit()) {
ui->mUserInput = uiData.mUserInput.GetIntValue();
}
else if (eCSSUnit_Auto == uiData.mUserInput.GetUnit() ||
eCSSUnit_Initial == uiData.mUserInput.GetUnit()) {
ui->mUserInput = NS_STYLE_USER_INPUT_AUTO;
}
else if (eCSSUnit_None == uiData.mUserInput.GetUnit()) {
ui->mUserInput = NS_STYLE_USER_INPUT_NONE;
}
else if (eCSSUnit_Inherit == uiData.mUserInput.GetUnit()) {
inherited = PR_TRUE;
ui->mUserInput = parentUI->mUserInput;
}
// user-modify: enum, inherit
if (eCSSUnit_Enumerated == uiData.mUserModify.GetUnit()) {
ui->mUserModify = uiData.mUserModify.GetIntValue();
}
else if (eCSSUnit_Inherit == uiData.mUserModify.GetUnit()) {
inherited = PR_TRUE;
ui->mUserModify = parentUI->mUserModify;
}
else if (eCSSUnit_Initial == uiData.mUserModify.GetUnit()) {
ui->mUserModify = NS_STYLE_USER_MODIFY_READ_ONLY;
}
// user-focus: none, normal, enum, inherit
if (eCSSUnit_Enumerated == uiData.mUserFocus.GetUnit()) {
ui->mUserFocus = uiData.mUserFocus.GetIntValue();
}
else if (eCSSUnit_None == uiData.mUserFocus.GetUnit() ||
eCSSUnit_Initial == uiData.mUserFocus.GetUnit()) {
ui->mUserFocus = NS_STYLE_USER_FOCUS_NONE;
}
else if (eCSSUnit_Normal == uiData.mUserFocus.GetUnit()) {
ui->mUserFocus = NS_STYLE_USER_FOCUS_NORMAL;
}
else if (eCSSUnit_Inherit == uiData.mUserFocus.GetUnit()) {
inherited = PR_TRUE;
ui->mUserFocus = parentUI->mUserFocus;
}
COMPUTE_END_INHERITED(UserInterface, ui)
}
const void*
nsRuleNode::ComputeUIResetData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(UIReset, (), ui, parentUI, UserInterface, uiData)
// user-select: none, enum, inherit
if (eCSSUnit_Enumerated == uiData.mUserSelect.GetUnit()) {
ui->mUserSelect = uiData.mUserSelect.GetIntValue();
}
else if (eCSSUnit_None == uiData.mUserSelect.GetUnit()) {
ui->mUserSelect = NS_STYLE_USER_SELECT_NONE;
}
else if (eCSSUnit_Inherit == uiData.mUserSelect.GetUnit()) {
inherited = PR_TRUE;
ui->mUserSelect = parentUI->mUserSelect;
}
else if (eCSSUnit_Initial == uiData.mUserSelect.GetUnit()) {
// FIXME There's no other way to specify this value!
ui->mUserSelect = NS_STYLE_USER_SELECT_AUTO;
}
// ime-mode: auto, normal, enum, inherit
if (eCSSUnit_Auto == uiData.mIMEMode.GetUnit() ||
eCSSUnit_Initial == uiData.mIMEMode.GetUnit()) {
ui->mIMEMode = NS_STYLE_IME_MODE_AUTO;
}
else if (eCSSUnit_Normal == uiData.mIMEMode.GetUnit()) {
ui->mIMEMode = NS_STYLE_IME_MODE_NORMAL;
}
else if (eCSSUnit_Enumerated == uiData.mIMEMode.GetUnit()) {
ui->mIMEMode = uiData.mIMEMode.GetIntValue();
}
else if (eCSSUnit_Inherit == uiData.mIMEMode.GetUnit()) {
inherited = PR_TRUE;
ui->mIMEMode = parentUI->mIMEMode;
}
// force-broken-image-icons: integer
if (eCSSUnit_Integer == uiData.mForceBrokenImageIcon.GetUnit()) {
ui->mForceBrokenImageIcon = uiData.mForceBrokenImageIcon.GetIntValue();
} else if (eCSSUnit_Inherit == uiData.mForceBrokenImageIcon.GetUnit()) {
inherited = PR_TRUE;
ui->mForceBrokenImageIcon = parentUI->mForceBrokenImageIcon;
} else if (eCSSUnit_Initial == uiData.mForceBrokenImageIcon.GetUnit()) {
ui->mForceBrokenImageIcon = 0;
}
COMPUTE_END_RESET(UIReset, ui)
}
const void*
nsRuleNode::ComputeDisplayData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Display, (), display, parentDisplay,
Display, displayData)
nsIAtom* pseudoTag = aContext->GetPseudoType();
PRBool generatedContent = (pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after);
NS_ASSERTION(!generatedContent || parentContext,
"Must have parent context for generated content");
if (parentDisplay == display && generatedContent)
parentDisplay = parentContext->GetStyleDisplay();
// opacity: factor, inherit
if (eCSSUnit_Number == displayData.mOpacity.GetUnit()) {
display->mOpacity = displayData.mOpacity.GetFloatValue();
if (display->mOpacity > 1.0f)
display->mOpacity = 1.0f;
if (display->mOpacity < 0.0f)
display->mOpacity = 0.0f;
}
else if (eCSSUnit_Inherit == displayData.mOpacity.GetUnit()) {
inherited = PR_TRUE;
display->mOpacity = parentDisplay->mOpacity;
}
else if (eCSSUnit_Initial == displayData.mOpacity.GetUnit()) {
display->mOpacity = 1.0f;
}
// display: enum, none, inherit
if (eCSSUnit_Enumerated == displayData.mDisplay.GetUnit()) {
display->mDisplay = displayData.mDisplay.GetIntValue();
}
else if (eCSSUnit_None == displayData.mDisplay.GetUnit()) {
display->mDisplay = NS_STYLE_DISPLAY_NONE;
}
else if (eCSSUnit_Inherit == displayData.mDisplay.GetUnit()) {
inherited = PR_TRUE;
display->mDisplay = parentDisplay->mDisplay;
}
else if (eCSSUnit_Initial == displayData.mDisplay.GetUnit()) {
display->mDisplay = NS_STYLE_DISPLAY_INLINE;
}
// appearance: enum, none, inherit
if (eCSSUnit_Enumerated == displayData.mAppearance.GetUnit()) {
display->mAppearance = displayData.mAppearance.GetIntValue();
}
else if (eCSSUnit_None == displayData.mAppearance.GetUnit() ||
eCSSUnit_Initial == displayData.mAppearance.GetUnit()) {
display->mAppearance = NS_THEME_NONE;
}
else if (eCSSUnit_Inherit == displayData.mAppearance.GetUnit()) {
inherited = PR_TRUE;
display->mAppearance = parentDisplay->mAppearance;
}
// binding: url, none, inherit
if (eCSSUnit_URL == displayData.mBinding.GetUnit()) {
nsCSSValue::URL* url = displayData.mBinding.GetURLStructValue();
NS_ASSERTION(url, "What's going on here?");
if (NS_LIKELY(url->mURI)) {
display->mBinding = url;
} else {
display->mBinding = nsnull;
}
}
else if (eCSSUnit_None == displayData.mBinding.GetUnit() ||
eCSSUnit_Initial == displayData.mBinding.GetUnit()) {
display->mBinding = nsnull;
}
else if (eCSSUnit_Inherit == displayData.mBinding.GetUnit()) {
inherited = PR_TRUE;
display->mBinding = parentDisplay->mBinding;
}
// position: enum, inherit
if (eCSSUnit_Enumerated == displayData.mPosition.GetUnit()) {
display->mPosition = displayData.mPosition.GetIntValue();
}
else if (eCSSUnit_Inherit == displayData.mPosition.GetUnit()) {
inherited = PR_TRUE;
display->mPosition = parentDisplay->mPosition;
}
else if (eCSSUnit_Initial == displayData.mPosition.GetUnit()) {
display->mPosition = NS_STYLE_POSITION_STATIC;
}
// clear: enum, none, inherit
if (eCSSUnit_Enumerated == displayData.mClear.GetUnit()) {
display->mBreakType = displayData.mClear.GetIntValue();
}
else if (eCSSUnit_None == displayData.mClear.GetUnit() ||
eCSSUnit_Initial == displayData.mClear.GetUnit()) {
display->mBreakType = NS_STYLE_CLEAR_NONE;
}
else if (eCSSUnit_Inherit == displayData.mClear.GetUnit()) {
inherited = PR_TRUE;
display->mBreakType = parentDisplay->mBreakType;
}
// temp fix for bug 24000
// Map 'auto' and 'avoid' to PR_FALSE, and 'always', 'left', and
// 'right' to PR_TRUE.
// "A conforming user agent may interpret the values 'left' and
// 'right' as 'always'." - CSS2.1, section 13.3.1
if (eCSSUnit_Enumerated == displayData.mBreakBefore.GetUnit()) {
display->mBreakBefore = (NS_STYLE_PAGE_BREAK_AVOID != displayData.mBreakBefore.GetIntValue());
}
else if (eCSSUnit_Auto == displayData.mBreakBefore.GetUnit() ||
eCSSUnit_Initial == displayData.mBreakBefore.GetUnit()) {
display->mBreakBefore = PR_FALSE;
}
else if (eCSSUnit_Inherit == displayData.mBreakBefore.GetUnit()) {
inherited = PR_TRUE;
display->mBreakBefore = parentDisplay->mBreakBefore;
}
if (eCSSUnit_Enumerated == displayData.mBreakAfter.GetUnit()) {
display->mBreakAfter = (NS_STYLE_PAGE_BREAK_AVOID != displayData.mBreakAfter.GetIntValue());
}
else if (eCSSUnit_Auto == displayData.mBreakAfter.GetUnit() ||
eCSSUnit_Initial == displayData.mBreakAfter.GetUnit()) {
display->mBreakAfter = PR_FALSE;
}
else if (eCSSUnit_Inherit == displayData.mBreakAfter.GetUnit()) {
inherited = PR_TRUE;
display->mBreakAfter = parentDisplay->mBreakAfter;
}
// end temp fix
// float: enum, none, inherit
if (eCSSUnit_Enumerated == displayData.mFloat.GetUnit()) {
display->mFloats = displayData.mFloat.GetIntValue();
}
else if (eCSSUnit_None == displayData.mFloat.GetUnit() ||
eCSSUnit_Initial == displayData.mFloat.GetUnit()) {
display->mFloats = NS_STYLE_FLOAT_NONE;
}
else if (eCSSUnit_Inherit == displayData.mFloat.GetUnit()) {
inherited = PR_TRUE;
display->mFloats = parentDisplay->mFloats;
}
// overflow-x: enum, auto, inherit
if (eCSSUnit_Enumerated == displayData.mOverflowX.GetUnit()) {
display->mOverflowX = displayData.mOverflowX.GetIntValue();
}
else if (eCSSUnit_Auto == displayData.mOverflowX.GetUnit()) {
display->mOverflowX = NS_STYLE_OVERFLOW_AUTO;
}
else if (eCSSUnit_Inherit == displayData.mOverflowX.GetUnit()) {
inherited = PR_TRUE;
display->mOverflowX = parentDisplay->mOverflowX;
}
else if (eCSSUnit_Initial == displayData.mOverflowX.GetUnit()) {
display->mOverflowX = NS_STYLE_OVERFLOW_VISIBLE;
}
// overflow-y: enum, auto, inherit
if (eCSSUnit_Enumerated == displayData.mOverflowY.GetUnit()) {
display->mOverflowY = displayData.mOverflowY.GetIntValue();
}
else if (eCSSUnit_Auto == displayData.mOverflowY.GetUnit()) {
display->mOverflowY = NS_STYLE_OVERFLOW_AUTO;
}
else if (eCSSUnit_Inherit == displayData.mOverflowY.GetUnit()) {
inherited = PR_TRUE;
display->mOverflowY = parentDisplay->mOverflowY;
}
else if (eCSSUnit_Initial == displayData.mOverflowY.GetUnit()) {
display->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.
inherited = PR_TRUE;
// 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;
}
// clip property: length, auto, inherit
if (eCSSUnit_Inherit == displayData.mClip.mTop.GetUnit()) { // if one is inherit, they all are
inherited = PR_TRUE;
display->mClipFlags = parentDisplay->mClipFlags;
display->mClip = parentDisplay->mClip;
}
// if one is initial, they all are
else if (eCSSUnit_Initial == displayData.mClip.mTop.GetUnit()) {
display->mClipFlags = NS_STYLE_CLIP_AUTO;
display->mClip.SetRect(0,0,0,0);
}
else {
PRBool fullAuto = PR_TRUE;
display->mClipFlags = 0; // clear it
if (eCSSUnit_Auto == displayData.mClip.mTop.GetUnit()) {
display->mClip.y = 0;
display->mClipFlags |= NS_STYLE_CLIP_TOP_AUTO;
}
else if (displayData.mClip.mTop.IsLengthUnit()) {
display->mClip.y = CalcLength(displayData.mClip.mTop, aContext, mPresContext, inherited);
fullAuto = PR_FALSE;
}
if (eCSSUnit_Auto == displayData.mClip.mBottom.GetUnit()) {
// Setting to NS_MAXSIZE for the 'auto' case ensures that
// the clip rect is nonempty. It is important that mClip be
// nonempty if the actual clip rect could be nonempty.
display->mClip.height = NS_MAXSIZE;
display->mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO;
}
else if (displayData.mClip.mBottom.IsLengthUnit()) {
display->mClip.height = CalcLength(displayData.mClip.mBottom, aContext, mPresContext, inherited) -
display->mClip.y;
fullAuto = PR_FALSE;
}
if (eCSSUnit_Auto == displayData.mClip.mLeft.GetUnit()) {
display->mClip.x = 0;
display->mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO;
}
else if (displayData.mClip.mLeft.IsLengthUnit()) {
display->mClip.x = CalcLength(displayData.mClip.mLeft, aContext, mPresContext, inherited);
fullAuto = PR_FALSE;
}
if (eCSSUnit_Auto == displayData.mClip.mRight.GetUnit()) {
// Setting to NS_MAXSIZE for the 'auto' case ensures that
// the clip rect is nonempty. It is important that mClip be
// nonempty if the actual clip rect could be nonempty.
display->mClip.width = NS_MAXSIZE;
display->mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO;
}
else if (displayData.mClip.mRight.IsLengthUnit()) {
display->mClip.width = CalcLength(displayData.mClip.mRight, aContext, mPresContext, inherited) -
display->mClip.x;
fullAuto = PR_FALSE;
}
display->mClipFlags &= ~NS_STYLE_CLIP_TYPE_MASK;
if (fullAuto) {
display->mClipFlags |= NS_STYLE_CLIP_AUTO;
}
else {
display->mClipFlags |= NS_STYLE_CLIP_RECT;
}
}
// CSS2 specified fixups:
if (generatedContent) {
// According to CSS2 section 12.1, :before and :after
// pseudo-elements must not be positioned or floated (CSS2 12.1) and
// must be limited to certain display types (depending on the
// display type of the element to which they are attached).
// XXX These restrictions are no longer present in CSS2.1. We
// should ensure that we support removing them before doing so,
// though.
// XXXbz For example, the calls to WipeContainingBlock in the
// frame constructor will need to be changedif we allow
// block-level generated content inside inlines.
if (display->mPosition != NS_STYLE_POSITION_STATIC)
display->mPosition = NS_STYLE_POSITION_STATIC;
if (display->mFloats != NS_STYLE_FLOAT_NONE)
display->mFloats = NS_STYLE_FLOAT_NONE;
PRUint8 displayValue = display->mDisplay;
if (displayValue != NS_STYLE_DISPLAY_NONE &&
displayValue != NS_STYLE_DISPLAY_INLINE &&
displayValue != NS_STYLE_DISPLAY_INLINE_BLOCK) {
inherited = PR_TRUE;
if (parentDisplay->IsBlockOutside() ||
parentDisplay->mDisplay == NS_STYLE_DISPLAY_INLINE_BLOCK ||
parentDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CELL ||
parentDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_CAPTION) {
// If the subject of the selector is a block-level element,
// allowed values are 'none', 'inline', 'block', and 'marker'.
// If the value of the 'display' has any other value, the
// pseudo-element will behave as if the value were 'block'.
if (displayValue != NS_STYLE_DISPLAY_BLOCK &&
displayValue != NS_STYLE_DISPLAY_MARKER)
display->mDisplay = NS_STYLE_DISPLAY_BLOCK;
} else {
// If the subject of the selector is an inline-level element,
// allowed values are 'none' and 'inline'. If the value of the
// 'display' has any other value, the pseudo-element will behave
// as if the value were 'inline'.
display->mDisplay = NS_STYLE_DISPLAY_INLINE;
}
}
}
else if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
// CSS2 9.7 specifies display type corrections dealing with 'float'
// and 'position'. Since generated content can't be floated or
// positioned, we can deal with it here.
if (nsCSSPseudoElements::firstLetter == pseudoTag) {
// a non-floating first-letter must be inline
// XXX this fix can go away once bug 103189 is fixed correctly
display->mDisplay = NS_STYLE_DISPLAY_INLINE;
// We can't cache the data in the rule tree since if a more specific
// rule has 'float: left' we'll end up with the wrong 'display'
// property.
inherited = PR_TRUE;
}
if (display->IsAbsolutelyPositioned()) {
// 1) if position is 'absolute' or 'fixed' then display must be
// block-level and float must be 'none'
// Backup original display value for calculation of a hypothetical
// box (CSS2 10.6.4/10.6.5).
// See nsHTMLReflowState::CalculateHypotheticalBox
display->mOriginalDisplay = display->mDisplay;
EnsureBlockDisplay(display->mDisplay);
display->mFloats = NS_STYLE_FLOAT_NONE;
// We can't cache the data in the rule tree since if a more specific
// rule has 'position: static' we'll end up with problems with the
// 'display' and 'float' properties.
inherited = PR_TRUE;
} else if (display->mFloats != NS_STYLE_FLOAT_NONE) {
// 2) if float is not none, and display is not none, then we must
// set a block-level 'display' type per CSS2.1 section 9.7.
EnsureBlockDisplay(display->mDisplay);
// We can't cache the data in the rule tree since if a more specific
// rule has 'float: none' we'll end up with the wrong 'display'
// property.
inherited = PR_TRUE;
}
}
COMPUTE_END_RESET(Display, display)
}
const void*
nsRuleNode::ComputeVisibilityData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(Visibility, (mPresContext),
visibility, parentVisibility,
Display, displayData)
// direction: enum, inherit
if (eCSSUnit_Enumerated == displayData.mDirection.GetUnit()) {
visibility->mDirection = displayData.mDirection.GetIntValue();
if (NS_STYLE_DIRECTION_RTL == visibility->mDirection)
mPresContext->SetBidiEnabled(PR_TRUE);
}
else if (eCSSUnit_Inherit == displayData.mDirection.GetUnit()) {
inherited = PR_TRUE;
visibility->mDirection = parentVisibility->mDirection;
}
else if (eCSSUnit_Initial == displayData.mDirection.GetUnit()) {
PRUint32 bidiOptions = mPresContext->GetBidi();
if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL)
visibility->mDirection = NS_STYLE_DIRECTION_RTL;
else
visibility->mDirection = NS_STYLE_DIRECTION_LTR;
}
// visibility: enum, inherit
if (eCSSUnit_Enumerated == displayData.mVisibility.GetUnit()) {
visibility->mVisible = displayData.mVisibility.GetIntValue();
}
else if (eCSSUnit_Inherit == displayData.mVisibility.GetUnit()) {
inherited = PR_TRUE;
visibility->mVisible = parentVisibility->mVisible;
}
else if (eCSSUnit_Initial == displayData.mVisibility.GetUnit()) {
visibility->mVisible = NS_STYLE_VISIBILITY_VISIBLE;
}
// lang: string, inherit
// this is not a real CSS property, it is a html attribute mapped to CSS struture
if (eCSSUnit_String == displayData.mLang.GetUnit()) {
if (!gLangService) {
CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
}
if (gLangService) {
nsAutoString lang;
displayData.mLang.GetStringValue(lang);
visibility->mLangGroup = gLangService->LookupLanguage(lang);
}
}
COMPUTE_END_INHERITED(Visibility, visibility)
}
const void*
nsRuleNode::ComputeColorData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(Color, (mPresContext), color, parentColor,
Color, colorData)
// color: color, string, inherit
// Special case for currentColor. According to CSS3, setting color to 'currentColor'
// should behave as if it is inherited
if (colorData.mColor.GetUnit() == eCSSUnit_EnumColor &&
colorData.mColor.GetIntValue() == NS_COLOR_CURRENTCOLOR) {
color->mColor = parentColor->mColor;
inherited = PR_TRUE;
}
else if (colorData.mColor.GetUnit() == eCSSUnit_Initial) {
color->mColor = mPresContext->DefaultColor();
}
else {
SetColor(colorData.mColor, parentColor->mColor, mPresContext, aContext, color->mColor,
inherited);
}
COMPUTE_END_INHERITED(Color, color)
}
const void*
nsRuleNode::ComputeBackgroundData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Background, (mPresContext), bg, parentBG,
Color, colorData)
// save parentFlags in case bg == parentBG and we clobber them later
PRUint8 parentFlags = parentBG->mBackgroundFlags;
// background-color: color, string, enum (flags), inherit
if (eCSSUnit_Inherit == colorData.mBackColor.GetUnit()) { // do inherit first, so SetColor doesn't do it
bg->mBackgroundColor = parentBG->mBackgroundColor;
bg->mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
bg->mBackgroundFlags |= (parentFlags & NS_STYLE_BG_COLOR_TRANSPARENT);
inherited = PR_TRUE;
}
else if (SetColor(colorData.mBackColor, parentBG->mBackgroundColor,
mPresContext, aContext, bg->mBackgroundColor, inherited)) {
bg->mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
}
else if (eCSSUnit_Enumerated == colorData.mBackColor.GetUnit() ||
eCSSUnit_Initial == colorData.mBackColor.GetUnit()) {
bg->mBackgroundFlags |= NS_STYLE_BG_COLOR_TRANSPARENT;
}
// background-image: url (stored as image), none, inherit
if (eCSSUnit_Image == colorData.mBackImage.GetUnit()) {
bg->mBackgroundImage = colorData.mBackImage.GetImageValue();
}
else if (eCSSUnit_None == colorData.mBackImage.GetUnit() ||
eCSSUnit_Initial == colorData.mBackImage.GetUnit()) {
bg->mBackgroundImage = nsnull;
}
else if (eCSSUnit_Inherit == colorData.mBackImage.GetUnit()) {
inherited = PR_TRUE;
bg->mBackgroundImage = parentBG->mBackgroundImage;
}
if (bg->mBackgroundImage) {
bg->mBackgroundFlags &= ~NS_STYLE_BG_IMAGE_NONE;
} else {
bg->mBackgroundFlags |= NS_STYLE_BG_IMAGE_NONE;
}
// background-repeat: enum, inherit
if (eCSSUnit_Enumerated == colorData.mBackRepeat.GetUnit()) {
bg->mBackgroundRepeat = colorData.mBackRepeat.GetIntValue();
}
else if (eCSSUnit_Inherit == colorData.mBackRepeat.GetUnit()) {
inherited = PR_TRUE;
bg->mBackgroundRepeat = parentBG->mBackgroundRepeat;
}
else if (eCSSUnit_Initial == colorData.mBackRepeat.GetUnit()) {
bg->mBackgroundRepeat = NS_STYLE_BG_REPEAT_XY;
}
// background-attachment: enum, inherit
if (eCSSUnit_Enumerated == colorData.mBackAttachment.GetUnit()) {
bg->mBackgroundAttachment = colorData.mBackAttachment.GetIntValue();
}
else if (eCSSUnit_Inherit == colorData.mBackAttachment.GetUnit()) {
inherited = PR_TRUE;
bg->mBackgroundAttachment = parentBG->mBackgroundAttachment;
}
else if (eCSSUnit_Initial == colorData.mBackAttachment.GetUnit()) {
bg->mBackgroundAttachment = NS_STYLE_BG_ATTACHMENT_SCROLL;
}
// background-clip: enum, inherit, initial
if (eCSSUnit_Enumerated == colorData.mBackClip.GetUnit()) {
bg->mBackgroundClip = colorData.mBackClip.GetIntValue();
}
else if (eCSSUnit_Inherit == colorData.mBackClip.GetUnit()) {
bg->mBackgroundClip = parentBG->mBackgroundClip;
}
else if (eCSSUnit_Initial == colorData.mBackClip.GetUnit()) {
bg->mBackgroundClip = NS_STYLE_BG_CLIP_BORDER;
}
// background-inline-policy: enum, inherit, initial
if (eCSSUnit_Enumerated == colorData.mBackInlinePolicy.GetUnit()) {
bg->mBackgroundInlinePolicy = colorData.mBackInlinePolicy.GetIntValue();
}
else if (eCSSUnit_Inherit == colorData.mBackInlinePolicy.GetUnit()) {
bg->mBackgroundInlinePolicy = parentBG->mBackgroundInlinePolicy;
}
else if (eCSSUnit_Initial == colorData.mBackInlinePolicy.GetUnit()) {
bg->mBackgroundInlinePolicy = NS_STYLE_BG_INLINE_POLICY_CONTINUOUS;
}
// background-origin: enum, inherit, initial
if (eCSSUnit_Enumerated == colorData.mBackOrigin.GetUnit()) {
bg->mBackgroundOrigin = colorData.mBackOrigin.GetIntValue();
}
else if (eCSSUnit_Inherit == colorData.mBackOrigin.GetUnit()) {
bg->mBackgroundOrigin = parentBG->mBackgroundOrigin;
}
else if (eCSSUnit_Initial == colorData.mBackOrigin.GetUnit()) {
bg->mBackgroundOrigin = NS_STYLE_BG_ORIGIN_PADDING;
}
// background-position: enum, length, percent (flags), inherit
if (eCSSUnit_Percent == colorData.mBackPosition.mXValue.GetUnit()) {
bg->mBackgroundXPosition.mFloat = colorData.mBackPosition.mXValue.GetPercentValue();
bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_PERCENT;
bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_LENGTH;
}
else if (colorData.mBackPosition.mXValue.IsLengthUnit()) {
bg->mBackgroundXPosition.mCoord = CalcLength(colorData.mBackPosition.mXValue,
aContext, mPresContext, inherited);
bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_LENGTH;
bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_PERCENT;
}
else if (eCSSUnit_Enumerated == colorData.mBackPosition.mXValue.GetUnit()) {
switch (colorData.mBackPosition.mXValue.GetIntValue()) {
case NS_STYLE_BG_POSITION_LEFT:
bg->mBackgroundXPosition.mFloat = 0.0f;
break;
case NS_STYLE_BG_POSITION_RIGHT:
bg->mBackgroundXPosition.mFloat = 1.0f;
break;
default:
NS_NOTREACHED("unexpected value");
// fall through
case NS_STYLE_BG_POSITION_CENTER:
bg->mBackgroundXPosition.mFloat = 0.5f;
break;
}
bg->mBackgroundFlags |= NS_STYLE_BG_X_POSITION_PERCENT;
bg->mBackgroundFlags &= ~NS_STYLE_BG_X_POSITION_LENGTH;
}
else if (eCSSUnit_Inherit == colorData.mBackPosition.mXValue.GetUnit()) {
inherited = PR_TRUE;
bg->mBackgroundXPosition = parentBG->mBackgroundXPosition;
bg->mBackgroundFlags &= ~(NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT);
bg->mBackgroundFlags |= (parentFlags & (NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT));
}
else if (eCSSUnit_Initial == colorData.mBackPosition.mXValue.GetUnit()) {
bg->mBackgroundFlags &= ~(NS_STYLE_BG_X_POSITION_LENGTH | NS_STYLE_BG_X_POSITION_PERCENT);
}
if (eCSSUnit_Percent == colorData.mBackPosition.mYValue.GetUnit()) {
bg->mBackgroundYPosition.mFloat = colorData.mBackPosition.mYValue.GetPercentValue();
bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_PERCENT;
bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_LENGTH;
}
else if (colorData.mBackPosition.mYValue.IsLengthUnit()) {
bg->mBackgroundYPosition.mCoord = CalcLength(colorData.mBackPosition.mYValue,
aContext, mPresContext, inherited);
bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_LENGTH;
bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_PERCENT;
}
else if (eCSSUnit_Enumerated == colorData.mBackPosition.mYValue.GetUnit()) {
switch (colorData.mBackPosition.mYValue.GetIntValue()) {
case NS_STYLE_BG_POSITION_TOP:
bg->mBackgroundYPosition.mFloat = 0.0f;
break;
case NS_STYLE_BG_POSITION_BOTTOM:
bg->mBackgroundYPosition.mFloat = 1.0f;
break;
default:
NS_NOTREACHED("unexpected value");
// fall through
case NS_STYLE_BG_POSITION_CENTER:
bg->mBackgroundYPosition.mFloat = 0.5f;
break;
}
bg->mBackgroundFlags |= NS_STYLE_BG_Y_POSITION_PERCENT;
bg->mBackgroundFlags &= ~NS_STYLE_BG_Y_POSITION_LENGTH;
}
else if (eCSSUnit_Inherit == colorData.mBackPosition.mYValue.GetUnit()) {
inherited = PR_TRUE;
bg->mBackgroundYPosition = parentBG->mBackgroundYPosition;
bg->mBackgroundFlags &= ~(NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT);
bg->mBackgroundFlags |= (parentFlags & (NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT));
}
else if (eCSSUnit_Initial == colorData.mBackPosition.mYValue.GetUnit()) {
bg->mBackgroundFlags &= ~(NS_STYLE_BG_Y_POSITION_LENGTH | NS_STYLE_BG_Y_POSITION_PERCENT);
}
COMPUTE_END_RESET(Background, bg)
}
const void*
nsRuleNode::ComputeMarginData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Margin, (), margin, parentMargin, Margin, marginData)
// margin: length, percent, auto, inherit
nsStyleCoord coord;
nsCSSRect ourMargin(marginData.mMargin);
AdjustLogicalBoxProp(aContext,
marginData.mMarginLeftLTRSource,
marginData.mMarginLeftRTLSource,
marginData.mMarginStart, marginData.mMarginEnd,
NS_SIDE_LEFT, ourMargin, inherited);
AdjustLogicalBoxProp(aContext,
marginData.mMarginRightLTRSource,
marginData.mMarginRightRTLSource,
marginData.mMarginEnd, marginData.mMarginStart,
NS_SIDE_RIGHT, ourMargin, inherited);
NS_FOR_CSS_SIDES(side) {
nsStyleCoord parentCoord = parentMargin->mMargin.Get(side);
if (SetCoord(ourMargin.*(nsCSSRect::sides[side]),
coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited)) {
margin->mMargin.Set(side, coord);
}
}
margin->RecalcData();
COMPUTE_END_RESET(Margin, margin)
}
const void*
nsRuleNode::ComputeBorderData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Border, (mPresContext), border, parentBorder,
Margin, marginData)
// border-width, border-*-width: length, enum, inherit
nsStyleCoord coord;
nsCSSRect ourBorderWidth(marginData.mBorderWidth);
AdjustLogicalBoxProp(aContext,
marginData.mBorderLeftWidthLTRSource,
marginData.mBorderLeftWidthRTLSource,
marginData.mBorderStartWidth,
marginData.mBorderEndWidth,
NS_SIDE_LEFT, ourBorderWidth, inherited);
AdjustLogicalBoxProp(aContext,
marginData.mBorderRightWidthLTRSource,
marginData.mBorderRightWidthRTLSource,
marginData.mBorderEndWidth,
marginData.mBorderStartWidth,
NS_SIDE_RIGHT, ourBorderWidth, inherited);
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
const nsCSSValue &value = ourBorderWidth.*(nsCSSRect::sides[side]);
NS_ASSERTION(eCSSUnit_Percent != value.GetUnit(),
"Percentage borders not implemented yet "
"If implementing, make sure to fix all consumers of "
"nsStyleBorder, the IsPercentageAwareChild method, "
"the nsAbsoluteContainingBlock::FrameDependsOnContainer "
"method, the "
"nsLineLayout::IsPercentageAwareReplacedElement method "
"and probably some other places");
if (eCSSUnit_Enumerated == value.GetUnit()) {
NS_ASSERTION(value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THIN ||
value.GetIntValue() == NS_STYLE_BORDER_WIDTH_MEDIUM ||
value.GetIntValue() == NS_STYLE_BORDER_WIDTH_THICK,
"Unexpected enum value");
border->SetBorderWidth(side,
(mPresContext->GetBorderWidthTable())[value.GetIntValue()]);
}
// OK to pass bad aParentCoord since we're not passing SETCOORD_INHERIT
else if (SetCoord(value, coord, nsStyleCoord(), SETCOORD_LENGTH,
aContext, mPresContext, inherited)) {
if (coord.GetUnit() == eStyleUnit_Coord) {
border->SetBorderWidth(side, coord.GetCoordValue());
}
#ifdef DEBUG
else {
NS_ASSERTION(coord.GetUnit() == eStyleUnit_Chars, "unexpected unit");
NS_WARNING("Border set in chars; we don't handle that");
}
#endif
}
else if (eCSSUnit_Inherit == value.GetUnit()) {
inherited = PR_TRUE;
border->SetBorderWidth(side, parentBorder->GetBorderWidth(side));
}
else if (eCSSUnit_Initial == value.GetUnit()) {
border->SetBorderWidth(side,
(mPresContext->GetBorderWidthTable())[NS_STYLE_BORDER_WIDTH_MEDIUM]);
}
}
}
// border-style, border-*-style: enum, none, inherit
nsCSSRect ourStyle(marginData.mBorderStyle);
AdjustLogicalBoxProp(aContext,
marginData.mBorderLeftStyleLTRSource,
marginData.mBorderLeftStyleRTLSource,
marginData.mBorderStartStyle, marginData.mBorderEndStyle,
NS_SIDE_LEFT, ourStyle, inherited);
AdjustLogicalBoxProp(aContext,
marginData.mBorderRightStyleLTRSource,
marginData.mBorderRightStyleRTLSource,
marginData.mBorderEndStyle, marginData.mBorderStartStyle,
NS_SIDE_RIGHT, ourStyle, inherited);
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
const nsCSSValue &value = ourStyle.*(nsCSSRect::sides[side]);
nsCSSUnit unit = value.GetUnit();
if (eCSSUnit_Enumerated == unit) {
border->SetBorderStyle(side, value.GetIntValue());
}
else if (eCSSUnit_None == unit || eCSSUnit_Initial == unit) {
border->SetBorderStyle(side, NS_STYLE_BORDER_STYLE_NONE);
}
else if (eCSSUnit_Inherit == unit) {
inherited = PR_TRUE;
border->SetBorderStyle(side, parentBorder->GetBorderStyle(side));
}
}
}
// -moz-border-*-colors: color, string, enum
nscolor borderColor;
nscolor unused = NS_RGB(0,0,0);
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
nsCSSValueList* list =
marginData.mBorderColors.*(nsCSSValueListRect::sides[side]);
// FIXME Bug 389404: Implement inherit and -moz-initial.
if (list) {
// Some composite border color information has been specified for this
// border side.
border->EnsureBorderColors();
border->ClearBorderColors(side);
while (list) {
if (SetColor(list->mValue, unused, mPresContext, aContext, borderColor, inherited))
border->AppendBorderColor(side, borderColor, PR_FALSE);
else if (eCSSUnit_Enumerated == list->mValue.GetUnit() &&
NS_STYLE_COLOR_TRANSPARENT == list->mValue.GetIntValue())
border->AppendBorderColor(side, nsnull, PR_TRUE);
list = list->mNext;
}
}
}
}
// border-color, border-*-color: color, string, enum, inherit
nsCSSRect ourBorderColor(marginData.mBorderColor);
PRBool transparent;
PRBool foreground;
AdjustLogicalBoxProp(aContext,
marginData.mBorderLeftColorLTRSource,
marginData.mBorderLeftColorRTLSource,
marginData.mBorderStartColor, marginData.mBorderEndColor,
NS_SIDE_LEFT, ourBorderColor, inherited);
AdjustLogicalBoxProp(aContext,
marginData.mBorderRightColorLTRSource,
marginData.mBorderRightColorRTLSource,
marginData.mBorderEndColor, marginData.mBorderStartColor,
NS_SIDE_RIGHT, ourBorderColor, inherited);
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
const nsCSSValue &value = ourBorderColor.*(nsCSSRect::sides[side]);
if (eCSSUnit_Inherit == value.GetUnit()) {
if (parentContext) {
inherited = PR_TRUE;
parentBorder->GetBorderColor(side, borderColor,
transparent, foreground);
if (transparent)
border->SetBorderTransparent(side);
else if (foreground) {
// We want to inherit the color from the parent, not use the
// color on the element where this chunk of style data will be
// used. We can ensure that the data for the parent are fully
// computed (unlike for the element where this will be used, for
// which the color could be specified on a more specific rule).
border->SetBorderColor(side, parentContext->GetStyleColor()->mColor);
} else
border->SetBorderColor(side, borderColor);
} else {
// We're the root
border->SetBorderToForeground(side);
}
}
else if (SetColor(value, unused, mPresContext, aContext, borderColor, inherited)) {
border->SetBorderColor(side, borderColor);
}
else if (eCSSUnit_Enumerated == value.GetUnit()) {
switch (value.GetIntValue()) {
case NS_STYLE_COLOR_TRANSPARENT:
border->SetBorderTransparent(side);
break;
case NS_STYLE_COLOR_MOZ_USE_TEXT_COLOR:
border->SetBorderToForeground(side);
break;
}
}
else if (eCSSUnit_Initial == value.GetUnit()) {
border->SetBorderToForeground(side);
}
}
}
// -moz-border-radius: length, percent, inherit
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
nsStyleCoord parentCoord = parentBorder->mBorderRadius.Get(side);
if (SetCoord(marginData.mBorderRadius.*(nsCSSRect::sides[side]), coord,
parentCoord, SETCOORD_LPH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited))
border->mBorderRadius.Set(side, coord);
}
}
// float-edge: enum, inherit
if (eCSSUnit_Enumerated == marginData.mFloatEdge.GetUnit())
border->mFloatEdge = marginData.mFloatEdge.GetIntValue();
else if (eCSSUnit_Inherit == marginData.mFloatEdge.GetUnit()) {
inherited = PR_TRUE;
border->mFloatEdge = parentBorder->mFloatEdge;
}
else if (eCSSUnit_Initial == marginData.mFloatEdge.GetUnit()) {
border->mFloatEdge = NS_STYLE_FLOAT_EDGE_CONTENT;
}
COMPUTE_END_RESET(Border, border)
}
const void*
nsRuleNode::ComputePaddingData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Padding, (), padding, parentPadding, Margin, marginData)
// padding: length, percent, inherit
nsStyleCoord coord;
nsCSSRect ourPadding(marginData.mPadding);
AdjustLogicalBoxProp(aContext,
marginData.mPaddingLeftLTRSource,
marginData.mPaddingLeftRTLSource,
marginData.mPaddingStart, marginData.mPaddingEnd,
NS_SIDE_LEFT, ourPadding, inherited);
AdjustLogicalBoxProp(aContext,
marginData.mPaddingRightLTRSource,
marginData.mPaddingRightRTLSource,
marginData.mPaddingEnd, marginData.mPaddingStart,
NS_SIDE_RIGHT, ourPadding, inherited);
NS_FOR_CSS_SIDES(side) {
nsStyleCoord parentCoord = parentPadding->mPadding.Get(side);
if (SetCoord(ourPadding.*(nsCSSRect::sides[side]),
coord, parentCoord, SETCOORD_LPH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited)) {
padding->mPadding.Set(side, coord);
}
}
padding->RecalcData();
COMPUTE_END_RESET(Padding, padding)
}
const void*
nsRuleNode::ComputeOutlineData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Outline, (mPresContext), outline, parentOutline,
Margin, marginData)
// outline-width: length, enum, inherit
if (eCSSUnit_Initial == marginData.mOutlineWidth.GetUnit()) {
outline->mOutlineWidth =
nsStyleCoord(NS_STYLE_BORDER_WIDTH_MEDIUM, eStyleUnit_Enumerated);
}
else {
SetCoord(marginData.mOutlineWidth, outline->mOutlineWidth,
parentOutline->mOutlineWidth, SETCOORD_LEH, aContext,
mPresContext, inherited);
}
// outline-offset: length, inherit
SetCoord(marginData.mOutlineOffset, outline->mOutlineOffset, parentOutline->mOutlineOffset,
SETCOORD_LH | SETCOORD_INITIAL_ZERO, aContext, mPresContext,
inherited);
// outline-color: color, string, enum, inherit
nscolor outlineColor;
nscolor unused = NS_RGB(0,0,0);
if (eCSSUnit_Inherit == marginData.mOutlineColor.GetUnit()) {
if (parentContext) {
inherited = PR_TRUE;
if (parentOutline->GetOutlineColor(outlineColor))
outline->SetOutlineColor(outlineColor);
else {
#ifdef GFX_HAS_INVERT
outline->SetOutlineInitialColor();
#else
// We want to inherit the color from the parent, not use the
// color on the element where this chunk of style data will be
// used. We can ensure that the data for the parent are fully
// computed (unlike for the element where this will be used, for
// which the color could be specified on a more specific rule).
outline->SetOutlineColor(parentContext->GetStyleColor()->mColor);
#endif
}
} else {
outline->SetOutlineInitialColor();
}
}
else if (SetColor(marginData.mOutlineColor, unused, mPresContext, aContext, outlineColor, inherited))
outline->SetOutlineColor(outlineColor);
else if (eCSSUnit_Enumerated == marginData.mOutlineColor.GetUnit() ||
eCSSUnit_Initial == marginData.mOutlineColor.GetUnit()) {
outline->SetOutlineInitialColor();
}
// -moz-outline-radius: length, percent, inherit
nsStyleCoord coord;
{ // scope for compilers with broken |for| loop scoping
NS_FOR_CSS_SIDES(side) {
nsStyleCoord parentCoord = parentOutline->mOutlineRadius.Get(side);
if (SetCoord(marginData.mOutlineRadius.*(nsCSSRect::sides[side]), coord,
parentCoord, SETCOORD_LPH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited))
outline->mOutlineRadius.Set(side, coord);
}
}
// outline-style: auto, enum, none, inherit
if (eCSSUnit_Enumerated == marginData.mOutlineStyle.GetUnit())
outline->SetOutlineStyle(marginData.mOutlineStyle.GetIntValue());
else if (eCSSUnit_None == marginData.mOutlineStyle.GetUnit() ||
eCSSUnit_Initial == marginData.mOutlineStyle.GetUnit())
outline->SetOutlineStyle(NS_STYLE_BORDER_STYLE_NONE);
else if (eCSSUnit_Auto == marginData.mOutlineStyle.GetUnit()) {
outline->SetOutlineStyle(NS_STYLE_BORDER_STYLE_AUTO);
} else if (eCSSUnit_Inherit == marginData.mOutlineStyle.GetUnit()) {
inherited = PR_TRUE;
outline->SetOutlineStyle(parentOutline->GetOutlineStyle());
}
outline->RecalcData(mPresContext);
COMPUTE_END_RESET(Outline, outline)
}
const void*
nsRuleNode::ComputeListData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(List, (), list, parentList, List, listData)
// list-style-type: enum, none, inherit
if (eCSSUnit_Enumerated == listData.mType.GetUnit()) {
list->mListStyleType = listData.mType.GetIntValue();
}
else if (eCSSUnit_None == listData.mType.GetUnit()) {
list->mListStyleType = NS_STYLE_LIST_STYLE_NONE;
}
else if (eCSSUnit_Inherit == listData.mType.GetUnit()) {
inherited = PR_TRUE;
list->mListStyleType = parentList->mListStyleType;
}
else if (eCSSUnit_Initial == listData.mType.GetUnit()) {
list->mListStyleType = NS_STYLE_LIST_STYLE_DISC;
}
// list-style-image: url, none, inherit
if (eCSSUnit_Image == listData.mImage.GetUnit()) {
list->mListStyleImage = listData.mImage.GetImageValue();
}
else if (eCSSUnit_None == listData.mImage.GetUnit() ||
eCSSUnit_Initial == listData.mImage.GetUnit()) {
list->mListStyleImage = nsnull;
}
else if (eCSSUnit_Inherit == listData.mImage.GetUnit()) {
inherited = PR_TRUE;
list->mListStyleImage = parentList->mListStyleImage;
}
// list-style-position: enum, inherit
if (eCSSUnit_Enumerated == listData.mPosition.GetUnit()) {
list->mListStylePosition = listData.mPosition.GetIntValue();
}
else if (eCSSUnit_Inherit == listData.mPosition.GetUnit()) {
inherited = PR_TRUE;
list->mListStylePosition = parentList->mListStylePosition;
}
else if (eCSSUnit_Initial == listData.mPosition.GetUnit()) {
list->mListStylePosition = NS_STYLE_LIST_STYLE_POSITION_OUTSIDE;
}
// image region property: length, auto, inherit
if (eCSSUnit_Inherit == listData.mImageRegion.mTop.GetUnit()) { // if one is inherit, they all are
inherited = PR_TRUE;
list->mImageRegion = parentList->mImageRegion;
}
// if one is -moz-initial, they all are
else if (eCSSUnit_Initial == listData.mImageRegion.mTop.GetUnit()) {
list->mImageRegion.Empty();
}
else {
if (eCSSUnit_Auto == listData.mImageRegion.mTop.GetUnit())
list->mImageRegion.y = 0;
else if (listData.mImageRegion.mTop.IsLengthUnit())
list->mImageRegion.y = CalcLength(listData.mImageRegion.mTop, aContext, mPresContext, inherited);
if (eCSSUnit_Auto == listData.mImageRegion.mBottom.GetUnit())
list->mImageRegion.height = 0;
else if (listData.mImageRegion.mBottom.IsLengthUnit())
list->mImageRegion.height = CalcLength(listData.mImageRegion.mBottom, aContext,
mPresContext, inherited) - list->mImageRegion.y;
if (eCSSUnit_Auto == listData.mImageRegion.mLeft.GetUnit())
list->mImageRegion.x = 0;
else if (listData.mImageRegion.mLeft.IsLengthUnit())
list->mImageRegion.x = CalcLength(listData.mImageRegion.mLeft, aContext, mPresContext, inherited);
if (eCSSUnit_Auto == listData.mImageRegion.mRight.GetUnit())
list->mImageRegion.width = 0;
else if (listData.mImageRegion.mRight.IsLengthUnit())
list->mImageRegion.width = CalcLength(listData.mImageRegion.mRight, aContext, mPresContext, inherited) -
list->mImageRegion.x;
}
COMPUTE_END_INHERITED(List, list)
}
const void*
nsRuleNode::ComputePositionData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Position, (), pos, parentPos, Position, posData)
// box offsets: length, percent, auto, inherit
nsStyleCoord coord;
NS_FOR_CSS_SIDES(side) {
nsStyleCoord parentCoord = parentPos->mOffset.Get(side);
if (SetCoord(posData.mOffset.*(nsCSSRect::sides[side]),
coord, parentCoord, SETCOORD_LPAH | SETCOORD_INITIAL_AUTO,
aContext, mPresContext, inherited)) {
pos->mOffset.Set(side, coord);
}
}
SetCoord(posData.mWidth, pos->mWidth, parentPos->mWidth,
SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO, aContext,
mPresContext, inherited);
SetCoord(posData.mMinWidth, pos->mMinWidth, parentPos->mMinWidth,
SETCOORD_LPEH | SETCOORD_INITIAL_ZERO, aContext,
mPresContext, inherited);
SetCoord(posData.mMaxWidth, pos->mMaxWidth, parentPos->mMaxWidth,
SETCOORD_LPOEH | SETCOORD_INITIAL_NONE, aContext,
mPresContext, inherited);
SetCoord(posData.mHeight, pos->mHeight, parentPos->mHeight,
SETCOORD_LPAH | SETCOORD_INITIAL_AUTO, aContext,
mPresContext, inherited);
SetCoord(posData.mMinHeight, pos->mMinHeight, parentPos->mMinHeight,
SETCOORD_LPH | SETCOORD_INITIAL_ZERO, aContext,
mPresContext, inherited);
SetCoord(posData.mMaxHeight, pos->mMaxHeight, parentPos->mMaxHeight,
SETCOORD_LPOH | SETCOORD_INITIAL_NONE, aContext,
mPresContext, inherited);
// box-sizing: enum, inherit
if (eCSSUnit_Enumerated == posData.mBoxSizing.GetUnit()) {
pos->mBoxSizing = posData.mBoxSizing.GetIntValue();
}
else if (eCSSUnit_Inherit == posData.mBoxSizing.GetUnit()) {
inherited = PR_TRUE;
pos->mBoxSizing = parentPos->mBoxSizing;
}
else if (eCSSUnit_Initial == posData.mBoxSizing.GetUnit()) {
pos->mBoxSizing = NS_STYLE_BOX_SIZING_CONTENT;
}
// z-index
if (! SetCoord(posData.mZIndex, pos->mZIndex, parentPos->mZIndex,
SETCOORD_IA | SETCOORD_INITIAL_AUTO, aContext,
nsnull, inherited)) {
if (eCSSUnit_Inherit == posData.mZIndex.GetUnit()) {
// handle inherit, because it's ok to inherit 'auto' here
inherited = PR_TRUE;
pos->mZIndex = parentPos->mZIndex;
}
}
COMPUTE_END_RESET(Position, pos)
}
const void*
nsRuleNode::ComputeTableData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Table, (), table, parentTable, Table, tableData)
// table-layout: auto, enum, inherit
if (eCSSUnit_Enumerated == tableData.mLayout.GetUnit())
table->mLayoutStrategy = tableData.mLayout.GetIntValue();
else if (eCSSUnit_Auto == tableData.mLayout.GetUnit() ||
eCSSUnit_Initial == tableData.mLayout.GetUnit())
table->mLayoutStrategy = NS_STYLE_TABLE_LAYOUT_AUTO;
else if (eCSSUnit_Inherit == tableData.mLayout.GetUnit()) {
inherited = PR_TRUE;
table->mLayoutStrategy = parentTable->mLayoutStrategy;
}
// rules: enum (not a real CSS prop)
if (eCSSUnit_Enumerated == tableData.mRules.GetUnit())
table->mRules = tableData.mRules.GetIntValue();
// frame: enum (not a real CSS prop)
if (eCSSUnit_Enumerated == tableData.mFrame.GetUnit())
table->mFrame = tableData.mFrame.GetIntValue();
// cols: enum, int (not a real CSS prop)
if (eCSSUnit_Enumerated == tableData.mCols.GetUnit() ||
eCSSUnit_Integer == tableData.mCols.GetUnit())
table->mCols = tableData.mCols.GetIntValue();
// span: pixels (not a real CSS prop)
if (eCSSUnit_Enumerated == tableData.mSpan.GetUnit() ||
eCSSUnit_Integer == tableData.mSpan.GetUnit())
table->mSpan = tableData.mSpan.GetIntValue();
COMPUTE_END_RESET(Table, table)
}
const void*
nsRuleNode::ComputeTableBorderData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(TableBorder, (mPresContext), table, parentTable,
Table, tableData)
// border-collapse: enum, inherit
if (eCSSUnit_Enumerated == tableData.mBorderCollapse.GetUnit()) {
table->mBorderCollapse = tableData.mBorderCollapse.GetIntValue();
}
else if (eCSSUnit_Inherit == tableData.mBorderCollapse.GetUnit()) {
inherited = PR_TRUE;
table->mBorderCollapse = parentTable->mBorderCollapse;
}
else if (eCSSUnit_Initial == tableData.mBorderCollapse.GetUnit()) {
table->mBorderCollapse = NS_STYLE_BORDER_SEPARATE;
}
// border-spacing-x: length, inherit
SetCoord(tableData.mBorderSpacing.mXValue, table->mBorderSpacingX,
parentTable->mBorderSpacingX, SETCOORD_LH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited);
// border-spacing-y: length, inherit
SetCoord(tableData.mBorderSpacing.mYValue, table->mBorderSpacingY,
parentTable->mBorderSpacingY, SETCOORD_LH | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited);
// caption-side: enum, inherit
if (eCSSUnit_Enumerated == tableData.mCaptionSide.GetUnit()) {
table->mCaptionSide = tableData.mCaptionSide.GetIntValue();
}
else if (eCSSUnit_Inherit == tableData.mCaptionSide.GetUnit()) {
inherited = PR_TRUE;
table->mCaptionSide = parentTable->mCaptionSide;
}
else if (eCSSUnit_Initial == tableData.mCaptionSide.GetUnit()) {
table->mCaptionSide = NS_STYLE_CAPTION_SIDE_TOP;
}
// empty-cells: enum, inherit
if (eCSSUnit_Enumerated == tableData.mEmptyCells.GetUnit()) {
table->mEmptyCells = tableData.mEmptyCells.GetIntValue();
}
else if (eCSSUnit_Inherit == tableData.mEmptyCells.GetUnit()) {
inherited = PR_TRUE;
table->mEmptyCells = parentTable->mEmptyCells;
}
else if (eCSSUnit_Initial == tableData.mEmptyCells.GetUnit()) {
table->mEmptyCells =
(mPresContext->CompatibilityMode() == eCompatibility_NavQuirks)
? NS_STYLE_TABLE_EMPTY_CELLS_SHOW_BACKGROUND
: NS_STYLE_TABLE_EMPTY_CELLS_SHOW;
}
COMPUTE_END_INHERITED(TableBorder, table)
}
const void*
nsRuleNode::ComputeContentData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Content, (), content, parentContent,
Content, contentData)
// content: [string, url, counter, attr, enum]+, normal, none, inherit
PRUint32 count;
nsAutoString buffer;
nsCSSValueList* contentValue = contentData.mContent;
if (contentValue) {
if (eCSSUnit_Normal == contentValue->mValue.GetUnit() ||
eCSSUnit_None == contentValue->mValue.GetUnit() ||
eCSSUnit_Initial == contentValue->mValue.GetUnit()) {
// "normal", "none", and "initial" all mean no content
content->AllocateContents(0);
}
else if (eCSSUnit_Inherit == contentValue->mValue.GetUnit()) {
inherited = PR_TRUE;
count = parentContent->ContentCount();
if (NS_SUCCEEDED(content->AllocateContents(count))) {
while (0 < count--) {
content->ContentAt(count) = parentContent->ContentAt(count);
}
}
}
else {
count = 0;
while (contentValue) {
count++;
contentValue = contentValue->mNext;
}
if (NS_SUCCEEDED(content->AllocateContents(count))) {
const nsAutoString nullStr;
count = 0;
contentValue = contentData.mContent;
while (contentValue) {
const nsCSSValue& value = contentValue->mValue;
nsCSSUnit unit = value.GetUnit();
nsStyleContentType type;
nsStyleContentData &data = content->ContentAt(count++);
switch (unit) {
case eCSSUnit_String: type = eStyleContentType_String; break;
case eCSSUnit_Image: type = eStyleContentType_Image; break;
case eCSSUnit_Attr: type = eStyleContentType_Attr; break;
case eCSSUnit_Counter: type = eStyleContentType_Counter; break;
case eCSSUnit_Counters: type = eStyleContentType_Counters; break;
case eCSSUnit_Enumerated:
switch (value.GetIntValue()) {
case NS_STYLE_CONTENT_OPEN_QUOTE:
type = eStyleContentType_OpenQuote; break;
case NS_STYLE_CONTENT_CLOSE_QUOTE:
type = eStyleContentType_CloseQuote; break;
case NS_STYLE_CONTENT_NO_OPEN_QUOTE:
type = eStyleContentType_NoOpenQuote; break;
case NS_STYLE_CONTENT_NO_CLOSE_QUOTE:
type = eStyleContentType_NoCloseQuote; break;
case NS_STYLE_CONTENT_ALT_CONTENT:
type = eStyleContentType_AltContent; break;
default:
NS_ERROR("bad content value");
}
break;
default:
NS_ERROR("bad content type");
}
data.mType = type;
if (type == eStyleContentType_Image) {
data.mContent.mImage = value.GetImageValue();
NS_IF_ADDREF(data.mContent.mImage);
}
else if (type <= eStyleContentType_Attr) {
value.GetStringValue(buffer);
Unquote(buffer);
data.mContent.mString = NS_strdup(buffer.get());
}
else if (type <= eStyleContentType_Counters) {
data.mContent.mCounters = value.GetArrayValue();
data.mContent.mCounters->AddRef();
}
else {
data.mContent.mString = nsnull;
}
contentValue = contentValue->mNext;
}
}
}
}
// counter-increment: [string [int]]+, none, inherit
nsCSSCounterData* ourIncrement = contentData.mCounterIncrement;
if (ourIncrement) {
if (eCSSUnit_None == ourIncrement->mCounter.GetUnit() ||
eCSSUnit_Initial == ourIncrement->mCounter.GetUnit()) {
content->AllocateCounterIncrements(0);
}
else if (eCSSUnit_Inherit == ourIncrement->mCounter.GetUnit()) {
inherited = PR_TRUE;
count = parentContent->CounterIncrementCount();
if (NS_SUCCEEDED(content->AllocateCounterIncrements(count))) {
while (0 < count--) {
const nsStyleCounterData *data =
parentContent->GetCounterIncrementAt(count);
content->SetCounterIncrementAt(count, data->mCounter, data->mValue);
}
}
}
else if (eCSSUnit_String == ourIncrement->mCounter.GetUnit()) {
count = 0;
while (ourIncrement) {
count++;
ourIncrement = ourIncrement->mNext;
}
if (NS_SUCCEEDED(content->AllocateCounterIncrements(count))) {
count = 0;
ourIncrement = contentData.mCounterIncrement;
while (ourIncrement) {
PRInt32 increment;
if (eCSSUnit_Integer == ourIncrement->mValue.GetUnit()) {
increment = ourIncrement->mValue.GetIntValue();
}
else {
increment = 1;
}
ourIncrement->mCounter.GetStringValue(buffer);
content->SetCounterIncrementAt(count++, buffer, increment);
ourIncrement = ourIncrement->mNext;
}
}
}
}
// counter-reset: [string [int]]+, none, inherit
nsCSSCounterData* ourReset = contentData.mCounterReset;
if (ourReset) {
if (eCSSUnit_None == ourReset->mCounter.GetUnit() ||
eCSSUnit_Initial == ourReset->mCounter.GetUnit()) {
content->AllocateCounterResets(0);
}
else if (eCSSUnit_Inherit == ourReset->mCounter.GetUnit()) {
inherited = PR_TRUE;
count = parentContent->CounterResetCount();
if (NS_SUCCEEDED(content->AllocateCounterResets(count))) {
while (0 < count--) {
const nsStyleCounterData *data =
parentContent->GetCounterResetAt(count);
content->SetCounterResetAt(count, data->mCounter, data->mValue);
}
}
}
else if (eCSSUnit_String == ourReset->mCounter.GetUnit()) {
count = 0;
while (ourReset) {
count++;
ourReset = ourReset->mNext;
}
if (NS_SUCCEEDED(content->AllocateCounterResets(count))) {
count = 0;
ourReset = contentData.mCounterReset;
while (ourReset) {
PRInt32 reset;
if (eCSSUnit_Integer == ourReset->mValue.GetUnit()) {
reset = ourReset->mValue.GetIntValue();
}
else {
reset = 0;
}
ourReset->mCounter.GetStringValue(buffer);
content->SetCounterResetAt(count++, buffer, reset);
ourReset = ourReset->mNext;
}
}
}
}
// marker-offset: length, auto, inherit
SetCoord(contentData.mMarkerOffset, content->mMarkerOffset, parentContent->mMarkerOffset,
SETCOORD_LH | SETCOORD_AUTO | SETCOORD_INITIAL_AUTO, aContext,
mPresContext, inherited);
COMPUTE_END_RESET(Content, content)
}
const void*
nsRuleNode::ComputeQuotesData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(Quotes, (), quotes, parentQuotes,
Content, contentData)
// quotes: [string string]+, none, inherit
PRUint32 count;
nsAutoString buffer;
nsCSSQuotes* ourQuotes = contentData.mQuotes;
if (ourQuotes) {
nsAutoString closeBuffer;
// FIXME Bug 389406: Implement eCSSUnit_Initial (correctly, unlike
// style structs), and remove the "initial" value from ua.css.
if (eCSSUnit_Inherit == ourQuotes->mOpen.GetUnit()) {
inherited = PR_TRUE;
count = parentQuotes->QuotesCount();
if (NS_SUCCEEDED(quotes->AllocateQuotes(count))) {
while (0 < count--) {
parentQuotes->GetQuotesAt(count, buffer, closeBuffer);
quotes->SetQuotesAt(count, buffer, closeBuffer);
}
}
}
else if (eCSSUnit_None == ourQuotes->mOpen.GetUnit()) {
quotes->AllocateQuotes(0);
}
else if (eCSSUnit_String == ourQuotes->mOpen.GetUnit()) {
count = 0;
while (ourQuotes) {
count++;
ourQuotes = ourQuotes->mNext;
}
if (NS_SUCCEEDED(quotes->AllocateQuotes(count))) {
count = 0;
ourQuotes = contentData.mQuotes;
while (ourQuotes) {
ourQuotes->mOpen.GetStringValue(buffer);
ourQuotes->mClose.GetStringValue(closeBuffer);
Unquote(buffer);
Unquote(closeBuffer);
quotes->SetQuotesAt(count++, buffer, closeBuffer);
ourQuotes = ourQuotes->mNext;
}
}
}
}
COMPUTE_END_INHERITED(Quotes, quotes)
}
const void*
nsRuleNode::ComputeXULData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(XUL, (), xul, parentXUL, XUL, xulData)
// box-align: enum, inherit
if (eCSSUnit_Enumerated == xulData.mBoxAlign.GetUnit()) {
xul->mBoxAlign = xulData.mBoxAlign.GetIntValue();
}
else if (eCSSUnit_Inherit == xulData.mBoxAlign.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxAlign = parentXUL->mBoxAlign;
}
else if (eCSSUnit_Initial == xulData.mBoxAlign.GetUnit()) {
xul->mBoxAlign = NS_STYLE_BOX_ALIGN_STRETCH;
}
// box-direction: enum, inherit
if (eCSSUnit_Enumerated == xulData.mBoxDirection.GetUnit()) {
xul->mBoxDirection = xulData.mBoxDirection.GetIntValue();
}
else if (eCSSUnit_Inherit == xulData.mBoxDirection.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxDirection = parentXUL->mBoxDirection;
}
else if (eCSSUnit_Initial == xulData.mBoxDirection.GetUnit()) {
xul->mBoxDirection = NS_STYLE_BOX_DIRECTION_NORMAL;
}
// box-flex: factor, inherit
if (eCSSUnit_Number == xulData.mBoxFlex.GetUnit()) {
xul->mBoxFlex = xulData.mBoxFlex.GetFloatValue();
}
else if (eCSSUnit_Inherit == xulData.mBoxFlex.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxFlex = parentXUL->mBoxFlex;
}
else if (eCSSUnit_Initial == xulData.mBoxFlex.GetUnit()) {
xul->mBoxFlex = 0.0f;
}
// box-orient: enum, inherit
if (eCSSUnit_Enumerated == xulData.mBoxOrient.GetUnit()) {
xul->mBoxOrient = xulData.mBoxOrient.GetIntValue();
}
else if (eCSSUnit_Inherit == xulData.mBoxOrient.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxOrient = parentXUL->mBoxOrient;
}
else if (eCSSUnit_Initial == xulData.mBoxOrient.GetUnit()) {
xul->mBoxOrient = NS_STYLE_BOX_ORIENT_HORIZONTAL;
}
// box-pack: enum, inherit
if (eCSSUnit_Enumerated == xulData.mBoxPack.GetUnit()) {
xul->mBoxPack = xulData.mBoxPack.GetIntValue();
}
else if (eCSSUnit_Inherit == xulData.mBoxPack.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxPack = parentXUL->mBoxPack;
}
else if (eCSSUnit_Initial == xulData.mBoxPack.GetUnit()) {
xul->mBoxPack = NS_STYLE_BOX_PACK_START;
}
// box-ordinal-group: integer
if (eCSSUnit_Integer == xulData.mBoxOrdinal.GetUnit()) {
xul->mBoxOrdinal = xulData.mBoxOrdinal.GetIntValue();
} else if (eCSSUnit_Inherit == xulData.mBoxOrdinal.GetUnit()) {
inherited = PR_TRUE;
xul->mBoxOrdinal = parentXUL->mBoxOrdinal;
} else if (eCSSUnit_Initial == xulData.mBoxOrdinal.GetUnit()) {
xul->mBoxOrdinal = 1;
}
COMPUTE_END_RESET(XUL, xul)
}
const void*
nsRuleNode::ComputeColumnData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(Column, (), column, parent, Column, columnData)
// column-width: length, auto, inherit
SetCoord(columnData.mColumnWidth,
column->mColumnWidth, parent->mColumnWidth,
SETCOORD_LAH | SETCOORD_INITIAL_AUTO,
aContext, mPresContext, inherited);
// column-gap: length, percentage, inherit, normal
SetCoord(columnData.mColumnGap,
column->mColumnGap, parent->mColumnGap,
SETCOORD_LPH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL,
aContext, mPresContext, inherited);
// column-count: auto, integer, inherit
if (eCSSUnit_Auto == columnData.mColumnCount.GetUnit() ||
eCSSUnit_Initial == columnData.mColumnCount.GetUnit()) {
column->mColumnCount = NS_STYLE_COLUMN_COUNT_AUTO;
} else if (eCSSUnit_Integer == columnData.mColumnCount.GetUnit()) {
column->mColumnCount = columnData.mColumnCount.GetIntValue();
// Max 1000 columns - wallpaper for bug 345583.
column->mColumnCount = PR_MIN(column->mColumnCount, 1000);
} else if (eCSSUnit_Inherit == columnData.mColumnCount.GetUnit()) {
inherited = PR_TRUE;
column->mColumnCount = parent->mColumnCount;
}
COMPUTE_END_RESET(Column, column)
}
#ifdef MOZ_SVG
static void
SetSVGPaint(const nsCSSValuePair& aValue, const nsStyleSVGPaint& parentPaint,
nsPresContext* aPresContext, nsStyleContext *aContext,
nsStyleSVGPaint& aResult, nsStyleSVGPaintType aInitialPaintType,
PRBool& aInherited)
{
nscolor color;
if (aValue.mXValue.GetUnit() == eCSSUnit_Inherit) {
aResult = parentPaint;
aInherited = PR_TRUE;
} else if (aValue.mXValue.GetUnit() == eCSSUnit_None) {
aResult.SetType(eStyleSVGPaintType_None);
} else if (aValue.mXValue.GetUnit() == eCSSUnit_Initial) {
aResult.SetType(aInitialPaintType);
aResult.mPaint.mColor = NS_RGB(0, 0, 0);
aResult.mFallbackColor = NS_RGB(0, 0, 0);
} else if (aValue.mXValue.GetUnit() == eCSSUnit_URL) {
aResult.SetType(eStyleSVGPaintType_Server);
aResult.mPaint.mPaintServer = aValue.mXValue.GetURLValue();
NS_IF_ADDREF(aResult.mPaint.mPaintServer);
if (aValue.mYValue.GetUnit() == eCSSUnit_None) {
aResult.mFallbackColor = NS_RGBA(0, 0, 0, 0);
} else {
NS_ASSERTION(aValue.mYValue.GetUnit() != eCSSUnit_Inherit, "cannot inherit fallback colour");
SetColor(aValue.mYValue, NS_RGB(0, 0, 0), aPresContext, aContext, aResult.mFallbackColor, aInherited);
}
} else if (SetColor(aValue.mXValue, parentPaint.mPaint.mColor, aPresContext, aContext, color, aInherited)) {
aResult.SetType(eStyleSVGPaintType_Color);
aResult.mPaint.mColor = color;
}
}
static void
SetSVGOpacity(const nsCSSValue& aValue, float parentOpacity, float& opacity, PRBool& aInherited)
{
if (aValue.GetUnit() == eCSSUnit_Inherit) {
opacity = parentOpacity;
aInherited = PR_TRUE;
}
else if (aValue.GetUnit() == eCSSUnit_Number) {
opacity = aValue.GetFloatValue();
opacity = PR_MAX(opacity, 0.0f);
opacity = PR_MIN(opacity, 1.0f);
}
else if (aValue.GetUnit() == eCSSUnit_Initial) {
opacity = 1.0f;
}
}
const void*
nsRuleNode::ComputeSVGData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_INHERITED(SVG, (), svg, parentSVG, SVG, SVGData)
// clip-rule: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mClipRule.GetUnit()) {
svg->mClipRule = SVGData.mClipRule.GetIntValue();
}
else if (eCSSUnit_Inherit == SVGData.mClipRule.GetUnit()) {
inherited = PR_TRUE;
svg->mClipRule = parentSVG->mClipRule;
}
else if (eCSSUnit_Initial == SVGData.mClipRule.GetUnit()) {
svg->mClipRule = NS_STYLE_FILL_RULE_NONZERO;
}
// color-interpolation: auto, sRGB, linearRGB, inherit
if (eCSSUnit_Enumerated == SVGData.mColorInterpolation.GetUnit()) {
svg->mColorInterpolation = SVGData.mColorInterpolation.GetIntValue();
}
else if (eCSSUnit_Auto == SVGData.mColorInterpolation.GetUnit()) {
svg->mColorInterpolation = NS_STYLE_COLOR_INTERPOLATION_AUTO;
}
else if (eCSSUnit_Inherit == SVGData.mColorInterpolation.GetUnit()) {
inherited = PR_TRUE;
svg->mColorInterpolation = parentSVG->mColorInterpolation;
}
else if (eCSSUnit_Initial == SVGData.mColorInterpolation.GetUnit()) {
svg->mColorInterpolation = NS_STYLE_COLOR_INTERPOLATION_SRGB;
}
// color-interpolation-filters: auto, sRGB, linearRGB, inherit
if (eCSSUnit_Enumerated == SVGData.mColorInterpolationFilters.GetUnit()) {
svg->mColorInterpolationFilters = SVGData.mColorInterpolationFilters.GetIntValue();
}
else if (eCSSUnit_Auto == SVGData.mColorInterpolationFilters.GetUnit()) {
svg->mColorInterpolationFilters = NS_STYLE_COLOR_INTERPOLATION_AUTO;
}
else if (eCSSUnit_Inherit == SVGData.mColorInterpolationFilters.GetUnit()) {
inherited = PR_TRUE;
svg->mColorInterpolationFilters = parentSVG->mColorInterpolationFilters;
}
else if (eCSSUnit_Initial == SVGData.mColorInterpolationFilters.GetUnit()) {
svg->mColorInterpolationFilters = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
}
// fill:
SetSVGPaint(SVGData.mFill, parentSVG->mFill, mPresContext, aContext,
svg->mFill, eStyleSVGPaintType_Color, inherited);
// fill-opacity:
SetSVGOpacity(SVGData.mFillOpacity, parentSVG->mFillOpacity,
svg->mFillOpacity, inherited);
// fill-rule: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mFillRule.GetUnit()) {
svg->mFillRule = SVGData.mFillRule.GetIntValue();
}
else if (eCSSUnit_Inherit == SVGData.mFillRule.GetUnit()) {
inherited = PR_TRUE;
svg->mFillRule = parentSVG->mFillRule;
}
else if (eCSSUnit_Initial == SVGData.mFillRule.GetUnit()) {
svg->mFillRule = NS_STYLE_FILL_RULE_NONZERO;
}
// marker-end: url, none, inherit
if (eCSSUnit_URL == SVGData.mMarkerEnd.GetUnit()) {
svg->mMarkerEnd = SVGData.mMarkerEnd.GetURLValue();
} else if (eCSSUnit_None == SVGData.mMarkerEnd.GetUnit() ||
eCSSUnit_Initial == SVGData.mMarkerEnd.GetUnit()) {
svg->mMarkerEnd = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mMarkerEnd.GetUnit()) {
inherited = PR_TRUE;
svg->mMarkerEnd = parentSVG->mMarkerEnd;
}
// marker-mid: url, none, inherit
if (eCSSUnit_URL == SVGData.mMarkerMid.GetUnit()) {
svg->mMarkerMid = SVGData.mMarkerMid.GetURLValue();
} else if (eCSSUnit_None == SVGData.mMarkerMid.GetUnit() ||
eCSSUnit_Initial == SVGData.mMarkerMid.GetUnit()) {
svg->mMarkerMid = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mMarkerMid.GetUnit()) {
inherited = PR_TRUE;
svg->mMarkerMid = parentSVG->mMarkerMid;
}
// marker-start: url, none, inherit
if (eCSSUnit_URL == SVGData.mMarkerStart.GetUnit()) {
svg->mMarkerStart = SVGData.mMarkerStart.GetURLValue();
} else if (eCSSUnit_None == SVGData.mMarkerStart.GetUnit() ||
eCSSUnit_Initial == SVGData.mMarkerStart.GetUnit()) {
svg->mMarkerStart = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mMarkerStart.GetUnit()) {
inherited = PR_TRUE;
svg->mMarkerStart = parentSVG->mMarkerStart;
}
// pointer-events: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mPointerEvents.GetUnit()) {
svg->mPointerEvents = SVGData.mPointerEvents.GetIntValue();
} else if (eCSSUnit_None == SVGData.mPointerEvents.GetUnit()) {
svg->mPointerEvents = NS_STYLE_POINTER_EVENTS_NONE;
} else if (eCSSUnit_Inherit == SVGData.mPointerEvents.GetUnit()) {
inherited = PR_TRUE;
svg->mPointerEvents = parentSVG->mPointerEvents;
} else if (eCSSUnit_Initial == SVGData.mPointerEvents.GetUnit()) {
svg->mPointerEvents = NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED;
}
// shape-rendering: enum, auto, inherit
if (eCSSUnit_Enumerated == SVGData.mShapeRendering.GetUnit()) {
svg->mShapeRendering = SVGData.mShapeRendering.GetIntValue();
}
else if (eCSSUnit_Auto == SVGData.mShapeRendering.GetUnit() ||
eCSSUnit_Initial == SVGData.mShapeRendering.GetUnit()) {
svg->mShapeRendering = NS_STYLE_SHAPE_RENDERING_AUTO;
}
else if (eCSSUnit_Inherit == SVGData.mShapeRendering.GetUnit()) {
inherited = PR_TRUE;
svg->mShapeRendering = parentSVG->mShapeRendering;
}
// stroke:
SetSVGPaint(SVGData.mStroke, parentSVG->mStroke, mPresContext, aContext,
svg->mStroke, eStyleSVGPaintType_None, inherited);
// stroke-dasharray: <dasharray>, none, inherit
nsCSSValueList *list = SVGData.mStrokeDasharray;
if (list) {
if (eCSSUnit_Inherit == list->mValue.GetUnit()) {
// only do the copy if weren't already set up by the copy constructor
// FIXME Bug 389408: This is broken when aStartStruct is non-null!
if (!svg->mStrokeDasharray) {
inherited = PR_TRUE;
svg->mStrokeDasharrayLength = parentSVG->mStrokeDasharrayLength;
if (svg->mStrokeDasharrayLength) {
svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength];
if (svg->mStrokeDasharray)
memcpy(svg->mStrokeDasharray,
parentSVG->mStrokeDasharray,
svg->mStrokeDasharrayLength * sizeof(nsStyleCoord));
else
svg->mStrokeDasharrayLength = 0;
}
}
} else {
delete [] svg->mStrokeDasharray;
svg->mStrokeDasharray = nsnull;
svg->mStrokeDasharrayLength = 0;
if (eCSSUnit_Initial != list->mValue.GetUnit() &&
eCSSUnit_None != list->mValue.GetUnit()) {
// count number of values
nsCSSValueList *value = SVGData.mStrokeDasharray;
while (nsnull != value) {
++svg->mStrokeDasharrayLength;
value = value->mNext;
}
NS_ASSERTION(svg->mStrokeDasharrayLength != 0, "no dasharray items");
svg->mStrokeDasharray = new nsStyleCoord[svg->mStrokeDasharrayLength];
if (svg->mStrokeDasharray) {
value = SVGData.mStrokeDasharray;
PRUint32 i = 0;
while (nsnull != value) {
SetCoord(value->mValue,
svg->mStrokeDasharray[i++], nsnull,
SETCOORD_LP | SETCOORD_FACTOR,
aContext, mPresContext, inherited);
value = value->mNext;
}
} else
svg->mStrokeDasharrayLength = 0;
}
}
}
// stroke-dashoffset: <dashoffset>, inherit
SetCoord(SVGData.mStrokeDashoffset,
svg->mStrokeDashoffset, parentSVG->mStrokeDashoffset,
SETCOORD_LPH | SETCOORD_FACTOR | SETCOORD_INITIAL_ZERO,
aContext, mPresContext, inherited);
// stroke-linecap: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mStrokeLinecap.GetUnit()) {
svg->mStrokeLinecap = SVGData.mStrokeLinecap.GetIntValue();
}
else if (eCSSUnit_Inherit == SVGData.mStrokeLinecap.GetUnit()) {
inherited = PR_TRUE;
svg->mStrokeLinecap = parentSVG->mStrokeLinecap;
}
else if (eCSSUnit_Initial == SVGData.mStrokeLinecap.GetUnit()) {
svg->mStrokeLinecap = NS_STYLE_STROKE_LINECAP_BUTT;
}
// stroke-linejoin: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mStrokeLinejoin.GetUnit()) {
svg->mStrokeLinejoin = SVGData.mStrokeLinejoin.GetIntValue();
}
else if (eCSSUnit_Inherit == SVGData.mStrokeLinejoin.GetUnit()) {
inherited = PR_TRUE;
svg->mStrokeLinejoin = parentSVG->mStrokeLinejoin;
}
else if (eCSSUnit_Initial == SVGData.mStrokeLinejoin.GetUnit()) {
svg->mStrokeLinejoin = NS_STYLE_STROKE_LINEJOIN_MITER;
}
// stroke-miterlimit: <miterlimit>, inherit
if (eCSSUnit_Number == SVGData.mStrokeMiterlimit.GetUnit()) {
svg->mStrokeMiterlimit = SVGData.mStrokeMiterlimit.GetFloatValue();
}
else if (eCSSUnit_Inherit == SVGData.mStrokeMiterlimit.GetUnit()) {
svg->mStrokeMiterlimit = parentSVG->mStrokeMiterlimit;
inherited = PR_TRUE;
}
else if (eCSSUnit_Initial == SVGData.mStrokeMiterlimit.GetUnit()) {
svg->mStrokeMiterlimit = 4.0f;
}
// stroke-opacity:
SetSVGOpacity(SVGData.mStrokeOpacity, parentSVG->mStrokeOpacity,
svg->mStrokeOpacity, inherited);
// stroke-width:
if (eCSSUnit_Initial == SVGData.mStrokeWidth.GetUnit()) {
svg->mStrokeWidth.SetCoordValue(nsPresContext::CSSPixelsToAppUnits(1));
} else {
SetCoord(SVGData.mStrokeWidth,
svg->mStrokeWidth, parentSVG->mStrokeWidth,
SETCOORD_LPH | SETCOORD_FACTOR,
aContext, mPresContext, inherited);
}
// text-anchor: enum, inherit
if (eCSSUnit_Enumerated == SVGData.mTextAnchor.GetUnit()) {
svg->mTextAnchor = SVGData.mTextAnchor.GetIntValue();
}
else if (eCSSUnit_Inherit == SVGData.mTextAnchor.GetUnit()) {
inherited = PR_TRUE;
svg->mTextAnchor = parentSVG->mTextAnchor;
}
else if (eCSSUnit_Initial == SVGData.mTextAnchor.GetUnit()) {
svg->mTextAnchor = NS_STYLE_TEXT_ANCHOR_START;
}
// text-rendering: enum, auto, inherit
if (eCSSUnit_Enumerated == SVGData.mTextRendering.GetUnit()) {
svg->mTextRendering = SVGData.mTextRendering.GetIntValue();
}
else if (eCSSUnit_Auto == SVGData.mTextRendering.GetUnit() ||
eCSSUnit_Initial == SVGData.mTextRendering.GetUnit()) {
svg->mTextRendering = NS_STYLE_TEXT_RENDERING_AUTO;
}
else if (eCSSUnit_Inherit == SVGData.mTextRendering.GetUnit()) {
inherited = PR_TRUE;
svg->mTextRendering = parentSVG->mTextRendering;
}
COMPUTE_END_INHERITED(SVG, svg)
}
const void*
nsRuleNode::ComputeSVGResetData(void* aStartStruct,
const nsRuleDataStruct& aData,
nsStyleContext* aContext,
nsRuleNode* aHighestNode,
const RuleDetail aRuleDetail, PRBool aInherited)
{
COMPUTE_START_RESET(SVGReset, (), svgReset, parentSVGReset, SVG, SVGData)
// stop-color:
if (eCSSUnit_Initial == SVGData.mStopColor.GetUnit()) {
svgReset->mStopColor = NS_RGB(0, 0, 0);
} else {
SetColor(SVGData.mStopColor, parentSVGReset->mStopColor,
mPresContext, aContext, svgReset->mStopColor, inherited);
}
// flood-color:
if (eCSSUnit_Initial == SVGData.mFloodColor.GetUnit()) {
svgReset->mFloodColor = NS_RGB(0, 0, 0);
} else {
SetColor(SVGData.mFloodColor, parentSVGReset->mFloodColor,
mPresContext, aContext, svgReset->mFloodColor, inherited);
}
// lighting-color:
if (eCSSUnit_Initial == SVGData.mLightingColor.GetUnit()) {
svgReset->mLightingColor = NS_RGB(255, 255, 255);
} else {
SetColor(SVGData.mLightingColor, parentSVGReset->mLightingColor,
mPresContext, aContext, svgReset->mLightingColor, inherited);
}
// clip-path: url, none, inherit
if (eCSSUnit_URL == SVGData.mClipPath.GetUnit()) {
svgReset->mClipPath = SVGData.mClipPath.GetURLValue();
} else if (eCSSUnit_None == SVGData.mClipPath.GetUnit() ||
eCSSUnit_Initial == SVGData.mClipPath.GetUnit()) {
svgReset->mClipPath = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mClipPath.GetUnit()) {
inherited = PR_TRUE;
svgReset->mClipPath = parentSVGReset->mClipPath;
}
// stop-opacity:
SetSVGOpacity(SVGData.mStopOpacity, parentSVGReset->mStopOpacity,
svgReset->mStopOpacity, inherited);
// flood-opacity:
SetSVGOpacity(SVGData.mFloodOpacity, parentSVGReset->mFloodOpacity,
svgReset->mFloodOpacity, inherited);
// dominant-baseline: enum, auto, inherit
if (eCSSUnit_Enumerated == SVGData.mDominantBaseline.GetUnit()) {
svgReset->mDominantBaseline = SVGData.mDominantBaseline.GetIntValue();
}
else if (eCSSUnit_Auto == SVGData.mDominantBaseline.GetUnit() ||
eCSSUnit_Initial == SVGData.mDominantBaseline.GetUnit()) {
svgReset->mDominantBaseline = NS_STYLE_DOMINANT_BASELINE_AUTO;
}
else if (eCSSUnit_Inherit == SVGData.mDominantBaseline.GetUnit()) {
inherited = PR_TRUE;
svgReset->mDominantBaseline = parentSVGReset->mDominantBaseline;
}
// filter: url, none, inherit
if (eCSSUnit_URL == SVGData.mFilter.GetUnit()) {
svgReset->mFilter = SVGData.mFilter.GetURLValue();
} else if (eCSSUnit_None == SVGData.mFilter.GetUnit() ||
eCSSUnit_Initial == SVGData.mFilter.GetUnit()) {
svgReset->mFilter = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mFilter.GetUnit()) {
inherited = PR_TRUE;
svgReset->mFilter = parentSVGReset->mFilter;
}
// mask: url, none, inherit
if (eCSSUnit_URL == SVGData.mMask.GetUnit()) {
svgReset->mMask = SVGData.mMask.GetURLValue();
} else if (eCSSUnit_None == SVGData.mMask.GetUnit() ||
eCSSUnit_Initial == SVGData.mMask.GetUnit()) {
svgReset->mMask = nsnull;
} else if (eCSSUnit_Inherit == SVGData.mMask.GetUnit()) {
inherited = PR_TRUE;
svgReset->mMask = parentSVGReset->mMask;
}
COMPUTE_END_RESET(SVGReset, svgReset)
}
#endif
inline const void*
nsRuleNode::GetParentData(const nsStyleStructID aSID)
{
NS_PRECONDITION(mDependentBits & nsCachedStyleData::GetBitForSID(aSID),
"should be called when node depends on parent data");
NS_ASSERTION(mStyleData.GetStyleData(aSID) == nsnull,
"both struct and dependent bits present");
// Walk up the rule tree from this rule node (towards less specific
// rules).
PRUint32 bit = nsCachedStyleData::GetBitForSID(aSID);
nsRuleNode *ruleNode = mParent;
while (ruleNode->mDependentBits & bit) {
NS_ASSERTION(ruleNode->mStyleData.GetStyleData(aSID) == nsnull,
"both struct and dependent bits present");
ruleNode = ruleNode->mParent;
}
return ruleNode->mStyleData.GetStyleData(aSID);
}
#define STYLE_STRUCT(name_, checkdata_cb_, ctor_args_) \
inline const nsStyle##name_ * \
nsRuleNode::GetParent##name_() \
{ \
NS_PRECONDITION(mDependentBits & \
nsCachedStyleData::GetBitForSID(eStyleStruct_##name_), \
"should be called when node depends on parent data"); \
NS_ASSERTION(mStyleData.GetStyle##name_() == nsnull, \
"both struct and dependent bits present"); \
/* Walk up the rule tree from this rule node (towards less specific */ \
/* rules). */ \
PRUint32 bit = nsCachedStyleData::GetBitForSID(eStyleStruct_##name_); \
nsRuleNode *ruleNode = mParent; \
while (ruleNode->mDependentBits & bit) { \
NS_ASSERTION(ruleNode->mStyleData.GetStyle##name_() == nsnull, \
"both struct and dependent bits present"); \
ruleNode = ruleNode->mParent; \
} \
\
return ruleNode->mStyleData.GetStyle##name_(); \
}
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
const void*
nsRuleNode::GetStyleData(nsStyleStructID aSID,
nsStyleContext* aContext,
PRBool aComputeData)
{
const void *data;
if (mDependentBits & nsCachedStyleData::GetBitForSID(aSID)) {
// We depend on an ancestor for this struct since the cached struct
// it has is also appropriate for this rule node. Just go up the
// rule tree and return the first cached struct we find.
data = GetParentData(aSID);
NS_ASSERTION(data, "dependent bits set but no cached struct present");
return data;
}
data = mStyleData.GetStyleData(aSID);
if (NS_LIKELY(data != nsnull))
return data; // We have a fully specified struct. Just return it.
if (NS_UNLIKELY(!aComputeData))
return nsnull;
// Nothing is cached. We'll have to delve further and examine our rules.
#define STYLE_STRUCT_TEST aSID
#define STYLE_STRUCT(name, checkdata_cb, ctor_args) \
data = Get##name##Data(aContext);
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
#undef STYLE_STRUCT_TEST
if (NS_LIKELY(data != nsnull))
return data;
NS_NOTREACHED("could not create style struct");
// To ensure that |GetStyleData| never returns null (even when we're
// out of memory), we'll get the style set and get a copy of the
// default values for the given style struct from the set. Note that
// this works fine even if |this| is a rule node that has been
// destroyed (leftover from a previous rule tree) but is somehow still
// used.
return mPresContext->PresShell()->StyleSet()->
DefaultStyleData()->GetStyleData(aSID);
}
// See comments above in GetStyleData for an explanation of what the
// code below does.
#define STYLE_STRUCT(name_, checkdata_cb_, ctor_args_) \
const nsStyle##name_* \
nsRuleNode::GetStyle##name_(nsStyleContext* aContext, PRBool aComputeData) \
{ \
const nsStyle##name_ *data; \
if (mDependentBits & \
nsCachedStyleData::GetBitForSID(eStyleStruct_##name_)) { \
data = GetParent##name_(); \
NS_ASSERTION(data, "dependent bits set but no cached struct present"); \
return data; \
} \
\
data = mStyleData.GetStyle##name_(); \
if (NS_LIKELY(data != nsnull)) \
return data; \
\
if (NS_UNLIKELY(!aComputeData)) \
return nsnull; \
\
data = \
static_cast<const nsStyle##name_ *>(Get##name_##Data(aContext)); \
\
if (NS_LIKELY(data != nsnull)) \
return data; \
\
NS_NOTREACHED("could not create style struct"); \
return \
static_cast<const nsStyle##name_ *>( \
mPresContext->PresShell()->StyleSet()-> \
DefaultStyleData()->GetStyleData(eStyleStruct_##name_)); \
}
#include "nsStyleStructList.h"
#undef STYLE_STRUCT
void
nsRuleNode::Mark()
{
for (nsRuleNode *node = this;
node && !(node->mDependentBits & NS_RULE_NODE_GC_MARK);
node = node->mParent)
node->mDependentBits |= NS_RULE_NODE_GC_MARK;
}
PR_STATIC_CALLBACK(PLDHashOperator)
SweepRuleNodeChildren(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(hdr);
if (entry->mRuleNode->Sweep())
return PL_DHASH_REMOVE; // implies NEXT, unless |ed with STOP
return PL_DHASH_NEXT;
}
PRBool
nsRuleNode::Sweep()
{
// If we're not marked, then we have to delete ourself.
// However, we never allow the root node to GC itself, because nsStyleSet
// wants to hold onto the root node and not worry about re-creating a
// rule walker if the root node is deleted.
if (!(mDependentBits & NS_RULE_NODE_GC_MARK) && !IsRoot()) {
Destroy();
return PR_TRUE;
}
// Clear our mark, for the next time around.
mDependentBits &= ~NS_RULE_NODE_GC_MARK;
// Call sweep on the children, since some may not be marked, and
// remove any deleted children from the child lists.
if (HaveChildren()) {
if (ChildrenAreHashed()) {
PLDHashTable *children = ChildrenHash();
PL_DHashTableEnumerate(children, SweepRuleNodeChildren, nsnull);
} else {
for (nsRuleList **children = ChildrenListPtr(); *children; ) {
if ((*children)->mRuleNode->Sweep()) {
// This rule node was destroyed, so remove this entry, and
// implicitly advance by making *children point to the next
// entry.
*children = (*children)->DestroySelf(mPresContext);
} else {
// Advance.
children = &(*children)->mNext;
}
}
}
}
return PR_FALSE;
}
/* static */ PRBool
nsRuleNode::HasAuthorSpecifiedBorderOrBackground(nsStyleContext* aStyleContext)
{
nsRuleDataColor colorData;
nsRuleDataMargin marginData;
/* We're relying on the use of |aStyleContext| not mutating it! */
nsRuleData ruleData(NS_STYLE_INHERIT_BIT(Background) |
NS_STYLE_INHERIT_BIT(Border),
aStyleContext->PresContext(), aStyleContext);
ruleData.mColorData = &colorData;
ruleData.mMarginData = &marginData;
nsCSSValue* values[] = {
&colorData.mBackColor,
&colorData.mBackImage,
&marginData.mBorderColor.mTop,
&marginData.mBorderStyle.mTop,
&marginData.mBorderWidth.mTop,
&marginData.mBorderColor.mRight,
&marginData.mBorderStyle.mRight,
&marginData.mBorderWidth.mRight,
&marginData.mBorderColor.mBottom,
&marginData.mBorderStyle.mBottom,
&marginData.mBorderWidth.mBottom,
&marginData.mBorderColor.mLeft,
&marginData.mBorderStyle.mLeft,
&marginData.mBorderWidth.mLeft
};
// We need to be careful not to count styles covered up by
// user-important or UA-important declarations.
for (nsRuleNode* ruleNode = aStyleContext->GetRuleNode(); ruleNode;
ruleNode = ruleNode->GetParent()) {
nsIStyleRule *rule = ruleNode->GetRule();
if (rule) {
ruleData.mLevel = ruleNode->GetLevel();
ruleData.mIsImportantRule = ruleNode->IsImportantRule();
rule->MapRuleInfoInto(&ruleData);
if (ruleData.mLevel == nsStyleSet::eAgentSheet ||
ruleData.mLevel == nsStyleSet::eUserSheet) {
// This is a rule whose effect we want to ignore, so if any of
// the properties we care about were set, set them to the dummy
// value that they'll never otherwise get.
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(values); ++i)
if (values[i]->GetUnit() != eCSSUnit_Null)
values[i]->SetDummyValue();
} else {
// If any of the values we care about was set by the above rule,
// we have author style.
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(values); ++i)
if (values[i]->GetUnit() != eCSSUnit_Null &&
values[i]->GetUnit() != eCSSUnit_Dummy) // see above
return PR_TRUE;
}
}
}
return PR_FALSE;
}