/* -*- 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 * Roger B. Sidje * Mats Palmgren * L. David Baron * * 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(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(aHdr); const nsRuleNode::Key *key = static_cast(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 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(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 (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 (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(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(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 (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSRect* RectAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return reinterpret_cast (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSValuePair* ValuePairAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return reinterpret_cast (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSValueList* ValueListAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return * reinterpret_cast (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSValueList** ValueListArrayAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return * reinterpret_cast (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSCounterData* CounterDataAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return * reinterpret_cast (reinterpret_cast(&aRuleDataStruct) + aOffset); } inline const nsCSSQuotes* QuotesAtOffset(const nsRuleDataStruct& aRuleDataStruct, size_t aOffset) { return * reinterpret_cast (reinterpret_cast(&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(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(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(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(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(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(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(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) { // 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(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: , 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: , 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: , 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(Get##name_##Data(aContext)); \ \ if (NS_LIKELY(data != nsnull)) \ return data; \ \ NS_NOTREACHED("could not create style struct"); \ return \ static_cast( \ 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(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; }