Bug 473680. Stop crashtest 458637-1.html early (returning success) if it's running...
[mozilla-central.git] / layout / style / nsCSSRuleProcessor.cpp
blob60f42cdfa574edc821000ba77d2ba0922bfae8b8
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
14 * License.
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.
23 * Contributor(s):
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)
52 #include "plarena.h"
54 #include "nsCRT.h"
55 #include "nsIAtom.h"
56 #include "pldhash.h"
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"
66 #include "nsString.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"
73 #include "nsCOMPtr.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"
82 #include "nsTArray.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;
96 struct RuleValue {
97 /**
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;
113 mNext = aNext;
114 return this;
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.
121 ~RuleValue()
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 {
128 void *mem;
129 PL_ARENA_ALLOCATE(mem, &aArena, aSize);
130 return mem;
133 nsICSSStyleRule* mRule;
134 nsCSSSelector* mSelector; // which of |mRule|'s selectors
135 PRInt32 mBackwardIndex; // High index means low weight/order.
136 RuleValue* mNext;
139 // ------------------------------
140 // Rule hash table
143 // Uses any of the sets of ops below.
144 struct RuleHashTableEntry : public PLDHashEntryHdr {
145 RuleValue *mRules; // linked list of |RuleValue|, null-terminated
148 static PLDHashNumber
149 RuleHash_CIHashKey(PLDHashTable *table, const void *key)
151 nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
153 nsAutoString str;
154 atom->ToString(str);
155 ToUpperCase(str);
156 return HashString(str);
159 typedef nsIAtom*
160 (* RuleHashGetKey) (PLDHashTable *table, const PLDHashEntryHdr *entry);
162 struct RuleHashTableOps {
163 PLDHashTableOps ops;
164 // Extra callback to avoid duplicating the matchEntry callback for
165 // each table. (There used to be a getKey callback in
166 // PLDHashTableOps.)
167 RuleHashGetKey getKey;
170 inline const RuleHashTableOps*
171 ToLocalOps(const PLDHashTableOps *aOps)
173 return (const RuleHashTableOps*)
174 (((const char*) aOps) - offsetof(RuleHashTableOps, ops));
177 static PRBool
178 RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
179 const void *key)
181 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
182 (key));
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)
188 return PR_TRUE;
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);
197 static PRBool
198 RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
199 const void *key)
201 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
202 (key));
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;
209 static nsIAtom*
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;
217 static nsIAtom*
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;
225 static nsIAtom*
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;
233 static PLDHashNumber
234 RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
236 return NS_PTR_TO_INT32(key);
239 static PRBool
240 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
241 const PLDHashEntryHdr *hdr,
242 const void *key)
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 = {
253 PL_DHashAllocTable,
254 PL_DHashFreeTable,
255 PL_DHashVoidPtrKeyStub,
256 RuleHash_CSMatchEntry,
257 PL_DHashMoveEntryStub,
258 PL_DHashClearEntryStub,
259 PL_DHashFinalizeStub,
260 NULL
262 RuleHash_TagTable_GetKey
265 // Case-sensitive ops.
266 static const RuleHashTableOps RuleHash_ClassTable_CSOps = {
268 PL_DHashAllocTable,
269 PL_DHashFreeTable,
270 PL_DHashVoidPtrKeyStub,
271 RuleHash_CSMatchEntry,
272 PL_DHashMoveEntryStub,
273 PL_DHashClearEntryStub,
274 PL_DHashFinalizeStub,
275 NULL
277 RuleHash_ClassTable_GetKey
280 // Case-insensitive ops.
281 static const RuleHashTableOps RuleHash_ClassTable_CIOps = {
283 PL_DHashAllocTable,
284 PL_DHashFreeTable,
285 RuleHash_CIHashKey,
286 RuleHash_CIMatchEntry,
287 PL_DHashMoveEntryStub,
288 PL_DHashClearEntryStub,
289 PL_DHashFinalizeStub,
290 NULL
292 RuleHash_ClassTable_GetKey
295 // Case-sensitive ops.
296 static const RuleHashTableOps RuleHash_IdTable_CSOps = {
298 PL_DHashAllocTable,
299 PL_DHashFreeTable,
300 PL_DHashVoidPtrKeyStub,
301 RuleHash_CSMatchEntry,
302 PL_DHashMoveEntryStub,
303 PL_DHashClearEntryStub,
304 PL_DHashFinalizeStub,
305 NULL
307 RuleHash_IdTable_GetKey
310 // Case-insensitive ops.
311 static const RuleHashTableOps RuleHash_IdTable_CIOps = {
313 PL_DHashAllocTable,
314 PL_DHashFreeTable,
315 RuleHash_CIHashKey,
316 RuleHash_CIMatchEntry,
317 PL_DHashMoveEntryStub,
318 PL_DHashClearEntryStub,
319 PL_DHashFinalizeStub,
320 NULL
322 RuleHash_IdTable_GetKey
325 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
326 PL_DHashAllocTable,
327 PL_DHashFreeTable,
328 RuleHash_NameSpaceTable_HashKey,
329 RuleHash_NameSpaceTable_MatchEntry,
330 PL_DHashMoveEntryStub,
331 PL_DHashClearEntryStub,
332 PL_DHashFinalizeStub,
333 NULL,
336 #undef RULE_HASH_STATS
337 #undef PRINT_UNIVERSAL_RULES
339 #ifdef DEBUG_dbaron
340 #define RULE_HASH_STATS
341 #define PRINT_UNIVERSAL_RULES
342 #endif
344 #ifdef RULE_HASH_STATS
345 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
346 #else
347 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
348 #endif
350 // Enumerator callback function.
351 typedef void (*RuleEnumFunc)(nsICSSStyleRule* aRule,
352 nsCSSSelector* aSelector,
353 void *aData);
355 class RuleHash {
356 public:
357 RuleHash(PRBool aQuirksMode);
358 ~RuleHash();
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; }
367 protected:
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
373 PRInt32 mRuleCount;
374 PLDHashTable mIdTable;
375 PLDHashTable mClassTable;
376 PLDHashTable mTagTable;
377 PLDHashTable mNameSpaceTable;
378 RuleValue *mUniversalRules;
380 RuleValue** mEnumList;
381 PRInt32 mEnumListSize;
383 PLArenaPool mArena;
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)
406 : mRuleCount(0),
407 mUniversalRules(nsnull),
408 mEnumList(nsnull), mEnumListSize(0)
409 #ifdef RULE_HASH_STATS
411 mUniversalSelectors(0),
412 mNameSpaceSelectors(0),
413 mTagSelectors(0),
414 mClassSelectors(0),
415 mIdSelectors(0),
416 mElementsMatched(0),
417 mPseudosMatched(0),
418 mElementUniversalCalls(0),
419 mElementNameSpaceCalls(0),
420 mElementTagCalls(0),
421 mElementClassCalls(0),
422 mElementIdCalls(0),
423 mPseudoTagCalls(0)
424 #endif
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
446 printf(
447 "RuleHash(%p):\n"
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,
455 mElementsMatched,
456 mPseudosMatched,
457 mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
458 mElementClassCalls, mElementIdCalls,
459 mPseudoTagCalls);
460 #ifdef PRINT_UNIVERSAL_RULES
462 RuleValue* value = mUniversalRules;
463 if (value) {
464 printf(" Universal rules:\n");
465 do {
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;
476 } while (value);
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) {
485 delete [] 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));
501 if (!entry)
502 return;
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_)
543 #else
544 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
545 PR_BEGIN_MACRO PR_END_MACRO
546 #endif
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
555 // time counting
556 PRInt32 testCount = classCount + 4;
558 if (mEnumListSize < testCount) {
559 delete [] mEnumList;
560 mEnumListSize = PR_MAX(testCount, MIN_ENUM_LIST_SIZE);
561 mEnumList = new RuleValue*[mEnumListSize];
564 PRInt32 valueCount = 0;
565 RULE_HASH_STAT_INCREMENT(mElementsMatched);
567 { // universal rules
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),
578 PL_DHASH_LOOKUP));
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);
594 if (nsnull != aID) {
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),
607 PL_DHASH_LOOKUP));
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) {
625 valueIndex = index;
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];
637 do {
638 (*aFunc)(value->mRule, value->mSelector, aData);
639 value = value->mNext;
640 } while (value);
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;
652 do {
653 RULE_HASH_STAT_INCREMENT(mPseudoTagCalls);
654 (*aFunc)(tagValue->mRule, tagValue->mSelector, aData);
655 tagValue = tagValue->mNext;
656 } while (tagValue);
660 //--------------------------------
662 // Attribute selectors hash table.
663 struct AttributeSelectorEntry : public PLDHashEntryHdr {
664 nsIAtom *mAttribute;
665 nsVoidArray *mSelectors;
668 static void
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 = {
677 PL_DHashAllocTable,
678 PL_DHashFreeTable,
679 PL_DHashVoidPtrKeyStub,
680 PL_DHashMatchEntryStub,
681 PL_DHashMoveEntryStub,
682 AttributeSelectorClearEntry,
683 PL_DHashFinalizeStub,
684 NULL
688 //--------------------------------
690 struct RuleCascadeData {
691 RuleCascadeData(nsIAtom *aMedium, PRBool aQuirksMode)
692 : mRuleHash(aQuirksMode),
693 mStateSelectors(),
694 mCacheKey(aMedium),
695 mNext(nsnull)
697 PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull,
698 sizeof(AttributeSelectorEntry), 16);
701 ~RuleCascadeData()
703 PL_DHashTableFinish(&mAttributeSelectors);
705 RuleHash mRuleHash;
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
721 nsVoidArray*
722 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
724 AttributeSelectorEntry *entry = static_cast<AttributeSelectorEntry*>
725 (PL_DHashTableOperate(&mAttributeSelectors, aAttribute, PL_DHASH_ADD));
726 if (!entry)
727 return nsnull;
728 if (!entry->mSelectors) {
729 if (!(entry->mSelectors = new nsVoidArray)) {
730 PL_DHashTableRawRemove(&mAttributeSelectors, entry);
731 return nsnull;
733 entry->mAttribute = aAttribute;
735 return entry->mSelectors;
738 // -------------------------------
739 // CSS Style rule processor implementation
742 nsCSSRuleProcessor::nsCSSRuleProcessor(const nsCOMArray<nsICSSStyleSheet>& aSheets,
743 PRUint8 aSheetType)
744 : mSheets(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);
757 mSheets.Clear();
758 ClearRuleCascades();
761 NS_IMPL_ISUPPORTS1(nsCSSRuleProcessor, nsIStyleRuleProcessor)
763 /* static */ void
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);
773 static PRBool
774 InitSystemMetrics()
776 NS_ASSERTION(!sSystemMetrics, "already initialized");
778 sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
779 NS_ENSURE_TRUE(sSystemMetrics, PR_FALSE);
781 nsresult rv;
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);
806 if (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"));
830 return PR_TRUE;
833 /* static */ void
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;
851 mContent = aContent;
852 mParentContent = nsnull;
853 mRuleWalker = aRuleWalker;
854 mScopedRoot = nsnull;
856 mContentTag = nsnull;
857 mContentID = nsnull;
858 mHasAttributes = PR_FALSE;
859 mIsHTMLContent = PR_FALSE;
860 mIsLink = PR_FALSE;
861 mLinkState = eLinkState_Unknown;
862 mEventState = 0;
863 mNameSpaceID = kNameSpaceID_Unknown;
864 mPreviousSiblingData = nsnull;
865 mParentData = nsnull;
866 mLanguage = nsnull;
867 mClasses = 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?
875 if (aCompat) {
876 mCompatMode = *aCompat;
877 } else if (NS_LIKELY(mPresContext)) {
878 mCompatMode = mPresContext->CompatibilityMode();
879 } else {
880 NS_ASSERTION(aContent, "Must have content");
881 NS_ASSERTION(aContent->GetOwnerDoc(), "Must have document");
882 mCompatMode = aContent->GetOwnerDoc()->GetCompatibilityMode();
885 if (aContent) {
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
893 if (mPresContext) {
894 mPresContext->EventStateManager()->GetContentState(aContent, mEventState);
895 } else {
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;
912 } else {
913 // get the namespace
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,
925 &mLinkState)) {
926 mIsLink = PR_TRUE;
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
932 if(!mIsLink &&
933 mHasAttributes &&
934 !(mIsHTMLContent || aContent->IsNodeOfType(nsINode::eXUL)) &&
935 nsStyleUtil::IsLink(aContent, linkHandler,
936 aRuleWalker != nsnull, &mLinkState)) {
937 mIsLink = PR_TRUE;
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);
956 do {
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;
970 if (d != this)
971 d->Destroy();
972 } while (destroyQueue.Count());
975 delete mLanguage;
978 const nsString* RuleProcessorData::GetLang()
980 if (!mLanguage) {
981 mLanguage = new nsString();
982 if (!mLanguage)
983 return nsnull;
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,
990 *mLanguage);
991 if (!hasAttr && content->IsNodeOfType(nsINode::eHTML)) {
992 hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang,
993 *mLanguage);
995 NS_ASSERTION(hasAttr || mLanguage->IsEmpty(),
996 "GetAttr that returns false should not make string non-empty");
997 if (hasAttr) {
998 break;
1003 return mLanguage;
1006 static inline PRInt32
1007 CSSNameSpaceID(nsIContent *aContent)
1009 return aContent->IsNodeOfType(nsINode::eHTML)
1010 ? kNameSpaceID_XHTML
1011 : aContent->GetNameSpaceID();
1014 PRInt32
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))
1025 return slot;
1027 if (mPreviousSiblingData &&
1028 (!aIsOfType ||
1029 (mPreviousSiblingData->mNameSpaceID == mNameSpaceID &&
1030 mPreviousSiblingData->mContentTag == mContentTag))) {
1031 slot = mPreviousSiblingData->mNthIndices[aIsOfType][aIsFromEnd];
1032 if (slot > 0) {
1033 slot += (aIsFromEnd ? -1 : 1);
1034 NS_ASSERTION(slot > 0, "How did that happen?");
1035 return slot;
1039 PRInt32 result = 1;
1040 nsIContent* parent = mParentContent;
1042 PRUint32 childCount;
1043 nsIContent * const * curChildPtr = parent->GetChildArray(&childCount);
1045 #ifdef DEBUG
1046 nsMutationGuard debugMutationGuard;
1047 #endif
1049 PRInt32 increment;
1050 nsIContent * const * stopPtr;
1051 if (aIsFromEnd) {
1052 stopPtr = curChildPtr - 1;
1053 curChildPtr = stopPtr + childCount;
1054 increment = -1;
1055 } else {
1056 increment = 1;
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
1064 break;
1066 nsIContent* child = *curChildPtr;
1067 if (child == mContent)
1068 break;
1069 if (child->IsNodeOfType(nsINode::eELEMENT) &&
1070 (!aIsOfType ||
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.
1076 result = -1;
1077 break;
1079 ++result;
1083 #ifdef DEBUG
1084 NS_ASSERTION(!debugMutationGuard.Mutated(0), "Unexpected mutations happened");
1085 #endif
1087 slot = result;
1088 return result;
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();
1098 while (p < p_end) {
1099 // skip leading space
1100 while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
1101 ++p;
1103 const PRUnichar *val_start = p;
1105 // look for space or end
1106 while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
1107 ++p;
1109 const PRUnichar *val_end = p;
1111 if (val_start < val_end &&
1112 aValue.Equals(Substring(val_start, val_end), aComparator))
1113 return PR_TRUE;
1115 ++p; // we know the next character is not whitespace
1117 return PR_FALSE;
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))
1166 return PR_FALSE;
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);
1186 default:
1187 NS_NOTREACHED("Shouldn't be ending up here");
1188 return PR_FALSE;
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
1211 PRBool aForStyling,
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
1220 return PR_FALSE;
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;
1241 if (parent) {
1242 if (setNodeFlags)
1243 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1245 PRInt32 index = -1;
1246 do {
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;
1257 if (parent) {
1258 if (setNodeFlags)
1259 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1261 PRUint32 index = parent->GetChildCount();
1262 do {
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;
1274 if (parent) {
1275 const PRBool checkFirst =
1276 pseudoClass->mAtom != nsCSSPseudoClasses::lastChild;
1277 const PRBool checkLast =
1278 pseudoClass->mAtom != nsCSSPseudoClasses::firstChild;
1279 if (setNodeFlags)
1280 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
1282 result = (!checkFirst ||
1283 data.GetNthIndex(PR_FALSE, PR_FALSE, PR_TRUE) == 1) &&
1284 (!checkLast ||
1285 data.GetNthIndex(PR_FALSE, PR_TRUE, PR_TRUE) == 1);
1286 } else {
1287 result = PR_FALSE;
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;
1295 if (parent) {
1296 PRBool isOfType =
1297 nsCSSPseudoClasses::nthOfType == pseudoClass->mAtom ||
1298 nsCSSPseudoClasses::nthLastOfType == pseudoClass->mAtom;
1299 PRBool isFromEnd =
1300 nsCSSPseudoClasses::nthLastChild == pseudoClass->mAtom ||
1301 nsCSSPseudoClasses::nthLastOfType == pseudoClass->mAtom;
1302 if (setNodeFlags) {
1303 if (isFromEnd)
1304 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1305 else
1306 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
1309 const PRInt32 index = data.GetNthIndex(isOfType, isFromEnd, PR_FALSE);
1310 if (index <= 0) {
1311 // Node is anonymous content (not really a child of its parent).
1312 result = PR_FALSE;
1313 } else {
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.
1318 if (a == 0) {
1319 result = b == index;
1320 } else {
1321 // Integer division in C does truncation (towards 0). So
1322 // check that the result is nonnegative, and that there was no
1323 // truncation.
1324 const PRInt32 n = (index - b) / a;
1325 result = n >= 0 && (a * n == index - b);
1328 } else {
1329 result = PR_FALSE;
1332 else if (nsCSSPseudoClasses::firstOfType == pseudoClass->mAtom ||
1333 nsCSSPseudoClasses::lastOfType == pseudoClass->mAtom ||
1334 nsCSSPseudoClasses::onlyOfType == pseudoClass->mAtom) {
1335 nsIContent *parent = data.mParentContent;
1336 if (parent) {
1337 const PRBool checkFirst =
1338 pseudoClass->mAtom != nsCSSPseudoClasses::lastOfType;
1339 const PRBool checkLast =
1340 pseudoClass->mAtom != nsCSSPseudoClasses::firstOfType;
1341 if (setNodeFlags) {
1342 if (checkLast)
1343 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
1344 else
1345 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_NOAPPEND);
1348 result = (!checkFirst ||
1349 data.GetNthIndex(PR_TRUE, PR_FALSE, PR_TRUE) == 1) &&
1350 (!checkLast ||
1351 data.GetNthIndex(PR_TRUE, PR_TRUE, PR_TRUE) == 1);
1352 } else {
1353 result = PR_FALSE;
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;
1362 PRInt32 index = -1;
1364 if (setNodeFlags)
1365 element->SetFlags(NODE_HAS_EMPTY_SELECTOR);
1367 do {
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;
1378 PRInt32 index = -1;
1380 if (setNodeFlags)
1381 element->SetFlags(NODE_HAS_SLOW_SELECTOR);
1383 do {
1384 child = element->GetChildAt(++index);
1385 } while (child &&
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()) {
1393 return PR_FALSE;
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;
1403 PRInt32 index = -1;
1405 result = PR_FALSE;
1406 if (element) {
1407 do {
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)) {
1413 result = PR_TRUE;
1414 break;
1416 } while (child);
1419 else if (nsCSSPseudoClasses::root == pseudoClass->mAtom) {
1420 result = (data.mParentContent == nsnull &&
1421 data.mContent &&
1422 data.mContent ==
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
1428 // outer one?
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");
1433 result = PR_FALSE;
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();
1447 if (doc) {
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
1451 // language codes.
1452 nsAutoString language;
1453 doc->GetContentLanguage(language);
1455 nsDependentString langString(pseudoClass->u.mString);
1456 language.StripWhitespace();
1457 PRInt32 begin = 0;
1458 PRInt32 len = language.Length();
1459 while (begin < len) {
1460 PRInt32 end = language.FindChar(PRUnichar(','), begin);
1461 if (end == kNotFound) {
1462 end = len;
1464 if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end-begin),
1465 langString,
1466 nsCaseInsensitiveStringComparator())) {
1467 result = PR_TRUE;
1468 break;
1470 begin = end + 1;
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)) {
1491 if (data.mIsLink) {
1492 if (nsCSSPseudoClasses::mozAnyLink == pseudoClass->mAtom) {
1493 result = PR_TRUE;
1495 else {
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) {
1503 result = PR_TRUE;
1504 if (aDependence)
1505 *aDependence = PR_TRUE;
1506 } else {
1507 result = ((eLinkState_Unvisited == data.mLinkState) ==
1508 (nsCSSPseudoClasses::link == pseudoClass->mAtom));
1512 else {
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:
1518 // <option>
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;
1581 #ifdef MOZ_MATHML
1582 else if (nsCSSPseudoClasses::mozMathIncrementScriptLevel == pseudoClass->mAtom) {
1583 stateToCheck = NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL;
1585 #endif
1586 else {
1587 NS_ERROR("CSS parser parsed a pseudo-class that we do not handle");
1588 result = PR_FALSE; // unknown pseudo class
1590 if (stateToCheck) {
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.
1600 !isNegated &&
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".
1606 result = PR_FALSE;
1607 } else {
1608 if (aStateMask & stateToCheck) {
1609 result = PR_TRUE;
1610 if (aDependence)
1611 *aDependence = PR_TRUE;
1612 } else {
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
1623 result = PR_FALSE;
1624 } else {
1625 NS_ASSERTION(data.mContent,
1626 "Must have content if either data.mHasAttributes or "
1627 "aAttribute is set!");
1628 result = PR_TRUE;
1629 nsAttrSelector* attr = aSelector->mAttrList;
1630 do {
1631 if (attr->mAttr == aAttribute) {
1632 // XXX we should really have a namespace, not just an attr
1633 // name, in HasAttributeDependentStyle!
1634 result = PR_TRUE;
1635 if (aDependence)
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();
1647 result = PR_FALSE;
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) {
1653 continue;
1655 if (attr->mFunction == NS_ATTR_FUNC_SET) {
1656 result = PR_TRUE;
1657 } else {
1658 nsAutoString value;
1659 #ifdef DEBUG
1660 PRBool hasAttr =
1661 #endif
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).
1673 if (result) {
1674 break;
1678 else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
1679 result =
1680 data.mContent->
1681 AttrValueIs(attr->mNameSpace, attr->mAttr, attr->mValue,
1682 attr->mCaseSensitive ? eCaseMatters : eIgnoreCase);
1684 else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) {
1685 result = PR_FALSE;
1687 else if (attr->mFunction != NS_ATTR_FUNC_SET) {
1688 nsAutoString value;
1689 #ifdef DEBUG
1690 PRBool hasAttr =
1691 #endif
1692 data.mContent->GetAttr(attr->mNameSpace, attr->mAttr, value);
1693 NS_ASSERTION(hasAttr, "HasAttr lied");
1694 result = AttrMatchesValue(attr, value);
1697 attr = attr->mNext;
1698 } while (attr && result);
1701 nsAtomList* IDList = aSelector->mIDList;
1702 if (result && IDList) {
1703 // test for ID match
1704 result = PR_FALSE;
1706 if (aAttribute && aAttribute == data.mContent->GetIDAttributeName()) {
1707 result = PR_TRUE;
1708 if (aDependence)
1709 *aDependence = PR_TRUE;
1711 else if (nsnull != data.mContentID) {
1712 // case sensitivity: bug 93371
1713 const PRBool isCaseSensitive =
1714 data.mCompatMode != eCompatibility_NavQuirks;
1716 result = PR_TRUE;
1717 if (isCaseSensitive) {
1718 do {
1719 if (IDList->mAtom != data.mContentID) {
1720 result = PR_FALSE;
1721 break;
1723 IDList = IDList->mNext;
1724 } while (IDList);
1725 } else {
1726 const char* id1Str;
1727 data.mContentID->GetUTF8String(&id1Str);
1728 nsDependentCString id1(id1Str);
1729 do {
1730 const char* id2Str;
1731 IDList->mAtom->GetUTF8String(&id2Str);
1732 if (!id1.Equals(id2Str, nsCaseInsensitiveCStringComparator())) {
1733 result = PR_FALSE;
1734 break;
1736 IDList = IDList->mNext;
1737 } while (IDList);
1742 if (result && aSelector->mClassList) {
1743 // test for class match
1744 if (aAttribute && aAttribute == data.mContent->GetClassAttributeName()) {
1745 result = PR_TRUE;
1746 if (aDependence)
1747 *aDependence = PR_TRUE;
1749 else {
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))) {
1758 result = PR_FALSE;
1759 break;
1761 classList = classList->mNext;
1766 // apply SelectorMatches to the negated selectors in the chain
1767 if (!isNegated) {
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;
1781 return result;
1784 #undef STATE_CHECK
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,
1794 PRBool aForStyling)
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
1801 // one.
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;
1809 if (!data) {
1810 nsIContent* content = prevdata->mContent;
1811 nsIContent* parent = content->GetParent();
1812 if (parent) {
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;
1823 break;
1829 // for descendant combinators and child combinators, the content
1830 // to test against is the parent
1831 else {
1832 data = prevdata->mParentData;
1833 if (!data) {
1834 nsIContent *content = prevdata->mContent->GetParent();
1835 // GetParent could return a document fragment; we only want
1836 // element parents.
1837 if (content && content->IsNodeOfType(nsINode::eELEMENT)) {
1838 data = RuleProcessorData::Create(prevdata->mPresContext, content,
1839 prevdata->mRuleWalker,
1840 prevdata->mCompatMode);
1841 prevdata->mParentData = data;
1845 if (! data) {
1846 return PR_FALSE;
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
1853 // same.
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)) {
1868 return PR_TRUE;
1871 selector = selector->mNext;
1873 else {
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
1880 prevdata = data;
1882 return PR_TRUE; // all the selectors matched.
1885 static void ContentEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
1886 void* aData)
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?)
1896 #ifdef DEBUG
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");
1900 #endif
1901 data->mRuleWalker->Forward(static_cast<nsIStyleRule*>(aRule));
1902 // nsStyleSet will deal with the !important rule
1907 NS_IMETHODIMP
1908 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
1910 NS_PRECONDITION(aData->mContent->IsNodeOfType(nsINode::eELEMENT),
1911 "content must be element");
1913 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
1915 if (cascade) {
1916 cascade->mRuleHash.EnumerateAllRules(aData->mNameSpaceID,
1917 aData->mContentTag,
1918 aData->mContentID,
1919 aData->mClasses,
1920 ContentEnumFunc,
1921 aData);
1923 return NS_OK;
1926 static void PseudoEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
1927 void* aData)
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);
1936 if (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;
1946 else {
1947 if (PRUnichar('>') == selector->mOperator) {
1948 return; // immediate parent didn't match
1953 if (selector &&
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?)
1961 #ifdef DEBUG
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");
1965 #endif
1966 data->mRuleWalker->Forward(static_cast<nsIStyleRule*>(aRule));
1967 // nsStyleSet will deal with the !important rule
1971 NS_IMETHODIMP
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);
1980 if (cascade) {
1981 cascade->mRuleHash.EnumerateTagRules(aData->mPseudoTag,
1982 PseudoEnumFunc, aData);
1984 return NS_OK;
1987 inline PRBool
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);
2019 return PR_TRUE;
2022 NS_IMETHODIMP
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);
2040 if (cascade)
2041 cascade->mStateSelectors.EnumerateForwards(StateEnumFunc, &data);
2042 *aResult = data.change;
2043 return NS_OK;
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,
2069 PR_TRUE) &&
2070 SelectorMatchesTree(*data, selector->mNext, PR_TRUE)) {
2071 enumData->change = nsReStyleHint(enumData->change | possibleChange);
2074 return PR_TRUE;
2077 NS_IMETHODIMP
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?
2097 #ifdef MOZ_SVG
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);
2104 #endif
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.
2114 if (cascade) {
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,
2125 PL_DHASH_LOOKUP));
2126 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
2127 entry->mSelectors->EnumerateForwards(AttributeEnumFunc, &data);
2131 *aResult = data.change;
2132 return NS_OK;
2135 NS_IMETHODIMP
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.
2146 if (old) {
2147 RefreshRuleCascade(aPresContext);
2149 *aRulesChanged = (old != mRuleCascades);
2150 return NS_OK;
2153 // Append all the currently-active font face rules to aArray. Return
2154 // true for success and false for failure.
2155 PRBool
2156 nsCSSRuleProcessor::AppendFontFaceRules(
2157 nsPresContext *aPresContext,
2158 nsTArray<nsFontFaceRuleContainer>& aArray)
2160 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
2162 if (cascade) {
2163 if (!aArray.AppendElements(cascade->mFontFaceRules))
2164 return PR_FALSE;
2167 return PR_TRUE;
2170 nsresult
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;
2179 while (data) {
2180 RuleCascadeData *next = data->mNext;
2181 delete data;
2182 data = next;
2184 return NS_OK;
2188 // This function should return true only for selectors that need to be
2189 // checked by |HasStateDependentStyle|.
2190 inline
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) ||
2217 #ifdef MOZ_MATHML
2218 (pseudoClass->mAtom == nsCSSPseudoClasses::mozMathIncrementScriptLevel) ||
2219 #endif
2220 (pseudoClass->mAtom == nsCSSPseudoClasses::defaultPseudo)) {
2221 return PR_TRUE;
2224 return PR_FALSE;
2227 static PRBool
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);
2269 if (!array)
2270 return PR_FALSE;
2271 array->AppendElement(selector);
2276 return PR_TRUE;
2279 struct PerWeightData {
2280 PRInt32 mWeight;
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));
2294 static PRBool
2295 MatchWeightEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
2296 const void *key)
2298 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
2299 return entry->data.mWeight == NS_PTR_TO_INT32(key);
2302 static PLDHashTableOps gRulesByWeightOps = {
2303 PL_DHashAllocTable,
2304 PL_DHashFreeTable,
2305 HashIntKey,
2306 MatchWeightEntry,
2307 PL_DHashMoveEntryStub,
2308 PL_DHashClearEntryStub,
2309 PL_DHashFinalizeStub,
2310 NULL
2313 struct CascadeEnumData {
2314 CascadeEnumData(nsPresContext* aPresContext,
2315 nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
2316 nsMediaQueryResultCacheKey& aKey,
2317 PLArenaPool& aArena,
2318 PRUint8 aSheetType)
2319 : mPresContext(aPresContext),
2320 mFontFaceRules(aFontFaceRules),
2321 mCacheKey(aKey),
2322 mArena(aArena),
2323 mSheetType(aSheetType)
2325 if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nsnull,
2326 sizeof(RuleByWeightEntry), 64))
2327 mRulesByWeight.ops = nsnull;
2330 ~CascadeEnumData()
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;
2343 PRUint8 mSheetType;
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.
2354 static PRBool
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),
2369 PL_DHASH_ADD));
2370 if (!entry)
2371 return PR_FALSE;
2372 entry->data.mWeight = weight;
2373 RuleValue *info =
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))
2385 return PR_FALSE;
2387 else if (nsICSSRule::FONT_FACE_RULE == type) {
2388 nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
2389 nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
2390 if (!ptr)
2391 return PR_FALSE;
2392 ptr->mRule = fontFaceRule;
2393 ptr->mSheetType = data->mSheetType;
2396 return PR_TRUE;
2399 /* static */ PRBool
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) &&
2409 sheet->mInner) {
2410 nsCSSStyleSheet* child = sheet->mInner->mFirstChild;
2411 while (child) {
2412 CascadeSheetEnumFunc(child, data);
2413 child = child->mNext;
2416 if (!sheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,
2417 data))
2418 return PR_FALSE;
2420 return PR_TRUE;
2423 static int CompareWeightData(const void* aArg1, const void* aArg2,
2424 void* closure)
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) :
2434 mIndex(0),
2435 mWeightArray(aArrayData)
2438 PRInt32 mIndex;
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;
2455 RuleCascadeData*
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;
2473 void
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;
2490 return;
2494 if (mSheets.Count() != 0) {
2495 nsAutoPtr<RuleCascadeData> newCascade(
2496 new RuleCascadeData(aPresContext->Medium(),
2497 eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
2498 if (newCascade) {
2499 CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
2500 newCascade->mCacheKey,
2501 newCascade->mRuleHash.Arena(),
2502 mSheetType);
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;
2520 while (i > 0) {
2521 --i;
2522 // and the secondary sort is by order. mRules are already backwards.
2523 RuleValue *ruleValue = weightArray[i].mRules;
2524 do {
2525 // Calling |AddRule| reuses mNext!
2526 RuleValue *next = ruleValue->mNext;
2527 if (!AddRule(ruleValue, newCascade))
2528 return; /* out of memory */
2529 ruleValue = next;
2530 } while (ruleValue);
2533 // Ensure that the current one is always mRuleCascades.
2534 newCascade->mNext = mRuleCascades;
2535 mRuleCascades = newCascade.forget();
2538 return;
2541 /* static */ PRBool
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)) {
2551 return PR_TRUE;
2555 aSelectorList = aSelectorList->mNext;
2558 return PR_FALSE;