1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7  * representation of CSS style rules (selectors+declaration), CSS
8  * selectors, and DOM objects for style rules, selectors, and
9  * declarations
10  */
11 
12 #include "mozilla/css/StyleRule.h"
13 
14 #include "mozilla/DeclarationBlockInlines.h"
15 #include "mozilla/StyleSheetInlines.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/css/GroupRule.h"
18 #include "mozilla/css/Declaration.h"
19 #include "nsIDocument.h"
20 #include "nsIAtom.h"
21 #include "nsString.h"
22 #include "nsStyleUtil.h"
23 #include "nsICSSStyleRuleDOMWrapper.h"
24 #include "nsDOMCSSDeclaration.h"
25 #include "nsNameSpaceManager.h"
26 #include "nsXMLNameSpaceMap.h"
27 #include "nsCSSPseudoClasses.h"
28 #include "nsCSSAnonBoxes.h"
29 #include "nsTArray.h"
30 #include "nsDOMClassInfoID.h"
31 #include "nsContentUtils.h"
32 #include "nsError.h"
33 #include "mozAutoDocUpdate.h"
34 
35 class nsIDOMCSSStyleDeclaration;
36 class nsIDOMCSSStyleSheet;
37 
38 using namespace mozilla;
39 
40 #define NS_IF_CLONE(member_)                                                  \
41   PR_BEGIN_MACRO                                                              \
42     if (member_) {                                                            \
43       result->member_ = member_->Clone();                                     \
44       if (!result->member_) {                                                 \
45         delete result;                                                        \
46         return nullptr;                                                        \
47       }                                                                       \
48     }                                                                         \
49   PR_END_MACRO
50 
51 #define NS_IF_DELETE(ptr)                                                     \
52   PR_BEGIN_MACRO                                                              \
53     delete ptr;                                                               \
54     ptr = nullptr;                                                             \
55   PR_END_MACRO
56 
57 /* ************************************************************************** */
58 
nsAtomList(nsIAtom * aAtom)59 nsAtomList::nsAtomList(nsIAtom* aAtom)
60   : mAtom(aAtom),
61     mNext(nullptr)
62 {
63   MOZ_COUNT_CTOR(nsAtomList);
64 }
65 
nsAtomList(const nsString & aAtomValue)66 nsAtomList::nsAtomList(const nsString& aAtomValue)
67   : mAtom(nullptr),
68     mNext(nullptr)
69 {
70   MOZ_COUNT_CTOR(nsAtomList);
71   mAtom = NS_Atomize(aAtomValue);
72 }
73 
74 nsAtomList*
Clone(bool aDeep) const75 nsAtomList::Clone(bool aDeep) const
76 {
77   nsAtomList *result = new nsAtomList(mAtom);
78   if (!result)
79     return nullptr;
80 
81   if (aDeep)
82     NS_CSS_CLONE_LIST_MEMBER(nsAtomList, this, mNext, result, (false));
83   return result;
84 }
85 
86 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const87 nsAtomList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
88 {
89   size_t n = 0;
90   const nsAtomList* a = this;
91   while (a) {
92     n += aMallocSizeOf(a);
93 
94     // The following members aren't measured:
95     // - a->mAtom, because it may be shared
96 
97     a = a->mNext;
98   }
99   return n;
100 }
101 
~nsAtomList(void)102 nsAtomList::~nsAtomList(void)
103 {
104   MOZ_COUNT_DTOR(nsAtomList);
105   NS_CSS_DELETE_LIST_MEMBER(nsAtomList, this, mNext);
106 }
107 
nsPseudoClassList(CSSPseudoClassType aType)108 nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType)
109   : mType(aType),
110     mNext(nullptr)
111 {
112   NS_ASSERTION(!nsCSSPseudoClasses::HasStringArg(aType) &&
113                !nsCSSPseudoClasses::HasNthPairArg(aType),
114                "unexpected pseudo-class");
115   MOZ_COUNT_CTOR(nsPseudoClassList);
116   u.mMemory = nullptr;
117 }
118 
nsPseudoClassList(CSSPseudoClassType aType,const char16_t * aString)119 nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
120                                      const char16_t* aString)
121   : mType(aType),
122     mNext(nullptr)
123 {
124   NS_ASSERTION(nsCSSPseudoClasses::HasStringArg(aType),
125                "unexpected pseudo-class");
126   NS_ASSERTION(aString, "string expected");
127   MOZ_COUNT_CTOR(nsPseudoClassList);
128   u.mString = NS_strdup(aString);
129 }
130 
nsPseudoClassList(CSSPseudoClassType aType,const int32_t * aIntPair)131 nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
132                                      const int32_t* aIntPair)
133   : mType(aType),
134     mNext(nullptr)
135 {
136   NS_ASSERTION(nsCSSPseudoClasses::HasNthPairArg(aType),
137                "unexpected pseudo-class");
138   NS_ASSERTION(aIntPair, "integer pair expected");
139   MOZ_COUNT_CTOR(nsPseudoClassList);
140   u.mNumbers =
141     static_cast<int32_t*>(nsMemory::Clone(aIntPair, sizeof(int32_t) * 2));
142 }
143 
144 // adopts aSelectorList
nsPseudoClassList(CSSPseudoClassType aType,nsCSSSelectorList * aSelectorList)145 nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
146                                      nsCSSSelectorList* aSelectorList)
147   : mType(aType),
148     mNext(nullptr)
149 {
150   NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
151                "unexpected pseudo-class");
152   NS_ASSERTION(aSelectorList, "selector list expected");
153   MOZ_COUNT_CTOR(nsPseudoClassList);
154   u.mSelectors = aSelectorList;
155 }
156 
157 nsPseudoClassList*
Clone(bool aDeep) const158 nsPseudoClassList::Clone(bool aDeep) const
159 {
160   nsPseudoClassList *result;
161   if (!u.mMemory) {
162     result = new nsPseudoClassList(mType);
163   } else if (nsCSSPseudoClasses::HasStringArg(mType)) {
164     result = new nsPseudoClassList(mType, u.mString);
165   } else if (nsCSSPseudoClasses::HasNthPairArg(mType)) {
166     result = new nsPseudoClassList(mType, u.mNumbers);
167   } else {
168     NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(mType),
169                  "unexpected pseudo-class");
170     // This constructor adopts its selector list argument.
171     result = new nsPseudoClassList(mType, u.mSelectors->Clone());
172   }
173 
174   if (aDeep)
175     NS_CSS_CLONE_LIST_MEMBER(nsPseudoClassList, this, mNext, result,
176                              (false));
177 
178   return result;
179 }
180 
181 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const182 nsPseudoClassList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
183 {
184   size_t n = 0;
185   const nsPseudoClassList* p = this;
186   while (p) {
187     n += aMallocSizeOf(p);
188     if (!p->u.mMemory) {
189       // do nothing
190 
191     } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) {
192       n += aMallocSizeOf(p->u.mString);
193 
194     } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) {
195       n += aMallocSizeOf(p->u.mNumbers);
196 
197     } else {
198       NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType),
199                    "unexpected pseudo-class");
200       n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf);
201     }
202     p = p->mNext;
203   }
204   return n;
205 }
206 
~nsPseudoClassList(void)207 nsPseudoClassList::~nsPseudoClassList(void)
208 {
209   MOZ_COUNT_DTOR(nsPseudoClassList);
210   if (nsCSSPseudoClasses::HasSelectorListArg(mType)) {
211     delete u.mSelectors;
212   } else if (u.mMemory) {
213     free(u.mMemory);
214   }
215   NS_CSS_DELETE_LIST_MEMBER(nsPseudoClassList, this, mNext);
216 }
217 
nsAttrSelector(int32_t aNameSpace,const nsString & aAttr)218 nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr)
219   : mValue(),
220     mNext(nullptr),
221     mLowercaseAttr(nullptr),
222     mCasedAttr(nullptr),
223     mNameSpace(aNameSpace),
224     mFunction(NS_ATTR_FUNC_SET),
225     // mValueCaseSensitivity doesn't matter; we have no value.
226     mValueCaseSensitivity(ValueCaseSensitivity::CaseSensitive)
227 {
228   MOZ_COUNT_CTOR(nsAttrSelector);
229 
230   nsAutoString lowercase;
231   nsContentUtils::ASCIIToLower(aAttr, lowercase);
232 
233   mCasedAttr = NS_Atomize(aAttr);
234   mLowercaseAttr = NS_Atomize(lowercase);
235 }
236 
nsAttrSelector(int32_t aNameSpace,const nsString & aAttr,uint8_t aFunction,const nsString & aValue,ValueCaseSensitivity aValueCaseSensitivity)237 nsAttrSelector::nsAttrSelector(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunction,
238                                const nsString& aValue,
239                                ValueCaseSensitivity aValueCaseSensitivity)
240   : mValue(aValue),
241     mNext(nullptr),
242     mLowercaseAttr(nullptr),
243     mCasedAttr(nullptr),
244     mNameSpace(aNameSpace),
245     mFunction(aFunction),
246     mValueCaseSensitivity(aValueCaseSensitivity)
247 {
248   MOZ_COUNT_CTOR(nsAttrSelector);
249 
250   nsAutoString lowercase;
251   nsContentUtils::ASCIIToLower(aAttr, lowercase);
252 
253   mCasedAttr = NS_Atomize(aAttr);
254   mLowercaseAttr = NS_Atomize(lowercase);
255 }
256 
nsAttrSelector(int32_t aNameSpace,nsIAtom * aLowercaseAttr,nsIAtom * aCasedAttr,uint8_t aFunction,const nsString & aValue,ValueCaseSensitivity aValueCaseSensitivity)257 nsAttrSelector::nsAttrSelector(int32_t aNameSpace,  nsIAtom* aLowercaseAttr,
258                                nsIAtom* aCasedAttr, uint8_t aFunction,
259                                const nsString& aValue,
260                                ValueCaseSensitivity aValueCaseSensitivity)
261   : mValue(aValue),
262     mNext(nullptr),
263     mLowercaseAttr(aLowercaseAttr),
264     mCasedAttr(aCasedAttr),
265     mNameSpace(aNameSpace),
266     mFunction(aFunction),
267     mValueCaseSensitivity(aValueCaseSensitivity)
268 {
269   MOZ_COUNT_CTOR(nsAttrSelector);
270 }
271 
272 nsAttrSelector*
Clone(bool aDeep) const273 nsAttrSelector::Clone(bool aDeep) const
274 {
275   nsAttrSelector *result =
276     new nsAttrSelector(mNameSpace, mLowercaseAttr, mCasedAttr,
277                        mFunction, mValue, mValueCaseSensitivity);
278 
279   if (aDeep)
280     NS_CSS_CLONE_LIST_MEMBER(nsAttrSelector, this, mNext, result, (false));
281 
282   return result;
283 }
284 
~nsAttrSelector(void)285 nsAttrSelector::~nsAttrSelector(void)
286 {
287   MOZ_COUNT_DTOR(nsAttrSelector);
288 
289   NS_CSS_DELETE_LIST_MEMBER(nsAttrSelector, this, mNext);
290 }
291 
292 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const293 nsAttrSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
294 {
295   size_t n = 0;
296   const nsAttrSelector* p = this;
297   while (p) {
298     n += aMallocSizeOf(p);
299     n += p->mValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
300     p = p->mNext;
301   }
302   return n;
303 }
304 
305 // -- nsCSSSelector -------------------------------
306 
nsCSSSelector(void)307 nsCSSSelector::nsCSSSelector(void)
308   : mLowercaseTag(nullptr),
309     mCasedTag(nullptr),
310     mIDList(nullptr),
311     mClassList(nullptr),
312     mPseudoClassList(nullptr),
313     mAttrList(nullptr),
314     mNegations(nullptr),
315     mNext(nullptr),
316     mNameSpace(kNameSpaceID_Unknown),
317     mOperator(0),
318     mPseudoType(CSSPseudoElementType::NotPseudo)
319 {
320   MOZ_COUNT_CTOR(nsCSSSelector);
321 }
322 
323 nsCSSSelector*
Clone(bool aDeepNext,bool aDeepNegations) const324 nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const
325 {
326   nsCSSSelector *result = new nsCSSSelector();
327   if (!result)
328     return nullptr;
329 
330   result->mNameSpace = mNameSpace;
331   result->mLowercaseTag = mLowercaseTag;
332   result->mCasedTag = mCasedTag;
333   result->mOperator = mOperator;
334   result->mPseudoType = mPseudoType;
335 
336   NS_IF_CLONE(mIDList);
337   NS_IF_CLONE(mClassList);
338   NS_IF_CLONE(mPseudoClassList);
339   NS_IF_CLONE(mAttrList);
340 
341   // No need to worry about multiple levels of recursion since an
342   // mNegations can't have an mNext.
343   NS_ASSERTION(!mNegations || !mNegations->mNext,
344                "mNegations can't have non-null mNext");
345   if (aDeepNegations) {
346     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNegations, result,
347                              (true, false));
348   }
349 
350   if (aDeepNext) {
351     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelector, this, mNext, result,
352                              (false, true));
353   }
354 
355   return result;
356 }
357 
~nsCSSSelector(void)358 nsCSSSelector::~nsCSSSelector(void)
359 {
360   MOZ_COUNT_DTOR(nsCSSSelector);
361   Reset();
362   // No need to worry about multiple levels of recursion since an
363   // mNegations can't have an mNext.
364   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNext);
365 }
366 
Reset(void)367 void nsCSSSelector::Reset(void)
368 {
369   mNameSpace = kNameSpaceID_Unknown;
370   mLowercaseTag = nullptr;
371   mCasedTag = nullptr;
372   NS_IF_DELETE(mIDList);
373   NS_IF_DELETE(mClassList);
374   NS_IF_DELETE(mPseudoClassList);
375   NS_IF_DELETE(mAttrList);
376   // No need to worry about multiple levels of recursion since an
377   // mNegations can't have an mNext.
378   NS_ASSERTION(!mNegations || !mNegations->mNext,
379                "mNegations can't have non-null mNext");
380   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelector, this, mNegations);
381   mOperator = char16_t(0);
382 }
383 
SetNameSpace(int32_t aNameSpace)384 void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
385 {
386   mNameSpace = aNameSpace;
387 }
388 
SetTag(const nsString & aTag)389 void nsCSSSelector::SetTag(const nsString& aTag)
390 {
391   if (aTag.IsEmpty()) {
392     mLowercaseTag = mCasedTag =  nullptr;
393     return;
394   }
395 
396   mCasedTag = NS_Atomize(aTag);
397 
398   nsAutoString lowercase;
399   nsContentUtils::ASCIIToLower(aTag, lowercase);
400   mLowercaseTag = NS_Atomize(lowercase);
401 }
402 
AddID(const nsString & aID)403 void nsCSSSelector::AddID(const nsString& aID)
404 {
405   if (!aID.IsEmpty()) {
406     nsAtomList** list = &mIDList;
407     while (nullptr != *list) {
408       list = &((*list)->mNext);
409     }
410     *list = new nsAtomList(aID);
411   }
412 }
413 
AddClass(const nsString & aClass)414 void nsCSSSelector::AddClass(const nsString& aClass)
415 {
416   if (!aClass.IsEmpty()) {
417     nsAtomList** list = &mClassList;
418     while (nullptr != *list) {
419       list = &((*list)->mNext);
420     }
421     *list = new nsAtomList(aClass);
422   }
423 }
424 
AddPseudoClass(CSSPseudoClassType aType)425 void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType)
426 {
427   AddPseudoClassInternal(new nsPseudoClassList(aType));
428 }
429 
AddPseudoClass(CSSPseudoClassType aType,const char16_t * aString)430 void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
431                                    const char16_t* aString)
432 {
433   AddPseudoClassInternal(new nsPseudoClassList(aType, aString));
434 }
435 
AddPseudoClass(CSSPseudoClassType aType,const int32_t * aIntPair)436 void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
437                                    const int32_t* aIntPair)
438 {
439   AddPseudoClassInternal(new nsPseudoClassList(aType, aIntPair));
440 }
441 
AddPseudoClass(CSSPseudoClassType aType,nsCSSSelectorList * aSelectorList)442 void nsCSSSelector::AddPseudoClass(CSSPseudoClassType aType,
443                                    nsCSSSelectorList* aSelectorList)
444 {
445   // Take ownership of nsCSSSelectorList instead of copying.
446   AddPseudoClassInternal(new nsPseudoClassList(aType, aSelectorList));
447 }
448 
AddPseudoClassInternal(nsPseudoClassList * aPseudoClass)449 void nsCSSSelector::AddPseudoClassInternal(nsPseudoClassList *aPseudoClass)
450 {
451   nsPseudoClassList** list = &mPseudoClassList;
452   while (nullptr != *list) {
453     list = &((*list)->mNext);
454   }
455   *list = aPseudoClass;
456 }
457 
AddAttribute(int32_t aNameSpace,const nsString & aAttr)458 void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr)
459 {
460   if (!aAttr.IsEmpty()) {
461     nsAttrSelector** list = &mAttrList;
462     while (nullptr != *list) {
463       list = &((*list)->mNext);
464     }
465     *list = new nsAttrSelector(aNameSpace, aAttr);
466   }
467 }
468 
AddAttribute(int32_t aNameSpace,const nsString & aAttr,uint8_t aFunc,const nsString & aValue,nsAttrSelector::ValueCaseSensitivity aCaseSensitivity)469 void nsCSSSelector::AddAttribute(int32_t aNameSpace, const nsString& aAttr, uint8_t aFunc,
470                                  const nsString& aValue,
471                                  nsAttrSelector::ValueCaseSensitivity aCaseSensitivity)
472 {
473   if (!aAttr.IsEmpty()) {
474     nsAttrSelector** list = &mAttrList;
475     while (nullptr != *list) {
476       list = &((*list)->mNext);
477     }
478     *list = new nsAttrSelector(aNameSpace, aAttr, aFunc, aValue, aCaseSensitivity);
479   }
480 }
481 
SetOperator(char16_t aOperator)482 void nsCSSSelector::SetOperator(char16_t aOperator)
483 {
484   mOperator = aOperator;
485 }
486 
CalcWeightWithoutNegations() const487 int32_t nsCSSSelector::CalcWeightWithoutNegations() const
488 {
489   int32_t weight = 0;
490 
491 #ifdef MOZ_XUL
492   MOZ_ASSERT(!(IsPseudoElement() &&
493                PseudoType() != CSSPseudoElementType::XULTree &&
494                mClassList),
495              "If non-XUL-tree pseudo-elements can have class selectors "
496              "after them, specificity calculation must be updated");
497 #else
498   MOZ_ASSERT(!(IsPseudoElement() && mClassList),
499              "If pseudo-elements can have class selectors "
500              "after them, specificity calculation must be updated");
501 #endif
502   MOZ_ASSERT(!(IsPseudoElement() && (mIDList || mAttrList)),
503              "If pseudo-elements can have id or attribute selectors "
504              "after them, specificity calculation must be updated");
505 
506   if (nullptr != mCasedTag) {
507     weight += 0x000001;
508   }
509   nsAtomList* list = mIDList;
510   while (nullptr != list) {
511     weight += 0x010000;
512     list = list->mNext;
513   }
514   list = mClassList;
515 #ifdef MOZ_XUL
516   // XUL tree pseudo-elements abuse mClassList to store some private
517   // data; ignore that.
518   if (PseudoType() == CSSPseudoElementType::XULTree) {
519     list = nullptr;
520   }
521 #endif
522   while (nullptr != list) {
523     weight += 0x000100;
524     list = list->mNext;
525   }
526   // FIXME (bug 561154):  This is incorrect for :-moz-any(), which isn't
527   // really a pseudo-class.  In order to handle :-moz-any() correctly,
528   // we need to compute specificity after we match, based on which
529   // option we matched with (and thus also need to try the
530   // highest-specificity options first).
531   nsPseudoClassList *plist = mPseudoClassList;
532   while (nullptr != plist) {
533     weight += 0x000100;
534     plist = plist->mNext;
535   }
536   nsAttrSelector* attr = mAttrList;
537   while (nullptr != attr) {
538     weight += 0x000100;
539     attr = attr->mNext;
540   }
541   return weight;
542 }
543 
CalcWeight() const544 int32_t nsCSSSelector::CalcWeight() const
545 {
546   // Loop over this selector and all its negations.
547   int32_t weight = 0;
548   for (const nsCSSSelector *n = this; n; n = n->mNegations) {
549     weight += n->CalcWeightWithoutNegations();
550   }
551   return weight;
552 }
553 
554 //
555 // Builds the textual representation of a selector. Called by DOM 2 CSS
556 // StyleRule:selectorText
557 //
558 void
ToString(nsAString & aString,CSSStyleSheet * aSheet,bool aAppend) const559 nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet,
560                         bool aAppend) const
561 {
562   if (!aAppend)
563    aString.Truncate();
564 
565   // selectors are linked from right-to-left, so the next selector in
566   // the linked list actually precedes this one in the resulting string
567   AutoTArray<const nsCSSSelector*, 8> stack;
568   for (const nsCSSSelector *s = this; s; s = s->mNext) {
569     stack.AppendElement(s);
570   }
571 
572   while (!stack.IsEmpty()) {
573     uint32_t index = stack.Length() - 1;
574     const nsCSSSelector *s = stack.ElementAt(index);
575     stack.RemoveElementAt(index);
576 
577     s->AppendToStringWithoutCombinators(aString, aSheet, false);
578 
579     // Append the combinator, if needed.
580     if (!stack.IsEmpty()) {
581       const nsCSSSelector *next = stack.ElementAt(index - 1);
582       char16_t oper = s->mOperator;
583       if (next->IsPseudoElement()) {
584         NS_ASSERTION(oper == char16_t(':'),
585                      "improperly chained pseudo element");
586       } else {
587         NS_ASSERTION(oper != char16_t(0),
588                      "compound selector without combinator");
589 
590         aString.Append(char16_t(' '));
591         if (oper != char16_t(' ')) {
592           aString.Append(oper);
593           aString.Append(char16_t(' '));
594         }
595       }
596     }
597   }
598 }
599 
600 void
AppendToStringWithoutCombinators(nsAString & aString,CSSStyleSheet * aSheet,bool aUseStandardNamespacePrefixes) const601 nsCSSSelector::AppendToStringWithoutCombinators(
602     nsAString& aString,
603     CSSStyleSheet* aSheet,
604     bool aUseStandardNamespacePrefixes) const
605 {
606   AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false,
607                                               aUseStandardNamespacePrefixes);
608 
609   for (const nsCSSSelector* negation = mNegations; negation;
610        negation = negation->mNegations) {
611     aString.AppendLiteral(":not(");
612     negation->AppendToStringWithoutCombinatorsOrNegations(
613         aString, aSheet, true, aUseStandardNamespacePrefixes);
614     aString.Append(char16_t(')'));
615   }
616 }
617 
618 #ifdef DEBUG
619 nsCString
RestrictedSelectorToString() const620 nsCSSSelector::RestrictedSelectorToString() const
621 {
622   MOZ_ASSERT(IsRestrictedSelector());
623 
624   nsString result;
625   AppendToStringWithoutCombinators(result, nullptr, true);
626   return NS_ConvertUTF16toUTF8(result);
627 }
628 
629 static bool
AppendStandardNamespacePrefixToString(nsAString & aString,int32_t aNameSpace)630 AppendStandardNamespacePrefixToString(nsAString& aString, int32_t aNameSpace)
631 {
632   if (aNameSpace == kNameSpaceID_Unknown) {
633     // Wildcard namespace; no prefix to write.
634     return false;
635   }
636   switch (aNameSpace) {
637     case kNameSpaceID_None:
638       break;
639     case kNameSpaceID_XML:
640       aString.AppendLiteral("xml");
641       break;
642     case kNameSpaceID_XHTML:
643       aString.AppendLiteral("html");
644       break;
645     case kNameSpaceID_XLink:
646       aString.AppendLiteral("xlink");
647       break;
648     case kNameSpaceID_XSLT:
649       aString.AppendLiteral("xsl");
650       break;
651     case kNameSpaceID_XBL:
652       aString.AppendLiteral("xbl");
653       break;
654     case kNameSpaceID_MathML:
655       aString.AppendLiteral("math");
656       break;
657     case kNameSpaceID_RDF:
658       aString.AppendLiteral("rdf");
659       break;
660     case kNameSpaceID_XUL:
661       aString.AppendLiteral("xul");
662       break;
663     case kNameSpaceID_SVG:
664       aString.AppendLiteral("svg");
665       break;
666     default:
667       aString.AppendLiteral("ns");
668       aString.AppendInt(aNameSpace);
669       break;
670   }
671   return true;
672 }
673 #endif
674 
675 void
AppendToStringWithoutCombinatorsOrNegations(nsAString & aString,CSSStyleSheet * aSheet,bool aIsNegated,bool aUseStandardNamespacePrefixes) const676 nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
677                    (nsAString& aString, CSSStyleSheet* aSheet,
678                    bool aIsNegated,
679                    bool aUseStandardNamespacePrefixes) const
680 {
681   nsAutoString temp;
682   bool isPseudoElement = IsPseudoElement();
683 
684   // For non-pseudo-element selectors or for lone pseudo-elements, deal with
685   // namespace prefixes.
686   bool wroteNamespace = false;
687   if (!isPseudoElement || !mNext) {
688     // append the namespace prefix if needed
689     nsXMLNameSpaceMap *sheetNS = aSheet ? aSheet->GetNameSpaceMap() : nullptr;
690 
691     // sheetNS is non-null if and only if we had an @namespace rule.  If it's
692     // null, that means that the only namespaces we could have are the
693     // wildcard namespace (which can be implicit in this case) and the "none"
694     // namespace, which then needs to be explicitly specified.
695     if (aUseStandardNamespacePrefixes) {
696 #ifdef DEBUG
697       // We have no sheet to look up prefix information from.  This is
698       // only for debugging, so use some "standard" prefixes that
699       // are recognizable.
700       wroteNamespace =
701         AppendStandardNamespacePrefixToString(aString, mNameSpace);
702       if (wroteNamespace) {
703         aString.Append(char16_t('|'));
704       }
705 #endif
706     } else if (!sheetNS) {
707       NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
708                    mNameSpace == kNameSpaceID_None,
709                    "How did we get this namespace?");
710       if (mNameSpace == kNameSpaceID_None) {
711         aString.Append(char16_t('|'));
712         wroteNamespace = true;
713       }
714     } else if (sheetNS->FindNameSpaceID(nullptr) == mNameSpace) {
715       // We have the default namespace (possibly including the wildcard
716       // namespace).  Do nothing.
717       NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown ||
718                    CanBeNamespaced(aIsNegated),
719                    "How did we end up with this namespace?");
720     } else if (mNameSpace == kNameSpaceID_None) {
721       NS_ASSERTION(CanBeNamespaced(aIsNegated),
722                    "How did we end up with this namespace?");
723       aString.Append(char16_t('|'));
724       wroteNamespace = true;
725     } else if (mNameSpace != kNameSpaceID_Unknown) {
726       NS_ASSERTION(CanBeNamespaced(aIsNegated),
727                    "How did we end up with this namespace?");
728       nsIAtom *prefixAtom = sheetNS->FindPrefix(mNameSpace);
729       NS_ASSERTION(prefixAtom, "how'd we get a non-default namespace "
730                    "without a prefix?");
731       nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(prefixAtom),
732                                          aString);
733       aString.Append(char16_t('|'));
734       wroteNamespace = true;
735     } else {
736       // A selector for an element in any namespace, while the default
737       // namespace is something else.  :not() is special in that the default
738       // namespace is not implied for non-type selectors, so if this is a
739       // negated non-type selector we don't need to output an explicit wildcard
740       // namespace here, since those default to a wildcard namespace.
741       if (CanBeNamespaced(aIsNegated)) {
742         aString.AppendLiteral("*|");
743         wroteNamespace = true;
744       }
745     }
746   }
747 
748   if (!mLowercaseTag) {
749     // Universal selector:  avoid writing the universal selector when we
750     // can avoid it, especially since we're required to avoid it for the
751     // inside of :not()
752     if (wroteNamespace ||
753         (!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
754          (aIsNegated || !mNegations))) {
755       aString.Append(char16_t('*'));
756     }
757   } else {
758     // Append the tag name
759     nsAutoString tag;
760     (isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
761     if (isPseudoElement) {
762       if (!mNext) {
763         // Lone pseudo-element selector -- toss in a wildcard type selector
764         // XXXldb Why?
765         aString.Append(char16_t('*'));
766       }
767       // While our atoms use one colon, most pseudo-elements require two
768       // colons (those not in CSS level 2) and all pseudo-elements allow
769       // two colons. So serialize to the non-deprecated two colon syntax.
770       aString.Append(char16_t(':'));
771       // This should not be escaped since (a) the pseudo-element string
772       // has a ":" that can't be escaped and (b) all pseudo-elements at
773       // this point are known, and therefore we know they don't need
774       // escaping.
775       aString.Append(tag);
776     } else {
777       nsStyleUtil::AppendEscapedCSSIdent(tag, aString);
778     }
779   }
780 
781   // Append the id, if there is one
782   if (mIDList) {
783     nsAtomList* list = mIDList;
784     while (list != nullptr) {
785       list->mAtom->ToString(temp);
786       aString.Append(char16_t('#'));
787       nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
788       list = list->mNext;
789     }
790   }
791 
792   // Append each class in the linked list
793   if (mClassList) {
794     if (isPseudoElement) {
795 #ifdef MOZ_XUL
796       MOZ_ASSERT(nsCSSAnonBoxes::IsTreePseudoElement(mLowercaseTag),
797                  "must be tree pseudo-element");
798 
799       aString.Append(char16_t('('));
800       for (nsAtomList* list = mClassList; list; list = list->mNext) {
801         nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(list->mAtom), aString);
802         aString.Append(char16_t(','));
803       }
804       // replace the final comma with a close-paren
805       aString.Replace(aString.Length() - 1, 1, char16_t(')'));
806 #else
807       NS_ERROR("Can't happen");
808 #endif
809     } else {
810       nsAtomList* list = mClassList;
811       while (list != nullptr) {
812         list->mAtom->ToString(temp);
813         aString.Append(char16_t('.'));
814         nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
815         list = list->mNext;
816       }
817     }
818   }
819 
820   // Append each attribute selector in the linked list
821   if (mAttrList) {
822     nsAttrSelector* list = mAttrList;
823     while (list != nullptr) {
824       aString.Append(char16_t('['));
825       // Append the namespace prefix
826       if (list->mNameSpace == kNameSpaceID_Unknown) {
827         aString.Append(char16_t('*'));
828         aString.Append(char16_t('|'));
829       } else if (list->mNameSpace != kNameSpaceID_None) {
830         if (aUseStandardNamespacePrefixes) {
831 #ifdef DEBUG
832           AppendStandardNamespacePrefixToString(aString, list->mNameSpace);
833           aString.Append(char16_t('|'));
834 #endif
835         } else if (aSheet) {
836           nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap();
837           nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace);
838           // Default namespaces don't apply to attribute selectors, so
839           // we must have a useful prefix.
840           NS_ASSERTION(prefixAtom,
841                        "How did we end up with a namespace if the prefix "
842                        "is unknown?");
843           nsAutoString prefix;
844           prefixAtom->ToString(prefix);
845           nsStyleUtil::AppendEscapedCSSIdent(prefix, aString);
846           aString.Append(char16_t('|'));
847         }
848       }
849       // Append the attribute name
850       list->mCasedAttr->ToString(temp);
851       nsStyleUtil::AppendEscapedCSSIdent(temp, aString);
852 
853       if (list->mFunction != NS_ATTR_FUNC_SET) {
854         // Append the function
855         if (list->mFunction == NS_ATTR_FUNC_INCLUDES)
856           aString.Append(char16_t('~'));
857         else if (list->mFunction == NS_ATTR_FUNC_DASHMATCH)
858           aString.Append(char16_t('|'));
859         else if (list->mFunction == NS_ATTR_FUNC_BEGINSMATCH)
860           aString.Append(char16_t('^'));
861         else if (list->mFunction == NS_ATTR_FUNC_ENDSMATCH)
862           aString.Append(char16_t('$'));
863         else if (list->mFunction == NS_ATTR_FUNC_CONTAINSMATCH)
864           aString.Append(char16_t('*'));
865 
866         aString.Append(char16_t('='));
867 
868         // Append the value
869         nsStyleUtil::AppendEscapedCSSString(list->mValue, aString);
870 
871         if (list->mValueCaseSensitivity ==
872               nsAttrSelector::ValueCaseSensitivity::CaseInsensitive) {
873           aString.Append(NS_LITERAL_STRING(" i"));
874         }
875       }
876 
877       aString.Append(char16_t(']'));
878 
879       list = list->mNext;
880     }
881   }
882 
883   // Append each pseudo-class in the linked list
884   for (nsPseudoClassList* list = mPseudoClassList; list; list = list->mNext) {
885     nsCSSPseudoClasses::PseudoTypeToString(list->mType, temp);
886     // This should not be escaped since (a) the pseudo-class string
887     // has a ":" that can't be escaped and (b) all pseudo-classes at
888     // this point are known, and therefore we know they don't need
889     // escaping.
890     aString.Append(temp);
891     if (list->u.mMemory) {
892       aString.Append(char16_t('('));
893       if (nsCSSPseudoClasses::HasStringArg(list->mType)) {
894         nsStyleUtil::AppendEscapedCSSIdent(
895           nsDependentString(list->u.mString), aString);
896       } else if (nsCSSPseudoClasses::HasNthPairArg(list->mType)) {
897         int32_t a = list->u.mNumbers[0],
898                 b = list->u.mNumbers[1];
899         temp.Truncate();
900         if (a != 0) {
901           if (a == -1) {
902             temp.Append(char16_t('-'));
903           } else if (a != 1) {
904             temp.AppendInt(a);
905           }
906           temp.Append(char16_t('n'));
907         }
908         if (b != 0 || a == 0) {
909           if (b >= 0 && a != 0) // check a != 0 for whether we printed above
910             temp.Append(char16_t('+'));
911           temp.AppendInt(b);
912         }
913         aString.Append(temp);
914       } else {
915         NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(list->mType),
916                      "unexpected pseudo-class");
917         nsString tmp;
918         list->u.mSelectors->ToString(tmp, aSheet);
919         aString.Append(tmp);
920       }
921       aString.Append(char16_t(')'));
922     }
923   }
924 }
925 
926 bool
CanBeNamespaced(bool aIsNegated) const927 nsCSSSelector::CanBeNamespaced(bool aIsNegated) const
928 {
929   return !aIsNegated ||
930          (!mIDList && !mClassList && !mPseudoClassList && !mAttrList);
931 }
932 
933 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const934 nsCSSSelector::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
935 {
936   size_t n = 0;
937   const nsCSSSelector* s = this;
938   while (s) {
939     n += aMallocSizeOf(s);
940 
941     #define MEASURE(x)   n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0;
942 
943     MEASURE(s->mIDList);
944     MEASURE(s->mClassList);
945     MEASURE(s->mPseudoClassList);
946     MEASURE(s->mNegations);
947     MEASURE(s->mAttrList);
948 
949     // The following members aren't measured:
950     // - s->mLowercaseTag, because it's an atom and therefore shared
951     // - s->mCasedTag, because it's an atom and therefore shared
952 
953     s = s->mNext;
954   }
955   return n;
956 }
957 
958 // -- nsCSSSelectorList -------------------------------
959 
nsCSSSelectorList(void)960 nsCSSSelectorList::nsCSSSelectorList(void)
961   : mSelectors(nullptr),
962     mWeight(0),
963     mNext(nullptr)
964 {
965   MOZ_COUNT_CTOR(nsCSSSelectorList);
966 }
967 
~nsCSSSelectorList()968 nsCSSSelectorList::~nsCSSSelectorList()
969 {
970   MOZ_COUNT_DTOR(nsCSSSelectorList);
971   delete mSelectors;
972   NS_CSS_DELETE_LIST_MEMBER(nsCSSSelectorList, this, mNext);
973 }
974 
975 nsCSSSelector*
AddSelector(char16_t aOperator)976 nsCSSSelectorList::AddSelector(char16_t aOperator)
977 {
978   nsCSSSelector* newSel = new nsCSSSelector();
979 
980   if (mSelectors) {
981     NS_ASSERTION(aOperator != char16_t(0), "chaining without combinator");
982     mSelectors->SetOperator(aOperator);
983   } else {
984     NS_ASSERTION(aOperator == char16_t(0), "combinator without chaining");
985   }
986 
987   newSel->mNext = mSelectors;
988   mSelectors = newSel;
989   return newSel;
990 }
991 
992 void
RemoveRightmostSelector()993 nsCSSSelectorList::RemoveRightmostSelector()
994 {
995   nsCSSSelector* current = mSelectors;
996   mSelectors = mSelectors->mNext;
997   MOZ_ASSERT(mSelectors,
998              "Rightmost selector has been removed, but now "
999              "mSelectors is null");
1000   mSelectors->SetOperator(char16_t(0));
1001 
1002   // Make sure that deleting current won't delete the whole list.
1003   current->mNext = nullptr;
1004   delete current;
1005 }
1006 
1007 void
ToString(nsAString & aResult,CSSStyleSheet * aSheet)1008 nsCSSSelectorList::ToString(nsAString& aResult, CSSStyleSheet* aSheet)
1009 {
1010   aResult.Truncate();
1011   nsCSSSelectorList *p = this;
1012   for (;;) {
1013     p->mSelectors->ToString(aResult, aSheet, true);
1014     p = p->mNext;
1015     if (!p)
1016       break;
1017     aResult.AppendLiteral(", ");
1018   }
1019 }
1020 
1021 nsCSSSelectorList*
Clone(bool aDeep) const1022 nsCSSSelectorList::Clone(bool aDeep) const
1023 {
1024   nsCSSSelectorList *result = new nsCSSSelectorList();
1025   result->mWeight = mWeight;
1026   NS_IF_CLONE(mSelectors);
1027 
1028   if (aDeep) {
1029     NS_CSS_CLONE_LIST_MEMBER(nsCSSSelectorList, this, mNext, result,
1030                              (false));
1031   }
1032   return result;
1033 }
1034 
1035 size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const1036 nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1037 {
1038   size_t n = 0;
1039   const nsCSSSelectorList* s = this;
1040   while (s) {
1041     n += aMallocSizeOf(s);
1042     n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0;
1043     s = s->mNext;
1044   }
1045   return n;
1046 }
1047 
1048 // --------------------------------------------------------
1049 
1050 namespace mozilla {
1051 namespace css {
1052 class DOMCSSStyleRule;
1053 } // namespace css
1054 } // namespace mozilla
1055 
1056 class DOMCSSDeclarationImpl : public nsDOMCSSDeclaration
1057 {
1058 protected:
1059   virtual ~DOMCSSDeclarationImpl(void);
1060 
1061 public:
1062   explicit DOMCSSDeclarationImpl(css::StyleRule *aRule);
1063 
1064   NS_IMETHOD GetParentRule(nsIDOMCSSRule **aParent) override;
1065   void DropReference(void);
1066   virtual DeclarationBlock* GetCSSDeclaration(Operation aOperation) override;
1067   virtual nsresult SetCSSDeclaration(DeclarationBlock* aDecl) override;
1068   virtual void GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv) override;
1069   virtual nsIDocument* DocToUpdate() override;
1070 
1071   // Override |AddRef| and |Release| for being a member of
1072   // |DOMCSSStyleRule|.  Also, we need to forward QI for cycle
1073   // collection things to DOMCSSStyleRule.
1074   NS_DECL_ISUPPORTS_INHERITED
1075 
GetParentObject()1076   virtual nsINode *GetParentObject() override
1077   {
1078     return mRule ? mRule->GetDocument() : nullptr;
1079   }
1080 
1081   friend class css::DOMCSSStyleRule;
1082 
1083 protected:
1084   // This reference is not reference-counted. The rule object tells us
1085   // when it's about to go away.
1086   css::StyleRule *mRule;
1087 
1088   inline css::DOMCSSStyleRule* DomRule();
1089 
1090 private:
1091   // NOT TO BE IMPLEMENTED
1092   // This object cannot be allocated on its own.  It must be a member of
1093   // DOMCSSStyleRule.
1094   void* operator new(size_t size) CPP_THROW_NEW;
1095 };
1096 
1097 namespace mozilla {
1098 namespace css {
1099 
1100 class DOMCSSStyleRule : public nsICSSStyleRuleDOMWrapper
1101 {
1102 public:
1103   explicit DOMCSSStyleRule(StyleRule *aRule);
1104 
1105   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
1106   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMCSSStyleRule)
1107   NS_DECL_NSIDOMCSSRULE
1108   NS_DECL_NSIDOMCSSSTYLERULE
1109 
1110   // nsICSSStyleRuleDOMWrapper
1111   NS_IMETHOD GetCSSStyleRule(StyleRule **aResult) override;
1112 
DOMDeclaration()1113   DOMCSSDeclarationImpl* DOMDeclaration() { return &mDOMDeclaration; }
1114 
1115   friend class ::DOMCSSDeclarationImpl;
1116 
1117 protected:
1118   virtual ~DOMCSSStyleRule();
1119 
1120   DOMCSSDeclarationImpl mDOMDeclaration;
1121 
Rule()1122   StyleRule* Rule() {
1123     return mDOMDeclaration.mRule;
1124   }
1125 };
1126 
1127 } // namespace css
1128 } // namespace mozilla
1129 
DOMCSSDeclarationImpl(css::StyleRule * aRule)1130 DOMCSSDeclarationImpl::DOMCSSDeclarationImpl(css::StyleRule *aRule)
1131   : mRule(aRule)
1132 {
1133   MOZ_COUNT_CTOR(DOMCSSDeclarationImpl);
1134 }
1135 
~DOMCSSDeclarationImpl(void)1136 DOMCSSDeclarationImpl::~DOMCSSDeclarationImpl(void)
1137 {
1138   NS_ASSERTION(!mRule, "DropReference not called.");
1139 
1140   MOZ_COUNT_DTOR(DOMCSSDeclarationImpl);
1141 }
1142 
DomRule()1143 inline css::DOMCSSStyleRule* DOMCSSDeclarationImpl::DomRule()
1144 {
1145   return reinterpret_cast<css::DOMCSSStyleRule*>
1146                          (reinterpret_cast<char*>(this) -
1147            offsetof(css::DOMCSSStyleRule, mDOMDeclaration));
1148 }
1149 
1150 NS_IMPL_ADDREF_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule())
1151 NS_IMPL_RELEASE_USING_AGGREGATOR(DOMCSSDeclarationImpl, DomRule())
1152 
1153 NS_INTERFACE_MAP_BEGIN(DOMCSSDeclarationImpl)
1154   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1155   // We forward the cycle collection interfaces to DomRule(), which is
1156   // never null (in fact, we're part of that object!)
1157   if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)) ||
1158       aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant))) {
1159     return DomRule()->QueryInterface(aIID, aInstancePtr);
1160   }
1161   else
NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)1162 NS_IMPL_QUERY_TAIL_INHERITING(nsDOMCSSDeclaration)
1163 
1164 void
1165 DOMCSSDeclarationImpl::DropReference(void)
1166 {
1167   mRule = nullptr;
1168 }
1169 
1170 DeclarationBlock*
GetCSSDeclaration(Operation aOperation)1171 DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation)
1172 {
1173   if (mRule) {
1174     if (aOperation != eOperation_Read) {
1175       RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
1176       if (sheet) {
1177         sheet->WillDirty();
1178       }
1179     }
1180     return mRule->GetDeclaration();
1181   } else {
1182     return nullptr;
1183   }
1184 }
1185 
1186 void
GetCSSParsingEnvironment(CSSParsingEnvironment & aCSSParseEnv)1187 DOMCSSDeclarationImpl::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv)
1188 {
1189   GetCSSParsingEnvironmentForRule(mRule, aCSSParseEnv);
1190 }
1191 
1192 NS_IMETHODIMP
GetParentRule(nsIDOMCSSRule ** aParent)1193 DOMCSSDeclarationImpl::GetParentRule(nsIDOMCSSRule **aParent)
1194 {
1195   NS_ENSURE_ARG_POINTER(aParent);
1196 
1197   if (!mRule) {
1198     *aParent = nullptr;
1199     return NS_OK;
1200   }
1201 
1202   NS_IF_ADDREF(*aParent = mRule->GetDOMRule());
1203   return NS_OK;
1204 }
1205 
1206 nsresult
SetCSSDeclaration(DeclarationBlock * aDecl)1207 DOMCSSDeclarationImpl::SetCSSDeclaration(DeclarationBlock* aDecl)
1208 {
1209   NS_PRECONDITION(mRule,
1210          "can only be called when |GetCSSDeclaration| returned a declaration");
1211 
1212   nsCOMPtr<nsIDocument> owningDoc;
1213   RefPtr<CSSStyleSheet> sheet = mRule->GetStyleSheet();
1214   if (sheet) {
1215     owningDoc = sheet->GetOwningDocument();
1216   }
1217 
1218   mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true);
1219 
1220   mRule->SetDeclaration(aDecl->AsGecko());
1221 
1222   if (sheet) {
1223     sheet->DidDirty();
1224   }
1225 
1226   if (owningDoc) {
1227     owningDoc->StyleRuleChanged(sheet, mRule);
1228   }
1229   return NS_OK;
1230 }
1231 
1232 nsIDocument*
DocToUpdate()1233 DOMCSSDeclarationImpl::DocToUpdate()
1234 {
1235   return nullptr;
1236 }
1237 
1238 namespace mozilla {
1239 namespace css {
1240 
DOMCSSStyleRule(StyleRule * aRule)1241 DOMCSSStyleRule::DOMCSSStyleRule(StyleRule* aRule)
1242   : mDOMDeclaration(aRule)
1243 {
1244 }
1245 
~DOMCSSStyleRule()1246 DOMCSSStyleRule::~DOMCSSStyleRule()
1247 {
1248 }
1249 
1250 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMCSSStyleRule)
1251   NS_INTERFACE_MAP_ENTRY(nsICSSStyleRuleDOMWrapper)
1252   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleRule)
1253   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule)
1254   NS_INTERFACE_MAP_ENTRY(nsISupports)
1255   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSStyleRule)
1256 NS_INTERFACE_MAP_END
1257 
1258 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMCSSStyleRule)
1259 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMCSSStyleRule)
1260 
1261 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMCSSStyleRule)
1262 
1263 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMCSSStyleRule)
1264   // Trace the wrapper for our declaration.  This just expands out
1265   // NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER which we can't use
1266   // directly because the wrapper is on the declaration, not on us.
1267   tmp->DOMDeclaration()->TraceWrapper(aCallbacks, aClosure);
1268 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1269 
1270 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMCSSStyleRule)
1271   // Unlink the wrapper for our declaraton.  This just expands out
1272   // NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER which we can't use
1273   // directly because the wrapper is on the declaration, not on us.
1274   tmp->DOMDeclaration()->ReleaseWrapper(static_cast<nsISupports*>(p));
1275 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1276 
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMCSSStyleRule)1277 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMCSSStyleRule)
1278   // Just NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS here: that will call
1279   // into our Trace hook, where we do the right thing with declarations
1280   // already.
1281   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1283 
1284 NS_IMETHODIMP
1285 DOMCSSStyleRule::GetType(uint16_t* aType)
1286 {
1287   *aType = nsIDOMCSSRule::STYLE_RULE;
1288 
1289   return NS_OK;
1290 }
1291 
1292 NS_IMETHODIMP
GetCssText(nsAString & aCssText)1293 DOMCSSStyleRule::GetCssText(nsAString& aCssText)
1294 {
1295   if (!Rule()) {
1296     aCssText.Truncate();
1297   } else {
1298     Rule()->GetCssText(aCssText);
1299   }
1300   return NS_OK;
1301 }
1302 
1303 NS_IMETHODIMP
SetCssText(const nsAString & aCssText)1304 DOMCSSStyleRule::SetCssText(const nsAString& aCssText)
1305 {
1306   if (Rule()) {
1307     Rule()->SetCssText(aCssText);
1308   }
1309   return NS_OK;
1310 }
1311 
1312 NS_IMETHODIMP
GetParentStyleSheet(nsIDOMCSSStyleSheet ** aSheet)1313 DOMCSSStyleRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet)
1314 {
1315   if (!Rule()) {
1316     *aSheet = nullptr;
1317     return NS_OK;
1318   }
1319   return Rule()->GetParentStyleSheet(aSheet);
1320 }
1321 
1322 NS_IMETHODIMP
GetParentRule(nsIDOMCSSRule ** aParentRule)1323 DOMCSSStyleRule::GetParentRule(nsIDOMCSSRule** aParentRule)
1324 {
1325   if (!Rule()) {
1326     *aParentRule = nullptr;
1327     return NS_OK;
1328   }
1329   return Rule()->GetParentRule(aParentRule);
1330 }
1331 
1332 css::Rule*
GetCSSRule()1333 DOMCSSStyleRule::GetCSSRule()
1334 {
1335   return Rule();
1336 }
1337 
1338 NS_IMETHODIMP
GetSelectorText(nsAString & aSelectorText)1339 DOMCSSStyleRule::GetSelectorText(nsAString& aSelectorText)
1340 {
1341   if (!Rule()) {
1342     aSelectorText.Truncate();
1343   } else {
1344     Rule()->GetSelectorText(aSelectorText);
1345   }
1346   return NS_OK;
1347 }
1348 
1349 NS_IMETHODIMP
SetSelectorText(const nsAString & aSelectorText)1350 DOMCSSStyleRule::SetSelectorText(const nsAString& aSelectorText)
1351 {
1352   if (Rule()) {
1353     Rule()->SetSelectorText(aSelectorText);
1354   }
1355   return NS_OK;
1356 }
1357 
1358 NS_IMETHODIMP
GetStyle(nsIDOMCSSStyleDeclaration ** aStyle)1359 DOMCSSStyleRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle)
1360 {
1361   *aStyle = &mDOMDeclaration;
1362   NS_ADDREF(*aStyle);
1363   return NS_OK;
1364 }
1365 
1366 NS_IMETHODIMP
GetCSSStyleRule(StyleRule ** aResult)1367 DOMCSSStyleRule::GetCSSStyleRule(StyleRule **aResult)
1368 {
1369   *aResult = Rule();
1370   NS_IF_ADDREF(*aResult);
1371   return NS_OK;
1372 }
1373 
1374 } // namespace css
1375 } // namespace mozilla
1376 
1377 // -- StyleRule ------------------------------------
1378 
1379 namespace mozilla {
1380 namespace css {
1381 
StyleRule(nsCSSSelectorList * aSelector,Declaration * aDeclaration,uint32_t aLineNumber,uint32_t aColumnNumber)1382 StyleRule::StyleRule(nsCSSSelectorList* aSelector,
1383                      Declaration* aDeclaration,
1384                      uint32_t aLineNumber,
1385                      uint32_t aColumnNumber)
1386   : Rule(aLineNumber, aColumnNumber),
1387     mSelector(aSelector),
1388     mDeclaration(aDeclaration)
1389 {
1390   NS_PRECONDITION(aDeclaration, "must have a declaration");
1391 
1392   mDeclaration->SetOwningRule(this);
1393 }
1394 
1395 // for |Clone|
StyleRule(const StyleRule & aCopy)1396 StyleRule::StyleRule(const StyleRule& aCopy)
1397   : Rule(aCopy),
1398     mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr),
1399     mDeclaration(new Declaration(*aCopy.mDeclaration))
1400 {
1401   mDeclaration->SetOwningRule(this);
1402   // rest is constructed lazily on existing data
1403 }
1404 
~StyleRule()1405 StyleRule::~StyleRule()
1406 {
1407   delete mSelector;
1408   if (mDOMRule) {
1409     mDOMRule->DOMDeclaration()->DropReference();
1410   }
1411 
1412   if (mDeclaration) {
1413     mDeclaration->SetOwningRule(nullptr);
1414   }
1415 }
1416 
1417 // QueryInterface implementation for StyleRule
1418 NS_INTERFACE_MAP_BEGIN(StyleRule)
1419   if (aIID.Equals(NS_GET_IID(mozilla::css::StyleRule))) {
1420     *aInstancePtr = this;
1421     NS_ADDREF_THIS();
1422     return NS_OK;
1423   }
1424   else
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,mozilla::css::Rule)1425   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule)
1426 NS_INTERFACE_MAP_END
1427 
1428 NS_IMPL_ADDREF(StyleRule)
1429 NS_IMPL_RELEASE(StyleRule)
1430 
1431 /* virtual */ int32_t
1432 StyleRule::GetType() const
1433 {
1434   return Rule::STYLE_RULE;
1435 }
1436 
1437 /* virtual */ already_AddRefed<Rule>
Clone() const1438 StyleRule::Clone() const
1439 {
1440   RefPtr<Rule> clone = new StyleRule(*this);
1441   return clone.forget();
1442 }
1443 
1444 /* virtual */ nsIDOMCSSRule*
GetDOMRule()1445 StyleRule::GetDOMRule()
1446 {
1447   if (!mDOMRule) {
1448     if (!GetStyleSheet()) {
1449       // Inline style rules aren't supposed to have a DOM rule object, only
1450       // a declaration.  But if we do have one already, from a style sheet
1451       // rule that used to be in a document, we still want to return it.
1452       return nullptr;
1453     }
1454     mDOMRule = new DOMCSSStyleRule(this);
1455   }
1456   return mDOMRule;
1457 }
1458 
1459 /* virtual */ nsIDOMCSSRule*
GetExistingDOMRule()1460 StyleRule::GetExistingDOMRule()
1461 {
1462   return mDOMRule;
1463 }
1464 
1465 void
SetDeclaration(Declaration * aDecl)1466 StyleRule::SetDeclaration(Declaration* aDecl)
1467 {
1468   if (aDecl == mDeclaration) {
1469     return;
1470   }
1471   mDeclaration->SetOwningRule(nullptr);
1472   mDeclaration = aDecl;
1473   mDeclaration->SetOwningRule(this);
1474 }
1475 
1476 #ifdef DEBUG
1477 /* virtual */ void
List(FILE * out,int32_t aIndent) const1478 StyleRule::List(FILE* out, int32_t aIndent) const
1479 {
1480   nsAutoCString str;
1481   // Indent
1482   for (int32_t index = aIndent; --index >= 0; ) {
1483     str.AppendLiteral("  ");
1484   }
1485 
1486   if (mSelector) {
1487     nsAutoString buffer;
1488     mSelector->ToString(buffer, GetStyleSheet());
1489     AppendUTF16toUTF8(buffer, str);
1490     str.Append(' ');
1491   }
1492 
1493   if (nullptr != mDeclaration) {
1494     nsAutoString buffer;
1495     str.AppendLiteral("{ ");
1496     mDeclaration->ToString(buffer);
1497     AppendUTF16toUTF8(buffer, str);
1498     str.Append('}');
1499     CSSStyleSheet* sheet = GetStyleSheet();
1500     if (sheet) {
1501       nsIURI* uri = sheet->GetSheetURI();
1502       if (uri) {
1503         str.Append(" /* ");
1504         str.Append(uri->GetSpecOrDefault());
1505         str.Append(':');
1506         str.AppendInt(mLineNumber);
1507         str.Append(" */");
1508       }
1509     }
1510   }
1511   else {
1512     str.AppendLiteral("{ null declaration }");
1513   }
1514   str.Append('\n');
1515   fprintf_stderr(out, "%s", str.get());
1516 }
1517 #endif
1518 
1519 void
GetCssText(nsAString & aCssText)1520 StyleRule::GetCssText(nsAString& aCssText)
1521 {
1522   if (mSelector) {
1523     mSelector->ToString(aCssText, GetStyleSheet());
1524     aCssText.Append(char16_t(' '));
1525   }
1526   aCssText.Append(char16_t('{'));
1527   aCssText.Append(char16_t(' '));
1528   if (mDeclaration)
1529   {
1530     nsAutoString   tempString;
1531     mDeclaration->ToString( tempString );
1532     aCssText.Append( tempString );
1533   }
1534   aCssText.Append(char16_t(' '));
1535   aCssText.Append(char16_t('}'));
1536 }
1537 
1538 void
SetCssText(const nsAString & aCssText)1539 StyleRule::SetCssText(const nsAString& aCssText)
1540 {
1541   // XXX TBI - need to re-parse rule & declaration
1542 }
1543 
1544 void
GetSelectorText(nsAString & aSelectorText)1545 StyleRule::GetSelectorText(nsAString& aSelectorText)
1546 {
1547   if (mSelector)
1548     mSelector->ToString(aSelectorText, GetStyleSheet());
1549   else
1550     aSelectorText.Truncate();
1551 }
1552 
1553 void
SetSelectorText(const nsAString & aSelectorText)1554 StyleRule::SetSelectorText(const nsAString& aSelectorText)
1555 {
1556   // XXX TBI - get a parser and re-parse the selectors,
1557   // XXX then need to re-compute the cascade
1558   // XXX and dirty sheet
1559 }
1560 
1561 /* virtual */ size_t
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const1562 StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1563 {
1564   size_t n = aMallocSizeOf(this);
1565   n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0;
1566   n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0;
1567 
1568   // Measurement of the following members may be added later if DMD finds it is
1569   // worthwhile:
1570   // - mDOMRule;
1571 
1572   return n;
1573 }
1574 
1575 
1576 } // namespace css
1577 } // namespace mozilla
1578