michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * representation of CSS style rules (selectors+declaration), CSS michael@0: * selectors, and DOM objects for style rules, selectors, and michael@0: * declarations michael@0: */ michael@0: michael@0: #include "mozilla/css/StyleRule.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/css/GroupRule.h" michael@0: #include "mozilla/css/Declaration.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsString.h" michael@0: #include "nsStyleUtil.h" michael@0: #include "nsICSSStyleRuleDOMWrapper.h" michael@0: #include "nsDOMCSSDeclaration.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsXMLNameSpaceMap.h" michael@0: #include "nsCSSPseudoElements.h" michael@0: #include "nsCSSPseudoClasses.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsTArray.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsError.h" michael@0: #include "mozAutoDocUpdate.h" michael@0: michael@0: class nsIDOMCSSStyleDeclaration; michael@0: class nsIDOMCSSStyleSheet; michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define NS_IF_CLONE(member_) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (member_) { \ michael@0: result->member_ = member_->Clone(); \ michael@0: if (!result->member_) { \ michael@0: delete result; \ michael@0: return nullptr; \ michael@0: } \ michael@0: } \ michael@0: PR_END_MACRO michael@0: michael@0: #define NS_IF_DELETE(ptr) \ michael@0: PR_BEGIN_MACRO \ michael@0: delete ptr; \ michael@0: ptr = nullptr; \ michael@0: PR_END_MACRO michael@0: michael@0: /* ************************************************************************** */ michael@0: michael@0: nsAtomList::nsAtomList(nsIAtom* aAtom) michael@0: : mAtom(aAtom), michael@0: mNext(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsAtomList); michael@0: } michael@0: michael@0: nsAtomList::nsAtomList(const nsString& aAtomValue) michael@0: : mAtom(nullptr), michael@0: mNext(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsAtomList); michael@0: mAtom = do_GetAtom(aAtomValue); michael@0: } michael@0: michael@0: nsAtomList* michael@0: nsAtomList::Clone(bool aDeep) const michael@0: { michael@0: nsAtomList *result = new nsAtomList(mAtom); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: if (aDeep) michael@0: NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false)); michael@0: return result; michael@0: } michael@0: michael@0: size_t michael@0: nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: const nsAtomList* a = this; michael@0: while (a) { michael@0: n += aMallocSizeOf(a); michael@0: michael@0: // The following members aren't measured: michael@0: // - a->mAtom, because it may be shared michael@0: michael@0: a = a->mNext; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: nsAtomList::~nsAtomList(void) michael@0: { michael@0: MOZ_COUNT_DTOR(nsAtomList); michael@0: NS_CSS_DELETE_LIST_MEMBER(nsAtomList, this, mNext); michael@0: } michael@0: michael@0: nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType) michael@0: : mType(aType), michael@0: mNext(nullptr) michael@0: { michael@0: NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) && michael@0: !nsCSSPseudoClasses::HasNthPairArg(aType), michael@0: "unexpected pseudo-class"); michael@0: MOZ_COUNT_CTOR(nsPseudoClassList); michael@0: u.mMemory = nullptr; michael@0: } michael@0: michael@0: nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, michael@0: const char16_t* aString) michael@0: : mType(aType), michael@0: mNext(nullptr) michael@0: { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType), michael@0: "unexpected pseudo-class"); michael@0: NS_ASSERTION(aString, "string expected"); michael@0: MOZ_COUNT_CTOR(nsPseudoClassList); michael@0: u.mString = NS_strdup(aString); michael@0: } michael@0: michael@0: nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, michael@0: const int32_t* aIntPair) michael@0: : mType(aType), michael@0: mNext(nullptr) michael@0: { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType), michael@0: "unexpected pseudo-class"); michael@0: NS_ASSERTION(aIntPair, "integer pair expected"); michael@0: MOZ_COUNT_CTOR(nsPseudoClassList); michael@0: u.mNumbers = michael@0: static_cast(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2)); michael@0: } michael@0: michael@0: // adopts aSelectorList michael@0: nsPseudoClassList::nsPseudoClassList(nsCSSPseudoClasses::Type aType, michael@0: nsCSSSelectorList* aSelectorList) michael@0: : mType(aType), michael@0: mNext(nullptr) michael@0: { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType), michael@0: "unexpected pseudo-class"); michael@0: NS_ASSERTION(aSelectorList, "selector list expected"); michael@0: MOZ_COUNT_CTOR(nsPseudoClassList); michael@0: u.mSelectors = aSelectorList; michael@0: } michael@0: michael@0: nsPseudoClassList* michael@0: nsPseudoClassList::Clone(bool aDeep) const michael@0: { michael@0: nsPseudoClassList *result; michael@0: if (!u.mMemory) { michael@0: result = new nsPseudoClassList(mType); michael@0: } else if (nsCSSPseudoClasses::HasStringArg(mType)) { michael@0: result = new nsPseudoClassList(mType, u.mString); michael@0: } else if (nsCSSPseudoClasses::HasNthPairArg(mType)) { michael@0: result = new nsPseudoClassList(mType, u.mNumbers); michael@0: } else { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType), michael@0: "unexpected pseudo-class"); michael@0: // This constructor adopts its selector list argument. michael@0: result = new nsPseudoClassList(mType, u.mSelectors->Clone()); michael@0: } michael@0: michael@0: if (aDeep) michael@0: NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result, michael@0: (false)); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: size_t michael@0: nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: const nsPseudoClassList* p = this; michael@0: while (p) { michael@0: n += aMallocSizeOf(p); michael@0: if (!p->u.mMemory) { michael@0: // do nothing michael@0: michael@0: } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) { michael@0: n += aMallocSizeOf(p->u.mString); michael@0: michael@0: } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) { michael@0: n += aMallocSizeOf(p->u.mNumbers); michael@0: michael@0: } else { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType), michael@0: "unexpected pseudo-class"); michael@0: n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: p = p->mNext; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: nsPseudoClassList::~nsPseudoClassList(void) michael@0: { michael@0: MOZ_COUNT_DTOR(nsPseudoClassList); michael@0: if (nsCSSPseudoClasses::HasSelectorListArg(mType)) { michael@0: delete u.mSelectors; michael@0: } else if (u.mMemory) { michael@0: NS_Free(u.mMemory); michael@0: } michael@0: NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext); michael@0: } michael@0: michael@0: nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr) michael@0: : mValue(), michael@0: mNext(nullptr), michael@0: mLowercaseAttr(nullptr), michael@0: mCasedAttr(nullptr), michael@0: mNameSpace(aNameSpace), michael@0: mFunction(NS_ATTR_FUNC_SET), michael@0: mCaseSensitive(1) michael@0: { michael@0: MOZ_COUNT_CTOR(nsAttrSelector); michael@0: michael@0: nsAutoString lowercase; michael@0: nsContentUtils::ASCIIToLower(aAttr, lowercase); michael@0: michael@0: mCasedAttr = do_GetAtom(aAttr); michael@0: mLowercaseAttr = do_GetAtom(lowercase); michael@0: } michael@0: michael@0: nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction, michael@0: const nsString& aValue, bool aCaseSensitive) michael@0: : mValue(aValue), michael@0: mNext(nullptr), michael@0: mLowercaseAttr(nullptr), michael@0: mCasedAttr(nullptr), michael@0: mNameSpace(aNameSpace), michael@0: mFunction(aFunction), michael@0: mCaseSensitive(aCaseSensitive) michael@0: { michael@0: MOZ_COUNT_CTOR(nsAttrSelector); michael@0: michael@0: nsAutoString lowercase; michael@0: nsContentUtils::ASCIIToLower(aAttr, lowercase); michael@0: michael@0: mCasedAttr = do_GetAtom(aAttr); michael@0: mLowercaseAttr = do_GetAtom(lowercase); michael@0: } michael@0: michael@0: nsAttrSelector::nsAttrSelector(int32_t aNameSpace, nsIAtom* aLowercaseAttr, michael@0: nsIAtom* aCasedAttr, uint8_t aFunction, michael@0: const nsString& aValue, bool aCaseSensitive) michael@0: : mValue(aValue), michael@0: mNext(nullptr), michael@0: mLowercaseAttr(aLowercaseAttr), michael@0: mCasedAttr(aCasedAttr), michael@0: mNameSpace(aNameSpace), michael@0: mFunction(aFunction), michael@0: mCaseSensitive(aCaseSensitive) michael@0: { michael@0: MOZ_COUNT_CTOR(nsAttrSelector); michael@0: } michael@0: michael@0: nsAttrSelector* michael@0: nsAttrSelector::Clone(bool aDeep) const michael@0: { michael@0: nsAttrSelector *result = michael@0: new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr, michael@0: mFunction, mValue, mCaseSensitive); michael@0: michael@0: if (aDeep) michael@0: NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false)); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsAttrSelector::~nsAttrSelector(void) michael@0: { michael@0: MOZ_COUNT_DTOR(nsAttrSelector); michael@0: michael@0: NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext); michael@0: } michael@0: michael@0: // -- nsCSSSelector ------------------------------- michael@0: michael@0: nsCSSSelector::nsCSSSelector(void) michael@0: : mLowercaseTag(nullptr), michael@0: mCasedTag(nullptr), michael@0: mIDList(nullptr), michael@0: mClassList(nullptr), michael@0: mPseudoClassList(nullptr), michael@0: mAttrList(nullptr), michael@0: mNegations(nullptr), michael@0: mNext(nullptr), michael@0: mNameSpace(kNameSpaceID_Unknown), michael@0: mOperator(0), michael@0: mPseudoType(nsCSSPseudoElements::ePseudo_NotPseudoElement) michael@0: { michael@0: MOZ_COUNT_CTOR(nsCSSSelector); michael@0: static_assert(nsCSSPseudoElements::ePseudo_MAX < INT16_MAX, michael@0: "nsCSSPseudoElements::Type values overflow mPseudoType"); michael@0: } michael@0: michael@0: nsCSSSelector* michael@0: nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const michael@0: { michael@0: nsCSSSelector *result = new nsCSSSelector(); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: result->mNameSpace = mNameSpace; michael@0: result->mLowercaseTag = mLowercaseTag; michael@0: result->mCasedTag = mCasedTag; michael@0: result->mOperator = mOperator; michael@0: result->mPseudoType = mPseudoType; michael@0: michael@0: NS_IF_CLONE(mIDList); michael@0: NS_IF_CLONE(mClassList); michael@0: NS_IF_CLONE(mPseudoClassList); michael@0: NS_IF_CLONE(mAttrList); michael@0: michael@0: // No need to worry about multiple levels of recursion since an michael@0: // mNegations can't have an mNext. michael@0: NS_ASSERTION(!mNegations || !mNegations->mNext, michael@0: "mNegations can't have non-null mNext"); michael@0: if (aDeepNegations) { michael@0: NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result, michael@0: (true, false)); michael@0: } michael@0: michael@0: if (aDeepNext) { michael@0: NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result, michael@0: (false, true)); michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsCSSSelector::~nsCSSSelector(void) michael@0: { michael@0: MOZ_COUNT_DTOR(nsCSSSelector); michael@0: Reset(); michael@0: // No need to worry about multiple levels of recursion since an michael@0: // mNegations can't have an mNext. michael@0: NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext); michael@0: } michael@0: michael@0: void nsCSSSelector::Reset(void) michael@0: { michael@0: mNameSpace = kNameSpaceID_Unknown; michael@0: mLowercaseTag = nullptr; michael@0: mCasedTag = nullptr; michael@0: NS_IF_DELETE(mIDList); michael@0: NS_IF_DELETE(mClassList); michael@0: NS_IF_DELETE(mPseudoClassList); michael@0: NS_IF_DELETE(mAttrList); michael@0: // No need to worry about multiple levels of recursion since an michael@0: // mNegations can't have an mNext. michael@0: NS_ASSERTION(!mNegations || !mNegations->mNext, michael@0: "mNegations can't have non-null mNext"); michael@0: NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations); michael@0: mOperator = char16_t(0); michael@0: } michael@0: michael@0: void nsCSSSelector::SetNameSpace(int32_t aNameSpace) michael@0: { michael@0: mNameSpace = aNameSpace; michael@0: } michael@0: michael@0: void nsCSSSelector::SetTag(const nsString& aTag) michael@0: { michael@0: if (aTag.IsEmpty()) { michael@0: mLowercaseTag = mCasedTag = nullptr; michael@0: return; michael@0: } michael@0: michael@0: mCasedTag = do_GetAtom(aTag); michael@0: michael@0: nsAutoString lowercase; michael@0: nsContentUtils::ASCIIToLower(aTag, lowercase); michael@0: mLowercaseTag = do_GetAtom(lowercase); michael@0: } michael@0: michael@0: void nsCSSSelector::AddID(const nsString& aID) michael@0: { michael@0: if (!aID.IsEmpty()) { michael@0: nsAtomList** list = &mIDList; michael@0: while (nullptr != *list) { michael@0: list = &((*list)->mNext); michael@0: } michael@0: *list = new nsAtomList(aID); michael@0: } michael@0: } michael@0: michael@0: void nsCSSSelector::AddClass(const nsString& aClass) michael@0: { michael@0: if (!aClass.IsEmpty()) { michael@0: nsAtomList** list = &mClassList; michael@0: while (nullptr != *list) { michael@0: list = &((*list)->mNext); michael@0: } michael@0: *list = new nsAtomList(aClass); michael@0: } michael@0: } michael@0: michael@0: void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType) michael@0: { michael@0: AddPseudoClassInternal(new nsPseudoClassList(aType)); michael@0: } michael@0: michael@0: void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, michael@0: const char16_t* aString) michael@0: { michael@0: AddPseudoClassInternal(new nsPseudoClassList(aType, aString)); michael@0: } michael@0: michael@0: void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, michael@0: const int32_t* aIntPair) michael@0: { michael@0: AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair)); michael@0: } michael@0: michael@0: void nsCSSSelector::AddPseudoClass(nsCSSPseudoClasses::Type aType, michael@0: nsCSSSelectorList* aSelectorList) michael@0: { michael@0: // Take ownership of nsCSSSelectorList instead of copying. michael@0: AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList)); michael@0: } michael@0: michael@0: void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass) michael@0: { michael@0: nsPseudoClassList** list = &mPseudoClassList; michael@0: while (nullptr != *list) { michael@0: list = &((*list)->mNext); michael@0: } michael@0: *list = aPseudoClass; michael@0: } michael@0: michael@0: void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr) michael@0: { michael@0: if (!aAttr.IsEmpty()) { michael@0: nsAttrSelector** list = &mAttrList; michael@0: while (nullptr != *list) { michael@0: list = &((*list)->mNext); michael@0: } michael@0: *list = new nsAttrSelector(aNameSpace, aAttr); michael@0: } michael@0: } michael@0: michael@0: void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc, michael@0: const nsString& aValue, bool aCaseSensitive) michael@0: { michael@0: if (!aAttr.IsEmpty()) { michael@0: nsAttrSelector** list = &mAttrList; michael@0: while (nullptr != *list) { michael@0: list = &((*list)->mNext); michael@0: } michael@0: *list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitive); michael@0: } michael@0: } michael@0: michael@0: void nsCSSSelector::SetOperator(char16_t aOperator) michael@0: { michael@0: mOperator = aOperator; michael@0: } michael@0: michael@0: int32_t nsCSSSelector::CalcWeightWithoutNegations() const michael@0: { michael@0: int32_t weight = 0; michael@0: michael@0: #ifdef MOZ_XUL michael@0: MOZ_ASSERT(!(IsPseudoElement() && michael@0: PseudoType() != nsCSSPseudoElements::ePseudo_XULTree && michael@0: mClassList), michael@0: "If non-XUL-tree pseudo-elements can have class selectors " michael@0: "after them, specificity calculation must be updated"); michael@0: #else michael@0: MOZ_ASSERT(!(IsPseudoElement() && mClassList), michael@0: "If pseudo-elements can have class selectors " michael@0: "after them, specificity calculation must be updated"); michael@0: #endif michael@0: MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)), michael@0: "If pseudo-elements can have id or attribute selectors " michael@0: "after them, specificity calculation must be updated"); michael@0: michael@0: if (nullptr != mCasedTag) { michael@0: weight += 0x000001; michael@0: } michael@0: nsAtomList* list = mIDList; michael@0: while (nullptr != list) { michael@0: weight += 0x010000; michael@0: list = list->mNext; michael@0: } michael@0: list = mClassList; michael@0: #ifdef MOZ_XUL michael@0: // XUL tree pseudo-elements abuse mClassList to store some private michael@0: // data; ignore that. michael@0: if (PseudoType() == nsCSSPseudoElements::ePseudo_XULTree) { michael@0: list = nullptr; michael@0: } michael@0: #endif michael@0: while (nullptr != list) { michael@0: weight += 0x000100; michael@0: list = list->mNext; michael@0: } michael@0: // FIXME (bug 561154): This is incorrect for :-moz-any(), which isn't michael@0: // really a pseudo-class. In order to handle :-moz-any() correctly, michael@0: // we need to compute specificity after we match, based on which michael@0: // option we matched with (and thus also need to try the michael@0: // highest-specificity options first). michael@0: nsPseudoClassList *plist = mPseudoClassList; michael@0: while (nullptr != plist) { michael@0: weight += 0x000100; michael@0: plist = plist->mNext; michael@0: } michael@0: nsAttrSelector* attr = mAttrList; michael@0: while (nullptr != attr) { michael@0: weight += 0x000100; michael@0: attr = attr->mNext; michael@0: } michael@0: return weight; michael@0: } michael@0: michael@0: int32_t nsCSSSelector::CalcWeight() const michael@0: { michael@0: // Loop over this selector and all its negations. michael@0: int32_t weight = 0; michael@0: for (const nsCSSSelector *n = this; n; n = n->mNegations) { michael@0: weight += n->CalcWeightWithoutNegations(); michael@0: } michael@0: return weight; michael@0: } michael@0: michael@0: // michael@0: // Builds the textual representation of a selector. Called by DOM 2 CSS michael@0: // StyleRule:selectorText michael@0: // michael@0: void michael@0: nsCSSSelector::ToString(nsAString& aString, nsCSSStyleSheet* aSheet, michael@0: bool aAppend) const michael@0: { michael@0: if (!aAppend) michael@0: aString.Truncate(); michael@0: michael@0: // selectors are linked from right-to-left, so the next selector in michael@0: // the linked list actually precedes this one in the resulting string michael@0: nsAutoTArray stack; michael@0: for (const nsCSSSelector *s = this; s; s = s->mNext) { michael@0: stack.AppendElement(s); michael@0: } michael@0: michael@0: while (!stack.IsEmpty()) { michael@0: uint32_t index = stack.Length() - 1; michael@0: const nsCSSSelector *s = stack.ElementAt(index); michael@0: stack.RemoveElementAt(index); michael@0: michael@0: s->AppendToStringWithoutCombinators(aString, aSheet); michael@0: michael@0: // Append the combinator, if needed. michael@0: if (!stack.IsEmpty()) { michael@0: const nsCSSSelector *next = stack.ElementAt(index - 1); michael@0: char16_t oper = s->mOperator; michael@0: if (next->IsPseudoElement()) { michael@0: NS_ASSERTION(oper == char16_t(':'), michael@0: "improperly chained pseudo element"); michael@0: } else { michael@0: NS_ASSERTION(oper != char16_t(0), michael@0: "compound selector without combinator"); michael@0: michael@0: aString.Append(char16_t(' ')); michael@0: if (oper != char16_t(' ')) { michael@0: aString.Append(oper); michael@0: aString.Append(char16_t(' ')); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSSelector::AppendToStringWithoutCombinators michael@0: (nsAString& aString, nsCSSStyleSheet* aSheet) const michael@0: { michael@0: AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false); michael@0: michael@0: for (const nsCSSSelector* negation = mNegations; negation; michael@0: negation = negation->mNegations) { michael@0: aString.AppendLiteral(":not("); michael@0: negation->AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, michael@0: true); michael@0: aString.Append(char16_t(')')); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations michael@0: (nsAString& aString, nsCSSStyleSheet* aSheet, michael@0: bool aIsNegated) const michael@0: { michael@0: nsAutoString temp; michael@0: bool isPseudoElement = IsPseudoElement(); michael@0: michael@0: // For non-pseudo-element selectors or for lone pseudo-elements, deal with michael@0: // namespace prefixes. michael@0: bool wroteNamespace = false; michael@0: if (!isPseudoElement || !mNext) { michael@0: // append the namespace prefix if needed michael@0: nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr; michael@0: michael@0: // sheetNS is non-null if and only if we had an @namespace rule. If it's michael@0: // null, that means that the only namespaces we could have are the michael@0: // wildcard namespace (which can be implicit in this case) and the "none" michael@0: // namespace, which then needs to be explicitly specified. michael@0: if (!sheetNS) { michael@0: NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || michael@0: mNameSpace == kNameSpaceID_None, michael@0: "How did we get this namespace?"); michael@0: if (mNameSpace == kNameSpaceID_None) { michael@0: aString.Append(char16_t('|')); michael@0: wroteNamespace = true; michael@0: } michael@0: } else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) { michael@0: // We have the default namespace (possibly including the wildcard michael@0: // namespace). Do nothing. michael@0: NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || michael@0: CanBeNamespaced(aIsNegated), michael@0: "How did we end up with this namespace?"); michael@0: } else if (mNameSpace == kNameSpaceID_None) { michael@0: NS_ASSERTION(CanBeNamespaced(aIsNegated), michael@0: "How did we end up with this namespace?"); michael@0: aString.Append(char16_t('|')); michael@0: wroteNamespace = true; michael@0: } else if (mNameSpace != kNameSpaceID_Unknown) { michael@0: NS_ASSERTION(CanBeNamespaced(aIsNegated), michael@0: "How did we end up with this namespace?"); michael@0: nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace); michael@0: NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace " michael@0: "without a prefix?"); michael@0: nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom), michael@0: aString); michael@0: aString.Append(char16_t('|')); michael@0: wroteNamespace = true; michael@0: } else { michael@0: // A selector for an element in any namespace, while the default michael@0: // namespace is something else. :not() is special in that the default michael@0: // namespace is not implied for non-type selectors, so if this is a michael@0: // negated non-type selector we don't need to output an explicit wildcard michael@0: // namespace here, since those default to a wildcard namespace. michael@0: if (CanBeNamespaced(aIsNegated)) { michael@0: aString.AppendLiteral("*|"); michael@0: wroteNamespace = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!mLowercaseTag) { michael@0: // Universal selector: avoid writing the universal selector when we michael@0: // can avoid it, especially since we're required to avoid it for the michael@0: // inside of :not() michael@0: if (wroteNamespace || michael@0: (!mIDList && !mClassList && !mPseudoClassList && !mAttrList && michael@0: (aIsNegated || !mNegations))) { michael@0: aString.Append(char16_t('*')); michael@0: } michael@0: } else { michael@0: // Append the tag name michael@0: nsAutoString tag; michael@0: (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag); michael@0: if (isPseudoElement) { michael@0: if (!mNext) { michael@0: // Lone pseudo-element selector -- toss in a wildcard type selector michael@0: // XXXldb Why? michael@0: aString.Append(char16_t('*')); michael@0: } michael@0: if (!nsCSSPseudoElements::IsCSS2PseudoElement(mLowercaseTag)) { michael@0: aString.Append(char16_t(':')); michael@0: } michael@0: // This should not be escaped since (a) the pseudo-element string michael@0: // has a ":" that can't be escaped and (b) all pseudo-elements at michael@0: // this point are known, and therefore we know they don't need michael@0: // escaping. michael@0: aString.Append(tag); michael@0: } else { michael@0: nsStyleUtil::AppendEscapedCSSIdent(tag, aString); michael@0: } michael@0: } michael@0: michael@0: // Append the id, if there is one michael@0: if (mIDList) { michael@0: nsAtomList* list = mIDList; michael@0: while (list != nullptr) { michael@0: list->mAtom->ToString(temp); michael@0: aString.Append(char16_t('#')); michael@0: nsStyleUtil::AppendEscapedCSSIdent(temp, aString); michael@0: list = list->mNext; michael@0: } michael@0: } michael@0: michael@0: // Append each class in the linked list michael@0: if (mClassList) { michael@0: if (isPseudoElement) { michael@0: #ifdef MOZ_XUL michael@0: NS_ABORT_IF_FALSE(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag), michael@0: "must be tree pseudo-element"); michael@0: michael@0: aString.Append(char16_t('(')); michael@0: for (nsAtomList* list = mClassList; list; list = list->mNext) { michael@0: nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString); michael@0: aString.Append(char16_t(',')); michael@0: } michael@0: // replace the final comma with a close-paren michael@0: aString.Replace(aString.Length() - 1, 1, char16_t(')')); michael@0: #else michael@0: NS_ERROR("Can't happen"); michael@0: #endif michael@0: } else { michael@0: nsAtomList* list = mClassList; michael@0: while (list != nullptr) { michael@0: list->mAtom->ToString(temp); michael@0: aString.Append(char16_t('.')); michael@0: nsStyleUtil::AppendEscapedCSSIdent(temp, aString); michael@0: list = list->mNext; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Append each attribute selector in the linked list michael@0: if (mAttrList) { michael@0: nsAttrSelector* list = mAttrList; michael@0: while (list != nullptr) { michael@0: aString.Append(char16_t('[')); michael@0: // Append the namespace prefix michael@0: if (list->mNameSpace == kNameSpaceID_Unknown) { michael@0: aString.Append(char16_t('*')); michael@0: aString.Append(char16_t('|')); michael@0: } else if (list->mNameSpace != kNameSpaceID_None) { michael@0: if (aSheet) { michael@0: nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); michael@0: nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace); michael@0: // Default namespaces don't apply to attribute selectors, so michael@0: // we must have a useful prefix. michael@0: NS_ASSERTION(prefixAtom, michael@0: "How did we end up with a namespace if the prefix " michael@0: "is unknown?"); michael@0: nsAutoString prefix; michael@0: prefixAtom->ToString(prefix); michael@0: nsStyleUtil::AppendEscapedCSSIdent(prefix, aString); michael@0: aString.Append(char16_t('|')); michael@0: } michael@0: } michael@0: // Append the attribute name michael@0: list->mCasedAttr->ToString(temp); michael@0: nsStyleUtil::AppendEscapedCSSIdent(temp, aString); michael@0: michael@0: if (list->mFunction != NS_ATTR_FUNC_SET) { michael@0: // Append the function michael@0: if (list->mFunction == NS_ATTR_FUNC_INCLUDES) michael@0: aString.Append(char16_t('~')); michael@0: else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH) michael@0: aString.Append(char16_t('|')); michael@0: else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH) michael@0: aString.Append(char16_t('^')); michael@0: else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH) michael@0: aString.Append(char16_t('$')); michael@0: else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH) michael@0: aString.Append(char16_t('*')); michael@0: michael@0: aString.Append(char16_t('=')); michael@0: michael@0: // Append the value michael@0: nsStyleUtil::AppendEscapedCSSString(list->mValue, aString); michael@0: } michael@0: michael@0: aString.Append(char16_t(']')); michael@0: michael@0: list = list->mNext; michael@0: } michael@0: } michael@0: michael@0: // Append each pseudo-class in the linked list michael@0: for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) { michael@0: nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp); michael@0: // This should not be escaped since (a) the pseudo-class string michael@0: // has a ":" that can't be escaped and (b) all pseudo-classes at michael@0: // this point are known, and therefore we know they don't need michael@0: // escaping. michael@0: aString.Append(temp); michael@0: if (list->u.mMemory) { michael@0: aString.Append(char16_t('(')); michael@0: if (nsCSSPseudoClasses::HasStringArg(list->mType)) { michael@0: nsStyleUtil::AppendEscapedCSSIdent( michael@0: nsDependentString(list->u.mString), aString); michael@0: } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) { michael@0: int32_t a = list->u.mNumbers[0], michael@0: b = list->u.mNumbers[1]; michael@0: temp.Truncate(); michael@0: if (a != 0) { michael@0: if (a == -1) { michael@0: temp.Append(char16_t('-')); michael@0: } else if (a != 1) { michael@0: temp.AppendInt(a); michael@0: } michael@0: temp.Append(char16_t('n')); michael@0: } michael@0: if (b != 0 || a == 0) { michael@0: if (b >= 0 && a != 0) // check a != 0 for whether we printed above michael@0: temp.Append(char16_t('+')); michael@0: temp.AppendInt(b); michael@0: } michael@0: aString.Append(temp); michael@0: } else { michael@0: NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType), michael@0: "unexpected pseudo-class"); michael@0: nsString tmp; michael@0: list->u.mSelectors->ToString(tmp, aSheet); michael@0: aString.Append(tmp); michael@0: } michael@0: aString.Append(char16_t(')')); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsCSSSelector::CanBeNamespaced(bool aIsNegated) const michael@0: { michael@0: return !aIsNegated || michael@0: (!mIDList && !mClassList && !mPseudoClassList && !mAttrList); michael@0: } michael@0: michael@0: size_t michael@0: nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: const nsCSSSelector* s = this; michael@0: while (s) { michael@0: n += aMallocSizeOf(s); michael@0: michael@0: #define MEASURE(x) n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0; michael@0: michael@0: MEASURE(s->mIDList); michael@0: MEASURE(s->mClassList); michael@0: MEASURE(s->mPseudoClassList); michael@0: MEASURE(s->mNegations); michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - s->mAttrList michael@0: // michael@0: // The following members aren't measured: michael@0: // - s->mLowercaseTag, because it's an atom and therefore shared michael@0: // - s->mCasedTag, because it's an atom and therefore shared michael@0: michael@0: s = s->mNext; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: // -- nsCSSSelectorList ------------------------------- michael@0: michael@0: nsCSSSelectorList::nsCSSSelectorList(void) michael@0: : mSelectors(nullptr), michael@0: mWeight(0), michael@0: mNext(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(nsCSSSelectorList); michael@0: } michael@0: michael@0: nsCSSSelectorList::~nsCSSSelectorList() michael@0: { michael@0: MOZ_COUNT_DTOR(nsCSSSelectorList); michael@0: delete mSelectors; michael@0: NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext); michael@0: } michael@0: michael@0: nsCSSSelector* michael@0: nsCSSSelectorList::AddSelector(char16_t aOperator) michael@0: { michael@0: nsCSSSelector* newSel = new nsCSSSelector(); michael@0: michael@0: if (mSelectors) { michael@0: NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator"); michael@0: mSelectors->SetOperator(aOperator); michael@0: } else { michael@0: NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining"); michael@0: } michael@0: michael@0: newSel->mNext = mSelectors; michael@0: mSelectors = newSel; michael@0: return newSel; michael@0: } michael@0: michael@0: void michael@0: nsCSSSelectorList::ToString(nsAString& aResult, nsCSSStyleSheet* aSheet) michael@0: { michael@0: aResult.Truncate(); michael@0: nsCSSSelectorList *p = this; michael@0: for (;;) { michael@0: p->mSelectors->ToString(aResult, aSheet, true); michael@0: p = p->mNext; michael@0: if (!p) michael@0: break; michael@0: aResult.AppendLiteral(", "); michael@0: } michael@0: } michael@0: michael@0: nsCSSSelectorList* michael@0: nsCSSSelectorList::Clone(bool aDeep) const michael@0: { michael@0: nsCSSSelectorList *result = new nsCSSSelectorList(); michael@0: result->mWeight = mWeight; michael@0: NS_IF_CLONE(mSelectors); michael@0: michael@0: if (aDeep) { michael@0: NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result, michael@0: (false)); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: size_t michael@0: nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = 0; michael@0: const nsCSSSelectorList* s = this; michael@0: while (s) { michael@0: n += aMallocSizeOf(s); michael@0: n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0; michael@0: s = s->mNext; michael@0: } michael@0: return n; michael@0: } michael@0: michael@0: // -- ImportantRule ---------------------------------- michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: ImportantRule::ImportantRule(Declaration* aDeclaration) michael@0: : mDeclaration(aDeclaration) michael@0: { michael@0: } michael@0: michael@0: ImportantRule::~ImportantRule() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(ImportantRule, nsIStyleRule) michael@0: michael@0: /* virtual */ void michael@0: ImportantRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: mDeclaration->MapImportantRuleInfoInto(aRuleData); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: ImportantRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: // Indent michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: michael@0: fprintf(out, "! Important declaration=%p\n", michael@0: static_cast(mDeclaration)); michael@0: } michael@0: #endif michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla michael@0: michael@0: // -------------------------------------------------------- michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: class DOMCSSStyleRule; michael@0: } michael@0: } michael@0: michael@0: class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration michael@0: { michael@0: public: michael@0: DOMCSSDeclarationImpl(css::StyleRule *aRule); michael@0: virtual ~DOMCSSDeclarationImpl(void); michael@0: michael@0: NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) MOZ_OVERRIDE; michael@0: void DropReference(void); michael@0: virtual css::Declaration* GetCSSDeclaration(bool aAllocate) MOZ_OVERRIDE; michael@0: virtual nsresult SetCSSDeclaration(css::Declaration* aDecl) MOZ_OVERRIDE; michael@0: virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) MOZ_OVERRIDE; michael@0: virtual nsIDocument* DocToUpdate() MOZ_OVERRIDE; michael@0: michael@0: // Override |AddRef| and |Release| for being a member of michael@0: // |DOMCSSStyleRule|. Also, we need to forward QI for cycle michael@0: // collection things to DOMCSSStyleRule. michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: virtual nsINode *GetParentObject() MOZ_OVERRIDE michael@0: { michael@0: return mRule ? mRule->GetDocument() : nullptr; michael@0: } michael@0: michael@0: friend class css::DOMCSSStyleRule; michael@0: michael@0: protected: michael@0: // This reference is not reference-counted. The rule object tells us michael@0: // when it's about to go away. michael@0: css::StyleRule *mRule; michael@0: michael@0: inline css::DOMCSSStyleRule* DomRule(); michael@0: michael@0: private: michael@0: // NOT TO BE IMPLEMENTED michael@0: // This object cannot be allocated on its own. It must be a member of michael@0: // DOMCSSStyleRule. michael@0: void* operator new(size_t size) CPP_THROW_NEW; michael@0: }; michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: class DOMCSSStyleRule : public nsICSSStyleRuleDOMWrapper michael@0: { michael@0: public: michael@0: DOMCSSStyleRule(StyleRule *aRule); michael@0: virtual ~DOMCSSStyleRule(); michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMCSSStyleRule) michael@0: NS_DECL_NSIDOMCSSRULE michael@0: NS_DECL_NSIDOMCSSSTYLERULE michael@0: michael@0: // nsICSSStyleRuleDOMWrapper michael@0: NS_IMETHOD GetCSSStyleRule(StyleRule **aResult); michael@0: michael@0: DOMCSSDeclarationImpl* DOMDeclaration() { return &mDOMDeclaration; } michael@0: michael@0: friend class ::DOMCSSDeclarationImpl; michael@0: michael@0: protected: michael@0: DOMCSSDeclarationImpl mDOMDeclaration; michael@0: michael@0: StyleRule* Rule() { michael@0: return mDOMDeclaration.mRule; michael@0: } michael@0: }; michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla michael@0: michael@0: DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule) michael@0: : mRule(aRule) michael@0: { michael@0: MOZ_COUNT_CTOR(DOMCSSDeclarationImpl); michael@0: } michael@0: michael@0: DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void) michael@0: { michael@0: NS_ASSERTION(!mRule, "DropReference not called."); michael@0: michael@0: MOZ_COUNT_DTOR(DOMCSSDeclarationImpl); michael@0: } michael@0: michael@0: inline css::DOMCSSStyleRule* DOMCSSDeclarationImpl::DomRule() michael@0: { michael@0: return reinterpret_cast michael@0: (reinterpret_cast(this) - michael@0: offsetof(css::DOMCSSStyleRule, mDOMDeclaration)); michael@0: } michael@0: michael@0: NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule()) michael@0: NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule()) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: // We forward the cycle collection interfaces to DomRule(), which is michael@0: // never null (in fact, we're part of that object!) michael@0: if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) || michael@0: aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) { michael@0: return DomRule()->QueryInterface(aIID, aInstancePtr); michael@0: } michael@0: else michael@0: NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration) michael@0: michael@0: void michael@0: DOMCSSDeclarationImpl::DropReference(void) michael@0: { michael@0: mRule = nullptr; michael@0: } michael@0: michael@0: css::Declaration* michael@0: DOMCSSDeclarationImpl::GetCSSDeclaration(bool aAllocate) michael@0: { michael@0: if (mRule) { michael@0: return mRule->GetDeclaration(); michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) michael@0: { michael@0: GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aParent); michael@0: michael@0: if (!mRule) { michael@0: *aParent = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IF_ADDREF(*aParent = mRule->GetDOMRule()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: DOMCSSDeclarationImpl::SetCSSDeclaration(css::Declaration* aDecl) michael@0: { michael@0: NS_PRECONDITION(mRule, michael@0: "can only be called when |GetCSSDeclaration| returned a declaration"); michael@0: michael@0: nsCOMPtr owningDoc; michael@0: nsCOMPtr sheet = mRule->GetStyleSheet(); michael@0: if (sheet) { michael@0: owningDoc = sheet->GetOwningDocument(); michael@0: } michael@0: michael@0: mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true); michael@0: michael@0: nsRefPtr oldRule = mRule; michael@0: mRule = oldRule->DeclarationChanged(aDecl, true).take(); michael@0: if (!mRule) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: nsrefcnt cnt = mRule->Release(); michael@0: if (cnt == 0) { michael@0: NS_NOTREACHED("container didn't take ownership"); michael@0: mRule = nullptr; michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (owningDoc) { michael@0: owningDoc->StyleRuleChanged(sheet, oldRule, mRule); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIDocument* michael@0: DOMCSSDeclarationImpl::DocToUpdate() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: // needs to be outside the namespace michael@0: DOMCI_DATA(CSSStyleRule, css::DOMCSSStyleRule) michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: DOMCSSStyleRule::DOMCSSStyleRule(StyleRule* aRule) michael@0: : mDOMDeclaration(aRule) michael@0: { michael@0: } michael@0: michael@0: DOMCSSStyleRule::~DOMCSSStyleRule() michael@0: { michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCSSStyleRule) michael@0: NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleRule) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCSSStyleRule) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCSSStyleRule) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(DOMCSSStyleRule) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMCSSStyleRule) michael@0: // Trace the wrapper for our declaration. This just expands out michael@0: // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use michael@0: // directly because the wrapper is on the declaration, not on us. michael@0: tmp->DOMDeclaration()->TraceWrapper(aCallbacks, aClosure); michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMCSSStyleRule) michael@0: // Unlink the wrapper for our declaraton. This just expands out michael@0: // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use michael@0: // directly because the wrapper is on the declaration, not on us. michael@0: tmp->DOMDeclaration()->ReleaseWrapper(static_cast(p)); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMCSSStyleRule) michael@0: // Just NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS here: that will call michael@0: // into our Trace hook, where we do the right thing with declarations michael@0: // already. michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetType(uint16_t* aType) michael@0: { michael@0: *aType = nsIDOMCSSRule::STYLE_RULE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetCssText(nsAString& aCssText) michael@0: { michael@0: if (!Rule()) { michael@0: aCssText.Truncate(); michael@0: } else { michael@0: Rule()->GetCssText(aCssText); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::SetCssText(const nsAString& aCssText) michael@0: { michael@0: if (Rule()) { michael@0: Rule()->SetCssText(aCssText); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet) michael@0: { michael@0: if (!Rule()) { michael@0: *aSheet = nullptr; michael@0: return NS_OK; michael@0: } michael@0: return Rule()->GetParentStyleSheet(aSheet); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule) michael@0: { michael@0: if (!Rule()) { michael@0: *aParentRule = nullptr; michael@0: return NS_OK; michael@0: } michael@0: return Rule()->GetParentRule(aParentRule); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetSelectorText(nsAString& aSelectorText) michael@0: { michael@0: if (!Rule()) { michael@0: aSelectorText.Truncate(); michael@0: } else { michael@0: Rule()->GetSelectorText(aSelectorText); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::SetSelectorText(const nsAString& aSelectorText) michael@0: { michael@0: if (Rule()) { michael@0: Rule()->SetSelectorText(aSelectorText); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) michael@0: { michael@0: *aStyle = &mDOMDeclaration; michael@0: NS_ADDREF(*aStyle); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DOMCSSStyleRule::GetCSSStyleRule(StyleRule **aResult) michael@0: { michael@0: *aResult = Rule(); michael@0: NS_IF_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla michael@0: michael@0: // -- StyleRule ------------------------------------ michael@0: michael@0: namespace mozilla { michael@0: namespace css { michael@0: michael@0: StyleRule::StyleRule(nsCSSSelectorList* aSelector, michael@0: Declaration* aDeclaration) michael@0: : Rule(), michael@0: mSelector(aSelector), michael@0: mDeclaration(aDeclaration), michael@0: mImportantRule(nullptr), michael@0: mDOMRule(nullptr), michael@0: mLineNumber(0), michael@0: mColumnNumber(0), michael@0: mWasMatched(false) michael@0: { michael@0: NS_PRECONDITION(aDeclaration, "must have a declaration"); michael@0: } michael@0: michael@0: // for |Clone| michael@0: StyleRule::StyleRule(const StyleRule& aCopy) michael@0: : Rule(aCopy), michael@0: mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr), michael@0: mDeclaration(new Declaration(*aCopy.mDeclaration)), michael@0: mImportantRule(nullptr), michael@0: mDOMRule(nullptr), michael@0: mLineNumber(aCopy.mLineNumber), michael@0: mColumnNumber(aCopy.mColumnNumber), michael@0: mWasMatched(false) michael@0: { michael@0: // rest is constructed lazily on existing data michael@0: } michael@0: michael@0: // for |SetCSSDeclaration| michael@0: StyleRule::StyleRule(StyleRule& aCopy, michael@0: Declaration* aDeclaration) michael@0: : Rule(aCopy), michael@0: mSelector(aCopy.mSelector), michael@0: mDeclaration(aDeclaration), michael@0: mImportantRule(nullptr), michael@0: mDOMRule(aCopy.mDOMRule), michael@0: mLineNumber(aCopy.mLineNumber), michael@0: mColumnNumber(aCopy.mColumnNumber), michael@0: mWasMatched(false) michael@0: { michael@0: // The DOM rule is replacing |aCopy| with |this|, so transfer michael@0: // the reverse pointer as well (and transfer ownership). michael@0: aCopy.mDOMRule = nullptr; michael@0: michael@0: // Similarly for the selector. michael@0: aCopy.mSelector = nullptr; michael@0: michael@0: // We are probably replacing the old declaration with |aDeclaration| michael@0: // instead of taking ownership of the old declaration; only null out michael@0: // aCopy.mDeclaration if we are taking ownership. michael@0: if (mDeclaration == aCopy.mDeclaration) { michael@0: // This should only ever happen if the declaration was modifiable. michael@0: mDeclaration->AssertMutable(); michael@0: aCopy.mDeclaration = nullptr; michael@0: } michael@0: } michael@0: michael@0: StyleRule::~StyleRule() michael@0: { michael@0: delete mSelector; michael@0: delete mDeclaration; michael@0: NS_IF_RELEASE(mImportantRule); michael@0: if (mDOMRule) { michael@0: mDOMRule->DOMDeclaration()->DropReference(); michael@0: NS_RELEASE(mDOMRule); michael@0: } michael@0: } michael@0: michael@0: // QueryInterface implementation for StyleRule michael@0: NS_INTERFACE_MAP_BEGIN(StyleRule) michael@0: if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) { michael@0: *aInstancePtr = this; michael@0: NS_ADDREF_THIS(); michael@0: return NS_OK; michael@0: } michael@0: else michael@0: NS_INTERFACE_MAP_ENTRY(nsIStyleRule) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(StyleRule) michael@0: NS_IMPL_RELEASE(StyleRule) michael@0: michael@0: void michael@0: StyleRule::RuleMatched() michael@0: { michael@0: if (!mWasMatched) { michael@0: NS_ABORT_IF_FALSE(!mImportantRule, "should not have important rule yet"); michael@0: michael@0: mWasMatched = true; michael@0: mDeclaration->SetImmutable(); michael@0: if (mDeclaration->HasImportantData()) { michael@0: NS_ADDREF(mImportantRule = new ImportantRule(mDeclaration)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* virtual */ int32_t michael@0: StyleRule::GetType() const michael@0: { michael@0: return Rule::STYLE_RULE; michael@0: } michael@0: michael@0: /* virtual */ already_AddRefed michael@0: StyleRule::Clone() const michael@0: { michael@0: nsRefPtr clone = new StyleRule(*this); michael@0: return clone.forget(); michael@0: } michael@0: michael@0: /* virtual */ nsIDOMCSSRule* michael@0: StyleRule::GetDOMRule() michael@0: { michael@0: if (!mDOMRule) { michael@0: if (!GetStyleSheet()) { michael@0: // Inline style rules aren't supposed to have a DOM rule object, only michael@0: // a declaration. But if we do have one already, from a style sheet michael@0: // rule that used to be in a document, we still want to return it. michael@0: return nullptr; michael@0: } michael@0: mDOMRule = new DOMCSSStyleRule(this); michael@0: NS_ADDREF(mDOMRule); michael@0: } michael@0: return mDOMRule; michael@0: } michael@0: michael@0: /* virtual */ nsIDOMCSSRule* michael@0: StyleRule::GetExistingDOMRule() michael@0: { michael@0: return mDOMRule; michael@0: } michael@0: michael@0: /* virtual */ already_AddRefed michael@0: StyleRule::DeclarationChanged(Declaration* aDecl, michael@0: bool aHandleContainer) michael@0: { michael@0: nsRefPtr clone = new StyleRule(*this, aDecl); michael@0: michael@0: if (aHandleContainer) { michael@0: nsCSSStyleSheet* sheet = GetStyleSheet(); michael@0: if (mParentRule) { michael@0: if (sheet) { michael@0: sheet->ReplaceRuleInGroup(mParentRule, this, clone); michael@0: } else { michael@0: mParentRule->ReplaceStyleRule(this, clone); michael@0: } michael@0: } else if (sheet) { michael@0: sheet->ReplaceStyleRule(this, clone); michael@0: } michael@0: } michael@0: michael@0: return clone.forget(); michael@0: } michael@0: michael@0: /* virtual */ void michael@0: StyleRule::MapRuleInfoInto(nsRuleData* aRuleData) michael@0: { michael@0: NS_ABORT_IF_FALSE(mWasMatched, michael@0: "somebody forgot to call css::StyleRule::RuleMatched"); michael@0: mDeclaration->MapNormalRuleInfoInto(aRuleData); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: /* virtual */ void michael@0: StyleRule::List(FILE* out, int32_t aIndent) const michael@0: { michael@0: // Indent michael@0: for (int32_t index = aIndent; --index >= 0; ) fputs(" ", out); michael@0: michael@0: nsAutoString buffer; michael@0: if (mSelector) michael@0: mSelector->ToString(buffer, GetStyleSheet()); michael@0: michael@0: buffer.AppendLiteral(" "); michael@0: fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out); michael@0: if (nullptr != mDeclaration) { michael@0: mDeclaration->List(out); michael@0: } michael@0: else { michael@0: fputs("{ null declaration }", out); michael@0: } michael@0: fputs("\n", out); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: StyleRule::GetCssText(nsAString& aCssText) michael@0: { michael@0: if (mSelector) { michael@0: mSelector->ToString(aCssText, GetStyleSheet()); michael@0: aCssText.Append(char16_t(' ')); michael@0: } michael@0: aCssText.Append(char16_t('{')); michael@0: aCssText.Append(char16_t(' ')); michael@0: if (mDeclaration) michael@0: { michael@0: nsAutoString tempString; michael@0: mDeclaration->ToString( tempString ); michael@0: aCssText.Append( tempString ); michael@0: } michael@0: aCssText.Append(char16_t(' ')); michael@0: aCssText.Append(char16_t('}')); michael@0: } michael@0: michael@0: void michael@0: StyleRule::SetCssText(const nsAString& aCssText) michael@0: { michael@0: // XXX TBI - need to re-parse rule & declaration michael@0: } michael@0: michael@0: void michael@0: StyleRule::GetSelectorText(nsAString& aSelectorText) michael@0: { michael@0: if (mSelector) michael@0: mSelector->ToString(aSelectorText, GetStyleSheet()); michael@0: else michael@0: aSelectorText.Truncate(); michael@0: } michael@0: michael@0: void michael@0: StyleRule::SetSelectorText(const nsAString& aSelectorText) michael@0: { michael@0: // XXX TBI - get a parser and re-parse the selectors, michael@0: // XXX then need to re-compute the cascade michael@0: // XXX and dirty sheet michael@0: } michael@0: michael@0: /* virtual */ size_t michael@0: StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0; michael@0: n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0; michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - mImportantRule; michael@0: // - mDOMRule; michael@0: michael@0: return n; michael@0: } michael@0: michael@0: michael@0: } // namespace css michael@0: } // namespace mozilla