layout/style/nsCSSRuleProcessor.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * style rule processor for CSS style sheets, responsible for selector
michael@0 9 * matching and cascading
michael@0 10 */
michael@0 11
michael@0 12 #define PL_ARENA_CONST_ALIGN_MASK 7
michael@0 13 // We want page-sized arenas so there's no fragmentation involved.
michael@0 14 // Including plarena.h must come first to avoid it being included by some
michael@0 15 // header file thereby making PL_ARENA_CONST_ALIGN_MASK ineffective.
michael@0 16 #define NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE (4096)
michael@0 17 #include "plarena.h"
michael@0 18
michael@0 19 #include "nsCSSRuleProcessor.h"
michael@0 20 #include "nsRuleProcessorData.h"
michael@0 21 #include <algorithm>
michael@0 22 #include "nsIAtom.h"
michael@0 23 #include "pldhash.h"
michael@0 24 #include "nsICSSPseudoComparator.h"
michael@0 25 #include "mozilla/MemoryReporting.h"
michael@0 26 #include "mozilla/css/StyleRule.h"
michael@0 27 #include "mozilla/css/GroupRule.h"
michael@0 28 #include "nsIDocument.h"
michael@0 29 #include "nsPresContext.h"
michael@0 30 #include "nsGkAtoms.h"
michael@0 31 #include "nsUnicharUtils.h"
michael@0 32 #include "nsError.h"
michael@0 33 #include "nsRuleWalker.h"
michael@0 34 #include "nsCSSPseudoClasses.h"
michael@0 35 #include "nsCSSPseudoElements.h"
michael@0 36 #include "nsIContent.h"
michael@0 37 #include "nsCOMPtr.h"
michael@0 38 #include "nsHashKeys.h"
michael@0 39 #include "nsStyleUtil.h"
michael@0 40 #include "nsQuickSort.h"
michael@0 41 #include "nsAttrValue.h"
michael@0 42 #include "nsAttrValueInlines.h"
michael@0 43 #include "nsAttrName.h"
michael@0 44 #include "nsTArray.h"
michael@0 45 #include "nsContentUtils.h"
michael@0 46 #include "nsIMediaList.h"
michael@0 47 #include "nsCSSRules.h"
michael@0 48 #include "nsStyleSet.h"
michael@0 49 #include "mozilla/dom/Element.h"
michael@0 50 #include "nsNthIndexCache.h"
michael@0 51 #include "mozilla/ArrayUtils.h"
michael@0 52 #include "mozilla/EventStates.h"
michael@0 53 #include "mozilla/Preferences.h"
michael@0 54 #include "mozilla/LookAndFeel.h"
michael@0 55 #include "mozilla/Likely.h"
michael@0 56
michael@0 57 using namespace mozilla;
michael@0 58 using namespace mozilla::dom;
michael@0 59
michael@0 60 #define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
michael@0 61
michael@0 62 static bool gSupportVisitedPseudo = true;
michael@0 63
michael@0 64 static nsTArray< nsCOMPtr<nsIAtom> >* sSystemMetrics = 0;
michael@0 65
michael@0 66 #ifdef XP_WIN
michael@0 67 uint8_t nsCSSRuleProcessor::sWinThemeId = LookAndFeel::eWindowsTheme_Generic;
michael@0 68 #endif
michael@0 69
michael@0 70 /**
michael@0 71 * A struct representing a given CSS rule and a particular selector
michael@0 72 * from that rule's selector list.
michael@0 73 */
michael@0 74 struct RuleSelectorPair {
michael@0 75 RuleSelectorPair(css::StyleRule* aRule, nsCSSSelector* aSelector)
michael@0 76 : mRule(aRule), mSelector(aSelector) {}
michael@0 77 // If this class ever grows a destructor, deal with
michael@0 78 // PerWeightDataListItem appropriately.
michael@0 79
michael@0 80 css::StyleRule* mRule;
michael@0 81 nsCSSSelector* mSelector; // which of |mRule|'s selectors
michael@0 82 };
michael@0 83
michael@0 84 #define NS_IS_ANCESTOR_OPERATOR(ch) \
michael@0 85 ((ch) == char16_t(' ') || (ch) == char16_t('>'))
michael@0 86
michael@0 87 /**
michael@0 88 * A struct representing a particular rule in an ordered list of rules
michael@0 89 * (the ordering depending on the weight of mSelector and the order of
michael@0 90 * our rules to start with).
michael@0 91 */
michael@0 92 struct RuleValue : RuleSelectorPair {
michael@0 93 enum {
michael@0 94 eMaxAncestorHashes = 4
michael@0 95 };
michael@0 96
michael@0 97 RuleValue(const RuleSelectorPair& aRuleSelectorPair, int32_t aIndex,
michael@0 98 bool aQuirksMode) :
michael@0 99 RuleSelectorPair(aRuleSelectorPair),
michael@0 100 mIndex(aIndex)
michael@0 101 {
michael@0 102 CollectAncestorHashes(aQuirksMode);
michael@0 103 }
michael@0 104
michael@0 105 int32_t mIndex; // High index means high weight/order.
michael@0 106 uint32_t mAncestorSelectorHashes[eMaxAncestorHashes];
michael@0 107
michael@0 108 private:
michael@0 109 void CollectAncestorHashes(bool aQuirksMode) {
michael@0 110 // Collect up our mAncestorSelectorHashes. It's not clear whether it's
michael@0 111 // better to stop once we've found eMaxAncestorHashes of them or to keep
michael@0 112 // going and preferentially collect information from selectors higher up the
michael@0 113 // chain... Let's do the former for now.
michael@0 114 size_t hashIndex = 0;
michael@0 115 for (nsCSSSelector* sel = mSelector->mNext; sel; sel = sel->mNext) {
michael@0 116 if (!NS_IS_ANCESTOR_OPERATOR(sel->mOperator)) {
michael@0 117 // |sel| is going to select something that's not actually one of our
michael@0 118 // ancestors, so don't add it to mAncestorSelectorHashes. But keep
michael@0 119 // going, because it'll select a sibling of one of our ancestors, so its
michael@0 120 // ancestors would be our ancestors too.
michael@0 121 continue;
michael@0 122 }
michael@0 123
michael@0 124 // Now sel is supposed to select one of our ancestors. Grab
michael@0 125 // whatever info we can from it into mAncestorSelectorHashes.
michael@0 126 // But in qurks mode, don't grab IDs and classes because those
michael@0 127 // need to be matched case-insensitively.
michael@0 128 if (!aQuirksMode) {
michael@0 129 nsAtomList* ids = sel->mIDList;
michael@0 130 while (ids) {
michael@0 131 mAncestorSelectorHashes[hashIndex++] = ids->mAtom->hash();
michael@0 132 if (hashIndex == eMaxAncestorHashes) {
michael@0 133 return;
michael@0 134 }
michael@0 135 ids = ids->mNext;
michael@0 136 }
michael@0 137
michael@0 138 nsAtomList* classes = sel->mClassList;
michael@0 139 while (classes) {
michael@0 140 mAncestorSelectorHashes[hashIndex++] = classes->mAtom->hash();
michael@0 141 if (hashIndex == eMaxAncestorHashes) {
michael@0 142 return;
michael@0 143 }
michael@0 144 classes = classes->mNext;
michael@0 145 }
michael@0 146 }
michael@0 147
michael@0 148 // Only put in the tag name if it's all-lowercase. Otherwise we run into
michael@0 149 // trouble because we may test the wrong one of mLowercaseTag and
michael@0 150 // mCasedTag against the filter.
michael@0 151 if (sel->mLowercaseTag && sel->mCasedTag == sel->mLowercaseTag) {
michael@0 152 mAncestorSelectorHashes[hashIndex++] = sel->mLowercaseTag->hash();
michael@0 153 if (hashIndex == eMaxAncestorHashes) {
michael@0 154 return;
michael@0 155 }
michael@0 156 }
michael@0 157 }
michael@0 158
michael@0 159 while (hashIndex != eMaxAncestorHashes) {
michael@0 160 mAncestorSelectorHashes[hashIndex++] = 0;
michael@0 161 }
michael@0 162 }
michael@0 163 };
michael@0 164
michael@0 165 // ------------------------------
michael@0 166 // Rule hash table
michael@0 167 //
michael@0 168
michael@0 169 // Uses any of the sets of ops below.
michael@0 170 struct RuleHashTableEntry : public PLDHashEntryHdr {
michael@0 171 // If you add members that have heap allocated memory be sure to change the
michael@0 172 // logic in SizeOfRuleHashTableEntry().
michael@0 173 // Auto length 1, because we always have at least one entry in mRules.
michael@0 174 nsAutoTArray<RuleValue, 1> mRules;
michael@0 175 };
michael@0 176
michael@0 177 struct RuleHashTagTableEntry : public RuleHashTableEntry {
michael@0 178 // If you add members that have heap allocated memory be sure to change the
michael@0 179 // logic in RuleHash::SizeOf{In,Ex}cludingThis.
michael@0 180 nsCOMPtr<nsIAtom> mTag;
michael@0 181 };
michael@0 182
michael@0 183 static PLDHashNumber
michael@0 184 RuleHash_CIHashKey(PLDHashTable *table, const void *key)
michael@0 185 {
michael@0 186 nsIAtom *atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
michael@0 187
michael@0 188 nsAutoString str;
michael@0 189 atom->ToString(str);
michael@0 190 nsContentUtils::ASCIIToLower(str);
michael@0 191 return HashString(str);
michael@0 192 }
michael@0 193
michael@0 194 typedef nsIAtom*
michael@0 195 (* RuleHashGetKey) (PLDHashTable *table, const PLDHashEntryHdr *entry);
michael@0 196
michael@0 197 struct RuleHashTableOps {
michael@0 198 const PLDHashTableOps ops;
michael@0 199 // Extra callback to avoid duplicating the matchEntry callback for
michael@0 200 // each table. (There used to be a getKey callback in
michael@0 201 // PLDHashTableOps.)
michael@0 202 RuleHashGetKey getKey;
michael@0 203 };
michael@0 204
michael@0 205 inline const RuleHashTableOps*
michael@0 206 ToLocalOps(const PLDHashTableOps *aOps)
michael@0 207 {
michael@0 208 return (const RuleHashTableOps*)
michael@0 209 (((const char*) aOps) - offsetof(RuleHashTableOps, ops));
michael@0 210 }
michael@0 211
michael@0 212 static bool
michael@0 213 RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
michael@0 214 const void *key)
michael@0 215 {
michael@0 216 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
michael@0 217 (key));
michael@0 218 // Use our extra |getKey| callback to avoid code duplication.
michael@0 219 nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
michael@0 220
michael@0 221 // Check for case-sensitive match first.
michael@0 222 if (match_atom == entry_atom)
michael@0 223 return true;
michael@0 224
michael@0 225 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
michael@0 226 // in order to save on performance. This is only used in quirks mode
michael@0 227 // anyway.
michael@0 228
michael@0 229 return
michael@0 230 nsContentUtils::EqualsIgnoreASCIICase(nsDependentAtomString(entry_atom),
michael@0 231 nsDependentAtomString(match_atom));
michael@0 232 }
michael@0 233
michael@0 234 static bool
michael@0 235 RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
michael@0 236 const void *key)
michael@0 237 {
michael@0 238 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
michael@0 239 (key));
michael@0 240 // Use our extra |getKey| callback to avoid code duplication.
michael@0 241 nsIAtom *entry_atom = ToLocalOps(table->ops)->getKey(table, hdr);
michael@0 242
michael@0 243 return match_atom == entry_atom;
michael@0 244 }
michael@0 245
michael@0 246 static bool
michael@0 247 RuleHash_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 248 const void *key)
michael@0 249 {
michael@0 250 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
michael@0 251 new (entry) RuleHashTableEntry();
michael@0 252 return true;
michael@0 253 }
michael@0 254
michael@0 255 static void
michael@0 256 RuleHash_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
michael@0 257 {
michael@0 258 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(hdr);
michael@0 259 entry->~RuleHashTableEntry();
michael@0 260 }
michael@0 261
michael@0 262 static void
michael@0 263 RuleHash_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
michael@0 264 PLDHashEntryHdr *to)
michael@0 265 {
michael@0 266 NS_PRECONDITION(from != to, "This is not going to work!");
michael@0 267 RuleHashTableEntry *oldEntry =
michael@0 268 const_cast<RuleHashTableEntry*>(
michael@0 269 static_cast<const RuleHashTableEntry*>(from));
michael@0 270 RuleHashTableEntry *newEntry = new (to) RuleHashTableEntry();
michael@0 271 newEntry->mRules.SwapElements(oldEntry->mRules);
michael@0 272 oldEntry->~RuleHashTableEntry();
michael@0 273 }
michael@0 274
michael@0 275 static bool
michael@0 276 RuleHash_TagTable_MatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
michael@0 277 const void *key)
michael@0 278 {
michael@0 279 nsIAtom *match_atom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>
michael@0 280 (key));
michael@0 281 nsIAtom *entry_atom = static_cast<const RuleHashTagTableEntry*>(hdr)->mTag;
michael@0 282
michael@0 283 return match_atom == entry_atom;
michael@0 284 }
michael@0 285
michael@0 286 static bool
michael@0 287 RuleHash_TagTable_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 288 const void *key)
michael@0 289 {
michael@0 290 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
michael@0 291 new (entry) RuleHashTagTableEntry();
michael@0 292 entry->mTag = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
michael@0 293 return true;
michael@0 294 }
michael@0 295
michael@0 296 static void
michael@0 297 RuleHash_TagTable_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
michael@0 298 {
michael@0 299 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>(hdr);
michael@0 300 entry->~RuleHashTagTableEntry();
michael@0 301 }
michael@0 302
michael@0 303 static void
michael@0 304 RuleHash_TagTable_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
michael@0 305 PLDHashEntryHdr *to)
michael@0 306 {
michael@0 307 NS_PRECONDITION(from != to, "This is not going to work!");
michael@0 308 RuleHashTagTableEntry *oldEntry =
michael@0 309 const_cast<RuleHashTagTableEntry*>(
michael@0 310 static_cast<const RuleHashTagTableEntry*>(from));
michael@0 311 RuleHashTagTableEntry *newEntry = new (to) RuleHashTagTableEntry();
michael@0 312 newEntry->mTag.swap(oldEntry->mTag);
michael@0 313 newEntry->mRules.SwapElements(oldEntry->mRules);
michael@0 314 oldEntry->~RuleHashTagTableEntry();
michael@0 315 }
michael@0 316
michael@0 317 static nsIAtom*
michael@0 318 RuleHash_ClassTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
michael@0 319 {
michael@0 320 const RuleHashTableEntry *entry =
michael@0 321 static_cast<const RuleHashTableEntry*>(hdr);
michael@0 322 nsCSSSelector* selector = entry->mRules[0].mSelector;
michael@0 323 if (selector->IsPseudoElement()) {
michael@0 324 selector = selector->mNext;
michael@0 325 }
michael@0 326 return selector->mClassList->mAtom;
michael@0 327 }
michael@0 328
michael@0 329 static nsIAtom*
michael@0 330 RuleHash_IdTable_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
michael@0 331 {
michael@0 332 const RuleHashTableEntry *entry =
michael@0 333 static_cast<const RuleHashTableEntry*>(hdr);
michael@0 334 nsCSSSelector* selector = entry->mRules[0].mSelector;
michael@0 335 if (selector->IsPseudoElement()) {
michael@0 336 selector = selector->mNext;
michael@0 337 }
michael@0 338 return selector->mIDList->mAtom;
michael@0 339 }
michael@0 340
michael@0 341 static PLDHashNumber
michael@0 342 RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
michael@0 343 {
michael@0 344 return NS_PTR_TO_INT32(key);
michael@0 345 }
michael@0 346
michael@0 347 static bool
michael@0 348 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
michael@0 349 const PLDHashEntryHdr *hdr,
michael@0 350 const void *key)
michael@0 351 {
michael@0 352 const RuleHashTableEntry *entry =
michael@0 353 static_cast<const RuleHashTableEntry*>(hdr);
michael@0 354
michael@0 355 nsCSSSelector* selector = entry->mRules[0].mSelector;
michael@0 356 if (selector->IsPseudoElement()) {
michael@0 357 selector = selector->mNext;
michael@0 358 }
michael@0 359 return NS_PTR_TO_INT32(key) == selector->mNameSpace;
michael@0 360 }
michael@0 361
michael@0 362 static const PLDHashTableOps RuleHash_TagTable_Ops = {
michael@0 363 PL_DHashAllocTable,
michael@0 364 PL_DHashFreeTable,
michael@0 365 PL_DHashVoidPtrKeyStub,
michael@0 366 RuleHash_TagTable_MatchEntry,
michael@0 367 RuleHash_TagTable_MoveEntry,
michael@0 368 RuleHash_TagTable_ClearEntry,
michael@0 369 PL_DHashFinalizeStub,
michael@0 370 RuleHash_TagTable_InitEntry
michael@0 371 };
michael@0 372
michael@0 373 // Case-sensitive ops.
michael@0 374 static const RuleHashTableOps RuleHash_ClassTable_CSOps = {
michael@0 375 {
michael@0 376 PL_DHashAllocTable,
michael@0 377 PL_DHashFreeTable,
michael@0 378 PL_DHashVoidPtrKeyStub,
michael@0 379 RuleHash_CSMatchEntry,
michael@0 380 RuleHash_MoveEntry,
michael@0 381 RuleHash_ClearEntry,
michael@0 382 PL_DHashFinalizeStub,
michael@0 383 RuleHash_InitEntry
michael@0 384 },
michael@0 385 RuleHash_ClassTable_GetKey
michael@0 386 };
michael@0 387
michael@0 388 // Case-insensitive ops.
michael@0 389 static const RuleHashTableOps RuleHash_ClassTable_CIOps = {
michael@0 390 {
michael@0 391 PL_DHashAllocTable,
michael@0 392 PL_DHashFreeTable,
michael@0 393 RuleHash_CIHashKey,
michael@0 394 RuleHash_CIMatchEntry,
michael@0 395 RuleHash_MoveEntry,
michael@0 396 RuleHash_ClearEntry,
michael@0 397 PL_DHashFinalizeStub,
michael@0 398 RuleHash_InitEntry
michael@0 399 },
michael@0 400 RuleHash_ClassTable_GetKey
michael@0 401 };
michael@0 402
michael@0 403 // Case-sensitive ops.
michael@0 404 static const RuleHashTableOps RuleHash_IdTable_CSOps = {
michael@0 405 {
michael@0 406 PL_DHashAllocTable,
michael@0 407 PL_DHashFreeTable,
michael@0 408 PL_DHashVoidPtrKeyStub,
michael@0 409 RuleHash_CSMatchEntry,
michael@0 410 RuleHash_MoveEntry,
michael@0 411 RuleHash_ClearEntry,
michael@0 412 PL_DHashFinalizeStub,
michael@0 413 RuleHash_InitEntry
michael@0 414 },
michael@0 415 RuleHash_IdTable_GetKey
michael@0 416 };
michael@0 417
michael@0 418 // Case-insensitive ops.
michael@0 419 static const RuleHashTableOps RuleHash_IdTable_CIOps = {
michael@0 420 {
michael@0 421 PL_DHashAllocTable,
michael@0 422 PL_DHashFreeTable,
michael@0 423 RuleHash_CIHashKey,
michael@0 424 RuleHash_CIMatchEntry,
michael@0 425 RuleHash_MoveEntry,
michael@0 426 RuleHash_ClearEntry,
michael@0 427 PL_DHashFinalizeStub,
michael@0 428 RuleHash_InitEntry
michael@0 429 },
michael@0 430 RuleHash_IdTable_GetKey
michael@0 431 };
michael@0 432
michael@0 433 static const PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
michael@0 434 PL_DHashAllocTable,
michael@0 435 PL_DHashFreeTable,
michael@0 436 RuleHash_NameSpaceTable_HashKey,
michael@0 437 RuleHash_NameSpaceTable_MatchEntry,
michael@0 438 RuleHash_MoveEntry,
michael@0 439 RuleHash_ClearEntry,
michael@0 440 PL_DHashFinalizeStub,
michael@0 441 RuleHash_InitEntry
michael@0 442 };
michael@0 443
michael@0 444 #undef RULE_HASH_STATS
michael@0 445 #undef PRINT_UNIVERSAL_RULES
michael@0 446
michael@0 447 #ifdef RULE_HASH_STATS
michael@0 448 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
michael@0 449 #else
michael@0 450 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
michael@0 451 #endif
michael@0 452
michael@0 453 struct NodeMatchContext;
michael@0 454
michael@0 455 class RuleHash {
michael@0 456 public:
michael@0 457 RuleHash(bool aQuirksMode);
michael@0 458 ~RuleHash();
michael@0 459 void AppendRule(const RuleSelectorPair &aRuleInfo);
michael@0 460 void EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
michael@0 461 NodeMatchContext& aNodeMatchContext);
michael@0 462
michael@0 463 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 464 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 465
michael@0 466 protected:
michael@0 467 typedef nsTArray<RuleValue> RuleValueList;
michael@0 468 void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
michael@0 469 const RuleSelectorPair& aRuleInfo);
michael@0 470 void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
michael@0 471
michael@0 472 int32_t mRuleCount;
michael@0 473 // The hashtables are lazily initialized; we use a null .ops to
michael@0 474 // indicate that they need initialization.
michael@0 475 PLDHashTable mIdTable;
michael@0 476 PLDHashTable mClassTable;
michael@0 477 PLDHashTable mTagTable;
michael@0 478 PLDHashTable mNameSpaceTable;
michael@0 479 RuleValueList mUniversalRules;
michael@0 480
michael@0 481 struct EnumData {
michael@0 482 const RuleValue* mCurValue;
michael@0 483 const RuleValue* mEnd;
michael@0 484 };
michael@0 485 EnumData* mEnumList;
michael@0 486 int32_t mEnumListSize;
michael@0 487
michael@0 488 bool mQuirksMode;
michael@0 489
michael@0 490 inline EnumData ToEnumData(const RuleValueList& arr) {
michael@0 491 EnumData data = { arr.Elements(), arr.Elements() + arr.Length() };
michael@0 492 return data;
michael@0 493 }
michael@0 494
michael@0 495 #ifdef RULE_HASH_STATS
michael@0 496 uint32_t mUniversalSelectors;
michael@0 497 uint32_t mNameSpaceSelectors;
michael@0 498 uint32_t mTagSelectors;
michael@0 499 uint32_t mClassSelectors;
michael@0 500 uint32_t mIdSelectors;
michael@0 501
michael@0 502 uint32_t mElementsMatched;
michael@0 503
michael@0 504 uint32_t mElementUniversalCalls;
michael@0 505 uint32_t mElementNameSpaceCalls;
michael@0 506 uint32_t mElementTagCalls;
michael@0 507 uint32_t mElementClassCalls;
michael@0 508 uint32_t mElementIdCalls;
michael@0 509 #endif // RULE_HASH_STATS
michael@0 510 };
michael@0 511
michael@0 512 RuleHash::RuleHash(bool aQuirksMode)
michael@0 513 : mRuleCount(0),
michael@0 514 mUniversalRules(0),
michael@0 515 mEnumList(nullptr), mEnumListSize(0),
michael@0 516 mQuirksMode(aQuirksMode)
michael@0 517 #ifdef RULE_HASH_STATS
michael@0 518 ,
michael@0 519 mUniversalSelectors(0),
michael@0 520 mNameSpaceSelectors(0),
michael@0 521 mTagSelectors(0),
michael@0 522 mClassSelectors(0),
michael@0 523 mIdSelectors(0),
michael@0 524 mElementsMatched(0),
michael@0 525 mElementUniversalCalls(0),
michael@0 526 mElementNameSpaceCalls(0),
michael@0 527 mElementTagCalls(0),
michael@0 528 mElementClassCalls(0),
michael@0 529 mElementIdCalls(0)
michael@0 530 #endif
michael@0 531 {
michael@0 532 MOZ_COUNT_CTOR(RuleHash);
michael@0 533
michael@0 534 mTagTable.ops = nullptr;
michael@0 535 mIdTable.ops = nullptr;
michael@0 536 mClassTable.ops = nullptr;
michael@0 537 mNameSpaceTable.ops = nullptr;
michael@0 538 }
michael@0 539
michael@0 540 RuleHash::~RuleHash()
michael@0 541 {
michael@0 542 MOZ_COUNT_DTOR(RuleHash);
michael@0 543 #ifdef RULE_HASH_STATS
michael@0 544 printf(
michael@0 545 "RuleHash(%p):\n"
michael@0 546 " Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
michael@0 547 " Content Nodes: Elements(%u)\n"
michael@0 548 " Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
michael@0 549 static_cast<void*>(this),
michael@0 550 mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
michael@0 551 mClassSelectors, mIdSelectors,
michael@0 552 mElementsMatched,
michael@0 553 mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
michael@0 554 mElementClassCalls, mElementIdCalls);
michael@0 555 #ifdef PRINT_UNIVERSAL_RULES
michael@0 556 {
michael@0 557 if (mUniversalRules.Length() > 0) {
michael@0 558 printf(" Universal rules:\n");
michael@0 559 for (uint32_t i = 0; i < mUniversalRules.Length(); ++i) {
michael@0 560 RuleValue* value = &(mUniversalRules[i]);
michael@0 561 nsAutoString selectorText;
michael@0 562 uint32_t lineNumber = value->mRule->GetLineNumber();
michael@0 563 nsCOMPtr<nsIStyleSheet> sheet;
michael@0 564 value->mRule->GetStyleSheet(*getter_AddRefs(sheet));
michael@0 565 nsRefPtr<nsCSSStyleSheet> cssSheet = do_QueryObject(sheet);
michael@0 566 value->mSelector->ToString(selectorText, cssSheet);
michael@0 567
michael@0 568 printf(" line %d, %s\n",
michael@0 569 lineNumber, NS_ConvertUTF16toUTF8(selectorText).get());
michael@0 570 }
michael@0 571 }
michael@0 572 }
michael@0 573 #endif // PRINT_UNIVERSAL_RULES
michael@0 574 #endif // RULE_HASH_STATS
michael@0 575 // Rule Values are arena allocated no need to delete them. Their destructor
michael@0 576 // isn't doing any cleanup. So we dont even bother to enumerate through
michael@0 577 // the hash tables and call their destructors.
michael@0 578 if (nullptr != mEnumList) {
michael@0 579 delete [] mEnumList;
michael@0 580 }
michael@0 581 // delete arena for strings and small objects
michael@0 582 if (mIdTable.ops) {
michael@0 583 PL_DHashTableFinish(&mIdTable);
michael@0 584 }
michael@0 585 if (mClassTable.ops) {
michael@0 586 PL_DHashTableFinish(&mClassTable);
michael@0 587 }
michael@0 588 if (mTagTable.ops) {
michael@0 589 PL_DHashTableFinish(&mTagTable);
michael@0 590 }
michael@0 591 if (mNameSpaceTable.ops) {
michael@0 592 PL_DHashTableFinish(&mNameSpaceTable);
michael@0 593 }
michael@0 594 }
michael@0 595
michael@0 596 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
michael@0 597 const RuleSelectorPair& aRuleInfo)
michael@0 598 {
michael@0 599 // Get a new or existing entry.
michael@0 600 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
michael@0 601 (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
michael@0 602 if (!entry)
michael@0 603 return;
michael@0 604 entry->mRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
michael@0 605 }
michael@0 606
michael@0 607 static void
michael@0 608 AppendRuleToTagTable(PLDHashTable* aTable, nsIAtom* aKey,
michael@0 609 const RuleValue& aRuleInfo)
michael@0 610 {
michael@0 611 // Get a new or exisiting entry
michael@0 612 RuleHashTagTableEntry *entry = static_cast<RuleHashTagTableEntry*>
michael@0 613 (PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
michael@0 614 if (!entry)
michael@0 615 return;
michael@0 616
michael@0 617 entry->mRules.AppendElement(aRuleInfo);
michael@0 618 }
michael@0 619
michael@0 620 void RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
michael@0 621 {
michael@0 622 mUniversalRules.AppendElement(RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
michael@0 623 }
michael@0 624
michael@0 625 void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
michael@0 626 {
michael@0 627 nsCSSSelector *selector = aRuleInfo.mSelector;
michael@0 628 if (selector->IsPseudoElement()) {
michael@0 629 selector = selector->mNext;
michael@0 630 }
michael@0 631 if (nullptr != selector->mIDList) {
michael@0 632 if (!mIdTable.ops) {
michael@0 633 PL_DHashTableInit(&mIdTable,
michael@0 634 mQuirksMode ? &RuleHash_IdTable_CIOps.ops
michael@0 635 : &RuleHash_IdTable_CSOps.ops,
michael@0 636 nullptr, sizeof(RuleHashTableEntry), 16);
michael@0 637 }
michael@0 638 AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
michael@0 639 RULE_HASH_STAT_INCREMENT(mIdSelectors);
michael@0 640 }
michael@0 641 else if (nullptr != selector->mClassList) {
michael@0 642 if (!mClassTable.ops) {
michael@0 643 PL_DHashTableInit(&mClassTable,
michael@0 644 mQuirksMode ? &RuleHash_ClassTable_CIOps.ops
michael@0 645 : &RuleHash_ClassTable_CSOps.ops,
michael@0 646 nullptr, sizeof(RuleHashTableEntry), 16);
michael@0 647 }
michael@0 648 AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
michael@0 649 RULE_HASH_STAT_INCREMENT(mClassSelectors);
michael@0 650 }
michael@0 651 else if (selector->mLowercaseTag) {
michael@0 652 RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
michael@0 653 if (!mTagTable.ops) {
michael@0 654 PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nullptr,
michael@0 655 sizeof(RuleHashTagTableEntry), 16);
michael@0 656 }
michael@0 657 AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
michael@0 658 RULE_HASH_STAT_INCREMENT(mTagSelectors);
michael@0 659 if (selector->mCasedTag &&
michael@0 660 selector->mCasedTag != selector->mLowercaseTag) {
michael@0 661 AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
michael@0 662 RULE_HASH_STAT_INCREMENT(mTagSelectors);
michael@0 663 }
michael@0 664 }
michael@0 665 else if (kNameSpaceID_Unknown != selector->mNameSpace) {
michael@0 666 if (!mNameSpaceTable.ops) {
michael@0 667 PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nullptr,
michael@0 668 sizeof(RuleHashTableEntry), 16);
michael@0 669 }
michael@0 670 AppendRuleToTable(&mNameSpaceTable,
michael@0 671 NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
michael@0 672 RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
michael@0 673 }
michael@0 674 else { // universal tag selector
michael@0 675 AppendUniversalRule(aRuleInfo);
michael@0 676 RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
michael@0 677 }
michael@0 678 }
michael@0 679
michael@0 680 // this should cover practically all cases so we don't need to reallocate
michael@0 681 #define MIN_ENUM_LIST_SIZE 8
michael@0 682
michael@0 683 #ifdef RULE_HASH_STATS
michael@0 684 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
michael@0 685 (var_) += (list_).Length()
michael@0 686 #else
michael@0 687 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
michael@0 688 PR_BEGIN_MACRO PR_END_MACRO
michael@0 689 #endif
michael@0 690
michael@0 691 static inline
michael@0 692 void ContentEnumFunc(const RuleValue &value, nsCSSSelector* selector,
michael@0 693 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
michael@0 694 AncestorFilter *ancestorFilter);
michael@0 695
michael@0 696 void RuleHash::EnumerateAllRules(Element* aElement, ElementDependentRuleProcessorData* aData,
michael@0 697 NodeMatchContext& aNodeContext)
michael@0 698 {
michael@0 699 int32_t nameSpace = aElement->GetNameSpaceID();
michael@0 700 nsIAtom* tag = aElement->Tag();
michael@0 701 nsIAtom* id = aElement->GetID();
michael@0 702 const nsAttrValue* classList = aElement->GetClasses();
michael@0 703
michael@0 704 NS_ABORT_IF_FALSE(tag, "How could we not have a tag?");
michael@0 705
michael@0 706 int32_t classCount = classList ? classList->GetAtomCount() : 0;
michael@0 707
michael@0 708 // assume 1 universal, tag, id, and namespace, rather than wasting
michael@0 709 // time counting
michael@0 710 int32_t testCount = classCount + 4;
michael@0 711
michael@0 712 if (mEnumListSize < testCount) {
michael@0 713 delete [] mEnumList;
michael@0 714 mEnumListSize = std::max(testCount, MIN_ENUM_LIST_SIZE);
michael@0 715 mEnumList = new EnumData[mEnumListSize];
michael@0 716 }
michael@0 717
michael@0 718 int32_t valueCount = 0;
michael@0 719 RULE_HASH_STAT_INCREMENT(mElementsMatched);
michael@0 720
michael@0 721 if (mUniversalRules.Length() != 0) { // universal rules
michael@0 722 mEnumList[valueCount++] = ToEnumData(mUniversalRules);
michael@0 723 RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
michael@0 724 }
michael@0 725 // universal rules within the namespace
michael@0 726 if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.ops) {
michael@0 727 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
michael@0 728 (PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace),
michael@0 729 PL_DHASH_LOOKUP));
michael@0 730 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 731 mEnumList[valueCount++] = ToEnumData(entry->mRules);
michael@0 732 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
michael@0 733 }
michael@0 734 }
michael@0 735 if (mTagTable.ops) {
michael@0 736 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
michael@0 737 (PL_DHashTableOperate(&mTagTable, tag, PL_DHASH_LOOKUP));
michael@0 738 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 739 mEnumList[valueCount++] = ToEnumData(entry->mRules);
michael@0 740 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
michael@0 741 }
michael@0 742 }
michael@0 743 if (id && mIdTable.ops) {
michael@0 744 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
michael@0 745 (PL_DHashTableOperate(&mIdTable, id, PL_DHASH_LOOKUP));
michael@0 746 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 747 mEnumList[valueCount++] = ToEnumData(entry->mRules);
michael@0 748 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
michael@0 749 }
michael@0 750 }
michael@0 751 if (mClassTable.ops) {
michael@0 752 for (int32_t index = 0; index < classCount; ++index) {
michael@0 753 RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
michael@0 754 (PL_DHashTableOperate(&mClassTable, classList->AtomAt(index),
michael@0 755 PL_DHASH_LOOKUP));
michael@0 756 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 757 mEnumList[valueCount++] = ToEnumData(entry->mRules);
michael@0 758 RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
michael@0 759 }
michael@0 760 }
michael@0 761 }
michael@0 762 NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
michael@0 763
michael@0 764 if (valueCount > 0) {
michael@0 765 AncestorFilter *filter =
michael@0 766 aData->mTreeMatchContext.mAncestorFilter.HasFilter() ?
michael@0 767 &aData->mTreeMatchContext.mAncestorFilter : nullptr;
michael@0 768 #ifdef DEBUG
michael@0 769 if (filter) {
michael@0 770 filter->AssertHasAllAncestors(aElement);
michael@0 771 }
michael@0 772 #endif
michael@0 773 // Merge the lists while there are still multiple lists to merge.
michael@0 774 while (valueCount > 1) {
michael@0 775 int32_t valueIndex = 0;
michael@0 776 int32_t lowestRuleIndex = mEnumList[valueIndex].mCurValue->mIndex;
michael@0 777 for (int32_t index = 1; index < valueCount; ++index) {
michael@0 778 int32_t ruleIndex = mEnumList[index].mCurValue->mIndex;
michael@0 779 if (ruleIndex < lowestRuleIndex) {
michael@0 780 valueIndex = index;
michael@0 781 lowestRuleIndex = ruleIndex;
michael@0 782 }
michael@0 783 }
michael@0 784 const RuleValue *cur = mEnumList[valueIndex].mCurValue;
michael@0 785 ContentEnumFunc(*cur, cur->mSelector, aData, aNodeContext, filter);
michael@0 786 cur++;
michael@0 787 if (cur == mEnumList[valueIndex].mEnd) {
michael@0 788 mEnumList[valueIndex] = mEnumList[--valueCount];
michael@0 789 } else {
michael@0 790 mEnumList[valueIndex].mCurValue = cur;
michael@0 791 }
michael@0 792 }
michael@0 793
michael@0 794 // Fast loop over single value.
michael@0 795 for (const RuleValue *value = mEnumList[0].mCurValue,
michael@0 796 *end = mEnumList[0].mEnd;
michael@0 797 value != end; ++value) {
michael@0 798 ContentEnumFunc(*value, value->mSelector, aData, aNodeContext, filter);
michael@0 799 }
michael@0 800 }
michael@0 801 }
michael@0 802
michael@0 803 static size_t
michael@0 804 SizeOfRuleHashTableEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
michael@0 805 {
michael@0 806 RuleHashTableEntry* entry = static_cast<RuleHashTableEntry*>(aHdr);
michael@0 807 return entry->mRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 808 }
michael@0 809
michael@0 810 size_t
michael@0 811 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 812 {
michael@0 813 size_t n = 0;
michael@0 814
michael@0 815 if (mIdTable.ops) {
michael@0 816 n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
michael@0 817 SizeOfRuleHashTableEntry,
michael@0 818 aMallocSizeOf);
michael@0 819 }
michael@0 820
michael@0 821 if (mClassTable.ops) {
michael@0 822 n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
michael@0 823 SizeOfRuleHashTableEntry,
michael@0 824 aMallocSizeOf);
michael@0 825 }
michael@0 826
michael@0 827 if (mTagTable.ops) {
michael@0 828 n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
michael@0 829 SizeOfRuleHashTableEntry,
michael@0 830 aMallocSizeOf);
michael@0 831 }
michael@0 832
michael@0 833 if (mNameSpaceTable.ops) {
michael@0 834 n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
michael@0 835 SizeOfRuleHashTableEntry,
michael@0 836 aMallocSizeOf);
michael@0 837 }
michael@0 838
michael@0 839 n += mUniversalRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 840
michael@0 841 return n;
michael@0 842 }
michael@0 843
michael@0 844 size_t
michael@0 845 RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 846 {
michael@0 847 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 848 }
michael@0 849
michael@0 850 //--------------------------------
michael@0 851
michael@0 852 // A hash table mapping atoms to lists of selectors
michael@0 853 struct AtomSelectorEntry : public PLDHashEntryHdr {
michael@0 854 nsIAtom *mAtom;
michael@0 855 // Auto length 2, because a decent fraction of these arrays ends up
michael@0 856 // with 2 elements, and each entry is cheap.
michael@0 857 nsAutoTArray<nsCSSSelector*, 2> mSelectors;
michael@0 858 };
michael@0 859
michael@0 860 static void
michael@0 861 AtomSelector_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
michael@0 862 {
michael@0 863 (static_cast<AtomSelectorEntry*>(hdr))->~AtomSelectorEntry();
michael@0 864 }
michael@0 865
michael@0 866 static bool
michael@0 867 AtomSelector_InitEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 868 const void *key)
michael@0 869 {
michael@0 870 AtomSelectorEntry *entry = static_cast<AtomSelectorEntry*>(hdr);
michael@0 871 new (entry) AtomSelectorEntry();
michael@0 872 entry->mAtom = const_cast<nsIAtom*>(static_cast<const nsIAtom*>(key));
michael@0 873 return true;
michael@0 874 }
michael@0 875
michael@0 876 static void
michael@0 877 AtomSelector_MoveEntry(PLDHashTable *table, const PLDHashEntryHdr *from,
michael@0 878 PLDHashEntryHdr *to)
michael@0 879 {
michael@0 880 NS_PRECONDITION(from != to, "This is not going to work!");
michael@0 881 AtomSelectorEntry *oldEntry =
michael@0 882 const_cast<AtomSelectorEntry*>(static_cast<const AtomSelectorEntry*>(from));
michael@0 883 AtomSelectorEntry *newEntry = new (to) AtomSelectorEntry();
michael@0 884 newEntry->mAtom = oldEntry->mAtom;
michael@0 885 newEntry->mSelectors.SwapElements(oldEntry->mSelectors);
michael@0 886 oldEntry->~AtomSelectorEntry();
michael@0 887 }
michael@0 888
michael@0 889 static nsIAtom*
michael@0 890 AtomSelector_GetKey(PLDHashTable *table, const PLDHashEntryHdr *hdr)
michael@0 891 {
michael@0 892 const AtomSelectorEntry *entry = static_cast<const AtomSelectorEntry*>(hdr);
michael@0 893 return entry->mAtom;
michael@0 894 }
michael@0 895
michael@0 896 // Case-sensitive ops.
michael@0 897 static const PLDHashTableOps AtomSelector_CSOps = {
michael@0 898 PL_DHashAllocTable,
michael@0 899 PL_DHashFreeTable,
michael@0 900 PL_DHashVoidPtrKeyStub,
michael@0 901 PL_DHashMatchEntryStub,
michael@0 902 AtomSelector_MoveEntry,
michael@0 903 AtomSelector_ClearEntry,
michael@0 904 PL_DHashFinalizeStub,
michael@0 905 AtomSelector_InitEntry
michael@0 906 };
michael@0 907
michael@0 908 // Case-insensitive ops.
michael@0 909 static const RuleHashTableOps AtomSelector_CIOps = {
michael@0 910 {
michael@0 911 PL_DHashAllocTable,
michael@0 912 PL_DHashFreeTable,
michael@0 913 RuleHash_CIHashKey,
michael@0 914 RuleHash_CIMatchEntry,
michael@0 915 AtomSelector_MoveEntry,
michael@0 916 AtomSelector_ClearEntry,
michael@0 917 PL_DHashFinalizeStub,
michael@0 918 AtomSelector_InitEntry
michael@0 919 },
michael@0 920 AtomSelector_GetKey
michael@0 921 };
michael@0 922
michael@0 923 //--------------------------------
michael@0 924
michael@0 925 struct RuleCascadeData {
michael@0 926 RuleCascadeData(nsIAtom *aMedium, bool aQuirksMode)
michael@0 927 : mRuleHash(aQuirksMode),
michael@0 928 mStateSelectors(),
michael@0 929 mSelectorDocumentStates(0),
michael@0 930 mKeyframesRuleTable(16),
michael@0 931 mCacheKey(aMedium),
michael@0 932 mNext(nullptr),
michael@0 933 mQuirksMode(aQuirksMode)
michael@0 934 {
michael@0 935 // mAttributeSelectors is matching on the attribute _name_, not the value,
michael@0 936 // and we case-fold names at parse-time, so this is a case-sensitive match.
michael@0 937 PL_DHashTableInit(&mAttributeSelectors, &AtomSelector_CSOps, nullptr,
michael@0 938 sizeof(AtomSelectorEntry), 16);
michael@0 939 PL_DHashTableInit(&mAnonBoxRules, &RuleHash_TagTable_Ops, nullptr,
michael@0 940 sizeof(RuleHashTagTableEntry), 16);
michael@0 941 PL_DHashTableInit(&mIdSelectors,
michael@0 942 aQuirksMode ? &AtomSelector_CIOps.ops :
michael@0 943 &AtomSelector_CSOps,
michael@0 944 nullptr, sizeof(AtomSelectorEntry), 16);
michael@0 945 PL_DHashTableInit(&mClassSelectors,
michael@0 946 aQuirksMode ? &AtomSelector_CIOps.ops :
michael@0 947 &AtomSelector_CSOps,
michael@0 948 nullptr, sizeof(AtomSelectorEntry), 16);
michael@0 949 memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
michael@0 950 #ifdef MOZ_XUL
michael@0 951 PL_DHashTableInit(&mXULTreeRules, &RuleHash_TagTable_Ops, nullptr,
michael@0 952 sizeof(RuleHashTagTableEntry), 16);
michael@0 953 #endif
michael@0 954 }
michael@0 955
michael@0 956 ~RuleCascadeData()
michael@0 957 {
michael@0 958 PL_DHashTableFinish(&mAttributeSelectors);
michael@0 959 PL_DHashTableFinish(&mAnonBoxRules);
michael@0 960 PL_DHashTableFinish(&mIdSelectors);
michael@0 961 PL_DHashTableFinish(&mClassSelectors);
michael@0 962 #ifdef MOZ_XUL
michael@0 963 PL_DHashTableFinish(&mXULTreeRules);
michael@0 964 #endif
michael@0 965 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
michael@0 966 delete mPseudoElementRuleHashes[i];
michael@0 967 }
michael@0 968 }
michael@0 969
michael@0 970 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
michael@0 971
michael@0 972 RuleHash mRuleHash;
michael@0 973 RuleHash*
michael@0 974 mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
michael@0 975 nsTArray<nsCSSRuleProcessor::StateSelector> mStateSelectors;
michael@0 976 EventStates mSelectorDocumentStates;
michael@0 977 PLDHashTable mClassSelectors;
michael@0 978 PLDHashTable mIdSelectors;
michael@0 979 nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
michael@0 980 nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
michael@0 981 PLDHashTable mAttributeSelectors;
michael@0 982 PLDHashTable mAnonBoxRules;
michael@0 983 #ifdef MOZ_XUL
michael@0 984 PLDHashTable mXULTreeRules;
michael@0 985 #endif
michael@0 986
michael@0 987 nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
michael@0 988 nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
michael@0 989 nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
michael@0 990 nsTArray<nsCSSPageRule*> mPageRules;
michael@0 991
michael@0 992 nsDataHashtable<nsStringHashKey, nsCSSKeyframesRule*> mKeyframesRuleTable;
michael@0 993
michael@0 994 // Looks up or creates the appropriate list in |mAttributeSelectors|.
michael@0 995 // Returns null only on allocation failure.
michael@0 996 nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
michael@0 997
michael@0 998 nsMediaQueryResultCacheKey mCacheKey;
michael@0 999 RuleCascadeData* mNext; // for a different medium
michael@0 1000
michael@0 1001 const bool mQuirksMode;
michael@0 1002 };
michael@0 1003
michael@0 1004 static size_t
michael@0 1005 SizeOfSelectorsEntry(PLDHashEntryHdr* aHdr, MallocSizeOf aMallocSizeOf, void *)
michael@0 1006 {
michael@0 1007 AtomSelectorEntry* entry = static_cast<AtomSelectorEntry*>(aHdr);
michael@0 1008 return entry->mSelectors.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1009 }
michael@0 1010
michael@0 1011 size_t
michael@0 1012 RuleCascadeData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 1013 {
michael@0 1014 size_t n = aMallocSizeOf(this);
michael@0 1015
michael@0 1016 n += mRuleHash.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1017 for (uint32_t i = 0; i < ArrayLength(mPseudoElementRuleHashes); ++i) {
michael@0 1018 if (mPseudoElementRuleHashes[i])
michael@0 1019 n += mPseudoElementRuleHashes[i]->SizeOfIncludingThis(aMallocSizeOf);
michael@0 1020 }
michael@0 1021
michael@0 1022 n += mStateSelectors.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1023
michael@0 1024 n += PL_DHashTableSizeOfExcludingThis(&mIdSelectors,
michael@0 1025 SizeOfSelectorsEntry, aMallocSizeOf);
michael@0 1026 n += PL_DHashTableSizeOfExcludingThis(&mClassSelectors,
michael@0 1027 SizeOfSelectorsEntry, aMallocSizeOf);
michael@0 1028
michael@0 1029 n += mPossiblyNegatedClassSelectors.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1030 n += mPossiblyNegatedIDSelectors.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1031
michael@0 1032 n += PL_DHashTableSizeOfExcludingThis(&mAttributeSelectors,
michael@0 1033 SizeOfSelectorsEntry, aMallocSizeOf);
michael@0 1034 n += PL_DHashTableSizeOfExcludingThis(&mAnonBoxRules,
michael@0 1035 SizeOfRuleHashTableEntry, aMallocSizeOf);
michael@0 1036 #ifdef MOZ_XUL
michael@0 1037 n += PL_DHashTableSizeOfExcludingThis(&mXULTreeRules,
michael@0 1038 SizeOfRuleHashTableEntry, aMallocSizeOf);
michael@0 1039 #endif
michael@0 1040
michael@0 1041 n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1042 n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1043 n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1044 n += mPageRules.SizeOfExcludingThis(aMallocSizeOf);
michael@0 1045
michael@0 1046 return n;
michael@0 1047 }
michael@0 1048
michael@0 1049 nsTArray<nsCSSSelector*>*
michael@0 1050 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
michael@0 1051 {
michael@0 1052 AtomSelectorEntry *entry =
michael@0 1053 static_cast<AtomSelectorEntry*>
michael@0 1054 (PL_DHashTableOperate(&mAttributeSelectors, aAttribute,
michael@0 1055 PL_DHASH_ADD));
michael@0 1056 if (!entry)
michael@0 1057 return nullptr;
michael@0 1058 return &entry->mSelectors;
michael@0 1059 }
michael@0 1060
michael@0 1061 // -------------------------------
michael@0 1062 // CSS Style rule processor implementation
michael@0 1063 //
michael@0 1064
michael@0 1065 nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets,
michael@0 1066 uint8_t aSheetType,
michael@0 1067 Element* aScopeElement)
michael@0 1068 : mSheets(aSheets)
michael@0 1069 , mRuleCascades(nullptr)
michael@0 1070 , mLastPresContext(nullptr)
michael@0 1071 , mScopeElement(aScopeElement)
michael@0 1072 , mSheetType(aSheetType)
michael@0 1073 {
michael@0 1074 NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet),
michael@0 1075 "aScopeElement must be specified iff aSheetType is "
michael@0 1076 "eScopedDocSheet");
michael@0 1077 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
michael@0 1078 mSheets[i]->AddRuleProcessor(this);
michael@0 1079 }
michael@0 1080 }
michael@0 1081
michael@0 1082 nsCSSRuleProcessor::~nsCSSRuleProcessor()
michael@0 1083 {
michael@0 1084 for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) {
michael@0 1085 mSheets[i]->DropRuleProcessor(this);
michael@0 1086 }
michael@0 1087 mSheets.Clear();
michael@0 1088 ClearRuleCascades();
michael@0 1089 }
michael@0 1090
michael@0 1091 NS_IMPL_ISUPPORTS(nsCSSRuleProcessor, nsIStyleRuleProcessor)
michael@0 1092
michael@0 1093 /* static */ nsresult
michael@0 1094 nsCSSRuleProcessor::Startup()
michael@0 1095 {
michael@0 1096 Preferences::AddBoolVarCache(&gSupportVisitedPseudo, VISITED_PSEUDO_PREF,
michael@0 1097 true);
michael@0 1098
michael@0 1099 return NS_OK;
michael@0 1100 }
michael@0 1101
michael@0 1102 static bool
michael@0 1103 InitSystemMetrics()
michael@0 1104 {
michael@0 1105 NS_ASSERTION(!sSystemMetrics, "already initialized");
michael@0 1106
michael@0 1107 sSystemMetrics = new nsTArray< nsCOMPtr<nsIAtom> >;
michael@0 1108 NS_ENSURE_TRUE(sSystemMetrics, false);
michael@0 1109
michael@0 1110 /***************************************************************************
michael@0 1111 * ANY METRICS ADDED HERE SHOULD ALSO BE ADDED AS MEDIA QUERIES IN *
michael@0 1112 * nsMediaFeatures.cpp *
michael@0 1113 ***************************************************************************/
michael@0 1114
michael@0 1115 int32_t metricResult =
michael@0 1116 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollArrowStyle);
michael@0 1117 if (metricResult & LookAndFeel::eScrollArrow_StartBackward) {
michael@0 1118 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_backward);
michael@0 1119 }
michael@0 1120 if (metricResult & LookAndFeel::eScrollArrow_StartForward) {
michael@0 1121 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_start_forward);
michael@0 1122 }
michael@0 1123 if (metricResult & LookAndFeel::eScrollArrow_EndBackward) {
michael@0 1124 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_backward);
michael@0 1125 }
michael@0 1126 if (metricResult & LookAndFeel::eScrollArrow_EndForward) {
michael@0 1127 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_end_forward);
michael@0 1128 }
michael@0 1129
michael@0 1130 metricResult =
michael@0 1131 LookAndFeel::GetInt(LookAndFeel::eIntID_ScrollSliderStyle);
michael@0 1132 if (metricResult != LookAndFeel::eScrollThumbStyle_Normal) {
michael@0 1133 sSystemMetrics->AppendElement(nsGkAtoms::scrollbar_thumb_proportional);
michael@0 1134 }
michael@0 1135
michael@0 1136 metricResult =
michael@0 1137 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInMenus);
michael@0 1138 if (metricResult) {
michael@0 1139 sSystemMetrics->AppendElement(nsGkAtoms::images_in_menus);
michael@0 1140 }
michael@0 1141
michael@0 1142 metricResult =
michael@0 1143 LookAndFeel::GetInt(LookAndFeel::eIntID_ImagesInButtons);
michael@0 1144 if (metricResult) {
michael@0 1145 sSystemMetrics->AppendElement(nsGkAtoms::images_in_buttons);
michael@0 1146 }
michael@0 1147
michael@0 1148 metricResult =
michael@0 1149 LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars);
michael@0 1150 if (metricResult) {
michael@0 1151 sSystemMetrics->AppendElement(nsGkAtoms::overlay_scrollbars);
michael@0 1152 }
michael@0 1153
michael@0 1154 metricResult =
michael@0 1155 LookAndFeel::GetInt(LookAndFeel::eIntID_MenuBarDrag);
michael@0 1156 if (metricResult) {
michael@0 1157 sSystemMetrics->AppendElement(nsGkAtoms::menubar_drag);
michael@0 1158 }
michael@0 1159
michael@0 1160 nsresult rv =
michael@0 1161 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsDefaultTheme, &metricResult);
michael@0 1162 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1163 sSystemMetrics->AppendElement(nsGkAtoms::windows_default_theme);
michael@0 1164 }
michael@0 1165
michael@0 1166 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacGraphiteTheme, &metricResult);
michael@0 1167 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1168 sSystemMetrics->AppendElement(nsGkAtoms::mac_graphite_theme);
michael@0 1169 }
michael@0 1170
michael@0 1171 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_MacLionTheme, &metricResult);
michael@0 1172 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1173 sSystemMetrics->AppendElement(nsGkAtoms::mac_lion_theme);
michael@0 1174 }
michael@0 1175
michael@0 1176 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_DWMCompositor, &metricResult);
michael@0 1177 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1178 sSystemMetrics->AppendElement(nsGkAtoms::windows_compositor);
michael@0 1179 }
michael@0 1180
michael@0 1181 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsGlass, &metricResult);
michael@0 1182 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1183 sSystemMetrics->AppendElement(nsGkAtoms::windows_glass);
michael@0 1184 }
michael@0 1185
michael@0 1186 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_ColorPickerAvailable, &metricResult);
michael@0 1187 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1188 sSystemMetrics->AppendElement(nsGkAtoms::color_picker_available);
michael@0 1189 }
michael@0 1190
michael@0 1191 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsClassic, &metricResult);
michael@0 1192 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1193 sSystemMetrics->AppendElement(nsGkAtoms::windows_classic);
michael@0 1194 }
michael@0 1195
michael@0 1196 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_TouchEnabled, &metricResult);
michael@0 1197 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1198 sSystemMetrics->AppendElement(nsGkAtoms::touch_enabled);
michael@0 1199 }
michael@0 1200
michael@0 1201 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
michael@0 1202 &metricResult);
michael@0 1203 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1204 sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
michael@0 1205 }
michael@0 1206
michael@0 1207 rv = LookAndFeel::GetInt(LookAndFeel::eIntID_PhysicalHomeButton,
michael@0 1208 &metricResult);
michael@0 1209 if (NS_SUCCEEDED(rv) && metricResult) {
michael@0 1210 sSystemMetrics->AppendElement(nsGkAtoms::physical_home_button);
michael@0 1211 }
michael@0 1212
michael@0 1213 #ifdef XP_WIN
michael@0 1214 if (NS_SUCCEEDED(
michael@0 1215 LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,
michael@0 1216 &metricResult))) {
michael@0 1217 nsCSSRuleProcessor::SetWindowsThemeIdentifier(static_cast<uint8_t>(metricResult));
michael@0 1218 switch(metricResult) {
michael@0 1219 case LookAndFeel::eWindowsTheme_Aero:
michael@0 1220 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero);
michael@0 1221 break;
michael@0 1222 case LookAndFeel::eWindowsTheme_AeroLite:
michael@0 1223 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_aero_lite);
michael@0 1224 break;
michael@0 1225 case LookAndFeel::eWindowsTheme_LunaBlue:
michael@0 1226 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_blue);
michael@0 1227 break;
michael@0 1228 case LookAndFeel::eWindowsTheme_LunaOlive:
michael@0 1229 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_olive);
michael@0 1230 break;
michael@0 1231 case LookAndFeel::eWindowsTheme_LunaSilver:
michael@0 1232 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_luna_silver);
michael@0 1233 break;
michael@0 1234 case LookAndFeel::eWindowsTheme_Royale:
michael@0 1235 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_royale);
michael@0 1236 break;
michael@0 1237 case LookAndFeel::eWindowsTheme_Zune:
michael@0 1238 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_zune);
michael@0 1239 break;
michael@0 1240 case LookAndFeel::eWindowsTheme_Generic:
michael@0 1241 sSystemMetrics->AppendElement(nsGkAtoms::windows_theme_generic);
michael@0 1242 break;
michael@0 1243 }
michael@0 1244 }
michael@0 1245 #endif
michael@0 1246
michael@0 1247 return true;
michael@0 1248 }
michael@0 1249
michael@0 1250 /* static */ void
michael@0 1251 nsCSSRuleProcessor::FreeSystemMetrics()
michael@0 1252 {
michael@0 1253 delete sSystemMetrics;
michael@0 1254 sSystemMetrics = nullptr;
michael@0 1255 }
michael@0 1256
michael@0 1257 /* static */ void
michael@0 1258 nsCSSRuleProcessor::Shutdown()
michael@0 1259 {
michael@0 1260 FreeSystemMetrics();
michael@0 1261 }
michael@0 1262
michael@0 1263 /* static */ bool
michael@0 1264 nsCSSRuleProcessor::HasSystemMetric(nsIAtom* aMetric)
michael@0 1265 {
michael@0 1266 if (!sSystemMetrics && !InitSystemMetrics()) {
michael@0 1267 return false;
michael@0 1268 }
michael@0 1269 return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
michael@0 1270 }
michael@0 1271
michael@0 1272 #ifdef XP_WIN
michael@0 1273 /* static */ uint8_t
michael@0 1274 nsCSSRuleProcessor::GetWindowsThemeIdentifier()
michael@0 1275 {
michael@0 1276 if (!sSystemMetrics)
michael@0 1277 InitSystemMetrics();
michael@0 1278 return sWinThemeId;
michael@0 1279 }
michael@0 1280 #endif
michael@0 1281
michael@0 1282 /* static */
michael@0 1283 EventStates
michael@0 1284 nsCSSRuleProcessor::GetContentState(Element* aElement, const TreeMatchContext& aTreeMatchContext)
michael@0 1285 {
michael@0 1286 EventStates state = aElement->StyleState();
michael@0 1287
michael@0 1288 // If we are not supposed to mark visited links as such, be sure to
michael@0 1289 // flip the bits appropriately. We want to do this here, rather
michael@0 1290 // than in GetContentStateForVisitedHandling, so that we don't
michael@0 1291 // expose that :visited support is disabled to the Web page.
michael@0 1292 if (state.HasState(NS_EVENT_STATE_VISITED) &&
michael@0 1293 (!gSupportVisitedPseudo ||
michael@0 1294 aElement->OwnerDoc()->IsBeingUsedAsImage() ||
michael@0 1295 aTreeMatchContext.mUsingPrivateBrowsing)) {
michael@0 1296 state &= ~NS_EVENT_STATE_VISITED;
michael@0 1297 state |= NS_EVENT_STATE_UNVISITED;
michael@0 1298 }
michael@0 1299 return state;
michael@0 1300 }
michael@0 1301
michael@0 1302 /* static */
michael@0 1303 bool
michael@0 1304 nsCSSRuleProcessor::IsLink(Element* aElement)
michael@0 1305 {
michael@0 1306 EventStates state = aElement->StyleState();
michael@0 1307 return state.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
michael@0 1308 }
michael@0 1309
michael@0 1310 /* static */
michael@0 1311 EventStates
michael@0 1312 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
michael@0 1313 Element* aElement,
michael@0 1314 const TreeMatchContext& aTreeMatchContext,
michael@0 1315 nsRuleWalker::VisitedHandlingType aVisitedHandling,
michael@0 1316 bool aIsRelevantLink)
michael@0 1317 {
michael@0 1318 EventStates contentState = GetContentState(aElement, aTreeMatchContext);
michael@0 1319 if (contentState.HasAtLeastOneOfStates(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) {
michael@0 1320 NS_ABORT_IF_FALSE(IsLink(aElement), "IsLink() should match state");
michael@0 1321 contentState &= ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED);
michael@0 1322 if (aIsRelevantLink) {
michael@0 1323 switch (aVisitedHandling) {
michael@0 1324 case nsRuleWalker::eRelevantLinkUnvisited:
michael@0 1325 contentState |= NS_EVENT_STATE_UNVISITED;
michael@0 1326 break;
michael@0 1327 case nsRuleWalker::eRelevantLinkVisited:
michael@0 1328 contentState |= NS_EVENT_STATE_VISITED;
michael@0 1329 break;
michael@0 1330 case nsRuleWalker::eLinksVisitedOrUnvisited:
michael@0 1331 contentState |= NS_EVENT_STATE_UNVISITED | NS_EVENT_STATE_VISITED;
michael@0 1332 break;
michael@0 1333 }
michael@0 1334 } else {
michael@0 1335 contentState |= NS_EVENT_STATE_UNVISITED;
michael@0 1336 }
michael@0 1337 }
michael@0 1338 return contentState;
michael@0 1339 }
michael@0 1340
michael@0 1341 /**
michael@0 1342 * A |NodeMatchContext| has data about matching a selector (without
michael@0 1343 * combinators) against a single node. It contains only input to the
michael@0 1344 * matching.
michael@0 1345 *
michael@0 1346 * Unlike |RuleProcessorData|, which is similar, a |NodeMatchContext|
michael@0 1347 * can vary depending on the selector matching process. In other words,
michael@0 1348 * there might be multiple NodeMatchContexts corresponding to a single
michael@0 1349 * node, but only one possible RuleProcessorData.
michael@0 1350 */
michael@0 1351 struct NodeMatchContext {
michael@0 1352 // In order to implement nsCSSRuleProcessor::HasStateDependentStyle,
michael@0 1353 // we need to be able to see if a node might match an
michael@0 1354 // event-state-dependent selector for any value of that event state.
michael@0 1355 // So mStateMask contains the states that should NOT be tested.
michael@0 1356 //
michael@0 1357 // NOTE: For |mStateMask| to work correctly, it's important that any
michael@0 1358 // change that changes multiple state bits include all those state
michael@0 1359 // bits in the notification. Otherwise, if multiple states change but
michael@0 1360 // we do separate notifications then we might determine the style is
michael@0 1361 // not state-dependent when it really is (e.g., determining that a
michael@0 1362 // :hover:active rule no longer matches when both states are unset).
michael@0 1363 const EventStates mStateMask;
michael@0 1364
michael@0 1365 // Is this link the unique link whose visitedness can affect the style
michael@0 1366 // of the node being matched? (That link is the nearest link to the
michael@0 1367 // node being matched that is itself or an ancestor.)
michael@0 1368 //
michael@0 1369 // Always false when TreeMatchContext::mForStyling is false. (We
michael@0 1370 // could figure it out for SelectorListMatches, but we're starting
michael@0 1371 // from the middle of the selector list when doing
michael@0 1372 // Has{Attribute,State}DependentStyle, so we can't tell. So when
michael@0 1373 // mForStyling is false, we have to assume we don't know.)
michael@0 1374 const bool mIsRelevantLink;
michael@0 1375
michael@0 1376 NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
michael@0 1377 : mStateMask(aStateMask)
michael@0 1378 , mIsRelevantLink(aIsRelevantLink)
michael@0 1379 {
michael@0 1380 }
michael@0 1381 };
michael@0 1382
michael@0 1383 static bool ValueIncludes(const nsSubstring& aValueList,
michael@0 1384 const nsSubstring& aValue,
michael@0 1385 const nsStringComparator& aComparator)
michael@0 1386 {
michael@0 1387 const char16_t *p = aValueList.BeginReading(),
michael@0 1388 *p_end = aValueList.EndReading();
michael@0 1389
michael@0 1390 while (p < p_end) {
michael@0 1391 // skip leading space
michael@0 1392 while (p != p_end && nsContentUtils::IsHTMLWhitespace(*p))
michael@0 1393 ++p;
michael@0 1394
michael@0 1395 const char16_t *val_start = p;
michael@0 1396
michael@0 1397 // look for space or end
michael@0 1398 while (p != p_end && !nsContentUtils::IsHTMLWhitespace(*p))
michael@0 1399 ++p;
michael@0 1400
michael@0 1401 const char16_t *val_end = p;
michael@0 1402
michael@0 1403 if (val_start < val_end &&
michael@0 1404 aValue.Equals(Substring(val_start, val_end), aComparator))
michael@0 1405 return true;
michael@0 1406
michael@0 1407 ++p; // we know the next character is not whitespace
michael@0 1408 }
michael@0 1409 return false;
michael@0 1410 }
michael@0 1411
michael@0 1412 // Return whether we should apply a "global" (i.e., universal-tag)
michael@0 1413 // selector for event states in quirks mode. Note that
michael@0 1414 // |IsLink()| is checked separately by the caller, so we return
michael@0 1415 // false for |nsGkAtoms::a|, which here means a named anchor.
michael@0 1416 inline bool IsQuirkEventSensitive(nsIAtom *aContentTag)
michael@0 1417 {
michael@0 1418 return bool ((nsGkAtoms::button == aContentTag) ||
michael@0 1419 (nsGkAtoms::img == aContentTag) ||
michael@0 1420 (nsGkAtoms::input == aContentTag) ||
michael@0 1421 (nsGkAtoms::label == aContentTag) ||
michael@0 1422 (nsGkAtoms::select == aContentTag) ||
michael@0 1423 (nsGkAtoms::textarea == aContentTag));
michael@0 1424 }
michael@0 1425
michael@0 1426
michael@0 1427 static inline bool
michael@0 1428 IsSignificantChild(nsIContent* aChild, bool aTextIsSignificant,
michael@0 1429 bool aWhitespaceIsSignificant)
michael@0 1430 {
michael@0 1431 return nsStyleUtil::IsSignificantChild(aChild, aTextIsSignificant,
michael@0 1432 aWhitespaceIsSignificant);
michael@0 1433 }
michael@0 1434
michael@0 1435 // This function is to be called once we have fetched a value for an attribute
michael@0 1436 // whose namespace and name match those of aAttrSelector. This function
michael@0 1437 // performs comparisons on the value only, based on aAttrSelector->mFunction.
michael@0 1438 static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
michael@0 1439 const nsString& aValue, bool isHTML)
michael@0 1440 {
michael@0 1441 NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
michael@0 1442
michael@0 1443 // http://lists.w3.org/Archives/Public/www-style/2008Apr/0038.html
michael@0 1444 // *= (CONTAINSMATCH) ~= (INCLUDES) ^= (BEGINSMATCH) $= (ENDSMATCH)
michael@0 1445 // all accept the empty string, but match nothing.
michael@0 1446 if (aAttrSelector->mValue.IsEmpty() &&
michael@0 1447 (aAttrSelector->mFunction == NS_ATTR_FUNC_INCLUDES ||
michael@0 1448 aAttrSelector->mFunction == NS_ATTR_FUNC_ENDSMATCH ||
michael@0 1449 aAttrSelector->mFunction == NS_ATTR_FUNC_BEGINSMATCH ||
michael@0 1450 aAttrSelector->mFunction == NS_ATTR_FUNC_CONTAINSMATCH))
michael@0 1451 return false;
michael@0 1452
michael@0 1453 const nsDefaultStringComparator defaultComparator;
michael@0 1454 const nsASCIICaseInsensitiveStringComparator ciComparator;
michael@0 1455 const nsStringComparator& comparator =
michael@0 1456 (aAttrSelector->mCaseSensitive || !isHTML)
michael@0 1457 ? static_cast<const nsStringComparator&>(defaultComparator)
michael@0 1458 : static_cast<const nsStringComparator&>(ciComparator);
michael@0 1459
michael@0 1460 switch (aAttrSelector->mFunction) {
michael@0 1461 case NS_ATTR_FUNC_EQUALS:
michael@0 1462 return aValue.Equals(aAttrSelector->mValue, comparator);
michael@0 1463 case NS_ATTR_FUNC_INCLUDES:
michael@0 1464 return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
michael@0 1465 case NS_ATTR_FUNC_DASHMATCH:
michael@0 1466 return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
michael@0 1467 case NS_ATTR_FUNC_ENDSMATCH:
michael@0 1468 return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
michael@0 1469 case NS_ATTR_FUNC_BEGINSMATCH:
michael@0 1470 return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
michael@0 1471 case NS_ATTR_FUNC_CONTAINSMATCH:
michael@0 1472 return FindInReadable(aAttrSelector->mValue, aValue, comparator);
michael@0 1473 default:
michael@0 1474 NS_NOTREACHED("Shouldn't be ending up here");
michael@0 1475 return false;
michael@0 1476 }
michael@0 1477 }
michael@0 1478
michael@0 1479 static inline bool
michael@0 1480 edgeChildMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
michael@0 1481 bool checkFirst, bool checkLast)
michael@0 1482 {
michael@0 1483 nsIContent *parent = aElement->GetParent();
michael@0 1484 if (!parent) {
michael@0 1485 return false;
michael@0 1486 }
michael@0 1487
michael@0 1488 if (aTreeMatchContext.mForStyling)
michael@0 1489 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
michael@0 1490
michael@0 1491 return (!checkFirst ||
michael@0 1492 aTreeMatchContext.mNthIndexCache.
michael@0 1493 GetNthIndex(aElement, false, false, true) == 1) &&
michael@0 1494 (!checkLast ||
michael@0 1495 aTreeMatchContext.mNthIndexCache.
michael@0 1496 GetNthIndex(aElement, false, true, true) == 1);
michael@0 1497 }
michael@0 1498
michael@0 1499 static inline bool
michael@0 1500 nthChildGenericMatches(Element* aElement,
michael@0 1501 TreeMatchContext& aTreeMatchContext,
michael@0 1502 nsPseudoClassList* pseudoClass,
michael@0 1503 bool isOfType, bool isFromEnd)
michael@0 1504 {
michael@0 1505 nsIContent *parent = aElement->GetParent();
michael@0 1506 if (!parent) {
michael@0 1507 return false;
michael@0 1508 }
michael@0 1509
michael@0 1510 if (aTreeMatchContext.mForStyling) {
michael@0 1511 if (isFromEnd)
michael@0 1512 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
michael@0 1513 else
michael@0 1514 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
michael@0 1515 }
michael@0 1516
michael@0 1517 const int32_t index = aTreeMatchContext.mNthIndexCache.
michael@0 1518 GetNthIndex(aElement, isOfType, isFromEnd, false);
michael@0 1519 if (index <= 0) {
michael@0 1520 // Node is anonymous content (not really a child of its parent).
michael@0 1521 return false;
michael@0 1522 }
michael@0 1523
michael@0 1524 const int32_t a = pseudoClass->u.mNumbers[0];
michael@0 1525 const int32_t b = pseudoClass->u.mNumbers[1];
michael@0 1526 // result should be true if there exists n >= 0 such that
michael@0 1527 // a * n + b == index.
michael@0 1528 if (a == 0) {
michael@0 1529 return b == index;
michael@0 1530 }
michael@0 1531
michael@0 1532 // Integer division in C does truncation (towards 0). So
michael@0 1533 // check that the result is nonnegative, and that there was no
michael@0 1534 // truncation.
michael@0 1535 const int32_t n = (index - b) / a;
michael@0 1536 return n >= 0 && (a * n == index - b);
michael@0 1537 }
michael@0 1538
michael@0 1539 static inline bool
michael@0 1540 edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
michael@0 1541 bool checkFirst, bool checkLast)
michael@0 1542 {
michael@0 1543 nsIContent *parent = aElement->GetParent();
michael@0 1544 if (!parent) {
michael@0 1545 return false;
michael@0 1546 }
michael@0 1547
michael@0 1548 if (aTreeMatchContext.mForStyling) {
michael@0 1549 if (checkLast)
michael@0 1550 parent->SetFlags(NODE_HAS_SLOW_SELECTOR);
michael@0 1551 else
michael@0 1552 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
michael@0 1553 }
michael@0 1554
michael@0 1555 return (!checkFirst ||
michael@0 1556 aTreeMatchContext.mNthIndexCache.
michael@0 1557 GetNthIndex(aElement, true, false, true) == 1) &&
michael@0 1558 (!checkLast ||
michael@0 1559 aTreeMatchContext.mNthIndexCache.
michael@0 1560 GetNthIndex(aElement, true, true, true) == 1);
michael@0 1561 }
michael@0 1562
michael@0 1563 static inline bool
michael@0 1564 checkGenericEmptyMatches(Element* aElement,
michael@0 1565 TreeMatchContext& aTreeMatchContext,
michael@0 1566 bool isWhitespaceSignificant)
michael@0 1567 {
michael@0 1568 nsIContent *child = nullptr;
michael@0 1569 int32_t index = -1;
michael@0 1570
michael@0 1571 if (aTreeMatchContext.mForStyling)
michael@0 1572 aElement->SetFlags(NODE_HAS_EMPTY_SELECTOR);
michael@0 1573
michael@0 1574 do {
michael@0 1575 child = aElement->GetChildAt(++index);
michael@0 1576 // stop at first non-comment (and non-whitespace for
michael@0 1577 // :-moz-only-whitespace) node
michael@0 1578 } while (child && !IsSignificantChild(child, true, isWhitespaceSignificant));
michael@0 1579 return (child == nullptr);
michael@0 1580 }
michael@0 1581
michael@0 1582 // Arrays of the states that are relevant for various pseudoclasses.
michael@0 1583 static const EventStates sPseudoClassStateDependences[] = {
michael@0 1584 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
michael@0 1585 EventStates(),
michael@0 1586 #define CSS_STATE_DEPENDENT_PSEUDO_CLASS(_name, _value, _pref, _states) \
michael@0 1587 _states,
michael@0 1588 #include "nsCSSPseudoClassList.h"
michael@0 1589 #undef CSS_STATE_DEPENDENT_PSEUDO_CLASS
michael@0 1590 #undef CSS_PSEUDO_CLASS
michael@0 1591 // Add more entries for our fake values to make sure we can't
michael@0 1592 // index out of bounds into this array no matter what.
michael@0 1593 EventStates(),
michael@0 1594 EventStates()
michael@0 1595 };
michael@0 1596
michael@0 1597 static const EventStates sPseudoClassStates[] = {
michael@0 1598 #define CSS_PSEUDO_CLASS(_name, _value, _pref) \
michael@0 1599 EventStates(),
michael@0 1600 #define CSS_STATE_PSEUDO_CLASS(_name, _value, _pref, _states) \
michael@0 1601 _states,
michael@0 1602 #include "nsCSSPseudoClassList.h"
michael@0 1603 #undef CSS_STATE_PSEUDO_CLASS
michael@0 1604 #undef CSS_PSEUDO_CLASS
michael@0 1605 // Add more entries for our fake values to make sure we can't
michael@0 1606 // index out of bounds into this array no matter what.
michael@0 1607 EventStates(),
michael@0 1608 EventStates()
michael@0 1609 };
michael@0 1610 static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
michael@0 1611 nsCSSPseudoClasses::ePseudoClass_NotPseudoClass + 1,
michael@0 1612 "ePseudoClass_NotPseudoClass is no longer at the end of"
michael@0 1613 "sPseudoClassStates");
michael@0 1614
michael@0 1615 static bool
michael@0 1616 StateSelectorMatches(Element* aElement,
michael@0 1617 nsCSSSelector* aSelector,
michael@0 1618 NodeMatchContext& aNodeMatchContext,
michael@0 1619 TreeMatchContext& aTreeMatchContext,
michael@0 1620 bool* const aDependence,
michael@0 1621 EventStates aStatesToCheck)
michael@0 1622 {
michael@0 1623 NS_PRECONDITION(!aStatesToCheck.IsEmpty(),
michael@0 1624 "should only need to call StateSelectorMatches if "
michael@0 1625 "aStatesToCheck is not empty");
michael@0 1626
michael@0 1627 const bool isNegated = aDependence != nullptr;
michael@0 1628
michael@0 1629 // Bit-based pseudo-classes
michael@0 1630 if (aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE) &&
michael@0 1631 aTreeMatchContext.mCompatMode == eCompatibility_NavQuirks &&
michael@0 1632 // global selector:
michael@0 1633 !aSelector->HasTagSelector() && !aSelector->mIDList &&
michael@0 1634 !aSelector->mClassList && !aSelector->mAttrList &&
michael@0 1635 // This (or the other way around) both make :not() asymmetric
michael@0 1636 // in quirks mode (and it's hard to work around since we're
michael@0 1637 // testing the current mNegations, not the first
michael@0 1638 // (unnegated)). This at least makes it closer to the spec.
michael@0 1639 !isNegated &&
michael@0 1640 // important for |IsQuirkEventSensitive|:
michael@0 1641 aElement->IsHTML() && !nsCSSRuleProcessor::IsLink(aElement) &&
michael@0 1642 !IsQuirkEventSensitive(aElement->Tag())) {
michael@0 1643 // In quirks mode, only make certain elements sensitive to
michael@0 1644 // selectors ":hover" and ":active".
michael@0 1645 return false;
michael@0 1646 }
michael@0 1647
michael@0 1648 if (aTreeMatchContext.mForStyling &&
michael@0 1649 aStatesToCheck.HasAtLeastOneOfStates(NS_EVENT_STATE_HOVER)) {
michael@0 1650 // Mark the element as having :hover-dependent style
michael@0 1651 aElement->SetHasRelevantHoverRules();
michael@0 1652 }
michael@0 1653
michael@0 1654 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(aStatesToCheck)) {
michael@0 1655 if (aDependence) {
michael@0 1656 *aDependence = true;
michael@0 1657 }
michael@0 1658 } else {
michael@0 1659 EventStates contentState =
michael@0 1660 nsCSSRuleProcessor::GetContentStateForVisitedHandling(
michael@0 1661 aElement,
michael@0 1662 aTreeMatchContext,
michael@0 1663 aTreeMatchContext.VisitedHandling(),
michael@0 1664 aNodeMatchContext.mIsRelevantLink);
michael@0 1665 if (!contentState.HasAtLeastOneOfStates(aStatesToCheck)) {
michael@0 1666 return false;
michael@0 1667 }
michael@0 1668 }
michael@0 1669
michael@0 1670 return true;
michael@0 1671 }
michael@0 1672
michael@0 1673 static bool
michael@0 1674 StateSelectorMatches(Element* aElement,
michael@0 1675 nsCSSSelector* aSelector,
michael@0 1676 NodeMatchContext& aNodeMatchContext,
michael@0 1677 TreeMatchContext& aTreeMatchContext)
michael@0 1678 {
michael@0 1679 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
michael@0 1680 pseudoClass; pseudoClass = pseudoClass->mNext) {
michael@0 1681 EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
michael@0 1682 if (!statesToCheck.IsEmpty() &&
michael@0 1683 !StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
michael@0 1684 aTreeMatchContext, nullptr, statesToCheck)) {
michael@0 1685 return false;
michael@0 1686 }
michael@0 1687 }
michael@0 1688 return true;
michael@0 1689 }
michael@0 1690
michael@0 1691 // |aDependence| has two functions:
michael@0 1692 // * when non-null, it indicates that we're processing a negation,
michael@0 1693 // which is done only when SelectorMatches calls itself recursively
michael@0 1694 // * what it points to should be set to true whenever a test is skipped
michael@0 1695 // because of aNodeMatchContent.mStateMask
michael@0 1696 static bool SelectorMatches(Element* aElement,
michael@0 1697 nsCSSSelector* aSelector,
michael@0 1698 NodeMatchContext& aNodeMatchContext,
michael@0 1699 TreeMatchContext& aTreeMatchContext,
michael@0 1700 bool* const aDependence = nullptr)
michael@0 1701
michael@0 1702 {
michael@0 1703 NS_PRECONDITION(!aSelector->IsPseudoElement(),
michael@0 1704 "Pseudo-element snuck into SelectorMatches?");
michael@0 1705 NS_ABORT_IF_FALSE(aTreeMatchContext.mForStyling ||
michael@0 1706 !aNodeMatchContext.mIsRelevantLink,
michael@0 1707 "mIsRelevantLink should be set to false when mForStyling "
michael@0 1708 "is false since we don't know how to set it correctly in "
michael@0 1709 "Has(Attribute|State)DependentStyle");
michael@0 1710
michael@0 1711 // namespace/tag match
michael@0 1712 // optimization : bail out early if we can
michael@0 1713 if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
michael@0 1714 aElement->GetNameSpaceID() != aSelector->mNameSpace))
michael@0 1715 return false;
michael@0 1716
michael@0 1717 if (aSelector->mLowercaseTag) {
michael@0 1718 nsIAtom* selectorTag =
michael@0 1719 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML()) ?
michael@0 1720 aSelector->mLowercaseTag : aSelector->mCasedTag;
michael@0 1721 if (selectorTag != aElement->Tag()) {
michael@0 1722 return false;
michael@0 1723 }
michael@0 1724 }
michael@0 1725
michael@0 1726 nsAtomList* IDList = aSelector->mIDList;
michael@0 1727 if (IDList) {
michael@0 1728 nsIAtom* id = aElement->GetID();
michael@0 1729 if (id) {
michael@0 1730 // case sensitivity: bug 93371
michael@0 1731 const bool isCaseSensitive =
michael@0 1732 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
michael@0 1733
michael@0 1734 if (isCaseSensitive) {
michael@0 1735 do {
michael@0 1736 if (IDList->mAtom != id) {
michael@0 1737 return false;
michael@0 1738 }
michael@0 1739 IDList = IDList->mNext;
michael@0 1740 } while (IDList);
michael@0 1741 } else {
michael@0 1742 // Use EqualsIgnoreASCIICase instead of full on unicode case conversion
michael@0 1743 // in order to save on performance. This is only used in quirks mode
michael@0 1744 // anyway.
michael@0 1745 nsDependentAtomString id1Str(id);
michael@0 1746 do {
michael@0 1747 if (!nsContentUtils::EqualsIgnoreASCIICase(id1Str,
michael@0 1748 nsDependentAtomString(IDList->mAtom))) {
michael@0 1749 return false;
michael@0 1750 }
michael@0 1751 IDList = IDList->mNext;
michael@0 1752 } while (IDList);
michael@0 1753 }
michael@0 1754 } else {
michael@0 1755 // Element has no id but we have an id selector
michael@0 1756 return false;
michael@0 1757 }
michael@0 1758 }
michael@0 1759
michael@0 1760 nsAtomList* classList = aSelector->mClassList;
michael@0 1761 if (classList) {
michael@0 1762 // test for class match
michael@0 1763 const nsAttrValue *elementClasses = aElement->GetClasses();
michael@0 1764 if (!elementClasses) {
michael@0 1765 // Element has no classes but we have a class selector
michael@0 1766 return false;
michael@0 1767 }
michael@0 1768
michael@0 1769 // case sensitivity: bug 93371
michael@0 1770 const bool isCaseSensitive =
michael@0 1771 aTreeMatchContext.mCompatMode != eCompatibility_NavQuirks;
michael@0 1772
michael@0 1773 while (classList) {
michael@0 1774 if (!elementClasses->Contains(classList->mAtom,
michael@0 1775 isCaseSensitive ?
michael@0 1776 eCaseMatters : eIgnoreCase)) {
michael@0 1777 return false;
michael@0 1778 }
michael@0 1779 classList = classList->mNext;
michael@0 1780 }
michael@0 1781 }
michael@0 1782
michael@0 1783 const bool isNegated = (aDependence != nullptr);
michael@0 1784 // The selectors for which we set node bits are, unfortunately, early
michael@0 1785 // in this function (because they're pseudo-classes, which are
michael@0 1786 // generally quick to test, and thus earlier). If they were later,
michael@0 1787 // we'd probably avoid setting those bits in more cases where setting
michael@0 1788 // them is unnecessary.
michael@0 1789 NS_ASSERTION(aNodeMatchContext.mStateMask.IsEmpty() ||
michael@0 1790 !aTreeMatchContext.mForStyling,
michael@0 1791 "mForStyling must be false if we're just testing for "
michael@0 1792 "state-dependence");
michael@0 1793
michael@0 1794 // test for pseudo class match
michael@0 1795 for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
michael@0 1796 pseudoClass; pseudoClass = pseudoClass->mNext) {
michael@0 1797 EventStates statesToCheck = sPseudoClassStates[pseudoClass->mType];
michael@0 1798 if (statesToCheck.IsEmpty()) {
michael@0 1799 // keep the cases here in the same order as the list in
michael@0 1800 // nsCSSPseudoClassList.h
michael@0 1801 switch (pseudoClass->mType) {
michael@0 1802 case nsCSSPseudoClasses::ePseudoClass_empty:
michael@0 1803 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, true)) {
michael@0 1804 return false;
michael@0 1805 }
michael@0 1806 break;
michael@0 1807
michael@0 1808 case nsCSSPseudoClasses::ePseudoClass_mozOnlyWhitespace:
michael@0 1809 if (!checkGenericEmptyMatches(aElement, aTreeMatchContext, false)) {
michael@0 1810 return false;
michael@0 1811 }
michael@0 1812 break;
michael@0 1813
michael@0 1814 case nsCSSPseudoClasses::ePseudoClass_mozEmptyExceptChildrenWithLocalname:
michael@0 1815 {
michael@0 1816 NS_ASSERTION(pseudoClass->u.mString, "Must have string!");
michael@0 1817 nsIContent *child = nullptr;
michael@0 1818 int32_t index = -1;
michael@0 1819
michael@0 1820 if (aTreeMatchContext.mForStyling)
michael@0 1821 // FIXME: This isn't sufficient to handle:
michael@0 1822 // :-moz-empty-except-children-with-localname() + E
michael@0 1823 // :-moz-empty-except-children-with-localname() ~ E
michael@0 1824 // because we don't know to restyle the grandparent of the
michael@0 1825 // inserted/removed element (as in bug 534804 for :empty).
michael@0 1826 aElement->SetFlags(NODE_HAS_SLOW_SELECTOR);
michael@0 1827 do {
michael@0 1828 child = aElement->GetChildAt(++index);
michael@0 1829 } while (child &&
michael@0 1830 (!IsSignificantChild(child, true, false) ||
michael@0 1831 (child->GetNameSpaceID() == aElement->GetNameSpaceID() &&
michael@0 1832 child->Tag()->Equals(nsDependentString(pseudoClass->u.mString)))));
michael@0 1833 if (child != nullptr) {
michael@0 1834 return false;
michael@0 1835 }
michael@0 1836 }
michael@0 1837 break;
michael@0 1838
michael@0 1839 case nsCSSPseudoClasses::ePseudoClass_lang:
michael@0 1840 {
michael@0 1841 NS_ASSERTION(nullptr != pseudoClass->u.mString, "null lang parameter");
michael@0 1842 if (!pseudoClass->u.mString || !*pseudoClass->u.mString) {
michael@0 1843 return false;
michael@0 1844 }
michael@0 1845
michael@0 1846 // We have to determine the language of the current element. Since
michael@0 1847 // this is currently no property and since the language is inherited
michael@0 1848 // from the parent we have to be prepared to look at all parent
michael@0 1849 // nodes. The language itself is encoded in the LANG attribute.
michael@0 1850 nsAutoString language;
michael@0 1851 aElement->GetLang(language);
michael@0 1852 if (!language.IsEmpty()) {
michael@0 1853 if (!nsStyleUtil::DashMatchCompare(language,
michael@0 1854 nsDependentString(pseudoClass->u.mString),
michael@0 1855 nsASCIICaseInsensitiveStringComparator())) {
michael@0 1856 return false;
michael@0 1857 }
michael@0 1858 // This pseudo-class matched; move on to the next thing
michael@0 1859 break;
michael@0 1860 }
michael@0 1861
michael@0 1862 nsIDocument* doc = aTreeMatchContext.mDocument;
michael@0 1863 if (doc) {
michael@0 1864 // Try to get the language from the HTTP header or if this
michael@0 1865 // is missing as well from the preferences.
michael@0 1866 // The content language can be a comma-separated list of
michael@0 1867 // language codes.
michael@0 1868 doc->GetContentLanguage(language);
michael@0 1869
michael@0 1870 nsDependentString langString(pseudoClass->u.mString);
michael@0 1871 language.StripWhitespace();
michael@0 1872 int32_t begin = 0;
michael@0 1873 int32_t len = language.Length();
michael@0 1874 while (begin < len) {
michael@0 1875 int32_t end = language.FindChar(char16_t(','), begin);
michael@0 1876 if (end == kNotFound) {
michael@0 1877 end = len;
michael@0 1878 }
michael@0 1879 if (nsStyleUtil::DashMatchCompare(Substring(language, begin,
michael@0 1880 end-begin),
michael@0 1881 langString,
michael@0 1882 nsASCIICaseInsensitiveStringComparator())) {
michael@0 1883 break;
michael@0 1884 }
michael@0 1885 begin = end + 1;
michael@0 1886 }
michael@0 1887 if (begin < len) {
michael@0 1888 // This pseudo-class matched
michael@0 1889 break;
michael@0 1890 }
michael@0 1891 }
michael@0 1892
michael@0 1893 return false;
michael@0 1894 }
michael@0 1895 break;
michael@0 1896
michael@0 1897 case nsCSSPseudoClasses::ePseudoClass_mozBoundElement:
michael@0 1898 if (aTreeMatchContext.mScopedRoot != aElement) {
michael@0 1899 return false;
michael@0 1900 }
michael@0 1901 break;
michael@0 1902
michael@0 1903 case nsCSSPseudoClasses::ePseudoClass_root:
michael@0 1904 if (aElement != aElement->OwnerDoc()->GetRootElement()) {
michael@0 1905 return false;
michael@0 1906 }
michael@0 1907 break;
michael@0 1908
michael@0 1909 case nsCSSPseudoClasses::ePseudoClass_any:
michael@0 1910 {
michael@0 1911 nsCSSSelectorList *l;
michael@0 1912 for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
michael@0 1913 nsCSSSelector *s = l->mSelectors;
michael@0 1914 NS_ABORT_IF_FALSE(!s->mNext && !s->IsPseudoElement(),
michael@0 1915 "parser failed");
michael@0 1916 if (SelectorMatches(aElement, s, aNodeMatchContext,
michael@0 1917 aTreeMatchContext)) {
michael@0 1918 break;
michael@0 1919 }
michael@0 1920 }
michael@0 1921 if (!l) {
michael@0 1922 return false;
michael@0 1923 }
michael@0 1924 }
michael@0 1925 break;
michael@0 1926
michael@0 1927 case nsCSSPseudoClasses::ePseudoClass_firstChild:
michael@0 1928 if (!edgeChildMatches(aElement, aTreeMatchContext, true, false)) {
michael@0 1929 return false;
michael@0 1930 }
michael@0 1931 break;
michael@0 1932
michael@0 1933 case nsCSSPseudoClasses::ePseudoClass_firstNode:
michael@0 1934 {
michael@0 1935 nsIContent *firstNode = nullptr;
michael@0 1936 nsIContent *parent = aElement->GetParent();
michael@0 1937 if (parent) {
michael@0 1938 if (aTreeMatchContext.mForStyling)
michael@0 1939 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
michael@0 1940
michael@0 1941 int32_t index = -1;
michael@0 1942 do {
michael@0 1943 firstNode = parent->GetChildAt(++index);
michael@0 1944 // stop at first non-comment and non-whitespace node
michael@0 1945 } while (firstNode &&
michael@0 1946 !IsSignificantChild(firstNode, true, false));
michael@0 1947 }
michael@0 1948 if (aElement != firstNode) {
michael@0 1949 return false;
michael@0 1950 }
michael@0 1951 }
michael@0 1952 break;
michael@0 1953
michael@0 1954 case nsCSSPseudoClasses::ePseudoClass_lastChild:
michael@0 1955 if (!edgeChildMatches(aElement, aTreeMatchContext, false, true)) {
michael@0 1956 return false;
michael@0 1957 }
michael@0 1958 break;
michael@0 1959
michael@0 1960 case nsCSSPseudoClasses::ePseudoClass_lastNode:
michael@0 1961 {
michael@0 1962 nsIContent *lastNode = nullptr;
michael@0 1963 nsIContent *parent = aElement->GetParent();
michael@0 1964 if (parent) {
michael@0 1965 if (aTreeMatchContext.mForStyling)
michael@0 1966 parent->SetFlags(NODE_HAS_EDGE_CHILD_SELECTOR);
michael@0 1967
michael@0 1968 uint32_t index = parent->GetChildCount();
michael@0 1969 do {
michael@0 1970 lastNode = parent->GetChildAt(--index);
michael@0 1971 // stop at first non-comment and non-whitespace node
michael@0 1972 } while (lastNode &&
michael@0 1973 !IsSignificantChild(lastNode, true, false));
michael@0 1974 }
michael@0 1975 if (aElement != lastNode) {
michael@0 1976 return false;
michael@0 1977 }
michael@0 1978 }
michael@0 1979 break;
michael@0 1980
michael@0 1981 case nsCSSPseudoClasses::ePseudoClass_onlyChild:
michael@0 1982 if (!edgeChildMatches(aElement, aTreeMatchContext, true, true)) {
michael@0 1983 return false;
michael@0 1984 }
michael@0 1985 break;
michael@0 1986
michael@0 1987 case nsCSSPseudoClasses::ePseudoClass_firstOfType:
michael@0 1988 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, false)) {
michael@0 1989 return false;
michael@0 1990 }
michael@0 1991 break;
michael@0 1992
michael@0 1993 case nsCSSPseudoClasses::ePseudoClass_lastOfType:
michael@0 1994 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, false, true)) {
michael@0 1995 return false;
michael@0 1996 }
michael@0 1997 break;
michael@0 1998
michael@0 1999 case nsCSSPseudoClasses::ePseudoClass_onlyOfType:
michael@0 2000 if (!edgeOfTypeMatches(aElement, aTreeMatchContext, true, true)) {
michael@0 2001 return false;
michael@0 2002 }
michael@0 2003 break;
michael@0 2004
michael@0 2005 case nsCSSPseudoClasses::ePseudoClass_nthChild:
michael@0 2006 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
michael@0 2007 false, false)) {
michael@0 2008 return false;
michael@0 2009 }
michael@0 2010 break;
michael@0 2011
michael@0 2012 case nsCSSPseudoClasses::ePseudoClass_nthLastChild:
michael@0 2013 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
michael@0 2014 false, true)) {
michael@0 2015 return false;
michael@0 2016 }
michael@0 2017 break;
michael@0 2018
michael@0 2019 case nsCSSPseudoClasses::ePseudoClass_nthOfType:
michael@0 2020 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
michael@0 2021 true, false)) {
michael@0 2022 return false;
michael@0 2023 }
michael@0 2024 break;
michael@0 2025
michael@0 2026 case nsCSSPseudoClasses::ePseudoClass_nthLastOfType:
michael@0 2027 if (!nthChildGenericMatches(aElement, aTreeMatchContext, pseudoClass,
michael@0 2028 true, true)) {
michael@0 2029 return false;
michael@0 2030 }
michael@0 2031 break;
michael@0 2032
michael@0 2033 case nsCSSPseudoClasses::ePseudoClass_mozIsHTML:
michael@0 2034 if (!aTreeMatchContext.mIsHTMLDocument || !aElement->IsHTML()) {
michael@0 2035 return false;
michael@0 2036 }
michael@0 2037 break;
michael@0 2038
michael@0 2039 case nsCSSPseudoClasses::ePseudoClass_mozSystemMetric:
michael@0 2040 {
michael@0 2041 nsCOMPtr<nsIAtom> metric = do_GetAtom(pseudoClass->u.mString);
michael@0 2042 if (!nsCSSRuleProcessor::HasSystemMetric(metric)) {
michael@0 2043 return false;
michael@0 2044 }
michael@0 2045 }
michael@0 2046 break;
michael@0 2047
michael@0 2048 case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir:
michael@0 2049 {
michael@0 2050 bool docIsRTL =
michael@0 2051 aTreeMatchContext.mDocument->GetDocumentState().
michael@0 2052 HasState(NS_DOCUMENT_STATE_RTL_LOCALE);
michael@0 2053
michael@0 2054 nsDependentString dirString(pseudoClass->u.mString);
michael@0 2055 NS_ASSERTION(dirString.EqualsLiteral("ltr") ||
michael@0 2056 dirString.EqualsLiteral("rtl"),
michael@0 2057 "invalid value for -moz-locale-dir");
michael@0 2058
michael@0 2059 if (dirString.EqualsLiteral("rtl") != docIsRTL) {
michael@0 2060 return false;
michael@0 2061 }
michael@0 2062 }
michael@0 2063 break;
michael@0 2064
michael@0 2065 case nsCSSPseudoClasses::ePseudoClass_mozLWTheme:
michael@0 2066 {
michael@0 2067 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() <=
michael@0 2068 nsIDocument::Doc_Theme_None) {
michael@0 2069 return false;
michael@0 2070 }
michael@0 2071 }
michael@0 2072 break;
michael@0 2073
michael@0 2074 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeBrightText:
michael@0 2075 {
michael@0 2076 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
michael@0 2077 nsIDocument::Doc_Theme_Bright) {
michael@0 2078 return false;
michael@0 2079 }
michael@0 2080 }
michael@0 2081 break;
michael@0 2082
michael@0 2083 case nsCSSPseudoClasses::ePseudoClass_mozLWThemeDarkText:
michael@0 2084 {
michael@0 2085 if (aTreeMatchContext.mDocument->GetDocumentLWTheme() !=
michael@0 2086 nsIDocument::Doc_Theme_Dark) {
michael@0 2087 return false;
michael@0 2088 }
michael@0 2089 }
michael@0 2090 break;
michael@0 2091
michael@0 2092 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive:
michael@0 2093 if (!aTreeMatchContext.mDocument->GetDocumentState().
michael@0 2094 HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
michael@0 2095 return false;
michael@0 2096 }
michael@0 2097 break;
michael@0 2098
michael@0 2099 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero:
michael@0 2100 {
michael@0 2101 if (!aElement->IsHTML(nsGkAtoms::table)) {
michael@0 2102 return false;
michael@0 2103 }
michael@0 2104 const nsAttrValue *val = aElement->GetParsedAttr(nsGkAtoms::border);
michael@0 2105 if (!val ||
michael@0 2106 (val->Type() == nsAttrValue::eInteger &&
michael@0 2107 val->GetIntegerValue() == 0)) {
michael@0 2108 return false;
michael@0 2109 }
michael@0 2110 }
michael@0 2111 break;
michael@0 2112
michael@0 2113 case nsCSSPseudoClasses::ePseudoClass_dir:
michael@0 2114 {
michael@0 2115 if (aDependence) {
michael@0 2116 EventStates states
michael@0 2117 = sPseudoClassStateDependences[pseudoClass->mType];
michael@0 2118 if (aNodeMatchContext.mStateMask.HasAtLeastOneOfStates(states)) {
michael@0 2119 *aDependence = true;
michael@0 2120 return false;
michael@0 2121 }
michael@0 2122 }
michael@0 2123
michael@0 2124 // if we only had to consider HTML, directionality would be exclusively
michael@0 2125 // LTR or RTL, and this could be just
michael@0 2126 //
michael@0 2127 // if (dirString.EqualsLiteral("rtl") !=
michael@0 2128 // aElement->StyleState().HasState(NS_EVENT_STATE_RTL)
michael@0 2129 //
michael@0 2130 // However, in markup languages where there is no direction attribute
michael@0 2131 // we have to consider the possibility that neither -moz-dir(rtl) nor
michael@0 2132 // -moz-dir(ltr) matches.
michael@0 2133 EventStates state = aElement->StyleState();
michael@0 2134 bool elementIsRTL = state.HasState(NS_EVENT_STATE_RTL);
michael@0 2135 bool elementIsLTR = state.HasState(NS_EVENT_STATE_LTR);
michael@0 2136 nsDependentString dirString(pseudoClass->u.mString);
michael@0 2137
michael@0 2138 if ((dirString.EqualsLiteral("rtl") && !elementIsRTL) ||
michael@0 2139 (dirString.EqualsLiteral("ltr") && !elementIsLTR)) {
michael@0 2140 return false;
michael@0 2141 }
michael@0 2142 }
michael@0 2143 break;
michael@0 2144
michael@0 2145 case nsCSSPseudoClasses::ePseudoClass_scope:
michael@0 2146 if (aTreeMatchContext.mForScopedStyle) {
michael@0 2147 if (aTreeMatchContext.mCurrentStyleScope) {
michael@0 2148 // If mCurrentStyleScope is null, aElement must be the style
michael@0 2149 // scope root. This is because the PopStyleScopeForSelectorMatching
michael@0 2150 // call in SelectorMatchesTree sets mCurrentStyleScope to null
michael@0 2151 // as soon as we visit the style scope element, and we won't
michael@0 2152 // progress further up the tree after this call to
michael@0 2153 // SelectorMatches. Thus if mCurrentStyleScope is still set,
michael@0 2154 // we know the selector does not match.
michael@0 2155 return false;
michael@0 2156 }
michael@0 2157 } else if (aTreeMatchContext.HasSpecifiedScope()) {
michael@0 2158 if (!aTreeMatchContext.IsScopeElement(aElement)) {
michael@0 2159 return false;
michael@0 2160 }
michael@0 2161 } else {
michael@0 2162 if (aElement != aElement->OwnerDoc()->GetRootElement()) {
michael@0 2163 return false;
michael@0 2164 }
michael@0 2165 }
michael@0 2166 break;
michael@0 2167
michael@0 2168 default:
michael@0 2169 NS_ABORT_IF_FALSE(false, "How did that happen?");
michael@0 2170 }
michael@0 2171 } else {
michael@0 2172 if (!StateSelectorMatches(aElement, aSelector, aNodeMatchContext,
michael@0 2173 aTreeMatchContext, aDependence,
michael@0 2174 statesToCheck)) {
michael@0 2175 return false;
michael@0 2176 }
michael@0 2177 }
michael@0 2178 }
michael@0 2179
michael@0 2180 bool result = true;
michael@0 2181 if (aSelector->mAttrList) {
michael@0 2182 // test for attribute match
michael@0 2183 if (!aElement->HasAttrs()) {
michael@0 2184 // if no attributes on the content, no match
michael@0 2185 return false;
michael@0 2186 } else {
michael@0 2187 result = true;
michael@0 2188 nsAttrSelector* attr = aSelector->mAttrList;
michael@0 2189 nsIAtom* matchAttribute;
michael@0 2190
michael@0 2191 do {
michael@0 2192 bool isHTML =
michael@0 2193 (aTreeMatchContext.mIsHTMLDocument && aElement->IsHTML());
michael@0 2194 matchAttribute = isHTML ? attr->mLowercaseAttr : attr->mCasedAttr;
michael@0 2195 if (attr->mNameSpace == kNameSpaceID_Unknown) {
michael@0 2196 // Attr selector with a wildcard namespace. We have to examine all
michael@0 2197 // the attributes on our content node.... This sort of selector is
michael@0 2198 // essentially a boolean OR, over all namespaces, of equivalent attr
michael@0 2199 // selectors with those namespaces. So to evaluate whether it
michael@0 2200 // matches, evaluate for each namespace (the only namespaces that
michael@0 2201 // have a chance at matching, of course, are ones that the element
michael@0 2202 // actually has attributes in), short-circuiting if we ever match.
michael@0 2203 result = false;
michael@0 2204 const nsAttrName* attrName;
michael@0 2205 for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
michael@0 2206 if (attrName->LocalName() != matchAttribute) {
michael@0 2207 continue;
michael@0 2208 }
michael@0 2209 if (attr->mFunction == NS_ATTR_FUNC_SET) {
michael@0 2210 result = true;
michael@0 2211 } else {
michael@0 2212 nsAutoString value;
michael@0 2213 #ifdef DEBUG
michael@0 2214 bool hasAttr =
michael@0 2215 #endif
michael@0 2216 aElement->GetAttr(attrName->NamespaceID(),
michael@0 2217 attrName->LocalName(), value);
michael@0 2218 NS_ASSERTION(hasAttr, "GetAttrNameAt lied");
michael@0 2219 result = AttrMatchesValue(attr, value, isHTML);
michael@0 2220 }
michael@0 2221
michael@0 2222 // At this point |result| has been set by us
michael@0 2223 // explicitly in this loop. If it's false, we may still match
michael@0 2224 // -- the content may have another attribute with the same name but
michael@0 2225 // in a different namespace. But if it's true, we are done (we
michael@0 2226 // can short-circuit the boolean OR described above).
michael@0 2227 if (result) {
michael@0 2228 break;
michael@0 2229 }
michael@0 2230 }
michael@0 2231 }
michael@0 2232 else if (attr->mFunction == NS_ATTR_FUNC_EQUALS) {
michael@0 2233 result =
michael@0 2234 aElement->
michael@0 2235 AttrValueIs(attr->mNameSpace, matchAttribute, attr->mValue,
michael@0 2236 (!isHTML || attr->mCaseSensitive) ? eCaseMatters
michael@0 2237 : eIgnoreCase);
michael@0 2238 }
michael@0 2239 else if (!aElement->HasAttr(attr->mNameSpace, matchAttribute)) {
michael@0 2240 result = false;
michael@0 2241 }
michael@0 2242 else if (attr->mFunction != NS_ATTR_FUNC_SET) {
michael@0 2243 nsAutoString value;
michael@0 2244 #ifdef DEBUG
michael@0 2245 bool hasAttr =
michael@0 2246 #endif
michael@0 2247 aElement->GetAttr(attr->mNameSpace, matchAttribute, value);
michael@0 2248 NS_ASSERTION(hasAttr, "HasAttr lied");
michael@0 2249 result = AttrMatchesValue(attr, value, isHTML);
michael@0 2250 }
michael@0 2251
michael@0 2252 attr = attr->mNext;
michael@0 2253 } while (attr && result);
michael@0 2254 }
michael@0 2255 }
michael@0 2256
michael@0 2257 // apply SelectorMatches to the negated selectors in the chain
michael@0 2258 if (!isNegated) {
michael@0 2259 for (nsCSSSelector *negation = aSelector->mNegations;
michael@0 2260 result && negation; negation = negation->mNegations) {
michael@0 2261 bool dependence = false;
michael@0 2262 result = !SelectorMatches(aElement, negation, aNodeMatchContext,
michael@0 2263 aTreeMatchContext, &dependence);
michael@0 2264 // If the selector does match due to the dependence on
michael@0 2265 // aNodeMatchContext.mStateMask, then we want to keep result true
michael@0 2266 // so that the final result of SelectorMatches is true. Doing so
michael@0 2267 // tells StateEnumFunc that there is a dependence on the state.
michael@0 2268 result = result || dependence;
michael@0 2269 }
michael@0 2270 }
michael@0 2271 return result;
michael@0 2272 }
michael@0 2273
michael@0 2274 #undef STATE_CHECK
michael@0 2275
michael@0 2276 // Right now, there are four operators:
michael@0 2277 // ' ', the descendant combinator, is greedy
michael@0 2278 // '~', the indirect adjacent sibling combinator, is greedy
michael@0 2279 // '+' and '>', the direct adjacent sibling and child combinators, are not
michael@0 2280 #define NS_IS_GREEDY_OPERATOR(ch) \
michael@0 2281 ((ch) == char16_t(' ') || (ch) == char16_t('~'))
michael@0 2282
michael@0 2283 static bool SelectorMatchesTree(Element* aPrevElement,
michael@0 2284 nsCSSSelector* aSelector,
michael@0 2285 TreeMatchContext& aTreeMatchContext,
michael@0 2286 bool aLookForRelevantLink)
michael@0 2287 {
michael@0 2288 MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
michael@0 2289 nsCSSSelector* selector = aSelector;
michael@0 2290 Element* prevElement = aPrevElement;
michael@0 2291 while (selector) { // check compound selectors
michael@0 2292 NS_ASSERTION(!selector->mNext ||
michael@0 2293 selector->mNext->mOperator != char16_t(0),
michael@0 2294 "compound selector without combinator");
michael@0 2295
michael@0 2296 // If after the previous selector match we are now outside the
michael@0 2297 // current style scope, we don't need to match any further.
michael@0 2298 if (aTreeMatchContext.mForScopedStyle &&
michael@0 2299 !aTreeMatchContext.IsWithinStyleScopeForSelectorMatching()) {
michael@0 2300 return false;
michael@0 2301 }
michael@0 2302
michael@0 2303 // for adjacent sibling combinators, the content to test against the
michael@0 2304 // selector is the previous sibling *element*
michael@0 2305 Element* element = nullptr;
michael@0 2306 if (char16_t('+') == selector->mOperator ||
michael@0 2307 char16_t('~') == selector->mOperator) {
michael@0 2308 // The relevant link must be an ancestor of the node being matched.
michael@0 2309 aLookForRelevantLink = false;
michael@0 2310 nsIContent* parent = prevElement->GetParent();
michael@0 2311 if (parent) {
michael@0 2312 if (aTreeMatchContext.mForStyling)
michael@0 2313 parent->SetFlags(NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
michael@0 2314
michael@0 2315 element = prevElement->GetPreviousElementSibling();
michael@0 2316 }
michael@0 2317 }
michael@0 2318 // for descendant combinators and child combinators, the element
michael@0 2319 // to test against is the parent
michael@0 2320 else {
michael@0 2321 nsIContent *content = prevElement->GetParent();
michael@0 2322 // GetParent could return a document fragment; we only want
michael@0 2323 // element parents.
michael@0 2324 if (content && content->IsElement()) {
michael@0 2325 element = content->AsElement();
michael@0 2326 if (aTreeMatchContext.mForScopedStyle) {
michael@0 2327 // We are moving up to the parent element; tell the
michael@0 2328 // TreeMatchContext, so that in case this element is the
michael@0 2329 // style scope element, selector matching stops before we
michael@0 2330 // traverse further up the tree.
michael@0 2331 aTreeMatchContext.PopStyleScopeForSelectorMatching(element);
michael@0 2332 }
michael@0 2333
michael@0 2334 // Compatibility hack: First try matching this selector as though the
michael@0 2335 // <xbl:children> element wasn't in the tree to allow old selectors
michael@0 2336 // were written before <xbl:children> participated in CSS selector
michael@0 2337 // matching to work.
michael@0 2338 if (selector->mOperator == '>' && element->IsActiveChildrenElement()) {
michael@0 2339 Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
michael@0 2340 if (SelectorMatchesTree(element, selector, aTreeMatchContext,
michael@0 2341 aLookForRelevantLink)) {
michael@0 2342 // It matched, don't try matching on the <xbl:children> element at
michael@0 2343 // all.
michael@0 2344 return true;
michael@0 2345 }
michael@0 2346 // We want to reset mCurrentStyleScope on aTreeMatchContext
michael@0 2347 // back to its state before the SelectorMatchesTree call, in
michael@0 2348 // case that call happens to traverse past the style scope element
michael@0 2349 // and sets it to null.
michael@0 2350 aTreeMatchContext.mCurrentStyleScope = styleScope;
michael@0 2351 }
michael@0 2352 }
michael@0 2353 }
michael@0 2354 if (!element) {
michael@0 2355 return false;
michael@0 2356 }
michael@0 2357 NodeMatchContext nodeContext(EventStates(),
michael@0 2358 aLookForRelevantLink &&
michael@0 2359 nsCSSRuleProcessor::IsLink(element));
michael@0 2360 if (nodeContext.mIsRelevantLink) {
michael@0 2361 // If we find an ancestor of the matched node that is a link
michael@0 2362 // during the matching process, then it's the relevant link (see
michael@0 2363 // constructor call above).
michael@0 2364 // Since we are still matching against selectors that contain
michael@0 2365 // :visited (they'll just fail), we will always find such a node
michael@0 2366 // during the selector matching process if there is a relevant
michael@0 2367 // link that can influence selector matching.
michael@0 2368 aLookForRelevantLink = false;
michael@0 2369 aTreeMatchContext.SetHaveRelevantLink();
michael@0 2370 }
michael@0 2371 if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext)) {
michael@0 2372 // to avoid greedy matching, we need to recur if this is a
michael@0 2373 // descendant or general sibling combinator and the next
michael@0 2374 // combinator is different, but we can make an exception for
michael@0 2375 // sibling, then parent, since a sibling's parent is always the
michael@0 2376 // same.
michael@0 2377 if (NS_IS_GREEDY_OPERATOR(selector->mOperator) &&
michael@0 2378 selector->mNext &&
michael@0 2379 selector->mNext->mOperator != selector->mOperator &&
michael@0 2380 !(selector->mOperator == '~' &&
michael@0 2381 NS_IS_ANCESTOR_OPERATOR(selector->mNext->mOperator))) {
michael@0 2382
michael@0 2383 // pretend the selector didn't match, and step through content
michael@0 2384 // while testing the same selector
michael@0 2385
michael@0 2386 // This approach is slightly strange in that when it recurs
michael@0 2387 // it tests from the top of the content tree, down. This
michael@0 2388 // doesn't matter much for performance since most selectors
michael@0 2389 // don't match. (If most did, it might be faster...)
michael@0 2390 Element* styleScope = aTreeMatchContext.mCurrentStyleScope;
michael@0 2391 if (SelectorMatchesTree(element, selector, aTreeMatchContext,
michael@0 2392 aLookForRelevantLink)) {
michael@0 2393 return true;
michael@0 2394 }
michael@0 2395 // We want to reset mCurrentStyleScope on aTreeMatchContext
michael@0 2396 // back to its state before the SelectorMatchesTree call, in
michael@0 2397 // case that call happens to traverse past the style scope element
michael@0 2398 // and sets it to null.
michael@0 2399 aTreeMatchContext.mCurrentStyleScope = styleScope;
michael@0 2400 }
michael@0 2401 selector = selector->mNext;
michael@0 2402 }
michael@0 2403 else {
michael@0 2404 // for adjacent sibling and child combinators, if we didn't find
michael@0 2405 // a match, we're done
michael@0 2406 if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
michael@0 2407 return false; // parent was required to match
michael@0 2408 }
michael@0 2409 }
michael@0 2410 prevElement = element;
michael@0 2411 }
michael@0 2412 return true; // all the selectors matched.
michael@0 2413 }
michael@0 2414
michael@0 2415 static inline
michael@0 2416 void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
michael@0 2417 ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
michael@0 2418 AncestorFilter *ancestorFilter)
michael@0 2419 {
michael@0 2420 if (nodeContext.mIsRelevantLink) {
michael@0 2421 data->mTreeMatchContext.SetHaveRelevantLink();
michael@0 2422 }
michael@0 2423 if (ancestorFilter &&
michael@0 2424 !ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
michael@0 2425 value.mAncestorSelectorHashes)) {
michael@0 2426 // We won't match; nothing else to do here
michael@0 2427 return;
michael@0 2428 }
michael@0 2429 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
michael@0 2430 data->mScope)) {
michael@0 2431 // The selector is for a rule in a scoped style sheet, and the subject
michael@0 2432 // of the selector matching is not in its scope.
michael@0 2433 return;
michael@0 2434 }
michael@0 2435 nsCSSSelector* selector = aSelector;
michael@0 2436 if (selector->IsPseudoElement()) {
michael@0 2437 PseudoElementRuleProcessorData* pdata =
michael@0 2438 static_cast<PseudoElementRuleProcessorData*>(data);
michael@0 2439 if (!pdata->mPseudoElement && selector->mPseudoClassList) {
michael@0 2440 // We can get here when calling getComputedStyle(aElt, aPseudo) if:
michael@0 2441 //
michael@0 2442 // * aPseudo is a pseudo-element that supports a user action
michael@0 2443 // pseudo-class, like "::-moz-placeholder";
michael@0 2444 // * there is a style rule that uses a pseudo-class on this
michael@0 2445 // pseudo-element in the document, like ::-moz-placeholder:hover; and
michael@0 2446 // * aElt does not have such a pseudo-element.
michael@0 2447 //
michael@0 2448 // We know that the selector can't match, since there is no element for
michael@0 2449 // the user action pseudo-class to match against.
michael@0 2450 return;
michael@0 2451 }
michael@0 2452 if (!StateSelectorMatches(pdata->mPseudoElement, aSelector, nodeContext,
michael@0 2453 data->mTreeMatchContext)) {
michael@0 2454 return;
michael@0 2455 }
michael@0 2456 selector = selector->mNext;
michael@0 2457 }
michael@0 2458 if (SelectorMatches(data->mElement, selector, nodeContext,
michael@0 2459 data->mTreeMatchContext)) {
michael@0 2460 nsCSSSelector *next = selector->mNext;
michael@0 2461 if (!next || SelectorMatchesTree(data->mElement, next,
michael@0 2462 data->mTreeMatchContext,
michael@0 2463 !nodeContext.mIsRelevantLink)) {
michael@0 2464 css::StyleRule *rule = value.mRule;
michael@0 2465 rule->RuleMatched();
michael@0 2466 data->mRuleWalker->Forward(rule);
michael@0 2467 // nsStyleSet will deal with the !important rule
michael@0 2468 }
michael@0 2469 }
michael@0 2470 }
michael@0 2471
michael@0 2472 /* virtual */ void
michael@0 2473 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
michael@0 2474 {
michael@0 2475 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2476
michael@0 2477 if (cascade) {
michael@0 2478 NodeMatchContext nodeContext(EventStates(),
michael@0 2479 nsCSSRuleProcessor::IsLink(aData->mElement));
michael@0 2480 cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
michael@0 2481 }
michael@0 2482 }
michael@0 2483
michael@0 2484 /* virtual */ void
michael@0 2485 nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
michael@0 2486 {
michael@0 2487 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2488
michael@0 2489 if (cascade) {
michael@0 2490 RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[aData->mPseudoType];
michael@0 2491 if (ruleHash) {
michael@0 2492 NodeMatchContext nodeContext(EventStates(),
michael@0 2493 nsCSSRuleProcessor::IsLink(aData->mElement));
michael@0 2494 ruleHash->EnumerateAllRules(aData->mElement, aData, nodeContext);
michael@0 2495 }
michael@0 2496 }
michael@0 2497 }
michael@0 2498
michael@0 2499 /* virtual */ void
michael@0 2500 nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData)
michael@0 2501 {
michael@0 2502 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2503
michael@0 2504 if (cascade && cascade->mAnonBoxRules.entryCount) {
michael@0 2505 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
michael@0 2506 (PL_DHashTableOperate(&cascade->mAnonBoxRules, aData->mPseudoTag,
michael@0 2507 PL_DHASH_LOOKUP));
michael@0 2508 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 2509 nsTArray<RuleValue>& rules = entry->mRules;
michael@0 2510 for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
michael@0 2511 value != end; ++value) {
michael@0 2512 value->mRule->RuleMatched();
michael@0 2513 aData->mRuleWalker->Forward(value->mRule);
michael@0 2514 }
michael@0 2515 }
michael@0 2516 }
michael@0 2517 }
michael@0 2518
michael@0 2519 #ifdef MOZ_XUL
michael@0 2520 /* virtual */ void
michael@0 2521 nsCSSRuleProcessor::RulesMatching(XULTreeRuleProcessorData* aData)
michael@0 2522 {
michael@0 2523 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2524
michael@0 2525 if (cascade && cascade->mXULTreeRules.entryCount) {
michael@0 2526 RuleHashTagTableEntry* entry = static_cast<RuleHashTagTableEntry*>
michael@0 2527 (PL_DHashTableOperate(&cascade->mXULTreeRules, aData->mPseudoTag,
michael@0 2528 PL_DHASH_LOOKUP));
michael@0 2529 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 2530 NodeMatchContext nodeContext(EventStates(),
michael@0 2531 nsCSSRuleProcessor::IsLink(aData->mElement));
michael@0 2532 nsTArray<RuleValue>& rules = entry->mRules;
michael@0 2533 for (RuleValue *value = rules.Elements(), *end = value + rules.Length();
michael@0 2534 value != end; ++value) {
michael@0 2535 if (aData->mComparator->PseudoMatches(value->mSelector)) {
michael@0 2536 ContentEnumFunc(*value, value->mSelector->mNext, aData, nodeContext,
michael@0 2537 nullptr);
michael@0 2538 }
michael@0 2539 }
michael@0 2540 }
michael@0 2541 }
michael@0 2542 }
michael@0 2543 #endif
michael@0 2544
michael@0 2545 static inline nsRestyleHint RestyleHintForOp(char16_t oper)
michael@0 2546 {
michael@0 2547 if (oper == char16_t('+') || oper == char16_t('~')) {
michael@0 2548 return eRestyle_LaterSiblings;
michael@0 2549 }
michael@0 2550
michael@0 2551 if (oper != char16_t(0)) {
michael@0 2552 return eRestyle_Subtree;
michael@0 2553 }
michael@0 2554
michael@0 2555 return eRestyle_Self;
michael@0 2556 }
michael@0 2557
michael@0 2558 nsRestyleHint
michael@0 2559 nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aData,
michael@0 2560 Element* aStatefulElement,
michael@0 2561 nsCSSPseudoElements::Type aPseudoType,
michael@0 2562 EventStates aStateMask)
michael@0 2563 {
michael@0 2564 MOZ_ASSERT(!aData->mTreeMatchContext.mForScopedStyle,
michael@0 2565 "mCurrentStyleScope will need to be saved and restored after the "
michael@0 2566 "SelectorMatchesTree call");
michael@0 2567
michael@0 2568 bool isPseudoElement =
michael@0 2569 aPseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement;
michael@0 2570
michael@0 2571 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2572
michael@0 2573 // Look up the content node in the state rule list, which points to
michael@0 2574 // any (CSS2 definition) simple selector (whether or not it is the
michael@0 2575 // subject) that has a state pseudo-class on it. This means that this
michael@0 2576 // code will be matching selectors that aren't real selectors in any
michael@0 2577 // stylesheet (e.g., if there is a selector "body > p:hover > a", then
michael@0 2578 // "body > p:hover" will be in |cascade->mStateSelectors|). Note that
michael@0 2579 // |ComputeSelectorStateDependence| below determines which selectors are in
michael@0 2580 // |cascade->mStateSelectors|.
michael@0 2581 nsRestyleHint hint = nsRestyleHint(0);
michael@0 2582 if (cascade) {
michael@0 2583 StateSelector *iter = cascade->mStateSelectors.Elements(),
michael@0 2584 *end = iter + cascade->mStateSelectors.Length();
michael@0 2585 NodeMatchContext nodeContext(aStateMask, false);
michael@0 2586 for(; iter != end; ++iter) {
michael@0 2587 nsCSSSelector* selector = iter->mSelector;
michael@0 2588 EventStates states = iter->mStates;
michael@0 2589
michael@0 2590 if (selector->IsPseudoElement() != isPseudoElement) {
michael@0 2591 continue;
michael@0 2592 }
michael@0 2593
michael@0 2594 nsCSSSelector* selectorForPseudo;
michael@0 2595 if (isPseudoElement) {
michael@0 2596 if (selector->PseudoType() != aPseudoType) {
michael@0 2597 continue;
michael@0 2598 }
michael@0 2599 selectorForPseudo = selector;
michael@0 2600 selector = selector->mNext;
michael@0 2601 }
michael@0 2602
michael@0 2603 nsRestyleHint possibleChange = RestyleHintForOp(selector->mOperator);
michael@0 2604
michael@0 2605 // If hint already includes all the bits of possibleChange,
michael@0 2606 // don't bother calling SelectorMatches, since even if it returns false
michael@0 2607 // hint won't change.
michael@0 2608 // Also don't bother calling SelectorMatches if none of the
michael@0 2609 // states passed in are relevant here.
michael@0 2610 if ((possibleChange & ~hint) &&
michael@0 2611 states.HasAtLeastOneOfStates(aStateMask) &&
michael@0 2612 // We can optimize away testing selectors that only involve :hover, a
michael@0 2613 // namespace, and a tag name against nodes that don't have the
michael@0 2614 // NodeHasRelevantHoverRules flag: such a selector didn't match
michael@0 2615 // the tag name or namespace the first time around (since the :hover
michael@0 2616 // didn't set the NodeHasRelevantHoverRules flag), so it won't
michael@0 2617 // match it now. Check for our selector only having :hover states, or
michael@0 2618 // the element having the hover rules flag, or the selector having
michael@0 2619 // some sort of non-namespace, non-tagname data in it.
michael@0 2620 (states != NS_EVENT_STATE_HOVER ||
michael@0 2621 aStatefulElement->HasRelevantHoverRules() ||
michael@0 2622 selector->mIDList || selector->mClassList ||
michael@0 2623 // We generally expect an mPseudoClassList, since we have a :hover.
michael@0 2624 // The question is whether we have anything else in there.
michael@0 2625 (selector->mPseudoClassList &&
michael@0 2626 (selector->mPseudoClassList->mNext ||
michael@0 2627 selector->mPseudoClassList->mType !=
michael@0 2628 nsCSSPseudoClasses::ePseudoClass_hover)) ||
michael@0 2629 selector->mAttrList || selector->mNegations) &&
michael@0 2630 (!isPseudoElement ||
michael@0 2631 StateSelectorMatches(aStatefulElement, selectorForPseudo,
michael@0 2632 nodeContext, aData->mTreeMatchContext,
michael@0 2633 nullptr, aStateMask)) &&
michael@0 2634 SelectorMatches(aData->mElement, selector, nodeContext,
michael@0 2635 aData->mTreeMatchContext) &&
michael@0 2636 SelectorMatchesTree(aData->mElement, selector->mNext,
michael@0 2637 aData->mTreeMatchContext,
michael@0 2638 false))
michael@0 2639 {
michael@0 2640 hint = nsRestyleHint(hint | possibleChange);
michael@0 2641 }
michael@0 2642 }
michael@0 2643 }
michael@0 2644 return hint;
michael@0 2645 }
michael@0 2646
michael@0 2647 nsRestyleHint
michael@0 2648 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData)
michael@0 2649 {
michael@0 2650 return HasStateDependentStyle(aData,
michael@0 2651 aData->mElement,
michael@0 2652 nsCSSPseudoElements::ePseudo_NotPseudoElement,
michael@0 2653 aData->mStateMask);
michael@0 2654 }
michael@0 2655
michael@0 2656 nsRestyleHint
michael@0 2657 nsCSSRuleProcessor::HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData)
michael@0 2658 {
michael@0 2659 return HasStateDependentStyle(aData,
michael@0 2660 aData->mPseudoElement,
michael@0 2661 aData->mPseudoType,
michael@0 2662 aData->mStateMask);
michael@0 2663 }
michael@0 2664
michael@0 2665 bool
michael@0 2666 nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData)
michael@0 2667 {
michael@0 2668 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2669
michael@0 2670 return cascade && cascade->mSelectorDocumentStates.HasAtLeastOneOfStates(aData->mStateMask);
michael@0 2671 }
michael@0 2672
michael@0 2673 struct AttributeEnumData {
michael@0 2674 AttributeEnumData(AttributeRuleProcessorData *aData)
michael@0 2675 : data(aData), change(nsRestyleHint(0)) {}
michael@0 2676
michael@0 2677 AttributeRuleProcessorData *data;
michael@0 2678 nsRestyleHint change;
michael@0 2679 };
michael@0 2680
michael@0 2681
michael@0 2682 static void
michael@0 2683 AttributeEnumFunc(nsCSSSelector* aSelector, AttributeEnumData* aData)
michael@0 2684 {
michael@0 2685 AttributeRuleProcessorData *data = aData->data;
michael@0 2686
michael@0 2687 if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
michael@0 2688 data->mScope)) {
michael@0 2689 // The selector is for a rule in a scoped style sheet, and the subject
michael@0 2690 // of the selector matching is not in its scope.
michael@0 2691 return;
michael@0 2692 }
michael@0 2693
michael@0 2694 nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator);
michael@0 2695
michael@0 2696 // If enumData->change already includes all the bits of possibleChange, don't
michael@0 2697 // bother calling SelectorMatches, since even if it returns false
michael@0 2698 // enumData->change won't change.
michael@0 2699 NodeMatchContext nodeContext(EventStates(), false);
michael@0 2700 if ((possibleChange & ~(aData->change)) &&
michael@0 2701 SelectorMatches(data->mElement, aSelector, nodeContext,
michael@0 2702 data->mTreeMatchContext) &&
michael@0 2703 SelectorMatchesTree(data->mElement, aSelector->mNext,
michael@0 2704 data->mTreeMatchContext, false)) {
michael@0 2705 aData->change = nsRestyleHint(aData->change | possibleChange);
michael@0 2706 }
michael@0 2707 }
michael@0 2708
michael@0 2709 static MOZ_ALWAYS_INLINE void
michael@0 2710 EnumerateSelectors(nsTArray<nsCSSSelector*>& aSelectors, AttributeEnumData* aData)
michael@0 2711 {
michael@0 2712 nsCSSSelector **iter = aSelectors.Elements(),
michael@0 2713 **end = iter + aSelectors.Length();
michael@0 2714 for (; iter != end; ++iter) {
michael@0 2715 AttributeEnumFunc(*iter, aData);
michael@0 2716 }
michael@0 2717 }
michael@0 2718
michael@0 2719 nsRestyleHint
michael@0 2720 nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData)
michael@0 2721 {
michael@0 2722 // We could try making use of aData->mModType, but :not rules make it a bit
michael@0 2723 // of a pain to do so... So just ignore it for now.
michael@0 2724
michael@0 2725 AttributeEnumData data(aData);
michael@0 2726
michael@0 2727 // Don't do our special handling of certain attributes if the attr
michael@0 2728 // hasn't changed yet.
michael@0 2729 if (aData->mAttrHasChanged) {
michael@0 2730 // check for the lwtheme and lwthemetextcolor attribute on root XUL elements
michael@0 2731 if ((aData->mAttribute == nsGkAtoms::lwtheme ||
michael@0 2732 aData->mAttribute == nsGkAtoms::lwthemetextcolor) &&
michael@0 2733 aData->mElement->GetNameSpaceID() == kNameSpaceID_XUL &&
michael@0 2734 aData->mElement == aData->mElement->OwnerDoc()->GetRootElement())
michael@0 2735 {
michael@0 2736 data.change = nsRestyleHint(data.change | eRestyle_Subtree);
michael@0 2737 }
michael@0 2738
michael@0 2739 // We don't know the namespace of the attribute, and xml:lang applies to
michael@0 2740 // all elements. If the lang attribute changes, we need to restyle our
michael@0 2741 // whole subtree, since the :lang selector on our descendants can examine
michael@0 2742 // our lang attribute.
michael@0 2743 if (aData->mAttribute == nsGkAtoms::lang) {
michael@0 2744 data.change = nsRestyleHint(data.change | eRestyle_Subtree);
michael@0 2745 }
michael@0 2746 }
michael@0 2747
michael@0 2748 RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
michael@0 2749
michael@0 2750 // Since we get both before and after notifications for attributes, we
michael@0 2751 // don't have to ignore aData->mAttribute while matching. Just check
michael@0 2752 // whether we have selectors relevant to aData->mAttribute that we
michael@0 2753 // match. If this is the before change notification, that will catch
michael@0 2754 // rules we might stop matching; if the after change notification, the
michael@0 2755 // ones we might have started matching.
michael@0 2756 if (cascade) {
michael@0 2757 if (aData->mAttribute == aData->mElement->GetIDAttributeName()) {
michael@0 2758 nsIAtom* id = aData->mElement->GetID();
michael@0 2759 if (id) {
michael@0 2760 AtomSelectorEntry *entry =
michael@0 2761 static_cast<AtomSelectorEntry*>
michael@0 2762 (PL_DHashTableOperate(&cascade->mIdSelectors,
michael@0 2763 id, PL_DHASH_LOOKUP));
michael@0 2764 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 2765 EnumerateSelectors(entry->mSelectors, &data);
michael@0 2766 }
michael@0 2767 }
michael@0 2768
michael@0 2769 EnumerateSelectors(cascade->mPossiblyNegatedIDSelectors, &data);
michael@0 2770 }
michael@0 2771
michael@0 2772 if (aData->mAttribute == aData->mElement->GetClassAttributeName()) {
michael@0 2773 const nsAttrValue* elementClasses = aData->mElement->GetClasses();
michael@0 2774 if (elementClasses) {
michael@0 2775 int32_t atomCount = elementClasses->GetAtomCount();
michael@0 2776 for (int32_t i = 0; i < atomCount; ++i) {
michael@0 2777 nsIAtom* curClass = elementClasses->AtomAt(i);
michael@0 2778 AtomSelectorEntry *entry =
michael@0 2779 static_cast<AtomSelectorEntry*>
michael@0 2780 (PL_DHashTableOperate(&cascade->mClassSelectors,
michael@0 2781 curClass, PL_DHASH_LOOKUP));
michael@0 2782 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 2783 EnumerateSelectors(entry->mSelectors, &data);
michael@0 2784 }
michael@0 2785 }
michael@0 2786 }
michael@0 2787
michael@0 2788 EnumerateSelectors(cascade->mPossiblyNegatedClassSelectors, &data);
michael@0 2789 }
michael@0 2790
michael@0 2791 AtomSelectorEntry *entry =
michael@0 2792 static_cast<AtomSelectorEntry*>
michael@0 2793 (PL_DHashTableOperate(&cascade->mAttributeSelectors,
michael@0 2794 aData->mAttribute, PL_DHASH_LOOKUP));
michael@0 2795 if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
michael@0 2796 EnumerateSelectors(entry->mSelectors, &data);
michael@0 2797 }
michael@0 2798 }
michael@0 2799
michael@0 2800 return data.change;
michael@0 2801 }
michael@0 2802
michael@0 2803 /* virtual */ bool
michael@0 2804 nsCSSRuleProcessor::MediumFeaturesChanged(nsPresContext* aPresContext)
michael@0 2805 {
michael@0 2806 RuleCascadeData *old = mRuleCascades;
michael@0 2807 // We don't want to do anything if there aren't any sets of rules
michael@0 2808 // cached yet (or somebody cleared them and is thus responsible for
michael@0 2809 // rebuilding things), since we should not build the rule cascade too
michael@0 2810 // early (e.g., before we know whether the quirk style sheet should be
michael@0 2811 // enabled). And if there's nothing cached, it doesn't matter if
michael@0 2812 // anything changed. See bug 448281.
michael@0 2813 if (old) {
michael@0 2814 RefreshRuleCascade(aPresContext);
michael@0 2815 }
michael@0 2816 return (old != mRuleCascades);
michael@0 2817 }
michael@0 2818
michael@0 2819 /* virtual */ size_t
michael@0 2820 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 2821 {
michael@0 2822 size_t n = 0;
michael@0 2823 n += mSheets.SizeOfExcludingThis(aMallocSizeOf);
michael@0 2824 for (RuleCascadeData* cascade = mRuleCascades; cascade;
michael@0 2825 cascade = cascade->mNext) {
michael@0 2826 n += cascade->SizeOfIncludingThis(aMallocSizeOf);
michael@0 2827 }
michael@0 2828
michael@0 2829 return n;
michael@0 2830 }
michael@0 2831
michael@0 2832 /* virtual */ size_t
michael@0 2833 nsCSSRuleProcessor::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 2834 {
michael@0 2835 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 2836 }
michael@0 2837
michael@0 2838 // Append all the currently-active font face rules to aArray. Return
michael@0 2839 // true for success and false for failure.
michael@0 2840 bool
michael@0 2841 nsCSSRuleProcessor::AppendFontFaceRules(
michael@0 2842 nsPresContext *aPresContext,
michael@0 2843 nsTArray<nsFontFaceRuleContainer>& aArray)
michael@0 2844 {
michael@0 2845 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
michael@0 2846
michael@0 2847 if (cascade) {
michael@0 2848 if (!aArray.AppendElements(cascade->mFontFaceRules))
michael@0 2849 return false;
michael@0 2850 }
michael@0 2851
michael@0 2852 return true;
michael@0 2853 }
michael@0 2854
michael@0 2855 nsCSSKeyframesRule*
michael@0 2856 nsCSSRuleProcessor::KeyframesRuleForName(nsPresContext* aPresContext,
michael@0 2857 const nsString& aName)
michael@0 2858 {
michael@0 2859 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
michael@0 2860
michael@0 2861 if (cascade) {
michael@0 2862 return cascade->mKeyframesRuleTable.Get(aName);
michael@0 2863 }
michael@0 2864
michael@0 2865 return nullptr;
michael@0 2866 }
michael@0 2867
michael@0 2868 // Append all the currently-active page rules to aArray. Return
michael@0 2869 // true for success and false for failure.
michael@0 2870 bool
michael@0 2871 nsCSSRuleProcessor::AppendPageRules(
michael@0 2872 nsPresContext* aPresContext,
michael@0 2873 nsTArray<nsCSSPageRule*>& aArray)
michael@0 2874 {
michael@0 2875 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
michael@0 2876
michael@0 2877 if (cascade) {
michael@0 2878 if (!aArray.AppendElements(cascade->mPageRules)) {
michael@0 2879 return false;
michael@0 2880 }
michael@0 2881 }
michael@0 2882
michael@0 2883 return true;
michael@0 2884 }
michael@0 2885
michael@0 2886 bool
michael@0 2887 nsCSSRuleProcessor::AppendFontFeatureValuesRules(
michael@0 2888 nsPresContext *aPresContext,
michael@0 2889 nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
michael@0 2890 {
michael@0 2891 RuleCascadeData* cascade = GetRuleCascade(aPresContext);
michael@0 2892
michael@0 2893 if (cascade) {
michael@0 2894 if (!aArray.AppendElements(cascade->mFontFeatureValuesRules))
michael@0 2895 return false;
michael@0 2896 }
michael@0 2897
michael@0 2898 return true;
michael@0 2899 }
michael@0 2900
michael@0 2901 nsresult
michael@0 2902 nsCSSRuleProcessor::ClearRuleCascades()
michael@0 2903 {
michael@0 2904 // We rely on our caller (perhaps indirectly) to do something that
michael@0 2905 // will rebuild style data and the user font set (either
michael@0 2906 // nsIPresShell::ReconstructStyleData or
michael@0 2907 // nsPresContext::RebuildAllStyleData).
michael@0 2908 RuleCascadeData *data = mRuleCascades;
michael@0 2909 mRuleCascades = nullptr;
michael@0 2910 while (data) {
michael@0 2911 RuleCascadeData *next = data->mNext;
michael@0 2912 delete data;
michael@0 2913 data = next;
michael@0 2914 }
michael@0 2915 return NS_OK;
michael@0 2916 }
michael@0 2917
michael@0 2918
michael@0 2919 // This function should return the set of states that this selector
michael@0 2920 // depends on; this is used to implement HasStateDependentStyle. It
michael@0 2921 // does NOT recur down into things like :not and :-moz-any.
michael@0 2922 inline
michael@0 2923 EventStates ComputeSelectorStateDependence(nsCSSSelector& aSelector)
michael@0 2924 {
michael@0 2925 EventStates states;
michael@0 2926 for (nsPseudoClassList* pseudoClass = aSelector.mPseudoClassList;
michael@0 2927 pseudoClass; pseudoClass = pseudoClass->mNext) {
michael@0 2928 // Tree pseudo-elements overload mPseudoClassList for things that
michael@0 2929 // aren't pseudo-classes.
michael@0 2930 if (pseudoClass->mType >= nsCSSPseudoClasses::ePseudoClass_Count) {
michael@0 2931 continue;
michael@0 2932 }
michael@0 2933 states |= sPseudoClassStateDependences[pseudoClass->mType];
michael@0 2934 }
michael@0 2935 return states;
michael@0 2936 }
michael@0 2937
michael@0 2938 static bool
michael@0 2939 AddSelector(RuleCascadeData* aCascade,
michael@0 2940 // The part between combinators at the top level of the selector
michael@0 2941 nsCSSSelector* aSelectorInTopLevel,
michael@0 2942 // The part we should look through (might be in :not or :-moz-any())
michael@0 2943 nsCSSSelector* aSelectorPart)
michael@0 2944 {
michael@0 2945 // It's worth noting that this loop over negations isn't quite
michael@0 2946 // optimal for two reasons. One, we could add something to one of
michael@0 2947 // these lists twice, which means we'll check it twice, but I don't
michael@0 2948 // think that's worth worrying about. (We do the same for multiple
michael@0 2949 // attribute selectors on the same attribute.) Two, we don't really
michael@0 2950 // need to check negations past the first in the current
michael@0 2951 // implementation (and they're rare as well), but that might change
michael@0 2952 // in the future if :not() is extended.
michael@0 2953 for (nsCSSSelector* negation = aSelectorPart; negation;
michael@0 2954 negation = negation->mNegations) {
michael@0 2955 // Track both document states and attribute dependence in pseudo-classes.
michael@0 2956 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
michael@0 2957 pseudoClass; pseudoClass = pseudoClass->mNext) {
michael@0 2958 switch (pseudoClass->mType) {
michael@0 2959 case nsCSSPseudoClasses::ePseudoClass_mozLocaleDir: {
michael@0 2960 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_RTL_LOCALE;
michael@0 2961 break;
michael@0 2962 }
michael@0 2963 case nsCSSPseudoClasses::ePseudoClass_mozWindowInactive: {
michael@0 2964 aCascade->mSelectorDocumentStates |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
michael@0 2965 break;
michael@0 2966 }
michael@0 2967 case nsCSSPseudoClasses::ePseudoClass_mozTableBorderNonzero: {
michael@0 2968 nsTArray<nsCSSSelector*> *array =
michael@0 2969 aCascade->AttributeListFor(nsGkAtoms::border);
michael@0 2970 if (!array) {
michael@0 2971 return false;
michael@0 2972 }
michael@0 2973 array->AppendElement(aSelectorInTopLevel);
michael@0 2974 break;
michael@0 2975 }
michael@0 2976 default: {
michael@0 2977 break;
michael@0 2978 }
michael@0 2979 }
michael@0 2980 }
michael@0 2981
michael@0 2982 // Build mStateSelectors.
michael@0 2983 EventStates dependentStates = ComputeSelectorStateDependence(*negation);
michael@0 2984 if (!dependentStates.IsEmpty()) {
michael@0 2985 aCascade->mStateSelectors.AppendElement(
michael@0 2986 nsCSSRuleProcessor::StateSelector(dependentStates,
michael@0 2987 aSelectorInTopLevel));
michael@0 2988 }
michael@0 2989
michael@0 2990 // Build mIDSelectors
michael@0 2991 if (negation == aSelectorInTopLevel) {
michael@0 2992 for (nsAtomList* curID = negation->mIDList; curID;
michael@0 2993 curID = curID->mNext) {
michael@0 2994 AtomSelectorEntry *entry =
michael@0 2995 static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mIdSelectors,
michael@0 2996 curID->mAtom,
michael@0 2997 PL_DHASH_ADD));
michael@0 2998 if (entry) {
michael@0 2999 entry->mSelectors.AppendElement(aSelectorInTopLevel);
michael@0 3000 }
michael@0 3001 }
michael@0 3002 } else if (negation->mIDList) {
michael@0 3003 aCascade->mPossiblyNegatedIDSelectors.AppendElement(aSelectorInTopLevel);
michael@0 3004 }
michael@0 3005
michael@0 3006 // Build mClassSelectors
michael@0 3007 if (negation == aSelectorInTopLevel) {
michael@0 3008 for (nsAtomList* curClass = negation->mClassList; curClass;
michael@0 3009 curClass = curClass->mNext) {
michael@0 3010 AtomSelectorEntry *entry =
michael@0 3011 static_cast<AtomSelectorEntry*>(PL_DHashTableOperate(&aCascade->mClassSelectors,
michael@0 3012 curClass->mAtom,
michael@0 3013 PL_DHASH_ADD));
michael@0 3014 if (entry) {
michael@0 3015 entry->mSelectors.AppendElement(aSelectorInTopLevel);
michael@0 3016 }
michael@0 3017 }
michael@0 3018 } else if (negation->mClassList) {
michael@0 3019 aCascade->mPossiblyNegatedClassSelectors.AppendElement(aSelectorInTopLevel);
michael@0 3020 }
michael@0 3021
michael@0 3022 // Build mAttributeSelectors.
michael@0 3023 for (nsAttrSelector *attr = negation->mAttrList; attr;
michael@0 3024 attr = attr->mNext) {
michael@0 3025 nsTArray<nsCSSSelector*> *array =
michael@0 3026 aCascade->AttributeListFor(attr->mCasedAttr);
michael@0 3027 if (!array) {
michael@0 3028 return false;
michael@0 3029 }
michael@0 3030 array->AppendElement(aSelectorInTopLevel);
michael@0 3031 if (attr->mLowercaseAttr != attr->mCasedAttr) {
michael@0 3032 array = aCascade->AttributeListFor(attr->mLowercaseAttr);
michael@0 3033 if (!array) {
michael@0 3034 return false;
michael@0 3035 }
michael@0 3036 array->AppendElement(aSelectorInTopLevel);
michael@0 3037 }
michael@0 3038 }
michael@0 3039
michael@0 3040 // Recur through any :-moz-any selectors
michael@0 3041 for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
michael@0 3042 pseudoClass; pseudoClass = pseudoClass->mNext) {
michael@0 3043 if (pseudoClass->mType == nsCSSPseudoClasses::ePseudoClass_any) {
michael@0 3044 for (nsCSSSelectorList *l = pseudoClass->u.mSelectors; l; l = l->mNext) {
michael@0 3045 nsCSSSelector *s = l->mSelectors;
michael@0 3046 if (!AddSelector(aCascade, aSelectorInTopLevel, s)) {
michael@0 3047 return false;
michael@0 3048 }
michael@0 3049 }
michael@0 3050 }
michael@0 3051 }
michael@0 3052 }
michael@0 3053
michael@0 3054 return true;
michael@0 3055 }
michael@0 3056
michael@0 3057 static bool
michael@0 3058 AddRule(RuleSelectorPair* aRuleInfo, RuleCascadeData* aCascade)
michael@0 3059 {
michael@0 3060 RuleCascadeData * const cascade = aCascade;
michael@0 3061
michael@0 3062 // Build the rule hash.
michael@0 3063 nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
michael@0 3064 if (MOZ_LIKELY(pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement)) {
michael@0 3065 cascade->mRuleHash.AppendRule(*aRuleInfo);
michael@0 3066 } else if (pseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount) {
michael@0 3067 RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[pseudoType];
michael@0 3068 if (!ruleHash) {
michael@0 3069 ruleHash = new RuleHash(cascade->mQuirksMode);
michael@0 3070 if (!ruleHash) {
michael@0 3071 // Out of memory; give up
michael@0 3072 return false;
michael@0 3073 }
michael@0 3074 }
michael@0 3075 NS_ASSERTION(aRuleInfo->mSelector->mNext,
michael@0 3076 "Must have mNext; parser screwed up");
michael@0 3077 NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == ':',
michael@0 3078 "Unexpected mNext combinator");
michael@0 3079 ruleHash->AppendRule(*aRuleInfo);
michael@0 3080 } else if (pseudoType == nsCSSPseudoElements::ePseudo_AnonBox) {
michael@0 3081 NS_ASSERTION(!aRuleInfo->mSelector->mCasedTag &&
michael@0 3082 !aRuleInfo->mSelector->mIDList &&
michael@0 3083 !aRuleInfo->mSelector->mClassList &&
michael@0 3084 !aRuleInfo->mSelector->mPseudoClassList &&
michael@0 3085 !aRuleInfo->mSelector->mAttrList &&
michael@0 3086 !aRuleInfo->mSelector->mNegations &&
michael@0 3087 !aRuleInfo->mSelector->mNext &&
michael@0 3088 aRuleInfo->mSelector->mNameSpace == kNameSpaceID_Unknown,
michael@0 3089 "Parser messed up with anon box selector");
michael@0 3090
michael@0 3091 // Index doesn't matter here, since we'll just be walking these
michael@0 3092 // rules in order; just pass 0.
michael@0 3093 AppendRuleToTagTable(&cascade->mAnonBoxRules,
michael@0 3094 aRuleInfo->mSelector->mLowercaseTag,
michael@0 3095 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
michael@0 3096 } else {
michael@0 3097 #ifdef MOZ_XUL
michael@0 3098 NS_ASSERTION(pseudoType == nsCSSPseudoElements::ePseudo_XULTree,
michael@0 3099 "Unexpected pseudo type");
michael@0 3100 // Index doesn't matter here, since we'll just be walking these
michael@0 3101 // rules in order; just pass 0.
michael@0 3102 AppendRuleToTagTable(&cascade->mXULTreeRules,
michael@0 3103 aRuleInfo->mSelector->mLowercaseTag,
michael@0 3104 RuleValue(*aRuleInfo, 0, aCascade->mQuirksMode));
michael@0 3105 #else
michael@0 3106 NS_NOTREACHED("Unexpected pseudo type");
michael@0 3107 #endif
michael@0 3108 }
michael@0 3109
michael@0 3110 for (nsCSSSelector* selector = aRuleInfo->mSelector;
michael@0 3111 selector; selector = selector->mNext) {
michael@0 3112 if (selector->IsPseudoElement()) {
michael@0 3113 nsCSSPseudoElements::Type pseudo = selector->PseudoType();
michael@0 3114 if (pseudo >= nsCSSPseudoElements::ePseudo_PseudoElementCount ||
michael@0 3115 !nsCSSPseudoElements::PseudoElementSupportsUserActionState(pseudo)) {
michael@0 3116 NS_ASSERTION(!selector->mNegations, "Shouldn't have negations");
michael@0 3117 // We do store selectors ending with pseudo-elements that allow :hover
michael@0 3118 // and :active after them in the hashtables corresponding to that
michael@0 3119 // selector's mNext (i.e. the thing that matches against the element),
michael@0 3120 // but we want to make sure that selectors for any other kinds of
michael@0 3121 // pseudo-elements don't end up in the hashtables. In particular, tree
michael@0 3122 // pseudos store strange things in mPseudoClassList that we don't want
michael@0 3123 // to try to match elements against.
michael@0 3124 continue;
michael@0 3125 }
michael@0 3126 }
michael@0 3127 if (!AddSelector(cascade, selector, selector)) {
michael@0 3128 return false;
michael@0 3129 }
michael@0 3130 }
michael@0 3131
michael@0 3132 return true;
michael@0 3133 }
michael@0 3134
michael@0 3135 struct PerWeightDataListItem : public RuleSelectorPair {
michael@0 3136 PerWeightDataListItem(css::StyleRule* aRule, nsCSSSelector* aSelector)
michael@0 3137 : RuleSelectorPair(aRule, aSelector)
michael@0 3138 , mNext(nullptr)
michael@0 3139 {}
michael@0 3140 // No destructor; these are arena-allocated
michael@0 3141
michael@0 3142
michael@0 3143 // Placement new to arena allocate the PerWeightDataListItem
michael@0 3144 void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW {
michael@0 3145 void *mem;
michael@0 3146 PL_ARENA_ALLOCATE(mem, &aArena, aSize);
michael@0 3147 return mem;
michael@0 3148 }
michael@0 3149
michael@0 3150 PerWeightDataListItem *mNext;
michael@0 3151 };
michael@0 3152
michael@0 3153 struct PerWeightData {
michael@0 3154 PerWeightData()
michael@0 3155 : mRuleSelectorPairs(nullptr)
michael@0 3156 , mTail(&mRuleSelectorPairs)
michael@0 3157 {}
michael@0 3158
michael@0 3159 int32_t mWeight;
michael@0 3160 PerWeightDataListItem *mRuleSelectorPairs;
michael@0 3161 PerWeightDataListItem **mTail;
michael@0 3162 };
michael@0 3163
michael@0 3164 struct RuleByWeightEntry : public PLDHashEntryHdr {
michael@0 3165 PerWeightData data; // mWeight is key, mRuleSelectorPairs are value
michael@0 3166 };
michael@0 3167
michael@0 3168 static PLDHashNumber
michael@0 3169 HashIntKey(PLDHashTable *table, const void *key)
michael@0 3170 {
michael@0 3171 return PLDHashNumber(NS_PTR_TO_INT32(key));
michael@0 3172 }
michael@0 3173
michael@0 3174 static bool
michael@0 3175 MatchWeightEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
michael@0 3176 const void *key)
michael@0 3177 {
michael@0 3178 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
michael@0 3179 return entry->data.mWeight == NS_PTR_TO_INT32(key);
michael@0 3180 }
michael@0 3181
michael@0 3182 static bool
michael@0 3183 InitWeightEntry(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 3184 const void *key)
michael@0 3185 {
michael@0 3186 RuleByWeightEntry* entry = static_cast<RuleByWeightEntry*>(hdr);
michael@0 3187 new (entry) RuleByWeightEntry();
michael@0 3188 return true;
michael@0 3189 }
michael@0 3190
michael@0 3191 static const PLDHashTableOps gRulesByWeightOps = {
michael@0 3192 PL_DHashAllocTable,
michael@0 3193 PL_DHashFreeTable,
michael@0 3194 HashIntKey,
michael@0 3195 MatchWeightEntry,
michael@0 3196 PL_DHashMoveEntryStub,
michael@0 3197 PL_DHashClearEntryStub,
michael@0 3198 PL_DHashFinalizeStub,
michael@0 3199 InitWeightEntry
michael@0 3200 };
michael@0 3201
michael@0 3202 struct CascadeEnumData {
michael@0 3203 CascadeEnumData(nsPresContext* aPresContext,
michael@0 3204 nsTArray<nsFontFaceRuleContainer>& aFontFaceRules,
michael@0 3205 nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
michael@0 3206 nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
michael@0 3207 nsTArray<nsCSSPageRule*>& aPageRules,
michael@0 3208 nsMediaQueryResultCacheKey& aKey,
michael@0 3209 uint8_t aSheetType)
michael@0 3210 : mPresContext(aPresContext),
michael@0 3211 mFontFaceRules(aFontFaceRules),
michael@0 3212 mKeyframesRules(aKeyframesRules),
michael@0 3213 mFontFeatureValuesRules(aFontFeatureValuesRules),
michael@0 3214 mPageRules(aPageRules),
michael@0 3215 mCacheKey(aKey),
michael@0 3216 mSheetType(aSheetType)
michael@0 3217 {
michael@0 3218 if (!PL_DHashTableInit(&mRulesByWeight, &gRulesByWeightOps, nullptr,
michael@0 3219 sizeof(RuleByWeightEntry), 64, fallible_t()))
michael@0 3220 mRulesByWeight.ops = nullptr;
michael@0 3221
michael@0 3222 // Initialize our arena
michael@0 3223 PL_INIT_ARENA_POOL(&mArena, "CascadeEnumDataArena",
michael@0 3224 NS_CASCADEENUMDATA_ARENA_BLOCK_SIZE);
michael@0 3225 }
michael@0 3226
michael@0 3227 ~CascadeEnumData()
michael@0 3228 {
michael@0 3229 if (mRulesByWeight.ops)
michael@0 3230 PL_DHashTableFinish(&mRulesByWeight);
michael@0 3231 PL_FinishArenaPool(&mArena);
michael@0 3232 }
michael@0 3233
michael@0 3234 nsPresContext* mPresContext;
michael@0 3235 nsTArray<nsFontFaceRuleContainer>& mFontFaceRules;
michael@0 3236 nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
michael@0 3237 nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
michael@0 3238 nsTArray<nsCSSPageRule*>& mPageRules;
michael@0 3239 nsMediaQueryResultCacheKey& mCacheKey;
michael@0 3240 PLArenaPool mArena;
michael@0 3241 // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
michael@0 3242 // provide a getter that gives me a *reference* to the value.
michael@0 3243 PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
michael@0 3244 uint8_t mSheetType;
michael@0 3245 };
michael@0 3246
michael@0 3247 /*
michael@0 3248 * This enumerates style rules in a sheet (and recursively into any
michael@0 3249 * grouping rules) in order to:
michael@0 3250 * (1) add any style rules, in order, into data->mRulesByWeight (for
michael@0 3251 * the primary CSS cascade), where they are separated by weight
michael@0 3252 * but kept in order per-weight, and
michael@0 3253 * (2) add any @font-face rules, in order, into data->mFontFaceRules.
michael@0 3254 * (3) add any @keyframes rules, in order, into data->mKeyframesRules.
michael@0 3255 * (4) add any @font-feature-value rules, in order,
michael@0 3256 * into data->mFontFeatureValuesRules.
michael@0 3257 * (5) add any @page rules, in order, into data->mPageRules.
michael@0 3258 */
michael@0 3259 static bool
michael@0 3260 CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
michael@0 3261 {
michael@0 3262 CascadeEnumData* data = (CascadeEnumData*)aData;
michael@0 3263 int32_t type = aRule->GetType();
michael@0 3264
michael@0 3265 if (css::Rule::STYLE_RULE == type) {
michael@0 3266 css::StyleRule* styleRule = static_cast<css::StyleRule*>(aRule);
michael@0 3267
michael@0 3268 for (nsCSSSelectorList *sel = styleRule->Selector();
michael@0 3269 sel; sel = sel->mNext) {
michael@0 3270 int32_t weight = sel->mWeight;
michael@0 3271 RuleByWeightEntry *entry = static_cast<RuleByWeightEntry*>(
michael@0 3272 PL_DHashTableOperate(&data->mRulesByWeight, NS_INT32_TO_PTR(weight),
michael@0 3273 PL_DHASH_ADD));
michael@0 3274 if (!entry)
michael@0 3275 return false;
michael@0 3276 entry->data.mWeight = weight;
michael@0 3277 // entry->data.mRuleSelectorPairs should be linked in forward order;
michael@0 3278 // entry->data.mTail is the slot to write to.
michael@0 3279 PerWeightDataListItem *newItem =
michael@0 3280 new (data->mArena) PerWeightDataListItem(styleRule, sel->mSelectors);
michael@0 3281 if (newItem) {
michael@0 3282 *(entry->data.mTail) = newItem;
michael@0 3283 entry->data.mTail = &newItem->mNext;
michael@0 3284 }
michael@0 3285 }
michael@0 3286 }
michael@0 3287 else if (css::Rule::MEDIA_RULE == type ||
michael@0 3288 css::Rule::DOCUMENT_RULE == type ||
michael@0 3289 css::Rule::SUPPORTS_RULE == type) {
michael@0 3290 css::GroupRule* groupRule = static_cast<css::GroupRule*>(aRule);
michael@0 3291 if (groupRule->UseForPresentation(data->mPresContext, data->mCacheKey))
michael@0 3292 if (!groupRule->EnumerateRulesForwards(CascadeRuleEnumFunc, aData))
michael@0 3293 return false;
michael@0 3294 }
michael@0 3295 else if (css::Rule::FONT_FACE_RULE == type) {
michael@0 3296 nsCSSFontFaceRule *fontFaceRule = static_cast<nsCSSFontFaceRule*>(aRule);
michael@0 3297 nsFontFaceRuleContainer *ptr = data->mFontFaceRules.AppendElement();
michael@0 3298 if (!ptr)
michael@0 3299 return false;
michael@0 3300 ptr->mRule = fontFaceRule;
michael@0 3301 ptr->mSheetType = data->mSheetType;
michael@0 3302 }
michael@0 3303 else if (css::Rule::KEYFRAMES_RULE == type) {
michael@0 3304 nsCSSKeyframesRule *keyframesRule =
michael@0 3305 static_cast<nsCSSKeyframesRule*>(aRule);
michael@0 3306 if (!data->mKeyframesRules.AppendElement(keyframesRule)) {
michael@0 3307 return false;
michael@0 3308 }
michael@0 3309 }
michael@0 3310 else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) {
michael@0 3311 nsCSSFontFeatureValuesRule *fontFeatureValuesRule =
michael@0 3312 static_cast<nsCSSFontFeatureValuesRule*>(aRule);
michael@0 3313 if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
michael@0 3314 return false;
michael@0 3315 }
michael@0 3316 }
michael@0 3317 else if (css::Rule::PAGE_RULE == type) {
michael@0 3318 nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(aRule);
michael@0 3319 if (!data->mPageRules.AppendElement(pageRule)) {
michael@0 3320 return false;
michael@0 3321 }
michael@0 3322 }
michael@0 3323 return true;
michael@0 3324 }
michael@0 3325
michael@0 3326 /* static */ bool
michael@0 3327 nsCSSRuleProcessor::CascadeSheet(nsCSSStyleSheet* aSheet, CascadeEnumData* aData)
michael@0 3328 {
michael@0 3329 if (aSheet->IsApplicable() &&
michael@0 3330 aSheet->UseForPresentation(aData->mPresContext, aData->mCacheKey) &&
michael@0 3331 aSheet->mInner) {
michael@0 3332 nsCSSStyleSheet* child = aSheet->mInner->mFirstChild;
michael@0 3333 while (child) {
michael@0 3334 CascadeSheet(child, aData);
michael@0 3335 child = child->mNext;
michael@0 3336 }
michael@0 3337
michael@0 3338 if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,
michael@0 3339 aData))
michael@0 3340 return false;
michael@0 3341 }
michael@0 3342 return true;
michael@0 3343 }
michael@0 3344
michael@0 3345 static int CompareWeightData(const void* aArg1, const void* aArg2,
michael@0 3346 void* closure)
michael@0 3347 {
michael@0 3348 const PerWeightData* arg1 = static_cast<const PerWeightData*>(aArg1);
michael@0 3349 const PerWeightData* arg2 = static_cast<const PerWeightData*>(aArg2);
michael@0 3350 return arg1->mWeight - arg2->mWeight; // put lower weight first
michael@0 3351 }
michael@0 3352
michael@0 3353
michael@0 3354 struct FillWeightArrayData {
michael@0 3355 FillWeightArrayData(PerWeightData* aArrayData) :
michael@0 3356 mIndex(0),
michael@0 3357 mWeightArray(aArrayData)
michael@0 3358 {
michael@0 3359 }
michael@0 3360 int32_t mIndex;
michael@0 3361 PerWeightData* mWeightArray;
michael@0 3362 };
michael@0 3363
michael@0 3364
michael@0 3365 static PLDHashOperator
michael@0 3366 FillWeightArray(PLDHashTable *table, PLDHashEntryHdr *hdr,
michael@0 3367 uint32_t number, void *arg)
michael@0 3368 {
michael@0 3369 FillWeightArrayData* data = static_cast<FillWeightArrayData*>(arg);
michael@0 3370 const RuleByWeightEntry *entry = (const RuleByWeightEntry *)hdr;
michael@0 3371
michael@0 3372 data->mWeightArray[data->mIndex++] = entry->data;
michael@0 3373
michael@0 3374 return PL_DHASH_NEXT;
michael@0 3375 }
michael@0 3376
michael@0 3377 RuleCascadeData*
michael@0 3378 nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
michael@0 3379 {
michael@0 3380 // FIXME: Make this infallible!
michael@0 3381
michael@0 3382 // If anything changes about the presentation context, we will be
michael@0 3383 // notified. Otherwise, our cache is valid if mLastPresContext
michael@0 3384 // matches aPresContext. (The only rule processors used for multiple
michael@0 3385 // pres contexts are for XBL. These rule processors are probably less
michael@0 3386 // likely to have @media rules, and thus the cache is pretty likely to
michael@0 3387 // hit instantly even when we're switching between pres contexts.)
michael@0 3388
michael@0 3389 if (!mRuleCascades || aPresContext != mLastPresContext) {
michael@0 3390 RefreshRuleCascade(aPresContext);
michael@0 3391 }
michael@0 3392 mLastPresContext = aPresContext;
michael@0 3393
michael@0 3394 return mRuleCascades;
michael@0 3395 }
michael@0 3396
michael@0 3397 void
michael@0 3398 nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext)
michael@0 3399 {
michael@0 3400 // Having RuleCascadeData objects be per-medium (over all variation
michael@0 3401 // caused by media queries, handled through mCacheKey) works for now
michael@0 3402 // since nsCSSRuleProcessor objects are per-document. (For a given
michael@0 3403 // set of stylesheets they can vary based on medium (@media) or
michael@0 3404 // document (@-moz-document).)
michael@0 3405
michael@0 3406 for (RuleCascadeData **cascadep = &mRuleCascades, *cascade;
michael@0 3407 (cascade = *cascadep); cascadep = &cascade->mNext) {
michael@0 3408 if (cascade->mCacheKey.Matches(aPresContext)) {
michael@0 3409 // Ensure that the current one is always mRuleCascades.
michael@0 3410 *cascadep = cascade->mNext;
michael@0 3411 cascade->mNext = mRuleCascades;
michael@0 3412 mRuleCascades = cascade;
michael@0 3413
michael@0 3414 return;
michael@0 3415 }
michael@0 3416 }
michael@0 3417
michael@0 3418 if (mSheets.Length() != 0) {
michael@0 3419 nsAutoPtr<RuleCascadeData> newCascade(
michael@0 3420 new RuleCascadeData(aPresContext->Medium(),
michael@0 3421 eCompatibility_NavQuirks == aPresContext->CompatibilityMode()));
michael@0 3422 if (newCascade) {
michael@0 3423 CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
michael@0 3424 newCascade->mKeyframesRules,
michael@0 3425 newCascade->mFontFeatureValuesRules,
michael@0 3426 newCascade->mPageRules,
michael@0 3427 newCascade->mCacheKey,
michael@0 3428 mSheetType);
michael@0 3429 if (!data.mRulesByWeight.ops)
michael@0 3430 return; /* out of memory */
michael@0 3431
michael@0 3432 for (uint32_t i = 0; i < mSheets.Length(); ++i) {
michael@0 3433 if (!CascadeSheet(mSheets.ElementAt(i), &data))
michael@0 3434 return; /* out of memory */
michael@0 3435 }
michael@0 3436
michael@0 3437 // Sort the hash table of per-weight linked lists by weight.
michael@0 3438 uint32_t weightCount = data.mRulesByWeight.entryCount;
michael@0 3439 nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
michael@0 3440 FillWeightArrayData fwData(weightArray);
michael@0 3441 PL_DHashTableEnumerate(&data.mRulesByWeight, FillWeightArray, &fwData);
michael@0 3442 NS_QuickSort(weightArray, weightCount, sizeof(PerWeightData),
michael@0 3443 CompareWeightData, nullptr);
michael@0 3444
michael@0 3445 // Put things into the rule hash.
michael@0 3446 // The primary sort is by weight...
michael@0 3447 for (uint32_t i = 0; i < weightCount; ++i) {
michael@0 3448 // and the secondary sort is by order. mRuleSelectorPairs is already in
michael@0 3449 // the right order..
michael@0 3450 for (PerWeightDataListItem *cur = weightArray[i].mRuleSelectorPairs;
michael@0 3451 cur;
michael@0 3452 cur = cur->mNext) {
michael@0 3453 if (!AddRule(cur, newCascade))
michael@0 3454 return; /* out of memory */
michael@0 3455 }
michael@0 3456 }
michael@0 3457
michael@0 3458 // Build mKeyframesRuleTable.
michael@0 3459 for (nsTArray<nsCSSKeyframesRule*>::size_type i = 0,
michael@0 3460 iEnd = newCascade->mKeyframesRules.Length(); i < iEnd; ++i) {
michael@0 3461 nsCSSKeyframesRule* rule = newCascade->mKeyframesRules[i];
michael@0 3462 newCascade->mKeyframesRuleTable.Put(rule->GetName(), rule);
michael@0 3463 }
michael@0 3464
michael@0 3465 // Ensure that the current one is always mRuleCascades.
michael@0 3466 newCascade->mNext = mRuleCascades;
michael@0 3467 mRuleCascades = newCascade.forget();
michael@0 3468 }
michael@0 3469 }
michael@0 3470 return;
michael@0 3471 }
michael@0 3472
michael@0 3473 /* static */ bool
michael@0 3474 nsCSSRuleProcessor::SelectorListMatches(Element* aElement,
michael@0 3475 TreeMatchContext& aTreeMatchContext,
michael@0 3476 nsCSSSelectorList* aSelectorList)
michael@0 3477 {
michael@0 3478 MOZ_ASSERT(!aTreeMatchContext.mForScopedStyle,
michael@0 3479 "mCurrentStyleScope will need to be saved and restored after the "
michael@0 3480 "SelectorMatchesTree call");
michael@0 3481
michael@0 3482 while (aSelectorList) {
michael@0 3483 nsCSSSelector* sel = aSelectorList->mSelectors;
michael@0 3484 NS_ASSERTION(sel, "Should have *some* selectors");
michael@0 3485 NS_ASSERTION(!sel->IsPseudoElement(), "Shouldn't have been called");
michael@0 3486 NodeMatchContext nodeContext(EventStates(), false);
michael@0 3487 if (SelectorMatches(aElement, sel, nodeContext, aTreeMatchContext)) {
michael@0 3488 nsCSSSelector* next = sel->mNext;
michael@0 3489 if (!next ||
michael@0 3490 SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) {
michael@0 3491 return true;
michael@0 3492 }
michael@0 3493 }
michael@0 3494
michael@0 3495 aSelectorList = aSelectorList->mNext;
michael@0 3496 }
michael@0 3497
michael@0 3498 return false;
michael@0 3499 }
michael@0 3500
michael@0 3501 // TreeMatchContext and AncestorFilter out of line methods
michael@0 3502 void
michael@0 3503 TreeMatchContext::InitAncestors(Element *aElement)
michael@0 3504 {
michael@0 3505 MOZ_ASSERT(!mAncestorFilter.mFilter);
michael@0 3506 MOZ_ASSERT(mAncestorFilter.mHashes.IsEmpty());
michael@0 3507 MOZ_ASSERT(mStyleScopes.IsEmpty());
michael@0 3508
michael@0 3509 mAncestorFilter.mFilter = new AncestorFilter::Filter();
michael@0 3510
michael@0 3511 if (MOZ_LIKELY(aElement)) {
michael@0 3512 MOZ_ASSERT(aElement->IsInDoc(),
michael@0 3513 "aElement must be in the document for the assumption that "
michael@0 3514 "GetParentNode() is non-null on all element ancestors of "
michael@0 3515 "aElement to be true");
michael@0 3516 // Collect up the ancestors
michael@0 3517 nsAutoTArray<Element*, 50> ancestors;
michael@0 3518 Element* cur = aElement;
michael@0 3519 do {
michael@0 3520 ancestors.AppendElement(cur);
michael@0 3521 nsINode* parent = cur->GetParentNode();
michael@0 3522 if (!parent->IsElement()) {
michael@0 3523 break;
michael@0 3524 }
michael@0 3525
michael@0 3526 cur = parent->AsElement();
michael@0 3527 } while (true);
michael@0 3528
michael@0 3529 // Now push them in reverse order.
michael@0 3530 for (uint32_t i = ancestors.Length(); i-- != 0; ) {
michael@0 3531 mAncestorFilter.PushAncestor(ancestors[i]);
michael@0 3532 PushStyleScope(ancestors[i]);
michael@0 3533 }
michael@0 3534 }
michael@0 3535 }
michael@0 3536
michael@0 3537 void
michael@0 3538 TreeMatchContext::InitStyleScopes(Element* aElement)
michael@0 3539 {
michael@0 3540 MOZ_ASSERT(mStyleScopes.IsEmpty());
michael@0 3541
michael@0 3542 if (MOZ_LIKELY(aElement)) {
michael@0 3543 // Collect up the ancestors
michael@0 3544 nsAutoTArray<Element*, 50> ancestors;
michael@0 3545 Element* cur = aElement;
michael@0 3546 do {
michael@0 3547 ancestors.AppendElement(cur);
michael@0 3548 nsINode* parent = cur->GetParentNode();
michael@0 3549 if (!parent || !parent->IsElement()) {
michael@0 3550 break;
michael@0 3551 }
michael@0 3552
michael@0 3553 cur = parent->AsElement();
michael@0 3554 } while (true);
michael@0 3555
michael@0 3556 // Now push them in reverse order.
michael@0 3557 for (uint32_t i = ancestors.Length(); i-- != 0; ) {
michael@0 3558 PushStyleScope(ancestors[i]);
michael@0 3559 }
michael@0 3560 }
michael@0 3561 }
michael@0 3562
michael@0 3563 void
michael@0 3564 AncestorFilter::PushAncestor(Element *aElement)
michael@0 3565 {
michael@0 3566 MOZ_ASSERT(mFilter);
michael@0 3567
michael@0 3568 uint32_t oldLength = mHashes.Length();
michael@0 3569
michael@0 3570 mPopTargets.AppendElement(oldLength);
michael@0 3571 #ifdef DEBUG
michael@0 3572 mElements.AppendElement(aElement);
michael@0 3573 #endif
michael@0 3574 mHashes.AppendElement(aElement->Tag()->hash());
michael@0 3575 nsIAtom *id = aElement->GetID();
michael@0 3576 if (id) {
michael@0 3577 mHashes.AppendElement(id->hash());
michael@0 3578 }
michael@0 3579 const nsAttrValue *classes = aElement->GetClasses();
michael@0 3580 if (classes) {
michael@0 3581 uint32_t classCount = classes->GetAtomCount();
michael@0 3582 for (uint32_t i = 0; i < classCount; ++i) {
michael@0 3583 mHashes.AppendElement(classes->AtomAt(i)->hash());
michael@0 3584 }
michael@0 3585 }
michael@0 3586
michael@0 3587 uint32_t newLength = mHashes.Length();
michael@0 3588 for (uint32_t i = oldLength; i < newLength; ++i) {
michael@0 3589 mFilter->add(mHashes[i]);
michael@0 3590 }
michael@0 3591 }
michael@0 3592
michael@0 3593 void
michael@0 3594 AncestorFilter::PopAncestor()
michael@0 3595 {
michael@0 3596 MOZ_ASSERT(!mPopTargets.IsEmpty());
michael@0 3597 MOZ_ASSERT(mPopTargets.Length() == mElements.Length());
michael@0 3598
michael@0 3599 uint32_t popTargetLength = mPopTargets.Length();
michael@0 3600 uint32_t newLength = mPopTargets[popTargetLength-1];
michael@0 3601
michael@0 3602 mPopTargets.TruncateLength(popTargetLength-1);
michael@0 3603 #ifdef DEBUG
michael@0 3604 mElements.TruncateLength(popTargetLength-1);
michael@0 3605 #endif
michael@0 3606
michael@0 3607 uint32_t oldLength = mHashes.Length();
michael@0 3608 for (uint32_t i = newLength; i < oldLength; ++i) {
michael@0 3609 mFilter->remove(mHashes[i]);
michael@0 3610 }
michael@0 3611 mHashes.TruncateLength(newLength);
michael@0 3612 }
michael@0 3613
michael@0 3614 #ifdef DEBUG
michael@0 3615 void
michael@0 3616 AncestorFilter::AssertHasAllAncestors(Element *aElement) const
michael@0 3617 {
michael@0 3618 nsINode* cur = aElement->GetParentNode();
michael@0 3619 while (cur && cur->IsElement()) {
michael@0 3620 MOZ_ASSERT(mElements.Contains(cur));
michael@0 3621 cur = cur->GetParentNode();
michael@0 3622 }
michael@0 3623 }
michael@0 3624 #endif

mercurial