forked from mirrors/gecko-dev
		
	 96722433e6
			
		
	
	
		96722433e6
		
	
	
	
	
		
			
			MozReview-Commit-ID: AnurVwl65fX --HG-- extra : rebase_source : 05d3820aab2c86b010728b5d2882a7a2e75e0617
		
			
				
	
	
		
			1528 lines
		
	
	
	
		
			43 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1528 lines
		
	
	
	
		
			43 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * representation of CSS style rules (selectors+declaration), CSS
 | |
|  * selectors, and DOM objects for style rules, selectors, and
 | |
|  * declarations
 | |
|  */
 | |
| 
 | |
| #include "mozilla/css/StyleRule.h"
 | |
| 
 | |
| #include "mozilla/DeclarationBlockInlines.h"
 | |
| #include "mozilla/StyleSheetInlines.h"
 | |
| #include "mozilla/MemoryReporting.h"
 | |
| #include "mozilla/css/GroupRule.h"
 | |
| #include "mozilla/css/Declaration.h"
 | |
| #include "mozilla/dom/CSSStyleRuleBinding.h"
 | |
| #include "nsIDocument.h"
 | |
| #include "nsAtom.h"
 | |
| #include "nsString.h"
 | |
| #include "nsStyleUtil.h"
 | |
| #include "nsDOMCSSDeclaration.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "nsXMLNameSpaceMap.h"
 | |
| #include "nsCSSPseudoClasses.h"
 | |
| #include "nsCSSAnonBoxes.h"
 | |
| #include "nsTArray.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsError.h"
 | |
| #include "mozAutoDocUpdate.h"
 | |
| #include "nsRuleProcessorData.h"
 | |
| 
 | |
| class nsIDOMCSSStyleDeclaration;
 | |
| class nsIDOMCSSStyleSheet;
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| #define NS_IF_CLONE(member_)                                                  \
 | |
|   PR_BEGIN_MACRO                                                              \
 | |
|     if (member_) {                                                            \
 | |
|       result->member_ = member_->Clone();                                     \
 | |
|       if (!result->member_) {                                                 \
 | |
|         delete result;                                                        \
 | |
|         return nullptr;                                                        \
 | |
|       }                                                                       \
 | |
|     }                                                                         \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| #define NS_IF_DELETE(ptr)                                                     \
 | |
|   PR_BEGIN_MACRO                                                              \
 | |
|     delete ptr;                                                               \
 | |
|     ptr = nullptr;                                                             \
 | |
|   PR_END_MACRO
 | |
| 
 | |
| /* ************************************************************************** */
 | |
| 
 | |
| nsAtomList::nsAtomList(nsAtom* aAtom)
 | |
|   : mAtom(aAtom),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsAtomList);
 | |
| }
 | |
| 
 | |
| nsAtomList::nsAtomList(const nsString& aAtomValue)
 | |
|   : mAtom(nullptr),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsAtomList);
 | |
|   mAtom = NS_Atomize(aAtomValue);
 | |
| }
 | |
| 
 | |
| nsAtomList*
 | |
| nsAtomList::Clone(bool aDeep) const
 | |
| {
 | |
|   nsAtomList *result = new nsAtomList(mAtom);
 | |
|   if (!result)
 | |
|     return nullptr;
 | |
| 
 | |
|   if (aDeep)
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false));
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   const nsAtomList* a = this;
 | |
|   while (a) {
 | |
|     n += aMallocSizeOf(a);
 | |
| 
 | |
|     // The following members aren't measured:
 | |
|     // - a->mAtom, because it may be shared
 | |
| 
 | |
|     a = a->mNext;
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| nsAtomList::~nsAtomList(void)
 | |
| {
 | |
|   MOZ_COUNT_DTOR(nsAtomList);
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsAtomList, this, mNext);
 | |
| }
 | |
| 
 | |
| nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType)
 | |
|   : mType(aType),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) &&
 | |
|                !nsCSSPseudoClasses::HasNthPairArg(aType),
 | |
|                "unexpected pseudo-class");
 | |
|   MOZ_COUNT_CTOR(nsPseudoClassList);
 | |
|   u.mMemory = nullptr;
 | |
| }
 | |
| 
 | |
| nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
 | |
|                                      const char16_t* aString)
 | |
|   : mType(aType),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType),
 | |
|                "unexpected pseudo-class");
 | |
|   NS_ASSERTION(aString, "string expected");
 | |
|   MOZ_COUNT_CTOR(nsPseudoClassList);
 | |
|   u.mString = NS_strdup(aString);
 | |
| }
 | |
| 
 | |
| nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
 | |
|                                      const int32_t* aIntPair)
 | |
|   : mType(aType),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType),
 | |
|                "unexpected pseudo-class");
 | |
|   NS_ASSERTION(aIntPair, "integer pair expected");
 | |
|   MOZ_COUNT_CTOR(nsPseudoClassList);
 | |
|   u.mNumbers =
 | |
|     static_cast<int32_t*>(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2));
 | |
| }
 | |
| 
 | |
| // adopts aSelectorList
 | |
| nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
 | |
|                                      nsCSSSelectorList* aSelectorList)
 | |
|   : mType(aType),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
 | |
|                "unexpected pseudo-class");
 | |
|   NS_ASSERTION(aSelectorList, "selector list expected");
 | |
|   MOZ_COUNT_CTOR(nsPseudoClassList);
 | |
|   u.mSelectors = aSelectorList;
 | |
| }
 | |
| 
 | |
| nsPseudoClassList*
 | |
| nsPseudoClassList::Clone(bool aDeep) const
 | |
| {
 | |
|   nsPseudoClassList *result;
 | |
|   if (!u.mMemory) {
 | |
|     result = new nsPseudoClassList(mType);
 | |
|   } else if (nsCSSPseudoClasses::HasStringArg(mType)) {
 | |
|     result = new nsPseudoClassList(mType, u.mString);
 | |
|   } else if (nsCSSPseudoClasses::HasNthPairArg(mType)) {
 | |
|     result = new nsPseudoClassList(mType, u.mNumbers);
 | |
|   } else {
 | |
|     NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType),
 | |
|                  "unexpected pseudo-class");
 | |
|     // This constructor adopts its selector list argument.
 | |
|     result = new nsPseudoClassList(mType, u.mSelectors->Clone());
 | |
|   }
 | |
| 
 | |
|   if (aDeep)
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result,
 | |
|                              (false));
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   const nsPseudoClassList* p = this;
 | |
|   while (p) {
 | |
|     n += aMallocSizeOf(p);
 | |
|     if (!p->u.mMemory) {
 | |
|       // do nothing
 | |
| 
 | |
|     } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) {
 | |
|       n += aMallocSizeOf(p->u.mString);
 | |
| 
 | |
|     } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) {
 | |
|       n += aMallocSizeOf(p->u.mNumbers);
 | |
| 
 | |
|     } else {
 | |
|       NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType),
 | |
|                    "unexpected pseudo-class");
 | |
|       n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf);
 | |
|     }
 | |
|     p = p->mNext;
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| nsPseudoClassList::~nsPseudoClassList(void)
 | |
| {
 | |
|   MOZ_COUNT_DTOR(nsPseudoClassList);
 | |
|   if (nsCSSPseudoClasses::HasSelectorListArg(mType)) {
 | |
|     delete u.mSelectors;
 | |
|   } else if (u.mMemory) {
 | |
|     free(u.mMemory);
 | |
|   }
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext);
 | |
| }
 | |
| 
 | |
| nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr)
 | |
|   : mValue(),
 | |
|     mNext(nullptr),
 | |
|     mLowercaseAttr(nullptr),
 | |
|     mCasedAttr(nullptr),
 | |
|     mNameSpace(aNameSpace),
 | |
|     mFunction(NS_ATTR_FUNC_SET),
 | |
|     // mValueCaseSensitivity doesn't matter; we have no value.
 | |
|     mValueCaseSensitivity(ValueCaseSensitivity::CaseSensitive)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsAttrSelector);
 | |
| 
 | |
|   nsAutoString lowercase;
 | |
|   nsContentUtils::ASCIIToLower(aAttr, lowercase);
 | |
| 
 | |
|   mCasedAttr = NS_Atomize(aAttr);
 | |
|   mLowercaseAttr = NS_Atomize(lowercase);
 | |
| }
 | |
| 
 | |
| nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction,
 | |
|                                const nsString& aValue,
 | |
|                                ValueCaseSensitivity aValueCaseSensitivity)
 | |
|   : mValue(aValue),
 | |
|     mNext(nullptr),
 | |
|     mLowercaseAttr(nullptr),
 | |
|     mCasedAttr(nullptr),
 | |
|     mNameSpace(aNameSpace),
 | |
|     mFunction(aFunction),
 | |
|     mValueCaseSensitivity(aValueCaseSensitivity)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsAttrSelector);
 | |
| 
 | |
|   nsAutoString lowercase;
 | |
|   nsContentUtils::ASCIIToLower(aAttr, lowercase);
 | |
| 
 | |
|   mCasedAttr = NS_Atomize(aAttr);
 | |
|   mLowercaseAttr = NS_Atomize(lowercase);
 | |
| }
 | |
| 
 | |
| nsAttrSelector::nsAttrSelector(int32_t aNameSpace,  nsAtom* aLowercaseAttr,
 | |
|                                nsAtom* aCasedAttr, uint8_t aFunction,
 | |
|                                const nsString& aValue,
 | |
|                                ValueCaseSensitivity aValueCaseSensitivity)
 | |
|   : mValue(aValue),
 | |
|     mNext(nullptr),
 | |
|     mLowercaseAttr(aLowercaseAttr),
 | |
|     mCasedAttr(aCasedAttr),
 | |
|     mNameSpace(aNameSpace),
 | |
|     mFunction(aFunction),
 | |
|     mValueCaseSensitivity(aValueCaseSensitivity)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsAttrSelector);
 | |
| }
 | |
| 
 | |
| nsAttrSelector*
 | |
| nsAttrSelector::Clone(bool aDeep) const
 | |
| {
 | |
|   nsAttrSelector *result =
 | |
|     new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr,
 | |
|                        mFunction, mValue, mValueCaseSensitivity);
 | |
| 
 | |
|   if (aDeep)
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false));
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsAttrSelector::~nsAttrSelector(void)
 | |
| {
 | |
|   MOZ_COUNT_DTOR(nsAttrSelector);
 | |
| 
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext);
 | |
| }
 | |
| 
 | |
| size_t
 | |
| nsAttrSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   const nsAttrSelector* p = this;
 | |
|   while (p) {
 | |
|     n += aMallocSizeOf(p);
 | |
|     n += p->mValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 | |
|     p = p->mNext;
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| // -- nsCSSSelector -------------------------------
 | |
| 
 | |
| nsCSSSelector::nsCSSSelector(void)
 | |
|   : mLowercaseTag(nullptr),
 | |
|     mCasedTag(nullptr),
 | |
|     mIDList(nullptr),
 | |
|     mClassList(nullptr),
 | |
|     mPseudoClassList(nullptr),
 | |
|     mAttrList(nullptr),
 | |
|     mNegations(nullptr),
 | |
|     mNext(nullptr),
 | |
|     mNameSpace(kNameSpaceID_Unknown),
 | |
|     mOperator(0),
 | |
|     mPseudoType(CSSPseudoElementType::NotPseudo)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsCSSSelector);
 | |
| }
 | |
| 
 | |
| nsCSSSelector*
 | |
| nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const
 | |
| {
 | |
|   nsCSSSelector *result = new nsCSSSelector();
 | |
|   if (!result)
 | |
|     return nullptr;
 | |
| 
 | |
|   result->mNameSpace = mNameSpace;
 | |
|   result->mLowercaseTag = mLowercaseTag;
 | |
|   result->mCasedTag = mCasedTag;
 | |
|   result->mOperator = mOperator;
 | |
|   result->mPseudoType = mPseudoType;
 | |
| 
 | |
|   NS_IF_CLONE(mIDList);
 | |
|   NS_IF_CLONE(mClassList);
 | |
|   NS_IF_CLONE(mPseudoClassList);
 | |
|   NS_IF_CLONE(mAttrList);
 | |
| 
 | |
|   // No need to worry about multiple levels of recursion since an
 | |
|   // mNegations can't have an mNext.
 | |
|   NS_ASSERTION(!mNegations || !mNegations->mNext,
 | |
|                "mNegations can't have non-null mNext");
 | |
|   if (aDeepNegations) {
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result,
 | |
|                              (true, false));
 | |
|   }
 | |
| 
 | |
|   if (aDeepNext) {
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result,
 | |
|                              (false, true));
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsCSSSelector::~nsCSSSelector(void)
 | |
| {
 | |
|   MOZ_COUNT_DTOR(nsCSSSelector);
 | |
|   Reset();
 | |
|   // No need to worry about multiple levels of recursion since an
 | |
|   // mNegations can't have an mNext.
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext);
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::Reset(void)
 | |
| {
 | |
|   mNameSpace = kNameSpaceID_Unknown;
 | |
|   mLowercaseTag = nullptr;
 | |
|   mCasedTag = nullptr;
 | |
|   NS_IF_DELETE(mIDList);
 | |
|   NS_IF_DELETE(mClassList);
 | |
|   NS_IF_DELETE(mPseudoClassList);
 | |
|   NS_IF_DELETE(mAttrList);
 | |
|   // No need to worry about multiple levels of recursion since an
 | |
|   // mNegations can't have an mNext.
 | |
|   NS_ASSERTION(!mNegations || !mNegations->mNext,
 | |
|                "mNegations can't have non-null mNext");
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations);
 | |
|   mOperator = char16_t(0);
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
 | |
| {
 | |
|   mNameSpace = aNameSpace;
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::SetTag(const nsString& aTag)
 | |
| {
 | |
|   if (aTag.IsEmpty()) {
 | |
|     mLowercaseTag = mCasedTag =  nullptr;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mCasedTag = NS_Atomize(aTag);
 | |
| 
 | |
|   nsAutoString lowercase;
 | |
|   nsContentUtils::ASCIIToLower(aTag, lowercase);
 | |
|   mLowercaseTag = NS_Atomize(lowercase);
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddID(const nsString& aID)
 | |
| {
 | |
|   if (!aID.IsEmpty()) {
 | |
|     nsAtomList** list = &mIDList;
 | |
|     while (nullptr != *list) {
 | |
|       list = &((*list)->mNext);
 | |
|     }
 | |
|     *list = new nsAtomList(aID);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddClass(const nsString& aClass)
 | |
| {
 | |
|   if (!aClass.IsEmpty()) {
 | |
|     nsAtomList** list = &mClassList;
 | |
|     while (nullptr != *list) {
 | |
|       list = &((*list)->mNext);
 | |
|     }
 | |
|     *list = new nsAtomList(aClass);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType)
 | |
| {
 | |
|   AddPseudoClassInternal(new nsPseudoClassList(aType));
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
 | |
|                                    const char16_t* aString)
 | |
| {
 | |
|   AddPseudoClassInternal(new nsPseudoClassList(aType, aString));
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
 | |
|                                    const int32_t* aIntPair)
 | |
| {
 | |
|   AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair));
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
 | |
|                                    nsCSSSelectorList* aSelectorList)
 | |
| {
 | |
|   // Take ownership of nsCSSSelectorList instead of copying.
 | |
|   AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList));
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass)
 | |
| {
 | |
|   nsPseudoClassList** list = &mPseudoClassList;
 | |
|   while (nullptr != *list) {
 | |
|     list = &((*list)->mNext);
 | |
|   }
 | |
|   *list = aPseudoClass;
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr)
 | |
| {
 | |
|   if (!aAttr.IsEmpty()) {
 | |
|     nsAttrSelector** list = &mAttrList;
 | |
|     while (nullptr != *list) {
 | |
|       list = &((*list)->mNext);
 | |
|     }
 | |
|     *list = new nsAttrSelector(aNameSpace, aAttr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc,
 | |
|                                  const nsString& aValue,
 | |
|                                  nsAttrSelector::ValueCaseSensitivity aCaseSensitivity)
 | |
| {
 | |
|   if (!aAttr.IsEmpty()) {
 | |
|     nsAttrSelector** list = &mAttrList;
 | |
|     while (nullptr != *list) {
 | |
|       list = &((*list)->mNext);
 | |
|     }
 | |
|     *list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitivity);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsCSSSelector::SetOperator(char16_t aOperator)
 | |
| {
 | |
|   mOperator = aOperator;
 | |
| }
 | |
| 
 | |
| int32_t nsCSSSelector::CalcWeightWithoutNegations() const
 | |
| {
 | |
|   int32_t weight = 0;
 | |
| 
 | |
| #ifdef MOZ_XUL
 | |
|   MOZ_ASSERT(!(IsPseudoElement() &&
 | |
|                PseudoType() != CSSPseudoElementType::XULTree &&
 | |
|                mClassList),
 | |
|              "If non-XUL-tree pseudo-elements can have class selectors "
 | |
|              "after them, specificity calculation must be updated");
 | |
| #else
 | |
|   MOZ_ASSERT(!(IsPseudoElement() && mClassList),
 | |
|              "If pseudo-elements can have class selectors "
 | |
|              "after them, specificity calculation must be updated");
 | |
| #endif
 | |
|   MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
 | |
|              "If pseudo-elements can have id or attribute selectors "
 | |
|              "after them, specificity calculation must be updated");
 | |
| 
 | |
|   if (IsPseudoElement()) {
 | |
|     weight += 1;
 | |
|   }
 | |
|   if (nullptr != mCasedTag) {
 | |
|     weight += 1;
 | |
|   }
 | |
|   nsAtomList* list = mIDList;
 | |
|   while (nullptr != list) {
 | |
|     weight += 1 << 20;
 | |
|     list = list->mNext;
 | |
|   }
 | |
|   list = mClassList;
 | |
| #ifdef MOZ_XUL
 | |
|   // XUL tree pseudo-elements abuse mClassList to store some private
 | |
|   // data; ignore that.
 | |
|   if (PseudoType() == CSSPseudoElementType::XULTree) {
 | |
|     list = nullptr;
 | |
|   }
 | |
| #endif
 | |
|   while (nullptr != list) {
 | |
|     weight += 1 << 10;
 | |
|     list = list->mNext;
 | |
|   }
 | |
|   // FIXME (bug 561154):  This is incorrect for :-moz-any(), which isn't
 | |
|   // really a pseudo-class.  In order to handle :-moz-any() correctly,
 | |
|   // we need to compute specificity after we match, based on which
 | |
|   // option we matched with (and thus also need to try the
 | |
|   // highest-specificity options first).
 | |
|   nsPseudoClassList *plist = mPseudoClassList;
 | |
|   while (nullptr != plist) {
 | |
|     weight += 1 << 10;
 | |
|     plist = plist->mNext;
 | |
|   }
 | |
|   nsAttrSelector* attr = mAttrList;
 | |
|   while (nullptr != attr) {
 | |
|     weight += 1 << 10;
 | |
|     attr = attr->mNext;
 | |
|   }
 | |
|   return weight;
 | |
| }
 | |
| 
 | |
| int32_t nsCSSSelector::CalcWeight() const
 | |
| {
 | |
|   // Loop over this selector and all its negations.
 | |
|   int32_t weight = 0;
 | |
|   for (const nsCSSSelector *n = this; n; n = n->mNegations) {
 | |
|     weight += n->CalcWeightWithoutNegations();
 | |
|   }
 | |
|   return weight;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Builds the textual representation of a selector. Called by DOM 2 CSS
 | |
| // StyleRule:selectorText
 | |
| //
 | |
| void
 | |
| nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet,
 | |
|                         bool aAppend) const
 | |
| {
 | |
|   if (!aAppend)
 | |
|    aString.Truncate();
 | |
| 
 | |
|   // selectors are linked from right-to-left, so the next selector in
 | |
|   // the linked list actually precedes this one in the resulting string
 | |
|   AutoTArray<const nsCSSSelector*, 8> stack;
 | |
|   for (const nsCSSSelector *s = this; s; s = s->mNext) {
 | |
|     stack.AppendElement(s);
 | |
|   }
 | |
| 
 | |
|   while (!stack.IsEmpty()) {
 | |
|     uint32_t index = stack.Length() - 1;
 | |
|     const nsCSSSelector *s = stack.ElementAt(index);
 | |
|     stack.RemoveElementAt(index);
 | |
| 
 | |
|     s->AppendToStringWithoutCombinators(aString, aSheet, false);
 | |
| 
 | |
|     // Append the combinator, if needed.
 | |
|     if (!stack.IsEmpty()) {
 | |
|       const nsCSSSelector *next = stack.ElementAt(index - 1);
 | |
|       char16_t oper = s->mOperator;
 | |
|       if (next->IsPseudoElement()) {
 | |
|         NS_ASSERTION(oper == char16_t(':'),
 | |
|                      "improperly chained pseudo element");
 | |
|       } else {
 | |
|         NS_ASSERTION(oper != char16_t(0),
 | |
|                      "compound selector without combinator");
 | |
| 
 | |
|         aString.Append(char16_t(' '));
 | |
|         if (oper != char16_t(' ')) {
 | |
|           aString.Append(oper);
 | |
|           aString.Append(char16_t(' '));
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsCSSSelector::AppendToStringWithoutCombinators(
 | |
|     nsAString& aString,
 | |
|     CSSStyleSheet* aSheet,
 | |
|     bool aUseStandardNamespacePrefixes) const
 | |
| {
 | |
|   AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false,
 | |
|                                               aUseStandardNamespacePrefixes);
 | |
| 
 | |
|   for (const nsCSSSelector* negation = mNegations; negation;
 | |
|        negation = negation->mNegations) {
 | |
|     aString.AppendLiteral(":not(");
 | |
|     negation->AppendToStringWithoutCombinatorsOrNegations(
 | |
|         aString, aSheet, true, aUseStandardNamespacePrefixes);
 | |
|     aString.Append(char16_t(')'));
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| nsCString
 | |
| nsCSSSelector::RestrictedSelectorToString() const
 | |
| {
 | |
|   MOZ_ASSERT(IsRestrictedSelector());
 | |
| 
 | |
|   nsString result;
 | |
|   AppendToStringWithoutCombinators(result, nullptr, true);
 | |
|   return NS_ConvertUTF16toUTF8(result);
 | |
| }
 | |
| 
 | |
| static bool
 | |
| AppendStandardNamespacePrefixToString(nsAString& aString, int32_t aNameSpace)
 | |
| {
 | |
|   if (aNameSpace == kNameSpaceID_Unknown) {
 | |
|     // Wildcard namespace; no prefix to write.
 | |
|     return false;
 | |
|   }
 | |
|   switch (aNameSpace) {
 | |
|     case kNameSpaceID_None:
 | |
|       break;
 | |
|     case kNameSpaceID_XML:
 | |
|       aString.AppendLiteral("xml");
 | |
|       break;
 | |
|     case kNameSpaceID_XHTML:
 | |
|       aString.AppendLiteral("html");
 | |
|       break;
 | |
|     case kNameSpaceID_XLink:
 | |
|       aString.AppendLiteral("xlink");
 | |
|       break;
 | |
|     case kNameSpaceID_XSLT:
 | |
|       aString.AppendLiteral("xsl");
 | |
|       break;
 | |
|     case kNameSpaceID_XBL:
 | |
|       aString.AppendLiteral("xbl");
 | |
|       break;
 | |
|     case kNameSpaceID_MathML:
 | |
|       aString.AppendLiteral("math");
 | |
|       break;
 | |
|     case kNameSpaceID_RDF:
 | |
|       aString.AppendLiteral("rdf");
 | |
|       break;
 | |
|     case kNameSpaceID_XUL:
 | |
|       aString.AppendLiteral("xul");
 | |
|       break;
 | |
|     case kNameSpaceID_SVG:
 | |
|       aString.AppendLiteral("svg");
 | |
|       break;
 | |
|     default:
 | |
|       aString.AppendLiteral("ns");
 | |
|       aString.AppendInt(aNameSpace);
 | |
|       break;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void
 | |
| nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
 | |
|                    (nsAString& aString, CSSStyleSheet* aSheet,
 | |
|                    bool aIsNegated,
 | |
|                    bool aUseStandardNamespacePrefixes) const
 | |
| {
 | |
|   nsAutoString temp;
 | |
|   bool isPseudoElement = IsPseudoElement();
 | |
| 
 | |
|   // For non-pseudo-element selectors or for lone pseudo-elements, deal with
 | |
|   // namespace prefixes.
 | |
|   bool wroteNamespace = false;
 | |
|   if (!isPseudoElement || !mNext) {
 | |
|     // append the namespace prefix if needed
 | |
|     nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr;
 | |
| 
 | |
|     // sheetNS is non-null if and only if we had an @namespace rule.  If it's
 | |
|     // null, that means that the only namespaces we could have are the
 | |
|     // wildcard namespace (which can be implicit in this case) and the "none"
 | |
|     // namespace, which then needs to be explicitly specified.
 | |
|     if (aUseStandardNamespacePrefixes) {
 | |
| #ifdef DEBUG
 | |
|       // We have no sheet to look up prefix information from.  This is
 | |
|       // only for debugging, so use some "standard" prefixes that
 | |
|       // are recognizable.
 | |
|       wroteNamespace =
 | |
|         AppendStandardNamespacePrefixToString(aString, mNameSpace);
 | |
|       if (wroteNamespace) {
 | |
|         aString.Append(char16_t('|'));
 | |
|       }
 | |
| #endif
 | |
|     } else if (!sheetNS) {
 | |
|       NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
 | |
|                    mNameSpace == kNameSpaceID_None,
 | |
|                    "How did we get this namespace?");
 | |
|       if (mNameSpace == kNameSpaceID_None) {
 | |
|         aString.Append(char16_t('|'));
 | |
|         wroteNamespace = true;
 | |
|       }
 | |
|     } else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) {
 | |
|       // We have the default namespace (possibly including the wildcard
 | |
|       // namespace).  Do nothing.
 | |
|       NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
 | |
|                    CanBeNamespaced(aIsNegated),
 | |
|                    "How did we end up with this namespace?");
 | |
|     } else if (mNameSpace == kNameSpaceID_None) {
 | |
|       NS_ASSERTION(CanBeNamespaced(aIsNegated),
 | |
|                    "How did we end up with this namespace?");
 | |
|       aString.Append(char16_t('|'));
 | |
|       wroteNamespace = true;
 | |
|     } else if (mNameSpace != kNameSpaceID_Unknown) {
 | |
|       NS_ASSERTION(CanBeNamespaced(aIsNegated),
 | |
|                    "How did we end up with this namespace?");
 | |
|       nsAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace);
 | |
|       NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace "
 | |
|                    "without a prefix?");
 | |
|       nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom),
 | |
|                                          aString);
 | |
|       aString.Append(char16_t('|'));
 | |
|       wroteNamespace = true;
 | |
|     } else {
 | |
|       // A selector for an element in any namespace, while the default
 | |
|       // namespace is something else.  :not() is special in that the default
 | |
|       // namespace is not implied for non-type selectors, so if this is a
 | |
|       // negated non-type selector we don't need to output an explicit wildcard
 | |
|       // namespace here, since those default to a wildcard namespace.
 | |
|       if (CanBeNamespaced(aIsNegated)) {
 | |
|         aString.AppendLiteral("*|");
 | |
|         wroteNamespace = true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!mLowercaseTag) {
 | |
|     // Universal selector:  avoid writing the universal selector when we
 | |
|     // can avoid it, especially since we're required to avoid it for the
 | |
|     // inside of :not()
 | |
|     if (wroteNamespace ||
 | |
|         (!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
 | |
|          (aIsNegated || !mNegations))) {
 | |
|       aString.Append(char16_t('*'));
 | |
|     }
 | |
|   } else {
 | |
|     // Append the tag name
 | |
|     nsAutoString tag;
 | |
|     (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
 | |
|     if (isPseudoElement) {
 | |
|       if (!mNext) {
 | |
|         // Lone pseudo-element selector -- toss in a wildcard type selector
 | |
|         // XXXldb Why?
 | |
|         aString.Append(char16_t('*'));
 | |
|       }
 | |
|       // While our atoms use one colon, most pseudo-elements require two
 | |
|       // colons (those not in CSS level 2) and all pseudo-elements allow
 | |
|       // two colons. So serialize to the non-deprecated two colon syntax.
 | |
|       aString.Append(char16_t(':'));
 | |
|       // This should not be escaped since (a) the pseudo-element string
 | |
|       // has a ":" that can't be escaped and (b) all pseudo-elements at
 | |
|       // this point are known, and therefore we know they don't need
 | |
|       // escaping.
 | |
|       aString.Append(tag);
 | |
|     } else {
 | |
|       nsStyleUtil::AppendEscapedCSSIdent(tag, aString);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Append the id, if there is one
 | |
|   if (mIDList) {
 | |
|     nsAtomList* list = mIDList;
 | |
|     while (list != nullptr) {
 | |
|       list->mAtom->ToString(temp);
 | |
|       aString.Append(char16_t('#'));
 | |
|       nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
 | |
|       list = list->mNext;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Append each class in the linked list
 | |
|   if (mClassList) {
 | |
|     if (isPseudoElement) {
 | |
| #ifdef MOZ_XUL
 | |
|       MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag),
 | |
|                  "must be tree pseudo-element");
 | |
| 
 | |
|       aString.Append(char16_t('('));
 | |
|       for (nsAtomList* list = mClassList; list; list = list->mNext) {
 | |
|         nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString);
 | |
|         aString.Append(char16_t(','));
 | |
|       }
 | |
|       // replace the final comma with a close-paren
 | |
|       aString.Replace(aString.Length() - 1, 1, char16_t(')'));
 | |
| #else
 | |
|       NS_ERROR("Can't happen");
 | |
| #endif
 | |
|     } else {
 | |
|       nsAtomList* list = mClassList;
 | |
|       while (list != nullptr) {
 | |
|         list->mAtom->ToString(temp);
 | |
|         aString.Append(char16_t('.'));
 | |
|         nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
 | |
|         list = list->mNext;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Append each attribute selector in the linked list
 | |
|   if (mAttrList) {
 | |
|     nsAttrSelector* list = mAttrList;
 | |
|     while (list != nullptr) {
 | |
|       aString.Append(char16_t('['));
 | |
|       // Append the namespace prefix
 | |
|       if (list->mNameSpace == kNameSpaceID_Unknown) {
 | |
|         aString.Append(char16_t('*'));
 | |
|         aString.Append(char16_t('|'));
 | |
|       } else if (list->mNameSpace != kNameSpaceID_None) {
 | |
|         if (aUseStandardNamespacePrefixes) {
 | |
| #ifdef DEBUG
 | |
|           AppendStandardNamespacePrefixToString(aString, list->mNameSpace);
 | |
|           aString.Append(char16_t('|'));
 | |
| #endif
 | |
|         } else if (aSheet) {
 | |
|           nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap();
 | |
|           nsAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace);
 | |
|           // Default namespaces don't apply to attribute selectors, so
 | |
|           // we must have a useful prefix.
 | |
|           NS_ASSERTION(prefixAtom,
 | |
|                        "How did we end up with a namespace if the prefix "
 | |
|                        "is unknown?");
 | |
|           nsAutoString prefix;
 | |
|           prefixAtom->ToString(prefix);
 | |
|           nsStyleUtil::AppendEscapedCSSIdent(prefix, aString);
 | |
|           aString.Append(char16_t('|'));
 | |
|         }
 | |
|       }
 | |
|       // Append the attribute name
 | |
|       list->mCasedAttr->ToString(temp);
 | |
|       nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
 | |
| 
 | |
|       if (list->mFunction != NS_ATTR_FUNC_SET) {
 | |
|         // Append the function
 | |
|         if (list->mFunction == NS_ATTR_FUNC_INCLUDES)
 | |
|           aString.Append(char16_t('~'));
 | |
|         else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH)
 | |
|           aString.Append(char16_t('|'));
 | |
|         else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH)
 | |
|           aString.Append(char16_t('^'));
 | |
|         else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH)
 | |
|           aString.Append(char16_t('$'));
 | |
|         else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)
 | |
|           aString.Append(char16_t('*'));
 | |
| 
 | |
|         aString.Append(char16_t('='));
 | |
| 
 | |
|         // Append the value
 | |
|         nsStyleUtil::AppendEscapedCSSString(list->mValue, aString);
 | |
| 
 | |
|         if (list->mValueCaseSensitivity ==
 | |
|               nsAttrSelector::ValueCaseSensitivity::CaseInsensitive) {
 | |
|           aString.AppendLiteral(u" i");
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       aString.Append(char16_t(']'));
 | |
| 
 | |
|       list = list->mNext;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Append each pseudo-class in the linked list
 | |
|   for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
 | |
|     nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
 | |
|     // This should not be escaped since (a) the pseudo-class string
 | |
|     // has a ":" that can't be escaped and (b) all pseudo-classes at
 | |
|     // this point are known, and therefore we know they don't need
 | |
|     // escaping.
 | |
|     aString.Append(temp);
 | |
|     if (list->u.mMemory) {
 | |
|       aString.Append(char16_t('('));
 | |
|       if (nsCSSPseudoClasses::HasStringArg(list->mType)) {
 | |
|         nsStyleUtil::AppendEscapedCSSIdent(
 | |
|           nsDependentString(list->u.mString), aString);
 | |
|       } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) {
 | |
|         int32_t a = list->u.mNumbers[0],
 | |
|                 b = list->u.mNumbers[1];
 | |
|         temp.Truncate();
 | |
|         if (a != 0) {
 | |
|           if (a == -1) {
 | |
|             temp.Append(char16_t('-'));
 | |
|           } else if (a != 1) {
 | |
|             temp.AppendInt(a);
 | |
|           }
 | |
|           temp.Append(char16_t('n'));
 | |
|         }
 | |
|         if (b != 0 || a == 0) {
 | |
|           if (b >= 0 && a != 0) // check a != 0 for whether we printed above
 | |
|             temp.Append(char16_t('+'));
 | |
|           temp.AppendInt(b);
 | |
|         }
 | |
|         aString.Append(temp);
 | |
|       } else {
 | |
|         NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType),
 | |
|                      "unexpected pseudo-class");
 | |
|         nsString tmp;
 | |
|         list->u.mSelectors->ToString(tmp, aSheet);
 | |
|         aString.Append(tmp);
 | |
|       }
 | |
|       aString.Append(char16_t(')'));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsCSSSelector::CanBeNamespaced(bool aIsNegated) const
 | |
| {
 | |
|   return !aIsNegated ||
 | |
|          (!mIDList && !mClassList && !mPseudoClassList && !mAttrList);
 | |
| }
 | |
| 
 | |
| size_t
 | |
| nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   const nsCSSSelector* s = this;
 | |
|   while (s) {
 | |
|     n += aMallocSizeOf(s);
 | |
| 
 | |
|     #define MEASURE(x)   n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0;
 | |
| 
 | |
|     MEASURE(s->mIDList);
 | |
|     MEASURE(s->mClassList);
 | |
|     MEASURE(s->mPseudoClassList);
 | |
|     MEASURE(s->mNegations);
 | |
|     MEASURE(s->mAttrList);
 | |
| 
 | |
|     // The following members aren't measured:
 | |
|     // - s->mLowercaseTag, because it's an atom and therefore shared
 | |
|     // - s->mCasedTag, because it's an atom and therefore shared
 | |
| 
 | |
|     s = s->mNext;
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| // -- nsCSSSelectorList -------------------------------
 | |
| 
 | |
| nsCSSSelectorList::nsCSSSelectorList(void)
 | |
|   : mSelectors(nullptr),
 | |
|     mWeight(0),
 | |
|     mNext(nullptr)
 | |
| {
 | |
|   MOZ_COUNT_CTOR(nsCSSSelectorList);
 | |
| }
 | |
| 
 | |
| nsCSSSelectorList::~nsCSSSelectorList()
 | |
| {
 | |
|   MOZ_COUNT_DTOR(nsCSSSelectorList);
 | |
|   delete mSelectors;
 | |
|   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext);
 | |
| }
 | |
| 
 | |
| nsCSSSelector*
 | |
| nsCSSSelectorList::AddSelector(char16_t aOperator)
 | |
| {
 | |
|   nsCSSSelector* newSel = new nsCSSSelector();
 | |
| 
 | |
|   if (mSelectors) {
 | |
|     NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator");
 | |
|     mSelectors->SetOperator(aOperator);
 | |
|   } else {
 | |
|     NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining");
 | |
|   }
 | |
| 
 | |
|   newSel->mNext = mSelectors;
 | |
|   mSelectors = newSel;
 | |
|   return newSel;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsCSSSelectorList::RemoveRightmostSelector()
 | |
| {
 | |
|   nsCSSSelector* current = mSelectors;
 | |
|   mSelectors = mSelectors->mNext;
 | |
|   MOZ_ASSERT(mSelectors,
 | |
|              "Rightmost selector has been removed, but now "
 | |
|              "mSelectors is null");
 | |
|   mSelectors->SetOperator(char16_t(0));
 | |
| 
 | |
|   // Make sure that deleting current won't delete the whole list.
 | |
|   current->mNext = nullptr;
 | |
|   delete current;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet)
 | |
| {
 | |
|   aResult.Truncate();
 | |
|   nsCSSSelectorList *p = this;
 | |
|   for (;;) {
 | |
|     p->mSelectors->ToString(aResult, aSheet, true);
 | |
|     p = p->mNext;
 | |
|     if (!p)
 | |
|       break;
 | |
|     aResult.AppendLiteral(", ");
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsCSSSelectorList*
 | |
| nsCSSSelectorList::Clone(bool aDeep) const
 | |
| {
 | |
|   nsCSSSelectorList *result = new nsCSSSelectorList();
 | |
|   result->mWeight = mWeight;
 | |
|   NS_IF_CLONE(mSelectors);
 | |
| 
 | |
|   if (aDeep) {
 | |
|     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result,
 | |
|                              (false));
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = 0;
 | |
|   const nsCSSSelectorList* s = this;
 | |
|   while (s) {
 | |
|     n += aMallocSizeOf(s);
 | |
|     n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
 | |
|     s = s->mNext;
 | |
|   }
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| // --------------------------------------------------------
 | |
| 
 | |
| class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration
 | |
| {
 | |
| protected:
 | |
|   // Needs to be protected so we can use NS_IMPL_ADDREF_USING_AGGREGATOR.
 | |
|   virtual ~DOMCSSDeclarationImpl(void);
 | |
| 
 | |
|   // But we need to allow UniquePtr to delete us.
 | |
|   friend class mozilla::DefaultDelete<DOMCSSDeclarationImpl>;
 | |
| 
 | |
| public:
 | |
|   explicit DOMCSSDeclarationImpl(css::StyleRule *aRule);
 | |
| 
 | |
|   NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override;
 | |
|   virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
 | |
|   virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override;
 | |
|   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
 | |
|                                         nsIPrincipal* aSubjectPrincipal) override;
 | |
|   nsDOMCSSDeclaration::ServoCSSParsingEnvironment
 | |
|   GetServoCSSParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
 | |
|   virtual nsIDocument* DocToUpdate() override;
 | |
| 
 | |
|   // Override |AddRef| and |Release| for being owned by StyleRule.  Also, we
 | |
|   // need to forward QI for cycle collection things to StyleRule.
 | |
|   NS_DECL_ISUPPORTS_INHERITED
 | |
| 
 | |
|   virtual nsINode *GetParentObject() override
 | |
|   {
 | |
|     return mRule ? mRule->GetDocument() : nullptr;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   // This reference is not reference-counted. The rule object owns us and we go
 | |
|   // away when it does.
 | |
|   css::StyleRule *mRule;
 | |
| };
 | |
| 
 | |
| DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
 | |
|   : mRule(aRule)
 | |
| {
 | |
| }
 | |
| 
 | |
| DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void)
 | |
| {
 | |
| }
 | |
| 
 | |
| NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
 | |
| NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, mRule)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   // We forward the cycle collection interfaces to mRule, which is
 | |
|   // never null.
 | |
|   if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
 | |
|       aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
 | |
|     return mRule->QueryInterface(aIID, aInstancePtr);
 | |
|   }
 | |
|   else
 | |
| NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)
 | |
| 
 | |
| DeclarationBlock*
 | |
| DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation)
 | |
| {
 | |
|   if (aOperation != eOperation_Read) {
 | |
|     RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
 | |
|     if (sheet) {
 | |
|       sheet->WillDirty();
 | |
|     }
 | |
|   }
 | |
|   return mRule->GetDeclaration();
 | |
| }
 | |
| 
 | |
| void
 | |
| DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv,
 | |
|                                                 nsIPrincipal* aSubjectPrincipal)
 | |
| {
 | |
|   GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
 | |
| }
 | |
| 
 | |
| nsDOMCSSDeclaration::ServoCSSParsingEnvironment
 | |
| DOMCSSDeclarationImpl::GetServoCSSParsingEnvironment(
 | |
|   nsIPrincipal* aSubjectPrincipal) const
 | |
| {
 | |
|   MOZ_CRASH("GetURLData shouldn't be calling on a Gecko rule");
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aParent);
 | |
| 
 | |
|   NS_IF_ADDREF(*aParent = mRule);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl)
 | |
| {
 | |
|   NS_PRECONDITION(mRule,
 | |
|          "can only be called when |GetCSSDeclaration| returned a declaration");
 | |
| 
 | |
|   nsCOMPtr<nsIDocument> doc;
 | |
|   RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
 | |
|   if (sheet) {
 | |
|     doc = sheet->GetAssociatedDocument();
 | |
|   }
 | |
| 
 | |
|   mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, true);
 | |
| 
 | |
|   mRule->SetDeclaration(aDecl->AsGecko());
 | |
| 
 | |
|   if (sheet) {
 | |
|     sheet->DidDirty();
 | |
|   }
 | |
| 
 | |
|   if (doc) {
 | |
|     doc->StyleRuleChanged(sheet, mRule);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsIDocument*
 | |
| DOMCSSDeclarationImpl::DocToUpdate()
 | |
| {
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // -- StyleRule ------------------------------------
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace css {
 | |
| 
 | |
| uint16_t
 | |
| StyleRule::Type() const
 | |
| {
 | |
|   return nsIDOMCSSRule::STYLE_RULE;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
 | |
| {
 | |
|   NS_ADDREF(*aStyle = Style());
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsICSSDeclaration*
 | |
| StyleRule::Style()
 | |
| {
 | |
|   if (!mDOMDeclaration) {
 | |
|     mDOMDeclaration.reset(new DOMCSSDeclarationImpl(this));
 | |
|   }
 | |
|   return mDOMDeclaration.get();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StyleRule::GetCSSStyleRule(BindingStyleRule **aResult)
 | |
| {
 | |
|   *aResult = this;
 | |
|   NS_ADDREF(*aResult);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| StyleRule::StyleRule(nsCSSSelectorList* aSelector,
 | |
|                      Declaration* aDeclaration,
 | |
|                      uint32_t aLineNumber,
 | |
|                      uint32_t aColumnNumber)
 | |
|   : BindingStyleRule(aLineNumber, aColumnNumber),
 | |
|     mSelector(aSelector),
 | |
|     mDeclaration(aDeclaration)
 | |
| {
 | |
|   NS_PRECONDITION(aDeclaration, "must have a declaration");
 | |
| 
 | |
|   mDeclaration->SetOwningRule(this);
 | |
| }
 | |
| 
 | |
| // for |Clone|
 | |
| StyleRule::StyleRule(const StyleRule& aCopy)
 | |
|   : BindingStyleRule(aCopy),
 | |
|     mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
 | |
|     mDeclaration(new Declaration(*aCopy.mDeclaration))
 | |
| {
 | |
|   mDeclaration->SetOwningRule(this);
 | |
|   // rest is constructed lazily on existing data
 | |
| }
 | |
| 
 | |
| StyleRule::~StyleRule()
 | |
| {
 | |
|   delete mSelector;
 | |
|   DropReferences();
 | |
| }
 | |
| 
 | |
| void
 | |
| StyleRule::DropReferences()
 | |
| {
 | |
|   if (mDeclaration) {
 | |
|     mDeclaration->SetOwningRule(nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // QueryInterface implementation for StyleRule
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleRule)
 | |
|   if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
 | |
|     *aInstancePtr = this;
 | |
|     NS_ADDREF_THIS();
 | |
|     return NS_OK;
 | |
|   }
 | |
|   else
 | |
|   NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
 | |
| NS_INTERFACE_MAP_END_INHERITING(Rule)
 | |
| 
 | |
| NS_IMPL_ADDREF_INHERITED(StyleRule, Rule)
 | |
| NS_IMPL_RELEASE_INHERITED(StyleRule, Rule)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(StyleRule)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(StyleRule, Rule)
 | |
|   // Keep this in sync with IsCCLeaf.
 | |
|   // Trace the wrapper for our declaration.  This just expands out
 | |
|   // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
 | |
|   // directly because the wrapper is on the declaration, not on us.
 | |
|   if (tmp->mDOMDeclaration) {
 | |
|     tmp->mDOMDeclaration->TraceWrapper(aCallbacks, aClosure);
 | |
|   }
 | |
| NS_IMPL_CYCLE_COLLECTION_TRACE_END
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(StyleRule, Rule)
 | |
|   // Unlink the wrapper for our declaraton.  This just expands out
 | |
|   // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
 | |
|   // directly because the wrapper is on the declaration, not on us.
 | |
|   if (tmp->mDOMDeclaration) {
 | |
|     tmp->mDOMDeclaration->ReleaseWrapper(static_cast<nsISupports*>(p));
 | |
|   }
 | |
|   tmp->DropReferences();
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(StyleRule, Rule)
 | |
|   // Keep this in sync with IsCCLeaf.
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| bool
 | |
| StyleRule::IsCCLeaf() const
 | |
| {
 | |
|   if (!Rule::IsCCLeaf()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return !mDOMDeclaration || !mDOMDeclaration->PreservingWrapper();
 | |
| }
 | |
| 
 | |
| /* virtual */ int32_t
 | |
| StyleRule::GetType() const
 | |
| {
 | |
|   return Rule::STYLE_RULE;
 | |
| }
 | |
| 
 | |
| /* virtual */ already_AddRefed<Rule>
 | |
| StyleRule::Clone() const
 | |
| {
 | |
|   RefPtr<Rule> clone = new StyleRule(*this);
 | |
|   return clone.forget();
 | |
| }
 | |
| 
 | |
| void
 | |
| StyleRule::SetDeclaration(Declaration* aDecl)
 | |
| {
 | |
|   if (aDecl == mDeclaration) {
 | |
|     return;
 | |
|   }
 | |
|   mDeclaration->SetOwningRule(nullptr);
 | |
|   mDeclaration = aDecl;
 | |
|   mDeclaration->SetOwningRule(this);
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG
 | |
| /* virtual */ void
 | |
| StyleRule::List(FILE* out, int32_t aIndent) const
 | |
| {
 | |
|   nsAutoCString str;
 | |
|   // Indent
 | |
|   for (int32_t index = aIndent; --index >= 0; ) {
 | |
|     str.AppendLiteral("  ");
 | |
|   }
 | |
| 
 | |
|   if (mSelector) {
 | |
|     nsAutoString buffer;
 | |
|     mSelector->ToString(buffer, GetStyleSheet());
 | |
|     AppendUTF16toUTF8(buffer, str);
 | |
|     str.Append(' ');
 | |
|   }
 | |
| 
 | |
|   if (nullptr != mDeclaration) {
 | |
|     nsAutoString buffer;
 | |
|     str.AppendLiteral("{ ");
 | |
|     mDeclaration->ToString(buffer);
 | |
|     AppendUTF16toUTF8(buffer, str);
 | |
|     str.Append('}');
 | |
|     CSSStyleSheet* sheet = GetStyleSheet();
 | |
|     if (sheet) {
 | |
|       nsIURI* uri = sheet->GetSheetURI();
 | |
|       if (uri) {
 | |
|         str.AppendLiteral(" /* ");
 | |
|         str.Append(uri->GetSpecOrDefault());
 | |
|         str.Append(':');
 | |
|         str.AppendInt(mLineNumber);
 | |
|         str.AppendLiteral(" */");
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   else {
 | |
|     str.AppendLiteral("{ null declaration }");
 | |
|   }
 | |
|   str.Append('\n');
 | |
|   fprintf_stderr(out, "%s", str.get());
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void
 | |
| StyleRule::GetCssTextImpl(nsAString& aCssText) const
 | |
| {
 | |
|   if (mSelector) {
 | |
|     mSelector->ToString(aCssText, GetStyleSheet());
 | |
|     aCssText.Append(char16_t(' '));
 | |
|   }
 | |
|   aCssText.Append(char16_t('{'));
 | |
|   aCssText.Append(char16_t(' '));
 | |
|   if (mDeclaration)
 | |
|   {
 | |
|     nsAutoString   tempString;
 | |
|     mDeclaration->ToString( tempString );
 | |
|     aCssText.Append( tempString );
 | |
|   }
 | |
|   aCssText.Append(char16_t(' '));
 | |
|   aCssText.Append(char16_t('}'));
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StyleRule::GetSelectorText(nsAString& aSelectorText)
 | |
| {
 | |
|   if (mSelector)
 | |
|     mSelector->ToString(aSelectorText, GetStyleSheet());
 | |
|   else
 | |
|     aSelectorText.Truncate();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| StyleRule::SetSelectorText(const nsAString& aSelectorText)
 | |
| {
 | |
|   // XXX TBI - get a parser and re-parse the selectors,
 | |
|   // XXX then need to re-compute the cascade
 | |
|   // XXX and dirty sheet
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /* virtual */ size_t
 | |
| StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 | |
| {
 | |
|   size_t n = aMallocSizeOf(this);
 | |
|   n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0;
 | |
|   n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0;
 | |
| 
 | |
|   // Measurement of the following members may be added later if DMD finds it is
 | |
|   // worthwhile:
 | |
|   // - mDOMRule;
 | |
| 
 | |
|   return n;
 | |
| }
 | |
| 
 | |
| nsCSSSelectorList*
 | |
| StyleRule::GetSelectorAtIndex(uint32_t aIndex, ErrorResult& rv)
 | |
| {
 | |
| 
 | |
|   for (nsCSSSelectorList* sel = mSelector; sel;
 | |
|        sel = sel->mNext, --aIndex) {
 | |
|     if (aIndex == 0) {
 | |
|       return sel;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Ran out of selectors
 | |
|   rv.Throw(NS_ERROR_INVALID_ARG);
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| StyleRule::GetSelectorCount()
 | |
| {
 | |
|   uint32_t count = 0;
 | |
|   for (nsCSSSelectorList* sel = mSelector; sel; sel = sel->mNext) {
 | |
|     ++count;
 | |
|   }
 | |
|   return count;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| StyleRule::GetSelectorText(uint32_t aSelectorIndex, nsAString& aText)
 | |
| {
 | |
|   ErrorResult rv;
 | |
|   nsCSSSelectorList* sel = GetSelectorAtIndex(aSelectorIndex, rv);
 | |
|   if (rv.Failed()) {
 | |
|     return rv.StealNSResult();
 | |
|   }
 | |
| 
 | |
|   sel->mSelectors->ToString(aText, GetStyleSheet(), false);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| StyleRule::GetSpecificity(uint32_t aSelectorIndex, uint64_t* aSpecificity)
 | |
| {
 | |
|   ErrorResult rv;
 | |
|   nsCSSSelectorList* sel = GetSelectorAtIndex(aSelectorIndex, rv);
 | |
|   if (rv.Failed()) {
 | |
|     return rv.StealNSResult();
 | |
|   }
 | |
| 
 | |
|   *aSpecificity = sel->mWeight;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| StyleRule::SelectorMatchesElement(Element* aElement,
 | |
|                                   uint32_t aSelectorIndex,
 | |
|                                   const nsAString& aPseudo,
 | |
|                                   bool* aMatches)
 | |
| {
 | |
|   ErrorResult rv;
 | |
|   nsCSSSelectorList* tail = GetSelectorAtIndex(aSelectorIndex, rv);
 | |
|   if (rv.Failed()) {
 | |
|     return rv.StealNSResult();
 | |
|   }
 | |
| 
 | |
|   // We want just the one list item, not the whole list tail
 | |
|   nsAutoPtr<nsCSSSelectorList> sel(tail->Clone(false));
 | |
| 
 | |
|   // Do not attempt to match if a pseudo element is requested and this is not
 | |
|   // a pseudo element selector, or vice versa.
 | |
|   if (aPseudo.IsEmpty() == sel->mSelectors->IsPseudoElement()) {
 | |
|     *aMatches = false;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!aPseudo.IsEmpty()) {
 | |
|     // We need to make sure that the requested pseudo element type
 | |
|     // matches the selector pseudo element type before proceeding.
 | |
|     RefPtr<nsAtom> pseudoElt = NS_Atomize(aPseudo);
 | |
|     if (sel->mSelectors->PseudoType() != nsCSSPseudoElements::
 | |
|           GetPseudoType(pseudoElt, CSSEnabledState::eIgnoreEnabledState)) {
 | |
|       *aMatches = false;
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     // We have a matching pseudo element, now remove it so we can compare
 | |
|     // directly against |element| when proceeding into SelectorListMatches.
 | |
|     // It's OK to do this - we just cloned sel and nothing else is using it.
 | |
|     sel->RemoveRightmostSelector();
 | |
|   }
 | |
| 
 | |
|   // XXXbz what exactly should we do with visited state here?  If we ever start
 | |
|   // caring about it, remember to do FlushPendingLinkUpdates().
 | |
|   TreeMatchContext matchingContext(false,
 | |
|                                    nsRuleWalker::eRelevantLinkUnvisited,
 | |
|                                    aElement->OwnerDoc(),
 | |
|                                    TreeMatchContext::eNeverMatchVisited);
 | |
|   *aMatches = nsCSSRuleProcessor::SelectorListMatches(aElement, matchingContext,
 | |
|                                                       sel);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NotNull<DeclarationBlock*>
 | |
| StyleRule::GetDeclarationBlock() const
 | |
| {
 | |
|   return WrapNotNull(mDeclaration);
 | |
| }
 | |
| 
 | |
| } // namespace css
 | |
| } // namespace mozilla
 |