1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * L. David Baron <dbaron@dbaron.org>
25 * Daniel Glazman <glazman@netscape.com>
26 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
27 * Rob Arnold <robarnold@mozilla.com>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
44 * style rule processor for CSS style sheets, responsible for selector
45 * matching and cascading
48 #include "nsCSSRuleProcessor.h"
50 #define PL_ARENA_CONST_ALIGN_MASK 7
51 #define NS_RULEHASH_ARENA_BLOCK_SIZE (256)
57 #include "nsHashtable.h"
58 #include "nsICSSPseudoComparator.h"
59 #include "nsCSSRuleProcessor.h"
60 #include "nsICSSStyleRule.h"
61 #include "nsICSSGroupRule.h"
62 #include "nsIDocument.h"
63 #include "nsPresContext.h"
64 #include "nsIEventStateManager.h"
65 #include "nsGkAtoms.h"
67 #include "nsUnicharUtils.h"
68 #include "nsVoidArray.h"
69 #include "nsDOMError.h"
70 #include "nsRuleWalker.h"
71 #include "nsCSSPseudoClasses.h"
72 #include "nsIContent.h"
74 #include "nsHashKeys.h"
75 #include "nsStyleUtil.h"
76 #include "nsQuickSort.h"
77 #include "nsAttrValue.h"
78 #include "nsAttrName.h"
79 #include "nsILookAndFeel.h"
80 #include "nsWidgetsCID.h"
81 #include "nsServiceManagerUtils.h"
83 #include "nsContentUtils.h"
84 #include "nsIMediaList.h"
85 #include "nsCSSRules.h"
86 #include "nsIPrincipal.h"
87 #include "nsStyleSet.h"
89 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
91 static PRBool gSupportVisitedPseudo
= PR_TRUE
;
93 static NS_DEFINE_CID(kLookAndFeelCID
, NS_LOOKANDFEEL_CID
);
94 static nsTArray
< nsCOMPtr
<nsIAtom
> >* sSystemMetrics
= 0;
98 * |RuleValue|s are constructed before they become part of the
99 * |RuleHash|, to act as rule/selector pairs. |Add| is called when
100 * they are added to the |RuleHash|, and can be considered the second
101 * half of the constructor.
103 * |RuleValue|s are added to the rule hash from highest weight/order
104 * to lowest (since this is the fast way to build a singly linked
105 * list), so the index used to remember the order is backwards.
107 RuleValue(nsICSSStyleRule
* aRule
, nsCSSSelector
* aSelector
)
108 : mRule(aRule
), mSelector(aSelector
) {}
110 RuleValue
* Add(PRInt32 aBackwardIndex
, RuleValue
*aNext
)
112 mBackwardIndex
= aBackwardIndex
;
117 // CAUTION: ~RuleValue will never get called as RuleValues are arena
118 // allocated and arena cleanup will take care of deleting memory.
119 // Add code to RuleHash::~RuleHash to get it to call the destructor
120 // if any more cleanup needs to happen.
123 // Rule values are arena allocated. No need for any deletion.
126 // Placement new to arena allocate the RuleValues
127 void *operator new(size_t aSize
, PLArenaPool
&aArena
) CPP_THROW_NEW
{
129 PL_ARENA_ALLOCATE(mem
, &aArena
, aSize
);
133 nsICSSStyleRule
* mRule
;
134 nsCSSSelector
* mSelector
; // which of |mRule|'s selectors
135 PRInt32 mBackwardIndex
; // High index means low weight/order.
139 // ------------------------------
143 // Uses any of the sets of ops below.
144 struct RuleHashTableEntry
: public PLDHashEntryHdr
{
145 RuleValue
*mRules
; // linked list of |RuleValue|, null-terminated
149 RuleHash_CIHashKey(PLDHashTable
*table
, const void *key
)
151 nsIAtom
*atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>(key
));
156 return HashString(str
);
160 (* RuleHashGetKey
) (PLDHashTable
*table
, const PLDHashEntryHdr
*entry
);
162 struct RuleHashTableOps
{
164 // Extra callback to avoid duplicating the matchEntry callback for
165 // each table. (There used to be a getKey callback in
167 RuleHashGetKey getKey
;
170 inline const RuleHashTableOps
*
171 ToLocalOps(const PLDHashTableOps
*aOps
)
173 return (const RuleHashTableOps
*)
174 (((const char*) aOps
) - offsetof(RuleHashTableOps
, ops
));
178 RuleHash_CIMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
181 nsIAtom
*match_atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>
183 // Use our extra |getKey| callback to avoid code duplication.
184 nsIAtom
*entry_atom
= ToLocalOps(table
->ops
)->getKey(table
, hdr
);
186 // Check for case-sensitive match first.
187 if (match_atom
== entry_atom
)
190 const char *match_str
, *entry_str
;
191 match_atom
->GetUTF8String(&match_str
);
192 entry_atom
->GetUTF8String(&entry_str
);
194 return (nsCRT::strcasecmp(entry_str
, match_str
) == 0);
198 RuleHash_CSMatchEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
201 nsIAtom
*match_atom
= const_cast<nsIAtom
*>(static_cast<const nsIAtom
*>
203 // Use our extra |getKey| callback to avoid code duplication.
204 nsIAtom
*entry_atom
= ToLocalOps(table
->ops
)->getKey(table
, hdr
);
206 return match_atom
== entry_atom
;
210 RuleHash_TagTable_GetKey(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
)
212 const RuleHashTableEntry
*entry
=
213 static_cast<const RuleHashTableEntry
*>(hdr
);
214 return entry
->mRules
->mSelector
->mTag
;
218 RuleHash_ClassTable_GetKey(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
)
220 const RuleHashTableEntry
*entry
=
221 static_cast<const RuleHashTableEntry
*>(hdr
);
222 return entry
->mRules
->mSelector
->mClassList
->mAtom
;
226 RuleHash_IdTable_GetKey(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
)
228 const RuleHashTableEntry
*entry
=
229 static_cast<const RuleHashTableEntry
*>(hdr
);
230 return entry
->mRules
->mSelector
->mIDList
->mAtom
;
234 RuleHash_NameSpaceTable_HashKey(PLDHashTable
*table
, const void *key
)
236 return NS_PTR_TO_INT32(key
);
240 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable
*table
,
241 const PLDHashEntryHdr
*hdr
,
244 const RuleHashTableEntry
*entry
=
245 static_cast<const RuleHashTableEntry
*>(hdr
);
247 return NS_PTR_TO_INT32(key
) ==
248 entry
->mRules
->mSelector
->mNameSpace
;
251 static const RuleHashTableOps RuleHash_TagTable_Ops
= {
255 PL_DHashVoidPtrKeyStub
,
256 RuleHash_CSMatchEntry
,
257 PL_DHashMoveEntryStub
,
258 PL_DHashClearEntryStub
,
259 PL_DHashFinalizeStub
,
262 RuleHash_TagTable_GetKey
265 // Case-sensitive ops.
266 static const RuleHashTableOps RuleHash_ClassTable_CSOps
= {
270 PL_DHashVoidPtrKeyStub
,
271 RuleHash_CSMatchEntry
,
272 PL_DHashMoveEntryStub
,
273 PL_DHashClearEntryStub
,
274 PL_DHashFinalizeStub
,
277 RuleHash_ClassTable_GetKey
280 // Case-insensitive ops.
281 static const RuleHashTableOps RuleHash_ClassTable_CIOps
= {
286 RuleHash_CIMatchEntry
,
287 PL_DHashMoveEntryStub
,
288 PL_DHashClearEntryStub
,
289 PL_DHashFinalizeStub
,
292 RuleHash_ClassTable_GetKey
295 // Case-sensitive ops.
296 static const RuleHashTableOps RuleHash_IdTable_CSOps
= {
300 PL_DHashVoidPtrKeyStub
,
301 RuleHash_CSMatchEntry
,
302 PL_DHashMoveEntryStub
,
303 PL_DHashClearEntryStub
,
304 PL_DHashFinalizeStub
,
307 RuleHash_IdTable_GetKey
310 // Case-insensitive ops.
311 static const RuleHashTableOps RuleHash_IdTable_CIOps
= {
316 RuleHash_CIMatchEntry
,
317 PL_DHashMoveEntryStub
,
318 PL_DHashClearEntryStub
,
319 PL_DHashFinalizeStub
,
322 RuleHash_IdTable_GetKey
325 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops
= {
328 RuleHash_NameSpaceTable_HashKey
,
329 RuleHash_NameSpaceTable_MatchEntry
,
330 PL_DHashMoveEntryStub
,
331 PL_DHashClearEntryStub
,
332 PL_DHashFinalizeStub
,
336 #undef RULE_HASH_STATS
337 #undef PRINT_UNIVERSAL_RULES
340 #define RULE_HASH_STATS
341 #define PRINT_UNIVERSAL_RULES
344 #ifdef RULE_HASH_STATS
345 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
347 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
350 // Enumerator callback function.
351 typedef void (*RuleEnumFunc
)(nsICSSStyleRule
* aRule
,
352 nsCSSSelector
* aSelector
,
357 RuleHash(PRBool aQuirksMode
);
359 void PrependRule(RuleValue
*aRuleInfo
);
360 void EnumerateAllRules(PRInt32 aNameSpace
, nsIAtom
* aTag
, nsIAtom
* aID
,
361 const nsAttrValue
* aClassList
,
362 RuleEnumFunc aFunc
, void* aData
);
363 void EnumerateTagRules(nsIAtom
* aTag
,
364 RuleEnumFunc aFunc
, void* aData
);
365 PLArenaPool
& Arena() { return mArena
; }
368 void PrependRuleToTable(PLDHashTable
* aTable
, const void* aKey
,
369 RuleValue
* aRuleInfo
);
370 void PrependUniversalRule(RuleValue
* aRuleInfo
);
372 // All rule values in these hashtables are arena allocated
374 PLDHashTable mIdTable
;
375 PLDHashTable mClassTable
;
376 PLDHashTable mTagTable
;
377 PLDHashTable mNameSpaceTable
;
378 RuleValue
*mUniversalRules
;
380 RuleValue
** mEnumList
;
381 PRInt32 mEnumListSize
;
385 #ifdef RULE_HASH_STATS
386 PRUint32 mUniversalSelectors
;
387 PRUint32 mNameSpaceSelectors
;
388 PRUint32 mTagSelectors
;
389 PRUint32 mClassSelectors
;
390 PRUint32 mIdSelectors
;
392 PRUint32 mElementsMatched
;
393 PRUint32 mPseudosMatched
;
395 PRUint32 mElementUniversalCalls
;
396 PRUint32 mElementNameSpaceCalls
;
397 PRUint32 mElementTagCalls
;
398 PRUint32 mElementClassCalls
;
399 PRUint32 mElementIdCalls
;
401 PRUint32 mPseudoTagCalls
;
402 #endif // RULE_HASH_STATS
405 RuleHash::RuleHash(PRBool aQuirksMode
)
407 mUniversalRules(nsnull
),
408 mEnumList(nsnull
), mEnumListSize(0)
409 #ifdef RULE_HASH_STATS
411 mUniversalSelectors(0),
412 mNameSpaceSelectors(0),
418 mElementUniversalCalls(0),
419 mElementNameSpaceCalls(0),
421 mElementClassCalls(0),
426 // Initialize our arena
427 PL_INIT_ARENA_POOL(&mArena
, "RuleHashArena", NS_RULEHASH_ARENA_BLOCK_SIZE
);
429 PL_DHashTableInit(&mTagTable
, &RuleHash_TagTable_Ops
.ops
, nsnull
,
430 sizeof(RuleHashTableEntry
), 64);
431 PL_DHashTableInit(&mIdTable
,
432 aQuirksMode
? &RuleHash_IdTable_CIOps
.ops
433 : &RuleHash_IdTable_CSOps
.ops
,
434 nsnull
, sizeof(RuleHashTableEntry
), 16);
435 PL_DHashTableInit(&mClassTable
,
436 aQuirksMode
? &RuleHash_ClassTable_CIOps
.ops
437 : &RuleHash_ClassTable_CSOps
.ops
,
438 nsnull
, sizeof(RuleHashTableEntry
), 16);
439 PL_DHashTableInit(&mNameSpaceTable
, &RuleHash_NameSpaceTable_Ops
, nsnull
,
440 sizeof(RuleHashTableEntry
), 16);
443 RuleHash::~RuleHash()
445 #ifdef RULE_HASH_STATS
448 " Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
449 " Content Nodes: Elements(%u) Pseudo-Elements(%u)\n"
450 " Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
451 " Pseudo-Element Calls: Tag(%u)\n",
452 static_cast<void*>(this),
453 mUniversalSelectors
, mNameSpaceSelectors
, mTagSelectors
,
454 mClassSelectors
, mIdSelectors
,
457 mElementUniversalCalls
, mElementNameSpaceCalls
, mElementTagCalls
,
458 mElementClassCalls
, mElementIdCalls
,
460 #ifdef PRINT_UNIVERSAL_RULES
462 RuleValue
* value
= mUniversalRules
;
464 printf(" Universal rules:\n");
466 nsAutoString selectorText
;
467 PRUint32 lineNumber
= value
->mRule
->GetLineNumber();
468 nsCOMPtr
<nsIStyleSheet
> sheet
;
469 value
->mRule
->GetStyleSheet(*getter_AddRefs(sheet
));
470 nsCOMPtr
<nsICSSStyleSheet
> cssSheet
= do_QueryInterface(sheet
);
471 value
->mSelector
->ToString(selectorText
, cssSheet
);
473 printf(" line %d, %s\n",
474 lineNumber
, NS_ConvertUTF16toUTF8(selectorText
).get());
475 value
= value
->mNext
;
479 #endif // PRINT_UNIVERSAL_RULES
480 #endif // RULE_HASH_STATS
481 // Rule Values are arena allocated no need to delete them. Their destructor
482 // isn't doing any cleanup. So we dont even bother to enumerate through
483 // the hash tables and call their destructors.
484 if (nsnull
!= mEnumList
) {
487 // delete arena for strings and small objects
488 PL_DHashTableFinish(&mIdTable
);
489 PL_DHashTableFinish(&mClassTable
);
490 PL_DHashTableFinish(&mTagTable
);
491 PL_DHashTableFinish(&mNameSpaceTable
);
492 PL_FinishArenaPool(&mArena
);
495 void RuleHash::PrependRuleToTable(PLDHashTable
* aTable
, const void* aKey
,
496 RuleValue
* aRuleInfo
)
498 // Get a new or existing entry.
499 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
500 (PL_DHashTableOperate(aTable
, aKey
, PL_DHASH_ADD
));
503 entry
->mRules
= aRuleInfo
->Add(mRuleCount
++, entry
->mRules
);
506 void RuleHash::PrependUniversalRule(RuleValue
*aRuleInfo
)
508 mUniversalRules
= aRuleInfo
->Add(mRuleCount
++, mUniversalRules
);
511 void RuleHash::PrependRule(RuleValue
*aRuleInfo
)
513 nsCSSSelector
*selector
= aRuleInfo
->mSelector
;
514 if (nsnull
!= selector
->mIDList
) {
515 PrependRuleToTable(&mIdTable
, selector
->mIDList
->mAtom
, aRuleInfo
);
516 RULE_HASH_STAT_INCREMENT(mIdSelectors
);
518 else if (nsnull
!= selector
->mClassList
) {
519 PrependRuleToTable(&mClassTable
, selector
->mClassList
->mAtom
, aRuleInfo
);
520 RULE_HASH_STAT_INCREMENT(mClassSelectors
);
522 else if (nsnull
!= selector
->mTag
) {
523 PrependRuleToTable(&mTagTable
, selector
->mTag
, aRuleInfo
);
524 RULE_HASH_STAT_INCREMENT(mTagSelectors
);
526 else if (kNameSpaceID_Unknown
!= selector
->mNameSpace
) {
527 PrependRuleToTable(&mNameSpaceTable
,
528 NS_INT32_TO_PTR(selector
->mNameSpace
), aRuleInfo
);
529 RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors
);
531 else { // universal tag selector
532 PrependUniversalRule(aRuleInfo
);
533 RULE_HASH_STAT_INCREMENT(mUniversalSelectors
);
537 // this should cover practically all cases so we don't need to reallocate
538 #define MIN_ENUM_LIST_SIZE 8
540 #ifdef RULE_HASH_STATS
541 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
542 do { ++(var_); (list_) = (list_)->mNext; } while (list_)
544 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
545 PR_BEGIN_MACRO PR_END_MACRO
548 void RuleHash::EnumerateAllRules(PRInt32 aNameSpace
, nsIAtom
* aTag
,
549 nsIAtom
* aID
, const nsAttrValue
* aClassList
,
550 RuleEnumFunc aFunc
, void* aData
)
552 PRInt32 classCount
= aClassList
? aClassList
->GetAtomCount() : 0;
554 // assume 1 universal, tag, id, and namespace, rather than wasting
556 PRInt32 testCount
= classCount
+ 4;
558 if (mEnumListSize
< testCount
) {
560 mEnumListSize
= PR_MAX(testCount
, MIN_ENUM_LIST_SIZE
);
561 mEnumList
= new RuleValue
*[mEnumListSize
];
564 PRInt32 valueCount
= 0;
565 RULE_HASH_STAT_INCREMENT(mElementsMatched
);
568 RuleValue
* value
= mUniversalRules
;
569 if (nsnull
!= value
) {
570 mEnumList
[valueCount
++] = value
;
571 RULE_HASH_STAT_INCREMENT_LIST_COUNT(value
, mElementUniversalCalls
);
574 // universal rules within the namespace
575 if (kNameSpaceID_Unknown
!= aNameSpace
) {
576 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
577 (PL_DHashTableOperate(&mNameSpaceTable
, NS_INT32_TO_PTR(aNameSpace
),
579 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
580 RuleValue
*value
= entry
->mRules
;
581 mEnumList
[valueCount
++] = value
;
582 RULE_HASH_STAT_INCREMENT_LIST_COUNT(value
, mElementNameSpaceCalls
);
585 if (nsnull
!= aTag
) {
586 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
587 (PL_DHashTableOperate(&mTagTable
, aTag
, PL_DHASH_LOOKUP
));
588 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
589 RuleValue
*value
= entry
->mRules
;
590 mEnumList
[valueCount
++] = value
;
591 RULE_HASH_STAT_INCREMENT_LIST_COUNT(value
, mElementTagCalls
);
595 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
596 (PL_DHashTableOperate(&mIdTable
, aID
, PL_DHASH_LOOKUP
));
597 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
598 RuleValue
*value
= entry
->mRules
;
599 mEnumList
[valueCount
++] = value
;
600 RULE_HASH_STAT_INCREMENT_LIST_COUNT(value
, mElementIdCalls
);
603 { // extra scope to work around compiler bugs with |for| scoping.
604 for (PRInt32 index
= 0; index
< classCount
; ++index
) {
605 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
606 (PL_DHashTableOperate(&mClassTable
, aClassList
->AtomAt(index
),
608 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
609 RuleValue
*value
= entry
->mRules
;
610 mEnumList
[valueCount
++] = value
;
611 RULE_HASH_STAT_INCREMENT_LIST_COUNT(value
, mElementClassCalls
);
615 NS_ASSERTION(valueCount
<= testCount
, "values exceeded list size");
617 if (valueCount
> 0) {
618 // Merge the lists while there are still multiple lists to merge.
619 while (valueCount
> 1) {
620 PRInt32 valueIndex
= 0;
621 PRInt32 highestRuleIndex
= mEnumList
[valueIndex
]->mBackwardIndex
;
622 for (PRInt32 index
= 1; index
< valueCount
; ++index
) {
623 PRInt32 ruleIndex
= mEnumList
[index
]->mBackwardIndex
;
624 if (ruleIndex
> highestRuleIndex
) {
626 highestRuleIndex
= ruleIndex
;
629 RuleValue
*cur
= mEnumList
[valueIndex
];
630 (*aFunc
)(cur
->mRule
, cur
->mSelector
, aData
);
631 RuleValue
*next
= cur
->mNext
;
632 mEnumList
[valueIndex
] = next
? next
: mEnumList
[--valueCount
];
635 // Fast loop over single value.
636 RuleValue
* value
= mEnumList
[0];
638 (*aFunc
)(value
->mRule
, value
->mSelector
, aData
);
639 value
= value
->mNext
;
644 void RuleHash::EnumerateTagRules(nsIAtom
* aTag
, RuleEnumFunc aFunc
, void* aData
)
646 RuleHashTableEntry
*entry
= static_cast<RuleHashTableEntry
*>
647 (PL_DHashTableOperate(&mTagTable
, aTag
, PL_DHASH_LOOKUP
));
649 RULE_HASH_STAT_INCREMENT(mPseudosMatched
);
650 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
651 RuleValue
*tagValue
= entry
->mRules
;
653 RULE_HASH_STAT_INCREMENT(mPseudoTagCalls
);
654 (*aFunc
)(tagValue
->mRule
, tagValue
->mSelector
, aData
);
655 tagValue
= tagValue
->mNext
;
660 //--------------------------------
662 // Attribute selectors hash table.
663 struct AttributeSelectorEntry
: public PLDHashEntryHdr
{
665 nsVoidArray
*mSelectors
;
669 AttributeSelectorClearEntry(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
)
671 AttributeSelectorEntry
*entry
= static_cast<AttributeSelectorEntry
*>(hdr
);
672 delete entry
->mSelectors
;
673 memset(entry
, 0, table
->entrySize
);
676 static const PLDHashTableOps AttributeSelectorOps
= {
679 PL_DHashVoidPtrKeyStub
,
680 PL_DHashMatchEntryStub
,
681 PL_DHashMoveEntryStub
,
682 AttributeSelectorClearEntry
,
683 PL_DHashFinalizeStub
,
688 //--------------------------------
690 struct RuleCascadeData
{
691 RuleCascadeData(nsIAtom
*aMedium
, PRBool aQuirksMode
)
692 : mRuleHash(aQuirksMode
),
697 PL_DHashTableInit(&mAttributeSelectors
, &AttributeSelectorOps
, nsnull
,
698 sizeof(AttributeSelectorEntry
), 16);
703 PL_DHashTableFinish(&mAttributeSelectors
);
706 nsVoidArray mStateSelectors
;
707 nsVoidArray mClassSelectors
;
708 nsVoidArray mIDSelectors
;
709 PLDHashTable mAttributeSelectors
; // nsIAtom* -> nsVoidArray*
711 nsTArray
<nsFontFaceRuleContainer
> mFontFaceRules
;
713 // Looks up or creates the appropriate list in |mAttributeSelectors|.
714 // Returns null only on allocation failure.
715 nsVoidArray
* AttributeListFor(nsIAtom
* aAttribute
);
717 nsMediaQueryResultCacheKey mCacheKey
;
718 RuleCascadeData
* mNext
; // for a different medium
722 RuleCascadeData::AttributeListFor(nsIAtom
* aAttribute
)
724 AttributeSelectorEntry
*entry
= static_cast<AttributeSelectorEntry
*>
725 (PL_DHashTableOperate(&mAttributeSelectors
, aAttribute
, PL_DHASH_ADD
));
728 if (!entry
->mSelectors
) {
729 if (!(entry
->mSelectors
= new nsVoidArray
)) {
730 PL_DHashTableRawRemove(&mAttributeSelectors
, entry
);
733 entry
->mAttribute
= aAttribute
;
735 return entry
->mSelectors
;
738 // -------------------------------
739 // CSS Style rule processor implementation
742 nsCSSRuleProcessor::nsCSSRuleProcessor(const nsCOMArray
<nsICSSStyleSheet
>& aSheets
,
745 , mRuleCascades(nsnull
)
746 , mLastPresContext(nsnull
)
747 , mSheetType(aSheetType
)
749 for (PRInt32 i
= mSheets
.Count() - 1; i
>= 0; --i
)
750 mSheets
[i
]->AddRuleProcessor(this);
753 nsCSSRuleProcessor::~nsCSSRuleProcessor()
755 for (PRInt32 i
= mSheets
.Count() - 1; i
>= 0; --i
)
756 mSheets
[i
]->DropRuleProcessor(this);
761 NS_IMPL_ISUPPORTS1(nsCSSRuleProcessor
, nsIStyleRuleProcessor
)
764 nsCSSRuleProcessor::Startup()
766 nsContentUtils::AddBoolPrefVarCache(VISITED_PSEUDO_PREF
,
767 &gSupportVisitedPseudo
);
768 // We want to default to true, not false as AddBoolPrefVarCache does.
769 gSupportVisitedPseudo
=
770 nsContentUtils::GetBoolPref(VISITED_PSEUDO_PREF
, PR_TRUE
);
776 NS_ASSERTION(!sSystemMetrics
, "already initialized");
778 sSystemMetrics
= new nsTArray
< nsCOMPtr
<nsIAtom
> >;
779 NS_ENSURE_TRUE(sSystemMetrics
, PR_FALSE
);
782 nsCOMPtr
<nsILookAndFeel
> lookAndFeel(do_GetService(kLookAndFeelCID
, &rv
));
783 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
785 PRInt32 metricResult
;
786 lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_ScrollArrowStyle
, metricResult
);
787 if (metricResult
& nsILookAndFeel::eMetric_ScrollArrowStartBackward
) {
788 sSystemMetrics
->AppendElement(do_GetAtom("scrollbar-start-backward"));
790 if (metricResult
& nsILookAndFeel::eMetric_ScrollArrowStartForward
) {
791 sSystemMetrics
->AppendElement(do_GetAtom("scrollbar-start-forward"));
793 if (metricResult
& nsILookAndFeel::eMetric_ScrollArrowEndBackward
) {
794 sSystemMetrics
->AppendElement(do_GetAtom("scrollbar-end-backward"));
796 if (metricResult
& nsILookAndFeel::eMetric_ScrollArrowEndForward
) {
797 sSystemMetrics
->AppendElement(do_GetAtom("scrollbar-end-forward"));
800 lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_ScrollSliderStyle
, metricResult
);
801 if (metricResult
!= nsILookAndFeel::eMetric_ScrollThumbStyleNormal
) {
802 sSystemMetrics
->AppendElement(do_GetAtom("scrollbar-thumb-proportional"));
805 lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_ImagesInMenus
, metricResult
);
807 sSystemMetrics
->AppendElement(do_GetAtom("images-in-menus"));
810 rv
= lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_WindowsDefaultTheme
, metricResult
);
811 if (NS_SUCCEEDED(rv
) && metricResult
) {
812 sSystemMetrics
->AppendElement(do_GetAtom("windows-default-theme"));
815 rv
= lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_MacGraphiteTheme
, metricResult
);
816 if (NS_SUCCEEDED(rv
) && metricResult
) {
817 sSystemMetrics
->AppendElement(do_GetAtom("mac-graphite-theme"));
820 rv
= lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_DWMCompositor
, metricResult
);
821 if (NS_SUCCEEDED(rv
) && metricResult
) {
822 sSystemMetrics
->AppendElement(do_GetAtom("windows-compositor"));
825 rv
= lookAndFeel
->GetMetric(nsILookAndFeel::eMetric_WindowsClassic
, metricResult
);
826 if (NS_SUCCEEDED(rv
) && metricResult
) {
827 sSystemMetrics
->AppendElement(do_GetAtom("windows-classic"));
834 nsCSSRuleProcessor::FreeSystemMetrics()
836 delete sSystemMetrics
;
837 sSystemMetrics
= nsnull
;
840 RuleProcessorData::RuleProcessorData(nsPresContext
* aPresContext
,
841 nsIContent
* aContent
,
842 nsRuleWalker
* aRuleWalker
,
843 nsCompatibility
* aCompat
/*= nsnull*/)
845 MOZ_COUNT_CTOR(RuleProcessorData
);
847 NS_ASSERTION(!aContent
|| aContent
->IsNodeOfType(nsINode::eELEMENT
),
848 "non-element leaked into SelectorMatches");
850 mPresContext
= aPresContext
;
852 mParentContent
= nsnull
;
853 mRuleWalker
= aRuleWalker
;
854 mScopedRoot
= nsnull
;
856 mContentTag
= nsnull
;
858 mHasAttributes
= PR_FALSE
;
859 mIsHTMLContent
= PR_FALSE
;
861 mLinkState
= eLinkState_Unknown
;
863 mNameSpaceID
= kNameSpaceID_Unknown
;
864 mPreviousSiblingData
= nsnull
;
865 mParentData
= nsnull
;
868 mNthIndices
[0][0] = -2;
869 mNthIndices
[0][1] = -2;
870 mNthIndices
[1][0] = -2;
871 mNthIndices
[1][1] = -2;
873 // get the compat. mode (unless it is provided)
874 // XXXbz is passing in the compat mode really that much of an optimization?
876 mCompatMode
= *aCompat
;
877 } else if (NS_LIKELY(mPresContext
)) {
878 mCompatMode
= mPresContext
->CompatibilityMode();
880 NS_ASSERTION(aContent
, "Must have content");
881 NS_ASSERTION(aContent
->GetOwnerDoc(), "Must have document");
882 mCompatMode
= aContent
->GetOwnerDoc()->GetCompatibilityMode();
886 NS_ASSERTION(aContent
->GetOwnerDoc(), "Document-less node here?");
888 // get the tag and parent
889 mContentTag
= aContent
->Tag();
890 mParentContent
= aContent
->GetParent();
892 // get the event state
894 mPresContext
->EventStateManager()->GetContentState(aContent
, mEventState
);
896 mEventState
= aContent
->IntrinsicState();
899 // get the ID and classes for the content
900 mContentID
= aContent
->GetID();
901 mClasses
= aContent
->GetClasses();
903 // see if there are attributes for the content
904 mHasAttributes
= aContent
->GetAttrCount() > 0;
906 // check for HTMLContent and Link status
907 if (aContent
->IsNodeOfType(nsINode::eHTML
)) {
908 mIsHTMLContent
= PR_TRUE
;
909 // Note that we want to treat non-XML HTML content as XHTML for namespace
910 // purposes, since html.css has that namespace declared.
911 mNameSpaceID
= kNameSpaceID_XHTML
;
914 mNameSpaceID
= aContent
->GetNameSpaceID();
917 // if HTML content and it has some attributes, check for an HTML link
918 // NOTE: optimization: cannot be a link if no attributes (since it needs an href)
919 nsILinkHandler
* linkHandler
=
920 mPresContext
? mPresContext
->GetLinkHandler() : nsnull
;
921 if (mIsHTMLContent
&& mHasAttributes
) {
922 // check if it is an HTML Link
923 if(nsStyleUtil::IsHTMLLink(aContent
, mContentTag
,
924 linkHandler
, aRuleWalker
!= nsnull
,
930 // if not an HTML link, check for a simple xlink (cannot be both HTML link and xlink)
931 // NOTE: optimization: cannot be an XLink if no attributes (since it needs an
934 !(mIsHTMLContent
|| aContent
->IsNodeOfType(nsINode::eXUL
)) &&
935 nsStyleUtil::IsLink(aContent
, linkHandler
,
936 aRuleWalker
!= nsnull
, &mLinkState
)) {
941 if (mLinkState
== eLinkState_Visited
&& !gSupportVisitedPseudo
) {
942 mLinkState
= eLinkState_Unvisited
;
946 RuleProcessorData::~RuleProcessorData()
948 MOZ_COUNT_DTOR(RuleProcessorData
);
950 // Destroy potentially long chains of previous sibling and parent data
951 // without more than one level of recursion.
952 if (mPreviousSiblingData
|| mParentData
) {
953 nsAutoVoidArray destroyQueue
;
954 destroyQueue
.AppendElement(this);
957 RuleProcessorData
*d
= static_cast<RuleProcessorData
*>
958 (destroyQueue
.FastElementAt(destroyQueue
.Count() - 1));
959 destroyQueue
.RemoveElementAt(destroyQueue
.Count() - 1);
961 if (d
->mPreviousSiblingData
) {
962 destroyQueue
.AppendElement(d
->mPreviousSiblingData
);
963 d
->mPreviousSiblingData
= nsnull
;
965 if (d
->mParentData
) {
966 destroyQueue
.AppendElement(d
->mParentData
);
967 d
->mParentData
= nsnull
;
972 } while (destroyQueue
.Count());
978 const nsString
* RuleProcessorData::GetLang()
981 mLanguage
= new nsString();
984 for (nsIContent
* content
= mContent
; content
;
985 content
= content
->GetParent()) {
986 if (content
->GetAttrCount() > 0) {
987 // xml:lang has precedence over lang on HTML elements (see
988 // XHTML1 section C.7).
989 PRBool hasAttr
= content
->GetAttr(kNameSpaceID_XML
, nsGkAtoms::lang
,
991 if (!hasAttr
&& content
->IsNodeOfType(nsINode::eHTML
)) {
992 hasAttr
= content
->GetAttr(kNameSpaceID_None
, nsGkAtoms::lang
,
995 NS_ASSERTION(hasAttr
|| mLanguage
->IsEmpty(),
996 "GetAttr that returns false should not make string non-empty");
1006 static inline PRInt32
1007 CSSNameSpaceID(nsIContent
*aContent
)
1009 return aContent
->IsNodeOfType(nsINode::eHTML
)
1010 ? kNameSpaceID_XHTML
1011 : aContent
->GetNameSpaceID();
1015 RuleProcessorData::GetNthIndex(PRBool aIsOfType
, PRBool aIsFromEnd
,
1016 PRBool aCheckEdgeOnly
)
1018 NS_ASSERTION(mParentContent
, "caller should check mParentContent");
1019 NS_ASSERTION(!mPreviousSiblingData
||
1020 mPreviousSiblingData
->mContent
->IsNodeOfType(nsINode::eELEMENT
),
1021 "Unexpected previous sibling data");
1023 PRInt32
&slot
= mNthIndices
[aIsOfType
][aIsFromEnd
];
1024 if (slot
!= -2 && (slot
!= -1 || aCheckEdgeOnly
))
1027 if (mPreviousSiblingData
&&
1029 (mPreviousSiblingData
->mNameSpaceID
== mNameSpaceID
&&
1030 mPreviousSiblingData
->mContentTag
== mContentTag
))) {
1031 slot
= mPreviousSiblingData
->mNthIndices
[aIsOfType
][aIsFromEnd
];
1033 slot
+= (aIsFromEnd
? -1 : 1);
1034 NS_ASSERTION(slot
> 0, "How did that happen?");
1040 nsIContent
* parent
= mParentContent
;
1042 PRUint32 childCount
;
1043 nsIContent
* const * curChildPtr
= parent
->GetChildArray(&childCount
);
1046 nsMutationGuard debugMutationGuard
;
1050 nsIContent
* const * stopPtr
;
1052 stopPtr
= curChildPtr
- 1;
1053 curChildPtr
= stopPtr
+ childCount
;
1057 stopPtr
= curChildPtr
+ childCount
;
1060 for ( ; ; curChildPtr
+= increment
) {
1061 if (curChildPtr
== stopPtr
) {
1062 // mContent is the root of an anonymous content subtree.
1063 result
= 0; // special value to indicate that it is not at any index
1066 nsIContent
* child
= *curChildPtr
;
1067 if (child
== mContent
)
1069 if (child
->IsNodeOfType(nsINode::eELEMENT
) &&
1071 (child
->Tag() == mContentTag
&&
1072 CSSNameSpaceID(child
) == mNameSpaceID
))) {
1073 if (aCheckEdgeOnly
) {
1074 // The caller only cares whether or not the result is 1, and we
1075 // now know it's not.
1084 NS_ASSERTION(!debugMutationGuard
.Mutated(0), "Unexpected mutations happened");
1091 static PRBool
ValueIncludes(const nsSubstring
& aValueList
,
1092 const nsSubstring
& aValue
,
1093 const nsStringComparator
& aComparator
)
1095 const PRUnichar
*p
= aValueList
.BeginReading(),
1096 *p_end
= aValueList
.EndReading();
1099 // skip leading space
1100 while (p
!= p_end
&& nsContentUtils::IsHTMLWhitespace(*p
))
1103 const PRUnichar
*val_start
= p
;
1105 // look for space or end
1106 while (p
!= p_end
&& !nsContentUtils::IsHTMLWhitespace(*p
))
1109 const PRUnichar
*val_end
= p
;
1111 if (val_start
< val_end
&&
1112 aValue
.Equals(Substring(val_start
, val_end
), aComparator
))
1115 ++p
; // we know the next character is not whitespace
1120 inline PRBool
IsLinkPseudo(nsIAtom
* aAtom
)
1122 return PRBool ((nsCSSPseudoClasses::link
== aAtom
) ||
1123 (nsCSSPseudoClasses::visited
== aAtom
) ||
1124 (nsCSSPseudoClasses::mozAnyLink
== aAtom
));
1127 // Return whether we should apply a "global" (i.e., universal-tag)
1128 // selector for event states in quirks mode. Note that
1129 // |data.mIsLink| is checked separately by the caller, so we return
1130 // false for |nsGkAtoms::a|, which here means a named anchor.
1131 inline PRBool
IsQuirkEventSensitive(nsIAtom
*aContentTag
)
1133 return PRBool ((nsGkAtoms::button
== aContentTag
) ||
1134 (nsGkAtoms::img
== aContentTag
) ||
1135 (nsGkAtoms::input
== aContentTag
) ||
1136 (nsGkAtoms::label
== aContentTag
) ||
1137 (nsGkAtoms::select
== aContentTag
) ||
1138 (nsGkAtoms::textarea
== aContentTag
));
1142 static inline PRBool
1143 IsSignificantChild(nsIContent
* aChild
, PRBool aTextIsSignificant
,
1144 PRBool aWhitespaceIsSignificant
)
1146 return nsStyleUtil::IsSignificantChild(aChild
, aTextIsSignificant
,
1147 aWhitespaceIsSignificant
);
1150 // This function is to be called once we have fetched a value for an attribute
1151 // whose namespace and name match those of aAttrSelector. This function
1152 // performs comparisons on the value only, based on aAttrSelector->mFunction.
1153 static PRBool
AttrMatchesValue(const nsAttrSelector
* aAttrSelector
,
1154 const nsString
& aValue
)
1156 NS_PRECONDITION(aAttrSelector
, "Must have an attribute selector");
1158 // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
1159 // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
1160 // all accept the empty string, but match nothing.
1161 if (aAttrSelector
->mValue
.IsEmpty() &&
1162 (aAttrSelector
->mFunction
== NS_ATTR_FUNC_INCLUDES
||
1163 aAttrSelector
->mFunction
== NS_ATTR_FUNC_ENDSMATCH
||
1164 aAttrSelector
->mFunction
== NS_ATTR_FUNC_BEGINSMATCH
||
1165 aAttrSelector
->mFunction
== NS_ATTR_FUNC_CONTAINSMATCH
))
1168 const nsDefaultStringComparator defaultComparator
;
1169 const nsCaseInsensitiveStringComparator ciComparator
;
1170 const nsStringComparator
& comparator
= aAttrSelector
->mCaseSensitive
1171 ? static_cast<const nsStringComparator
&>(defaultComparator
)
1172 : static_cast<const nsStringComparator
&>(ciComparator
);
1173 switch (aAttrSelector
->mFunction
) {
1174 case NS_ATTR_FUNC_EQUALS
:
1175 return aValue
.Equals(aAttrSelector
->mValue
, comparator
);
1176 case NS_ATTR_FUNC_INCLUDES
:
1177 return ValueIncludes(aValue
, aAttrSelector
->mValue
, comparator
);
1178 case NS_ATTR_FUNC_DASHMATCH
:
1179 return nsStyleUtil::DashMatchCompare(aValue
, aAttrSelector
->mValue
, comparator
);
1180 case NS_ATTR_FUNC_ENDSMATCH
:
1181 return StringEndsWith(aValue
, aAttrSelector
->mValue
, comparator
);
1182 case NS_ATTR_FUNC_BEGINSMATCH
:
1183 return StringBeginsWith(aValue
, aAttrSelector
->mValue
, comparator
);
1184 case NS_ATTR_FUNC_CONTAINSMATCH
:
1185 return FindInReadable(aAttrSelector
->mValue
, aValue
, comparator
);
1187 NS_NOTREACHED("Shouldn't be ending up here");
1192 // NOTE: For |aStateMask| and |aAttribute| to work correctly, it's
1193 // important that any change that changes multiple state bits and
1194 // maybe an attribute include all those state bits and the attribute
1195 // in the notification. Otherwise, if multiple states change but we
1196 // do separate notifications then we might determine the style is not
1197 // state-dependent when it really is (e.g., determining that a
1198 // :hover:active rule no longer matches when both states are unset).
1200 // If |aForStyling| is false, we shouldn't mark slow-selector bits on nodes.
1202 // |aDependence| has two functions:
1203 // * when non-null, it indicates that we're processing a negation,
1204 // which is done only when SelectorMatches calls itself recursively
1205 // * what it points to should be set to true whenever a test is skipped
1206 // because of aStateMask or aAttribute
1207 static PRBool
SelectorMatches(RuleProcessorData
&data
,
1208 nsCSSSelector
* aSelector
,
1209 PRInt32 aStateMask
, // states NOT to test
1210 nsIAtom
* aAttribute
, // attribute NOT to test
1212 PRBool
* const aDependence
= nsnull
)
1215 // namespace/tag match
1216 if ((kNameSpaceID_Unknown
!= aSelector
->mNameSpace
&&
1217 data
.mNameSpaceID
!= aSelector
->mNameSpace
) ||
1218 (aSelector
->mTag
&& aSelector
->mTag
!= data
.mContentTag
)) {
1219 // optimization : bail out early if we can
1223 PRBool result
= PR_TRUE
;
1224 const PRBool isNegated
= (aDependence
!= nsnull
);
1225 // The selectors for which we set node bits are, unfortunately, early
1226 // in this function (because they're pseudo-classes, which are
1227 // generally quick to test, and thus earlier). If they were later,
1228 // we'd probably avoid setting those bits in more cases where setting
1229 // them is unnecessary.
1230 const PRBool setNodeFlags
= aForStyling
&& aStateMask
== 0 && !aAttribute
;
1232 // test for pseudo class match
1233 // first-child, root, lang, active, focus, hover, link, visited...
1234 // XXX disabled, enabled, selected, selection
1235 for (nsPseudoClassList
* pseudoClass
= aSelector
->mPseudoClassList
;
1236 pseudoClass
&& result
; pseudoClass
= pseudoClass
->mNext
) {
1237 PRInt32 stateToCheck
= 0;
1238 if (nsCSSPseudoClasses::firstNode
== pseudoClass
->mAtom
) {
1239 nsIContent
*firstNode
= nsnull
;
1240 nsIContent
*parent
= data
.mParentContent
;
1243 parent
->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR
);
1247 firstNode
= parent
->GetChildAt(++index
);
1248 // stop at first non-comment and non-whitespace node
1249 } while (firstNode
&&
1250 !IsSignificantChild(firstNode
, PR_TRUE
, PR_FALSE
));
1252 result
= (data
.mContent
== firstNode
);
1254 else if (nsCSSPseudoClasses::lastNode
== pseudoClass
->mAtom
) {
1255 nsIContent
*lastNode
= nsnull
;
1256 nsIContent
*parent
= data
.mParentContent
;
1259 parent
->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR
);
1261 PRUint32 index
= parent
->GetChildCount();
1263 lastNode
= parent
->GetChildAt(--index
);
1264 // stop at first non-comment and non-whitespace node
1265 } while (lastNode
&&
1266 !IsSignificantChild(lastNode
, PR_TRUE
, PR_FALSE
));
1268 result
= (data
.mContent
== lastNode
);
1270 else if (nsCSSPseudoClasses::firstChild
== pseudoClass
->mAtom
||
1271 nsCSSPseudoClasses::lastChild
== pseudoClass
->mAtom
||
1272 nsCSSPseudoClasses::onlyChild
== pseudoClass
->mAtom
) {
1273 nsIContent
*parent
= data
.mParentContent
;
1275 const PRBool checkFirst
=
1276 pseudoClass
->mAtom
!= nsCSSPseudoClasses::lastChild
;
1277 const PRBool checkLast
=
1278 pseudoClass
->mAtom
!= nsCSSPseudoClasses::firstChild
;
1280 parent
->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR
);
1282 result
= (!checkFirst
||
1283 data
.GetNthIndex(PR_FALSE
, PR_FALSE
, PR_TRUE
) == 1) &&
1285 data
.GetNthIndex(PR_FALSE
, PR_TRUE
, PR_TRUE
) == 1);
1290 else if (nsCSSPseudoClasses::nthChild
== pseudoClass
->mAtom
||
1291 nsCSSPseudoClasses::nthLastChild
== pseudoClass
->mAtom
||
1292 nsCSSPseudoClasses::nthOfType
== pseudoClass
->mAtom
||
1293 nsCSSPseudoClasses::nthLastOfType
== pseudoClass
->mAtom
) {
1294 nsIContent
*parent
= data
.mParentContent
;
1297 nsCSSPseudoClasses::nthOfType
== pseudoClass
->mAtom
||
1298 nsCSSPseudoClasses::nthLastOfType
== pseudoClass
->mAtom
;
1300 nsCSSPseudoClasses::nthLastChild
== pseudoClass
->mAtom
||
1301 nsCSSPseudoClasses::nthLastOfType
== pseudoClass
->mAtom
;
1304 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR
);
1306 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND
);
1309 const PRInt32 index
= data
.GetNthIndex(isOfType
, isFromEnd
, PR_FALSE
);
1311 // Node is anonymous content (not really a child of its parent).
1314 const PRInt32 a
= pseudoClass
->u
.mNumbers
[0];
1315 const PRInt32 b
= pseudoClass
->u
.mNumbers
[1];
1316 // result should be true if there exists n >= 0 such that
1317 // a * n + b == index.
1319 result
= b
== index
;
1321 // Integer division in C does truncation (towards 0). So
1322 // check that the result is nonnegative, and that there was no
1324 const PRInt32 n
= (index
- b
) / a
;
1325 result
= n
>= 0 && (a
* n
== index
- b
);
1332 else if (nsCSSPseudoClasses::firstOfType
== pseudoClass
->mAtom
||
1333 nsCSSPseudoClasses::lastOfType
== pseudoClass
->mAtom
||
1334 nsCSSPseudoClasses::onlyOfType
== pseudoClass
->mAtom
) {
1335 nsIContent
*parent
= data
.mParentContent
;
1337 const PRBool checkFirst
=
1338 pseudoClass
->mAtom
!= nsCSSPseudoClasses::lastOfType
;
1339 const PRBool checkLast
=
1340 pseudoClass
->mAtom
!= nsCSSPseudoClasses::firstOfType
;
1343 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR
);
1345 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND
);
1348 result
= (!checkFirst
||
1349 data
.GetNthIndex(PR_TRUE
, PR_FALSE
, PR_TRUE
) == 1) &&
1351 data
.GetNthIndex(PR_TRUE
, PR_TRUE
, PR_TRUE
) == 1);
1356 else if (nsCSSPseudoClasses::empty
== pseudoClass
->mAtom
||
1357 nsCSSPseudoClasses::mozOnlyWhitespace
== pseudoClass
->mAtom
) {
1358 nsIContent
*child
= nsnull
;
1359 nsIContent
*element
= data
.mContent
;
1360 const PRBool isWhitespaceSignificant
=
1361 nsCSSPseudoClasses::empty
== pseudoClass
->mAtom
;
1365 element
->SetFlags(NODE_HAS_EMPTY_SELECTOR
);
1368 child
= element
->GetChildAt(++index
);
1369 // stop at first non-comment (and non-whitespace for
1370 // :-moz-only-whitespace) node
1371 } while (child
&& !IsSignificantChild(child
, PR_TRUE
, isWhitespaceSignificant
));
1372 result
= (child
== nsnull
);
1374 else if (nsCSSPseudoClasses::mozEmptyExceptChildrenWithLocalname
== pseudoClass
->mAtom
) {
1375 NS_ASSERTION(pseudoClass
->u
.mString
, "Must have string!");
1376 nsIContent
*child
= nsnull
;
1377 nsIContent
*element
= data
.mContent
;
1381 element
->SetFlags(NODE_HAS_SLOW_SELECTOR
);
1384 child
= element
->GetChildAt(++index
);
1386 (!IsSignificantChild(child
, PR_TRUE
, PR_FALSE
) ||
1387 (child
->GetNameSpaceID() == element
->GetNameSpaceID() &&
1388 child
->Tag()->Equals(nsDependentString(pseudoClass
->u
.mString
)))));
1389 result
= (child
== nsnull
);
1391 else if (nsCSSPseudoClasses::mozSystemMetric
== pseudoClass
->mAtom
) {
1392 if (!sSystemMetrics
&& !InitSystemMetrics()) {
1395 NS_ASSERTION(pseudoClass
->u
.mString
, "Must have string!");
1396 nsCOMPtr
<nsIAtom
> metric
= do_GetAtom(pseudoClass
->u
.mString
);
1397 result
= sSystemMetrics
->IndexOf(metric
) !=
1398 sSystemMetrics
->NoIndex
;
1400 else if (nsCSSPseudoClasses::mozHasHandlerRef
== pseudoClass
->mAtom
) {
1401 nsIContent
*child
= nsnull
;
1402 nsIContent
*element
= data
.mContent
;
1408 child
= element
->GetChildAt(++index
);
1409 if (child
&& child
->IsNodeOfType(nsINode::eHTML
) &&
1410 child
->Tag() == nsGkAtoms::param
&&
1411 child
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
1412 NS_LITERAL_STRING("pluginurl"), eIgnoreCase
)) {
1419 else if (nsCSSPseudoClasses::root
== pseudoClass
->mAtom
) {
1420 result
= (data
.mParentContent
== nsnull
&&
1423 data
.mContent
->GetOwnerDoc()->GetRootContent());
1425 else if (nsCSSPseudoClasses::mozBoundElement
== pseudoClass
->mAtom
) {
1426 // XXXldb How do we know where the selector came from? And what
1427 // if there are multiple bindings, and we should be matching the
1429 result
= (data
.mScopedRoot
&& data
.mScopedRoot
== data
.mContent
);
1431 else if (nsCSSPseudoClasses::lang
== pseudoClass
->mAtom
) {
1432 NS_ASSERTION(nsnull
!= pseudoClass
->u
.mString
, "null lang parameter");
1434 if (pseudoClass
->u
.mString
&& *pseudoClass
->u
.mString
) {
1435 // We have to determine the language of the current element. Since
1436 // this is currently no property and since the language is inherited
1437 // from the parent we have to be prepared to look at all parent
1438 // nodes. The language itself is encoded in the LANG attribute.
1439 const nsString
* lang
= data
.GetLang();
1440 if (lang
&& !lang
->IsEmpty()) { // null check for out-of-memory
1441 result
= nsStyleUtil::DashMatchCompare(*lang
,
1442 nsDependentString(pseudoClass
->u
.mString
),
1443 nsCaseInsensitiveStringComparator());
1445 else if (data
.mContent
) {
1446 nsIDocument
* doc
= data
.mContent
->GetDocument();
1448 // Try to get the language from the HTTP header or if this
1449 // is missing as well from the preferences.
1450 // The content language can be a comma-separated list of
1452 nsAutoString language
;
1453 doc
->GetContentLanguage(language
);
1455 nsDependentString
langString(pseudoClass
->u
.mString
);
1456 language
.StripWhitespace();
1458 PRInt32 len
= language
.Length();
1459 while (begin
< len
) {
1460 PRInt32 end
= language
.FindChar(PRUnichar(','), begin
);
1461 if (end
== kNotFound
) {
1464 if (nsStyleUtil::DashMatchCompare(Substring(language
, begin
, end
-begin
),
1466 nsCaseInsensitiveStringComparator())) {
1475 } else if (nsCSSPseudoClasses::active
== pseudoClass
->mAtom
) {
1476 stateToCheck
= NS_EVENT_STATE_ACTIVE
;
1478 else if (nsCSSPseudoClasses::focus
== pseudoClass
->mAtom
) {
1479 stateToCheck
= NS_EVENT_STATE_FOCUS
;
1481 else if (nsCSSPseudoClasses::hover
== pseudoClass
->mAtom
) {
1482 stateToCheck
= NS_EVENT_STATE_HOVER
;
1484 else if (nsCSSPseudoClasses::mozDragOver
== pseudoClass
->mAtom
) {
1485 stateToCheck
= NS_EVENT_STATE_DRAGOVER
;
1487 else if (nsCSSPseudoClasses::target
== pseudoClass
->mAtom
) {
1488 stateToCheck
= NS_EVENT_STATE_URLTARGET
;
1490 else if (IsLinkPseudo(pseudoClass
->mAtom
)) {
1492 if (nsCSSPseudoClasses::mozAnyLink
== pseudoClass
->mAtom
) {
1496 NS_ASSERTION(nsCSSPseudoClasses::link
== pseudoClass
->mAtom
||
1497 nsCSSPseudoClasses::visited
== pseudoClass
->mAtom
,
1498 "somebody changed IsLinkPseudo");
1499 NS_ASSERTION(data
.mLinkState
== eLinkState_Unvisited
||
1500 data
.mLinkState
== eLinkState_Visited
,
1501 "unexpected link state for mIsLink");
1502 if (aStateMask
& NS_EVENT_STATE_VISITED
) {
1505 *aDependence
= PR_TRUE
;
1507 result
= ((eLinkState_Unvisited
== data
.mLinkState
) ==
1508 (nsCSSPseudoClasses::link
== pseudoClass
->mAtom
));
1513 result
= PR_FALSE
; // not a link
1516 else if (nsCSSPseudoClasses::checked
== pseudoClass
->mAtom
) {
1517 // This pseudoclass matches the selected state on the following elements:
1519 // <input type=checkbox>
1520 // <input type=radio>
1521 stateToCheck
= NS_EVENT_STATE_CHECKED
;
1523 else if (nsCSSPseudoClasses::enabled
== pseudoClass
->mAtom
) {
1524 stateToCheck
= NS_EVENT_STATE_ENABLED
;
1526 else if (nsCSSPseudoClasses::disabled
== pseudoClass
->mAtom
) {
1527 stateToCheck
= NS_EVENT_STATE_DISABLED
;
1529 else if (nsCSSPseudoClasses::mozBroken
== pseudoClass
->mAtom
) {
1530 stateToCheck
= NS_EVENT_STATE_BROKEN
;
1532 else if (nsCSSPseudoClasses::mozUserDisabled
== pseudoClass
->mAtom
) {
1533 stateToCheck
= NS_EVENT_STATE_USERDISABLED
;
1535 else if (nsCSSPseudoClasses::mozSuppressed
== pseudoClass
->mAtom
) {
1536 stateToCheck
= NS_EVENT_STATE_SUPPRESSED
;
1538 else if (nsCSSPseudoClasses::mozLoading
== pseudoClass
->mAtom
) {
1539 stateToCheck
= NS_EVENT_STATE_LOADING
;
1541 else if (nsCSSPseudoClasses::mozTypeUnsupported
== pseudoClass
->mAtom
) {
1542 stateToCheck
= NS_EVENT_STATE_TYPE_UNSUPPORTED
;
1544 else if (nsCSSPseudoClasses::mozHandlerDisabled
== pseudoClass
->mAtom
) {
1545 stateToCheck
= NS_EVENT_STATE_HANDLER_DISABLED
;
1547 else if (nsCSSPseudoClasses::mozHandlerBlocked
== pseudoClass
->mAtom
) {
1548 stateToCheck
= NS_EVENT_STATE_HANDLER_BLOCKED
;
1550 else if (nsCSSPseudoClasses::defaultPseudo
== pseudoClass
->mAtom
) {
1551 stateToCheck
= NS_EVENT_STATE_DEFAULT
;
1553 else if (nsCSSPseudoClasses::required
== pseudoClass
->mAtom
) {
1554 stateToCheck
= NS_EVENT_STATE_REQUIRED
;
1556 else if (nsCSSPseudoClasses::optional
== pseudoClass
->mAtom
) {
1557 stateToCheck
= NS_EVENT_STATE_OPTIONAL
;
1559 else if (nsCSSPseudoClasses::valid
== pseudoClass
->mAtom
) {
1560 stateToCheck
= NS_EVENT_STATE_VALID
;
1562 else if (nsCSSPseudoClasses::invalid
== pseudoClass
->mAtom
) {
1563 stateToCheck
= NS_EVENT_STATE_INVALID
;
1565 else if (nsCSSPseudoClasses::inRange
== pseudoClass
->mAtom
) {
1566 stateToCheck
= NS_EVENT_STATE_INRANGE
;
1568 else if (nsCSSPseudoClasses::outOfRange
== pseudoClass
->mAtom
) {
1569 stateToCheck
= NS_EVENT_STATE_OUTOFRANGE
;
1571 else if (nsCSSPseudoClasses::mozReadOnly
== pseudoClass
->mAtom
) {
1572 stateToCheck
= NS_EVENT_STATE_MOZ_READONLY
;
1574 else if (nsCSSPseudoClasses::mozReadWrite
== pseudoClass
->mAtom
) {
1575 stateToCheck
= NS_EVENT_STATE_MOZ_READWRITE
;
1577 else if (nsCSSPseudoClasses::mozIsHTML
== pseudoClass
->mAtom
) {
1578 result
= data
.mIsHTMLContent
&&
1579 data
.mContent
->GetNameSpaceID() == kNameSpaceID_None
;
1582 else if (nsCSSPseudoClasses::mozMathIncrementScriptLevel
== pseudoClass
->mAtom
) {
1583 stateToCheck
= NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL
;
1587 NS_ERROR("CSS parser parsed a pseudo-class that we do not handle");
1588 result
= PR_FALSE
; // unknown pseudo class
1591 // check if the element is event-sensitive for :hover and :active
1592 if ((stateToCheck
& (NS_EVENT_STATE_HOVER
| NS_EVENT_STATE_ACTIVE
)) &&
1593 data
.mCompatMode
== eCompatibility_NavQuirks
&&
1594 // global selector (but don't check .class):
1595 !aSelector
->mTag
&& !aSelector
->mIDList
&& !aSelector
->mAttrList
&&
1596 // This (or the other way around) both make :not() asymmetric
1597 // in quirks mode (and it's hard to work around since we're
1598 // testing the current mNegations, not the first
1599 // (unnegated)). This at least makes it closer to the spec.
1601 // important for |IsQuirkEventSensitive|:
1602 data
.mIsHTMLContent
&& !data
.mIsLink
&&
1603 !IsQuirkEventSensitive(data
.mContentTag
)) {
1604 // In quirks mode, only make certain elements sensitive to
1605 // selectors ":hover" and ":active".
1608 if (aStateMask
& stateToCheck
) {
1611 *aDependence
= PR_TRUE
;
1613 result
= (0 != (data
.mEventState
& stateToCheck
));
1619 if (result
&& aSelector
->mAttrList
) {
1620 // test for attribute match
1621 if (!data
.mHasAttributes
&& !aAttribute
) {
1622 // if no attributes on the content, no match
1625 NS_ASSERTION(data
.mContent
,
1626 "Must have content if either data.mHasAttributes or "
1627 "aAttribute is set!");
1629 nsAttrSelector
* attr
= aSelector
->mAttrList
;
1631 if (attr
->mAttr
== aAttribute
) {
1632 // XXX we should really have a namespace, not just an attr
1633 // name, in HasAttributeDependentStyle!
1636 *aDependence
= PR_TRUE
;
1638 else if (attr
->mNameSpace
== kNameSpaceID_Unknown
) {
1639 // Attr selector with a wildcard namespace. We have to examine all
1640 // the attributes on our content node.... This sort of selector is
1641 // essentially a boolean OR, over all namespaces, of equivalent attr
1642 // selectors with those namespaces. So to evaluate whether it
1643 // matches, evaluate for each namespace (the only namespaces that
1644 // have a chance at matching, of course, are ones that the element
1645 // actually has attributes in), short-circuiting if we ever match.
1646 PRUint32 attrCount
= data
.mContent
->GetAttrCount();
1648 for (PRUint32 i
= 0; i
< attrCount
; ++i
) {
1649 const nsAttrName
* attrName
=
1650 data
.mContent
->GetAttrNameAt(i
);
1651 NS_ASSERTION(attrName
, "GetAttrCount lied or GetAttrNameAt failed");
1652 if (attrName
->LocalName() != attr
->mAttr
) {
1655 if (attr
->mFunction
== NS_ATTR_FUNC_SET
) {
1662 data
.mContent
->GetAttr(attrName
->NamespaceID(),
1663 attrName
->LocalName(), value
);
1664 NS_ASSERTION(hasAttr
, "GetAttrNameAt lied");
1665 result
= AttrMatchesValue(attr
, value
);
1668 // At this point |result| has been set by us
1669 // explicitly in this loop. If it's PR_FALSE, we may still match
1670 // -- the content may have another attribute with the same name but
1671 // in a different namespace. But if it's PR_TRUE, we are done (we
1672 // can short-circuit the boolean OR described above).
1678 else if (attr
->mFunction
== NS_ATTR_FUNC_EQUALS
) {
1681 AttrValueIs(attr
->mNameSpace
, attr
->mAttr
, attr
->mValue
,
1682 attr
->mCaseSensitive
? eCaseMatters
: eIgnoreCase
);
1684 else if (!data
.mContent
->HasAttr(attr
->mNameSpace
, attr
->mAttr
)) {
1687 else if (attr
->mFunction
!= NS_ATTR_FUNC_SET
) {
1692 data
.mContent
->GetAttr(attr
->mNameSpace
, attr
->mAttr
, value
);
1693 NS_ASSERTION(hasAttr
, "HasAttr lied");
1694 result
= AttrMatchesValue(attr
, value
);
1698 } while (attr
&& result
);
1701 nsAtomList
* IDList
= aSelector
->mIDList
;
1702 if (result
&& IDList
) {
1703 // test for ID match
1706 if (aAttribute
&& aAttribute
== data
.mContent
->GetIDAttributeName()) {
1709 *aDependence
= PR_TRUE
;
1711 else if (nsnull
!= data
.mContentID
) {
1712 // case sensitivity: bug 93371
1713 const PRBool isCaseSensitive
=
1714 data
.mCompatMode
!= eCompatibility_NavQuirks
;
1717 if (isCaseSensitive
) {
1719 if (IDList
->mAtom
!= data
.mContentID
) {
1723 IDList
= IDList
->mNext
;
1727 data
.mContentID
->GetUTF8String(&id1Str
);
1728 nsDependentCString
id1(id1Str
);
1731 IDList
->mAtom
->GetUTF8String(&id2Str
);
1732 if (!id1
.Equals(id2Str
, nsCaseInsensitiveCStringComparator())) {
1736 IDList
= IDList
->mNext
;
1742 if (result
&& aSelector
->mClassList
) {
1743 // test for class match
1744 if (aAttribute
&& aAttribute
== data
.mContent
->GetClassAttributeName()) {
1747 *aDependence
= PR_TRUE
;
1750 // case sensitivity: bug 93371
1751 const PRBool isCaseSensitive
=
1752 data
.mCompatMode
!= eCompatibility_NavQuirks
;
1754 nsAtomList
* classList
= aSelector
->mClassList
;
1755 const nsAttrValue
*elementClasses
= data
.mClasses
;
1756 while (nsnull
!= classList
) {
1757 if (!(elementClasses
&& elementClasses
->Contains(classList
->mAtom
, isCaseSensitive
? eCaseMatters
: eIgnoreCase
))) {
1761 classList
= classList
->mNext
;
1766 // apply SelectorMatches to the negated selectors in the chain
1768 for (nsCSSSelector
*negation
= aSelector
->mNegations
;
1769 result
&& negation
; negation
= negation
->mNegations
) {
1770 PRBool dependence
= PR_FALSE
;
1771 result
= !SelectorMatches(data
, negation
, aStateMask
,
1772 aAttribute
, aForStyling
, &dependence
);
1773 // If the selector does match due to the dependence on aStateMask
1774 // or aAttribute, then we want to keep result true so that the
1775 // final result of SelectorMatches is true. Doing so tells
1776 // StateEnumFunc or AttributeEnumFunc that there is a dependence
1777 // on the state or attribute.
1778 result
= result
|| dependence
;
1786 // Right now, there are four operators:
1787 // PRUnichar(0), the descendant combinator, is greedy
1788 // '~', the indirect adjacent sibling combinator, is greedy
1789 // '+' and '>', the direct adjacent sibling and child combinators, are not
1790 #define NS_IS_GREEDY_OPERATOR(ch) (ch == PRUnichar(0) || ch == PRUnichar('~'))
1792 static PRBool
SelectorMatchesTree(RuleProcessorData
& aPrevData
,
1793 nsCSSSelector
* aSelector
,
1796 nsCSSSelector
* selector
= aSelector
;
1797 RuleProcessorData
* prevdata
= &aPrevData
;
1798 while (selector
) { // check compound selectors
1799 // If we don't already have a RuleProcessorData for the next
1800 // appropriate content (whether parent or previous sibling), create
1803 // for adjacent sibling combinators, the content to test against the
1804 // selector is the previous sibling *element*
1805 RuleProcessorData
* data
;
1806 if (PRUnichar('+') == selector
->mOperator
||
1807 PRUnichar('~') == selector
->mOperator
) {
1808 data
= prevdata
->mPreviousSiblingData
;
1810 nsIContent
* content
= prevdata
->mContent
;
1811 nsIContent
* parent
= content
->GetParent();
1813 parent
->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND
);
1815 PRInt32 index
= parent
->IndexOf(content
);
1816 while (0 <= --index
) {
1817 content
= parent
->GetChildAt(index
);
1818 if (content
->IsNodeOfType(nsINode::eELEMENT
)) {
1819 data
= RuleProcessorData::Create(prevdata
->mPresContext
, content
,
1820 prevdata
->mRuleWalker
,
1821 prevdata
->mCompatMode
);
1822 prevdata
->mPreviousSiblingData
= data
;
1829 // for descendant combinators and child combinators, the content
1830 // to test against is the parent
1832 data
= prevdata
->mParentData
;
1834 nsIContent
*content
= prevdata
->mContent
->GetParent();
1835 // GetParent could return a document fragment; we only want
1837 if (content
&& content
->IsNodeOfType(nsINode::eELEMENT
)) {
1838 data
= RuleProcessorData::Create(prevdata
->mPresContext
, content
,
1839 prevdata
->mRuleWalker
,
1840 prevdata
->mCompatMode
);
1841 prevdata
->mParentData
= data
;
1848 if (SelectorMatches(*data
, selector
, 0, nsnull
, aForStyling
)) {
1849 // to avoid greedy matching, we need to recur if this is a
1850 // descendant or general sibling combinator and the next
1851 // combinator is different, but we can make an exception for
1852 // sibling, then parent, since a sibling's parent is always the
1854 if ((NS_IS_GREEDY_OPERATOR(selector
->mOperator
)) &&
1855 (selector
->mNext
) &&
1856 (selector
->mNext
->mOperator
!= selector
->mOperator
) &&
1857 !(selector
->mOperator
== '~' &&
1858 selector
->mNext
->mOperator
== PRUnichar(0))) {
1860 // pretend the selector didn't match, and step through content
1861 // while testing the same selector
1863 // This approach is slightly strange in that when it recurs
1864 // it tests from the top of the content tree, down. This
1865 // doesn't matter much for performance since most selectors
1866 // don't match. (If most did, it might be faster...)
1867 if (SelectorMatchesTree(*data
, selector
, aForStyling
)) {
1871 selector
= selector
->mNext
;
1874 // for adjacent sibling and child combinators, if we didn't find
1875 // a match, we're done
1876 if (!NS_IS_GREEDY_OPERATOR(selector
->mOperator
)) {
1877 return PR_FALSE
; // parent was required to match
1882 return PR_TRUE
; // all the selectors matched.
1885 static void ContentEnumFunc(nsICSSStyleRule
* aRule
, nsCSSSelector
* aSelector
,
1888 ElementRuleProcessorData
* data
= (ElementRuleProcessorData
*)aData
;
1890 if (SelectorMatches(*data
, aSelector
, 0, nsnull
, PR_TRUE
)) {
1891 nsCSSSelector
*next
= aSelector
->mNext
;
1892 if (!next
|| SelectorMatchesTree(*data
, next
, PR_TRUE
)) {
1893 // for performance, require that every implementation of
1894 // nsICSSStyleRule return the same pointer for nsIStyleRule (why
1895 // would anything multiply inherit nsIStyleRule anyway?)
1897 nsCOMPtr
<nsIStyleRule
> iRule
= do_QueryInterface(aRule
);
1898 NS_ASSERTION(static_cast<nsIStyleRule
*>(aRule
) == iRule
.get(),
1899 "Please fix QI so this performance optimization is valid");
1901 data
->mRuleWalker
->Forward(static_cast<nsIStyleRule
*>(aRule
));
1902 // nsStyleSet will deal with the !important rule
1908 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData
*aData
)
1910 NS_PRECONDITION(aData
->mContent
->IsNodeOfType(nsINode::eELEMENT
),
1911 "content must be element");
1913 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
1916 cascade
->mRuleHash
.EnumerateAllRules(aData
->mNameSpaceID
,
1926 static void PseudoEnumFunc(nsICSSStyleRule
* aRule
, nsCSSSelector
* aSelector
,
1929 PseudoRuleProcessorData
* data
= (PseudoRuleProcessorData
*)aData
;
1931 NS_ASSERTION(aSelector
->mTag
== data
->mPseudoTag
, "RuleHash failure");
1932 PRBool matches
= PR_TRUE
;
1933 if (data
->mComparator
)
1934 data
->mComparator
->PseudoMatches(data
->mPseudoTag
, aSelector
, &matches
);
1937 nsCSSSelector
*selector
= aSelector
->mNext
;
1939 if (selector
) { // test next selector specially
1940 if (PRUnichar('+') == selector
->mOperator
) {
1941 return; // not valid here, can't match
1943 if (SelectorMatches(*data
, selector
, 0, nsnull
, PR_TRUE
)) {
1944 selector
= selector
->mNext
;
1947 if (PRUnichar('>') == selector
->mOperator
) {
1948 return; // immediate parent didn't match
1954 (! SelectorMatchesTree(*data
, selector
, PR_TRUE
))) {
1955 return; // remaining selectors didn't match
1958 // for performance, require that every implementation of
1959 // nsICSSStyleRule return the same pointer for nsIStyleRule (why
1960 // would anything multiply inherit nsIStyleRule anyway?)
1962 nsCOMPtr
<nsIStyleRule
> iRule
= do_QueryInterface(aRule
);
1963 NS_ASSERTION(static_cast<nsIStyleRule
*>(aRule
) == iRule
.get(),
1964 "Please fix QI so this performance optimization is valid");
1966 data
->mRuleWalker
->Forward(static_cast<nsIStyleRule
*>(aRule
));
1967 // nsStyleSet will deal with the !important rule
1972 nsCSSRuleProcessor::RulesMatching(PseudoRuleProcessorData
* aData
)
1974 NS_PRECONDITION(!aData
->mContent
||
1975 aData
->mContent
->IsNodeOfType(nsINode::eELEMENT
),
1976 "content (if present) must be element");
1978 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
1981 cascade
->mRuleHash
.EnumerateTagRules(aData
->mPseudoTag
,
1982 PseudoEnumFunc
, aData
);
1988 IsSiblingOperator(PRUnichar oper
)
1990 return oper
== PRUnichar('+') || oper
== PRUnichar('~');
1993 struct StateEnumData
{
1994 StateEnumData(StateRuleProcessorData
*aData
)
1995 : data(aData
), change(nsReStyleHint(0)) {}
1997 StateRuleProcessorData
*data
;
1998 nsReStyleHint change
;
2001 static PRBool
StateEnumFunc(void* aSelector
, void* aData
)
2003 StateEnumData
*enumData
= static_cast<StateEnumData
*>(aData
);
2004 StateRuleProcessorData
*data
= enumData
->data
;
2005 nsCSSSelector
* selector
= static_cast<nsCSSSelector
*>(aSelector
);
2007 nsReStyleHint possibleChange
= IsSiblingOperator(selector
->mOperator
) ?
2008 eReStyle_LaterSiblings
: eReStyle_Self
;
2010 // If enumData->change already includes all the bits of possibleChange, don't
2011 // bother calling SelectorMatches, since even if it returns false
2012 // enumData->change won't change.
2013 if ((possibleChange
& ~(enumData
->change
)) &&
2014 SelectorMatches(*data
, selector
, data
->mStateMask
, nsnull
, PR_TRUE
) &&
2015 SelectorMatchesTree(*data
, selector
->mNext
, PR_TRUE
)) {
2016 enumData
->change
= nsReStyleHint(enumData
->change
| possibleChange
);
2023 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData
* aData
,
2024 nsReStyleHint
* aResult
)
2026 NS_PRECONDITION(aData
->mContent
->IsNodeOfType(nsINode::eELEMENT
),
2027 "content must be element");
2029 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
2031 // Look up the content node in the state rule list, which points to
2032 // any (CSS2 definition) simple selector (whether or not it is the
2033 // subject) that has a state pseudo-class on it. This means that this
2034 // code will be matching selectors that aren't real selectors in any
2035 // stylesheet (e.g., if there is a selector "body > p:hover > a", then
2036 // "body > p:hover" will be in |cascade->mStateSelectors|). Note that
2037 // |IsStateSelector| below determines which selectors are in
2038 // |cascade->mStateSelectors|.
2039 StateEnumData
data(aData
);
2041 cascade
->mStateSelectors
.EnumerateForwards(StateEnumFunc
, &data
);
2042 *aResult
= data
.change
;
2046 struct AttributeEnumData
{
2047 AttributeEnumData(AttributeRuleProcessorData
*aData
)
2048 : data(aData
), change(nsReStyleHint(0)) {}
2050 AttributeRuleProcessorData
*data
;
2051 nsReStyleHint change
;
2055 static PRBool
AttributeEnumFunc(void* aSelector
, void* aData
)
2057 AttributeEnumData
*enumData
= static_cast<AttributeEnumData
*>(aData
);
2058 AttributeRuleProcessorData
*data
= enumData
->data
;
2059 nsCSSSelector
* selector
= static_cast<nsCSSSelector
*>(aSelector
);
2061 nsReStyleHint possibleChange
= IsSiblingOperator(selector
->mOperator
) ?
2062 eReStyle_LaterSiblings
: eReStyle_Self
;
2064 // If enumData->change already includes all the bits of possibleChange, don't
2065 // bother calling SelectorMatches, since even if it returns false
2066 // enumData->change won't change.
2067 if ((possibleChange
& ~(enumData
->change
)) &&
2068 SelectorMatches(*data
, selector
, data
->mStateMask
, data
->mAttribute
,
2070 SelectorMatchesTree(*data
, selector
->mNext
, PR_TRUE
)) {
2071 enumData
->change
= nsReStyleHint(enumData
->change
| possibleChange
);
2078 nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData
* aData
,
2079 nsReStyleHint
* aResult
)
2081 NS_PRECONDITION(aData
->mContent
->IsNodeOfType(nsINode::eELEMENT
),
2082 "content must be element");
2084 AttributeEnumData
data(aData
);
2086 // Since we always have :-moz-any-link (and almost always have :link
2087 // and :visited rules from prefs), rather than hacking AddRule below
2088 // to add |href| to the hash, we'll just handle it here.
2089 if (aData
->mAttribute
== nsGkAtoms::href
&&
2090 aData
->mIsHTMLContent
&&
2091 (aData
->mContentTag
== nsGkAtoms::a
||
2092 aData
->mContentTag
== nsGkAtoms::area
||
2093 aData
->mContentTag
== nsGkAtoms::link
)) {
2094 data
.change
= nsReStyleHint(data
.change
| eReStyle_Self
);
2096 // XXX What about XLinks?
2098 // XXX should really check the attribute namespace is XLink
2099 if (aData
->mAttribute
== nsGkAtoms::href
&&
2100 aData
->mNameSpaceID
== kNameSpaceID_SVG
&&
2101 aData
->mContentTag
== nsGkAtoms::a
) {
2102 data
.change
= nsReStyleHint(data
.change
| eReStyle_Self
);
2105 // XXXbz now that :link and :visited are also states, do we need a
2106 // similar optimization in HasStateDependentStyle?
2108 RuleCascadeData
* cascade
= GetRuleCascade(aData
->mPresContext
);
2110 // We do the same thing for attributes that we do for state selectors
2111 // (see HasStateDependentStyle), except that instead of one big list
2112 // we have a hashtable with a per-attribute list.
2115 if (aData
->mAttribute
== aData
->mContent
->GetIDAttributeName()) {
2116 cascade
->mIDSelectors
.EnumerateForwards(AttributeEnumFunc
, &data
);
2119 if (aData
->mAttribute
== aData
->mContent
->GetClassAttributeName()) {
2120 cascade
->mClassSelectors
.EnumerateForwards(AttributeEnumFunc
, &data
);
2123 AttributeSelectorEntry
*entry
= static_cast<AttributeSelectorEntry
*>
2124 (PL_DHashTableOperate(&cascade
->mAttributeSelectors
, aData
->mAttribute
,
2126 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
2127 entry
->mSelectors
->EnumerateForwards(AttributeEnumFunc
, &data
);
2131 *aResult
= data
.change
;
2136 nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext
* aPresContext
,
2137 PRBool
* aRulesChanged
)
2139 RuleCascadeData
*old
= mRuleCascades
;
2140 // We don't want to do anything if there aren't any sets of rules
2141 // cached yet (or somebody cleared them and is thus responsible for
2142 // rebuilding things), since we should not build the rule cascade too
2143 // early (e.g., before we know whether the quirk style sheet should be
2144 // enabled). And if there's nothing cached, it doesn't matter if
2145 // anything changed. See bug 448281.
2147 RefreshRuleCascade(aPresContext
);
2149 *aRulesChanged
= (old
!= mRuleCascades
);
2153 // Append all the currently-active font face rules to aArray. Return
2154 // true for success and false for failure.
2156 nsCSSRuleProcessor::AppendFontFaceRules(
2157 nsPresContext
*aPresContext
,
2158 nsTArray
<nsFontFaceRuleContainer
>& aArray
)
2160 RuleCascadeData
* cascade
= GetRuleCascade(aPresContext
);
2163 if (!aArray
.AppendElements(cascade
->mFontFaceRules
))
2171 nsCSSRuleProcessor::ClearRuleCascades()
2173 // We rely on our caller (perhaps indirectly) to do something that
2174 // will rebuild style data and the user font set (either
2175 // nsIPresShell::ReconstructStyleData or
2176 // nsPresContext::RebuildAllStyleData).
2177 RuleCascadeData
*data
= mRuleCascades
;
2178 mRuleCascades
= nsnull
;
2180 RuleCascadeData
*next
= data
->mNext
;
2188 // This function should return true only for selectors that need to be
2189 // checked by |HasStateDependentStyle|.
2191 PRBool
IsStateSelector(nsCSSSelector
& aSelector
)
2193 for (nsPseudoClassList
* pseudoClass
= aSelector
.mPseudoClassList
;
2194 pseudoClass
; pseudoClass
= pseudoClass
->mNext
) {
2195 if ((pseudoClass
->mAtom
== nsCSSPseudoClasses::active
) ||
2196 (pseudoClass
->mAtom
== nsCSSPseudoClasses::checked
) ||
2197 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozDragOver
) ||
2198 (pseudoClass
->mAtom
== nsCSSPseudoClasses::focus
) ||
2199 (pseudoClass
->mAtom
== nsCSSPseudoClasses::hover
) ||
2200 (pseudoClass
->mAtom
== nsCSSPseudoClasses::target
) ||
2201 (pseudoClass
->mAtom
== nsCSSPseudoClasses::link
) ||
2202 (pseudoClass
->mAtom
== nsCSSPseudoClasses::visited
) ||
2203 (pseudoClass
->mAtom
== nsCSSPseudoClasses::enabled
) ||
2204 (pseudoClass
->mAtom
== nsCSSPseudoClasses::disabled
) ||
2205 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozBroken
) ||
2206 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozUserDisabled
) ||
2207 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozSuppressed
) ||
2208 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozLoading
) ||
2209 (pseudoClass
->mAtom
== nsCSSPseudoClasses::required
) ||
2210 (pseudoClass
->mAtom
== nsCSSPseudoClasses::optional
) ||
2211 (pseudoClass
->mAtom
== nsCSSPseudoClasses::valid
) ||
2212 (pseudoClass
->mAtom
== nsCSSPseudoClasses::invalid
) ||
2213 (pseudoClass
->mAtom
== nsCSSPseudoClasses::inRange
) ||
2214 (pseudoClass
->mAtom
== nsCSSPseudoClasses::outOfRange
) ||
2215 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozReadOnly
) ||
2216 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozReadWrite
) ||
2218 (pseudoClass
->mAtom
== nsCSSPseudoClasses::mozMathIncrementScriptLevel
) ||
2220 (pseudoClass
->mAtom
== nsCSSPseudoClasses::defaultPseudo
)) {
2228 AddRule(RuleValue
* aRuleInfo
, void* aCascade
)
2230 RuleCascadeData
*cascade
= static_cast<RuleCascadeData
*>(aCascade
);
2232 // Build the rule hash.
2233 cascade
->mRuleHash
.PrependRule(aRuleInfo
);
2235 nsVoidArray
* stateArray
= &cascade
->mStateSelectors
;
2236 nsVoidArray
* classArray
= &cascade
->mClassSelectors
;
2237 nsVoidArray
* idArray
= &cascade
->mIDSelectors
;
2239 for (nsCSSSelector
* selector
= aRuleInfo
->mSelector
;
2240 selector
; selector
= selector
->mNext
) {
2241 // It's worth noting that this loop over negations isn't quite
2242 // optimal for two reasons. One, we could add something to one of
2243 // these lists twice, which means we'll check it twice, but I don't
2244 // think that's worth worrying about. (We do the same for multiple
2245 // attribute selectors on the same attribute.) Two, we don't really
2246 // need to check negations past the first in the current
2247 // implementation (and they're rare as well), but that might change
2248 // in the future if :not() is extended.
2249 for (nsCSSSelector
* negation
= selector
; negation
;
2250 negation
= negation
->mNegations
) {
2251 // Build mStateSelectors.
2252 if (IsStateSelector(*negation
))
2253 stateArray
->AppendElement(selector
);
2255 // Build mIDSelectors
2256 if (negation
->mIDList
) {
2257 idArray
->AppendElement(selector
);
2260 // Build mClassSelectors
2261 if (negation
->mClassList
) {
2262 classArray
->AppendElement(selector
);
2265 // Build mAttributeSelectors.
2266 for (nsAttrSelector
*attr
= negation
->mAttrList
; attr
;
2267 attr
= attr
->mNext
) {
2268 nsVoidArray
*array
= cascade
->AttributeListFor(attr
->mAttr
);
2271 array
->AppendElement(selector
);
2279 struct PerWeightData
{
2281 RuleValue
* mRules
; // linked list (reverse order)
2284 struct RuleByWeightEntry
: public PLDHashEntryHdr
{
2285 PerWeightData data
; // mWeight is key, mRules are value
2288 static PLDHashNumber
2289 HashIntKey(PLDHashTable
*table
, const void *key
)
2291 return PLDHashNumber(NS_PTR_TO_INT32(key
));
2295 MatchWeightEntry(PLDHashTable
*table
, const PLDHashEntryHdr
*hdr
,
2298 const RuleByWeightEntry
*entry
= (const RuleByWeightEntry
*)hdr
;
2299 return entry
->data
.mWeight
== NS_PTR_TO_INT32(key
);
2302 static PLDHashTableOps gRulesByWeightOps
= {
2307 PL_DHashMoveEntryStub
,
2308 PL_DHashClearEntryStub
,
2309 PL_DHashFinalizeStub
,
2313 struct CascadeEnumData
{
2314 CascadeEnumData(nsPresContext
* aPresContext
,
2315 nsTArray
<nsFontFaceRuleContainer
>& aFontFaceRules
,
2316 nsMediaQueryResultCacheKey
& aKey
,
2317 PLArenaPool
& aArena
,
2319 : mPresContext(aPresContext
),
2320 mFontFaceRules(aFontFaceRules
),
2323 mSheetType(aSheetType
)
2325 if (!PL_DHashTableInit(&mRulesByWeight
, &gRulesByWeightOps
, nsnull
,
2326 sizeof(RuleByWeightEntry
), 64))
2327 mRulesByWeight
.ops
= nsnull
;
2332 if (mRulesByWeight
.ops
)
2333 PL_DHashTableFinish(&mRulesByWeight
);
2336 nsPresContext
* mPresContext
;
2337 nsTArray
<nsFontFaceRuleContainer
>& mFontFaceRules
;
2338 nsMediaQueryResultCacheKey
& mCacheKey
;
2339 // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
2340 // provide a getter that gives me a *reference* to the value.
2341 PLDHashTable mRulesByWeight
; // of RuleValue* linked lists (?)
2342 PLArenaPool
& mArena
;
2347 * This enumerates style rules in a sheet (and recursively into any
2348 * grouping rules) in order to:
2349 * (1) add any style rules, in order, into data->mRulesByWeight (for
2350 * the primary CSS cascade), where they are separated by weight
2351 * but kept in order per-weight, and
2352 * (2) add any @font-face rules, in order, into data->mFontFaceRules.
2355 CascadeRuleEnumFunc(nsICSSRule
* aRule
, void* aData
)
2357 CascadeEnumData
* data
= (CascadeEnumData
*)aData
;
2358 PRInt32 type
= nsICSSRule::UNKNOWN_RULE
;
2359 aRule
->GetType(type
);
2361 if (nsICSSRule::STYLE_RULE
== type
) {
2362 nsICSSStyleRule
* styleRule
= (nsICSSStyleRule
*)aRule
;
2364 for (nsCSSSelectorList
*sel
= styleRule
->Selector();
2365 sel
; sel
= sel
->mNext
) {
2366 PRInt32 weight
= sel
->mWeight
;
2367 RuleByWeightEntry
*entry
= static_cast<RuleByWeightEntry
*>(
2368 PL_DHashTableOperate(&data
->mRulesByWeight
, NS_INT32_TO_PTR(weight
),
2372 entry
->data
.mWeight
= weight
;
2374 new (data
->mArena
) RuleValue(styleRule
, sel
->mSelectors
);
2375 // entry->data.mRules must be in backwards order.
2376 info
->mNext
= entry
->data
.mRules
;
2377 entry
->data
.mRules
= info
;
2380 else if (nsICSSRule::MEDIA_RULE
== type
||
2381 nsICSSRule::DOCUMENT_RULE
== type
) {
2382 nsICSSGroupRule
* groupRule
= (nsICSSGroupRule
*)aRule
;
2383 if (groupRule
->UseForPresentation(data
->mPresContext
, data
->mCacheKey
))
2384 if (!groupRule
->EnumerateRulesForwards(CascadeRuleEnumFunc
, aData
))
2387 else if (nsICSSRule::FONT_FACE_RULE
== type
) {
2388 nsCSSFontFaceRule
*fontFaceRule
= static_cast<nsCSSFontFaceRule
*>(aRule
);
2389 nsFontFaceRuleContainer
*ptr
= data
->mFontFaceRules
.AppendElement();
2392 ptr
->mRule
= fontFaceRule
;
2393 ptr
->mSheetType
= data
->mSheetType
;
2400 nsCSSRuleProcessor::CascadeSheetEnumFunc(nsICSSStyleSheet
* aSheet
, void* aData
)
2402 nsCSSStyleSheet
* sheet
= static_cast<nsCSSStyleSheet
*>(aSheet
);
2403 CascadeEnumData
* data
= static_cast<CascadeEnumData
*>(aData
);
2404 PRBool bSheetApplicable
= PR_TRUE
;
2405 sheet
->GetApplicable(bSheetApplicable
);
2407 if (bSheetApplicable
&&
2408 sheet
->UseForPresentation(data
->mPresContext
, data
->mCacheKey
) &&
2410 nsCSSStyleSheet
* child
= sheet
->mInner
->mFirstChild
;
2412 CascadeSheetEnumFunc(child
, data
);
2413 child
= child
->mNext
;
2416 if (!sheet
->mInner
->mOrderedRules
.EnumerateForwards(CascadeRuleEnumFunc
,
2423 static int CompareWeightData(const void* aArg1
, const void* aArg2
,
2426 const PerWeightData
* arg1
= static_cast<const PerWeightData
*>(aArg1
);
2427 const PerWeightData
* arg2
= static_cast<const PerWeightData
*>(aArg2
);
2428 return arg1
->mWeight
- arg2
->mWeight
; // put lower weight first
2432 struct FillWeightArrayData
{
2433 FillWeightArrayData(PerWeightData
* aArrayData
) :
2435 mWeightArray(aArrayData
)
2439 PerWeightData
* mWeightArray
;
2443 static PLDHashOperator
2444 FillWeightArray(PLDHashTable
*table
, PLDHashEntryHdr
*hdr
,
2445 PRUint32 number
, void *arg
)
2447 FillWeightArrayData
* data
= static_cast<FillWeightArrayData
*>(arg
);
2448 const RuleByWeightEntry
*entry
= (const RuleByWeightEntry
*)hdr
;
2450 data
->mWeightArray
[data
->mIndex
++] = entry
->data
;
2452 return PL_DHASH_NEXT
;
2456 nsCSSRuleProcessor::GetRuleCascade(nsPresContext
* aPresContext
)
2458 // If anything changes about the presentation context, we will be
2459 // notified. Otherwise, our cache is valid if mLastPresContext
2460 // matches aPresContext. (The only rule processors used for multiple
2461 // pres contexts are for XBL. These rule processors are probably less
2462 // likely to have @media rules, and thus the cache is pretty likely to
2463 // hit instantly even when we're switching between pres contexts.)
2465 if (!mRuleCascades
|| aPresContext
!= mLastPresContext
) {
2466 RefreshRuleCascade(aPresContext
);
2468 mLastPresContext
= aPresContext
;
2470 return mRuleCascades
;
2474 nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext
* aPresContext
)
2476 // Having RuleCascadeData objects be per-medium (over all variation
2477 // caused by media queries, handled through mCacheKey) works for now
2478 // since nsCSSRuleProcessor objects are per-document. (For a given
2479 // set of stylesheets they can vary based on medium (@media) or
2480 // document (@-moz-document).)
2482 for (RuleCascadeData
**cascadep
= &mRuleCascades
, *cascade
;
2483 (cascade
= *cascadep
); cascadep
= &cascade
->mNext
) {
2484 if (cascade
->mCacheKey
.Matches(aPresContext
)) {
2485 // Ensure that the current one is always mRuleCascades.
2486 *cascadep
= cascade
->mNext
;
2487 cascade
->mNext
= mRuleCascades
;
2488 mRuleCascades
= cascade
;
2494 if (mSheets
.Count() != 0) {
2495 nsAutoPtr
<RuleCascadeData
> newCascade(
2496 new RuleCascadeData(aPresContext
->Medium(),
2497 eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode()));
2499 CascadeEnumData
data(aPresContext
, newCascade
->mFontFaceRules
,
2500 newCascade
->mCacheKey
,
2501 newCascade
->mRuleHash
.Arena(),
2503 if (!data
.mRulesByWeight
.ops
)
2504 return; /* out of memory */
2505 if (!mSheets
.EnumerateForwards(CascadeSheetEnumFunc
, &data
))
2506 return; /* out of memory */
2508 // Sort the hash table of per-weight linked lists by weight.
2509 PRUint32 weightCount
= data
.mRulesByWeight
.entryCount
;
2510 nsAutoArrayPtr
<PerWeightData
> weightArray(new PerWeightData
[weightCount
]);
2511 FillWeightArrayData
fwData(weightArray
);
2512 PL_DHashTableEnumerate(&data
.mRulesByWeight
, FillWeightArray
, &fwData
);
2513 NS_QuickSort(weightArray
, weightCount
, sizeof(PerWeightData
),
2514 CompareWeightData
, nsnull
);
2516 // Put things into the rule hash backwards because it's easier to
2517 // build a singly linked list lowest-first that way.
2518 // The primary sort is by weight...
2519 PRUint32 i
= weightCount
;
2522 // and the secondary sort is by order. mRules are already backwards.
2523 RuleValue
*ruleValue
= weightArray
[i
].mRules
;
2525 // Calling |AddRule| reuses mNext!
2526 RuleValue
*next
= ruleValue
->mNext
;
2527 if (!AddRule(ruleValue
, newCascade
))
2528 return; /* out of memory */
2530 } while (ruleValue
);
2533 // Ensure that the current one is always mRuleCascades.
2534 newCascade
->mNext
= mRuleCascades
;
2535 mRuleCascades
= newCascade
.forget();
2542 nsCSSRuleProcessor::SelectorListMatches(RuleProcessorData
& aData
,
2543 nsCSSSelectorList
* aSelectorList
)
2545 while (aSelectorList
) {
2546 nsCSSSelector
* sel
= aSelectorList
->mSelectors
;
2547 NS_ASSERTION(sel
, "Should have *some* selectors");
2548 if (SelectorMatches(aData
, sel
, 0, nsnull
, PR_FALSE
)) {
2549 nsCSSSelector
* next
= sel
->mNext
;
2550 if (!next
|| SelectorMatchesTree(aData
, next
, PR_FALSE
)) {
2555 aSelectorList
= aSelectorList
->mNext
;