Bug 525608 part 4. Change pseudo-element probing and resolution to not use EnumerateTagRules. r=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 10 Dec 2009 14:36:06 -0800
changeset 35539 732473bebf16066f0ee401a3de67ab6765f5751e
parent 35538 80b631ea5ad11b1126d0df9ffe2cb6a41ceb1db5
child 35540 62e1d35ef5b4f3073b226aad6da3e5825881b92b
push idunknown
push userunknown
push dateunknown
reviewersdbaron
bugs525608
milestone1.9.3a1pre
Bug 525608 part 4. Change pseudo-element probing and resolution to not use EnumerateTagRules. r=dbaron
layout/style/nsCSSRuleProcessor.cpp
layout/style/nsCSSRuleProcessor.h
layout/style/nsHTMLCSSStyleSheet.cpp
layout/style/nsHTMLStyleSheet.cpp
layout/style/nsHTMLStyleSheet.h
layout/style/nsIStyleRuleProcessor.h
layout/style/nsRuleProcessorData.h
layout/style/nsStyleSet.cpp
layout/style/nsStyleSet.h
layout/style/nsTransitionManager.cpp
layout/style/nsTransitionManager.h
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -64,16 +64,17 @@
 #include "nsPresContext.h"
 #include "nsIEventStateManager.h"
 #include "nsGkAtoms.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsDOMError.h"
 #include "nsRuleWalker.h"
 #include "nsCSSPseudoClasses.h"
+#include "nsCSSPseudoElements.h"
 #include "nsIContent.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsStyleUtil.h"
 #include "nsQuickSort.h"
 #include "nsAttrValue.h"
 #include "nsAttrName.h"
 #include "nsILookAndFeel.h"
@@ -358,17 +359,17 @@ typedef void (*RuleEnumFunc)(nsICSSStyle
 
 class RuleHash {
 public:
   RuleHash(PRBool aQuirksMode);
   ~RuleHash();
   void PrependRule(RuleValue *aRuleInfo);
   void EnumerateAllRules(PRInt32 aNameSpace, nsIAtom* aTag, nsIAtom* aID,
                          const nsAttrValue* aClassList,
-                         RuleEnumFunc aFunc, void* aData);
+                         RuleEnumFunc aFunc, RuleProcessorData* aData);
   void EnumerateTagRules(nsIAtom* aTag,
                          RuleEnumFunc aFunc, void* aData);
   PLArenaPool& Arena() { return mArena; }
 
 protected:
   void PrependRuleToTable(PLDHashTable* aTable, const void* aKey,
                           RuleValue* aRuleInfo);
   void PrependRuleToTagTable(const void* aKey, RuleValue* aRuleInfo);
@@ -423,16 +424,17 @@ RuleHash::RuleHash(PRBool aQuirksMode)
     mElementUniversalCalls(0),
     mElementNameSpaceCalls(0),
     mElementTagCalls(0),
     mElementClassCalls(0),
     mElementIdCalls(0),
     mPseudoTagCalls(0)
 #endif
 {
+  MOZ_COUNT_CTOR(RuleHash);
   // Initialize our arena
   PL_INIT_ARENA_POOL(&mArena, "RuleHashArena", NS_RULEHASH_ARENA_BLOCK_SIZE);
 
   PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nsnull,
                     sizeof(RuleHashTagTableEntry), 64);
   PL_DHashTableInit(&mIdTable,
                     aQuirksMode ? &RuleHash_IdTable_CIOps.ops
                                 : &RuleHash_IdTable_CSOps.ops,
@@ -442,16 +444,17 @@ RuleHash::RuleHash(PRBool aQuirksMode)
                                 : &RuleHash_ClassTable_CSOps.ops,
                     nsnull, sizeof(RuleHashTableEntry), 16);
   PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nsnull,
                     sizeof(RuleHashTableEntry), 16);
 }
 
 RuleHash::~RuleHash()
 {
+  MOZ_COUNT_DTOR(RuleHash);
 #ifdef RULE_HASH_STATS
   printf(
 "RuleHash(%p):\n"
 "  Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
 "  Content Nodes: Elements(%u) Pseudo-Elements(%u)\n"
 "  Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
 "  Pseudo-Element Calls: Tag(%u)\n",
          static_cast<void*>(this),
@@ -569,17 +572,17 @@ void RuleHash::PrependRule(RuleValue *aR
   do { ++(var_); (list_) = (list_)->mNext; } while (list_)
 #else
 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
   PR_BEGIN_MACRO PR_END_MACRO
 #endif
 
 void RuleHash::EnumerateAllRules(PRInt32 aNameSpace, nsIAtom* aTag,
                                  nsIAtom* aID, const nsAttrValue* aClassList,
-                                 RuleEnumFunc aFunc, void* aData)
+                                 RuleEnumFunc aFunc, RuleProcessorData* aData)
 {
   PRInt32 classCount = aClassList ? aClassList->GetAtomCount() : 0;
 
   // assume 1 universal, tag, id, and namespace, rather than wasting
   // time counting
   PRInt32 testCount = classCount + 4;
 
   if (mEnumListSize < testCount) {
@@ -594,45 +597,45 @@ void RuleHash::EnumerateAllRules(PRInt32
   { // universal rules
     RuleValue* value = mUniversalRules;
     if (nsnull != value) {
       mEnumList[valueCount++] = value;
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementUniversalCalls);
     }
   }
   // universal rules within the namespace
-  if (kNameSpaceID_Unknown != aNameSpace) {
+  if (kNameSpaceID_Unknown != aNameSpace && mNameSpaceTable.entryCount) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(aNameSpace),
                              PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       RuleValue *value = entry->mRules;
       mEnumList[valueCount++] = value;
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementNameSpaceCalls);
     }
   }
-  if (nsnull != aTag) {
+  if (aTag && mTagTable.entryCount) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableOperate(&mTagTable, aTag, PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       RuleValue *value = entry->mRules;
       mEnumList[valueCount++] = value;
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementTagCalls);
     }
   }
-  if (nsnull != aID) {
+  if (aID && mIdTable.entryCount) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableOperate(&mIdTable, aID, PL_DHASH_LOOKUP));
     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
       RuleValue *value = entry->mRules;
       mEnumList[valueCount++] = value;
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementIdCalls);
     }
   }
-  { // extra scope to work around compiler bugs with |for| scoping.
+  if (mClassTable.entryCount) {
     for (PRInt32 index = 0; index < classCount; ++index) {
       RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                              (PL_DHashTableOperate(&mClassTable, aClassList->AtomAt(index),
                              PL_DHASH_LOOKUP));
       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
         RuleValue *value = entry->mRules;
         mEnumList[valueCount++] = value;
         RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementClassCalls);
@@ -714,40 +717,49 @@ static const PLDHashTableOps AttributeSe
 
 //--------------------------------
 
 struct RuleCascadeData {
   RuleCascadeData(nsIAtom *aMedium, PRBool aQuirksMode)
     : mRuleHash(aQuirksMode),
       mStateSelectors(),
       mCacheKey(aMedium),
-      mNext(nsnull)
+      mNext(nsnull),
+      mQuirksMode(aQuirksMode)
   {
     PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull,
                       sizeof(AttributeSelectorEntry), 16);
+    memset(mPseudoElementRuleHashes, 0, sizeof(mPseudoElementRuleHashes));
   }
 
   ~RuleCascadeData()
   {
     PL_DHashTableFinish(&mAttributeSelectors);
+    for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(mPseudoElementRuleHashes); ++i) {
+      delete mPseudoElementRuleHashes[i];
+    }
   }
   RuleHash                 mRuleHash;
+  RuleHash*
+    mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   nsTArray<nsCSSSelector*> mStateSelectors;
   nsTArray<nsCSSSelector*> mClassSelectors;
   nsTArray<nsCSSSelector*> mIDSelectors;
   PLDHashTable             mAttributeSelectors;
 
   nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
 
   // Looks up or creates the appropriate list in |mAttributeSelectors|.
   // Returns null only on allocation failure.
   nsTArray<nsCSSSelector*>* AttributeListFor(nsIAtom* aAttribute);
 
   nsMediaQueryResultCacheKey mCacheKey;
   RuleCascadeData*  mNext; // for a different medium
+
+  const PRBool mQuirksMode;
 };
 
 nsTArray<nsCSSSelector*>*
 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
 {
   AttributeSelectorEntry *entry = static_cast<AttributeSelectorEntry*>
                                              (PL_DHashTableOperate(&mAttributeSelectors, aAttribute, PL_DHASH_ADD));
   if (!entry)
@@ -2002,17 +2014,17 @@ static PRBool SelectorMatchesTree(RulePr
     prevdata = data;
   }
   return PR_TRUE; // all the selectors matched.
 }
 
 static void ContentEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
                             void* aData)
 {
-  ElementRuleProcessorData* data = (ElementRuleProcessorData*)aData;
+  RuleProcessorData* data = (RuleProcessorData*)aData;
 
   if (SelectorMatches(*data, aSelector, 0, PR_TRUE)) {
     nsCSSSelector *next = aSelector->mNext;
     if (!next || SelectorMatchesTree(*data, next, PR_TRUE)) {
       // for performance, require that every implementation of
       // nsICSSStyleRule return the same pointer for nsIStyleRule (why
       // would anything multiply inherit nsIStyleRule anyway?)
 #ifdef DEBUG
@@ -2040,16 +2052,38 @@ nsCSSRuleProcessor::RulesMatching(Elemen
                                          aData->mContentID,
                                          aData->mClasses,
                                          ContentEnumFunc,
                                          aData);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCSSRuleProcessor::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  NS_PRECONDITION(aData->mContent->IsNodeOfType(nsINode::eELEMENT),
+                  "content must be element");
+
+  RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
+
+  if (cascade) {
+    RuleHash* ruleHash = cascade->mPseudoElementRuleHashes[aData->mPseudoType];
+    if (ruleHash) {
+      ruleHash->EnumerateAllRules(aData->mNameSpaceID,
+                                  aData->mContentTag,
+                                  aData->mContentID,
+                                  aData->mClasses,
+                                  ContentEnumFunc,
+                                  aData);
+    }
+  }
+  return NS_OK;
+}
+
 static void PseudoEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
                            void* aData)
 {
   PseudoRuleProcessorData* data = (PseudoRuleProcessorData*)aData;
 
   if (!aSelector->IsPseudoElement())
     return;
 
@@ -2363,17 +2397,35 @@ PRBool IsStateSelector(nsCSSSelector& aS
 }
 
 static PRBool
 AddRule(RuleValue* aRuleInfo, void* aCascade)
 {
   RuleCascadeData *cascade = static_cast<RuleCascadeData*>(aCascade);
 
   // Build the rule hash.
-  cascade->mRuleHash.PrependRule(aRuleInfo);
+  nsCSSPseudoElements::Type pseudoType = aRuleInfo->mSelector->PseudoType();
+  if (pseudoType < nsCSSPseudoElements::ePseudo_PseudoElementCount) {
+    RuleHash*& ruleHash = cascade->mPseudoElementRuleHashes[pseudoType];
+    if (!ruleHash) {
+      ruleHash = new RuleHash(cascade->mQuirksMode);
+      if (!ruleHash) {
+        // Out of memory; give up
+        return PR_FALSE;
+      }
+    }
+    NS_ASSERTION(aRuleInfo->mSelector->mNext,
+                 "Must have mNext; parser screwed up");
+    NS_ASSERTION(aRuleInfo->mSelector->mNext->mOperator == '>',
+                 "Unexpected mNext combinator");
+    aRuleInfo->mSelector = aRuleInfo->mSelector->mNext;
+    ruleHash->PrependRule(aRuleInfo);
+  } else {
+    cascade->mRuleHash.PrependRule(aRuleInfo);
+  }
 
   nsTArray<nsCSSSelector*>* stateArray = &cascade->mStateSelectors;
   nsTArray<nsCSSSelector*>* classArray = &cascade->mClassSelectors;
   nsTArray<nsCSSSelector*>* idArray = &cascade->mIDSelectors;
   
   for (nsCSSSelector* selector = aRuleInfo->mSelector;
            selector; selector = selector->mNext) {
     // It's worth noting that this loop over negations isn't quite
--- a/layout/style/nsCSSRuleProcessor.h
+++ b/layout/style/nsCSSRuleProcessor.h
@@ -86,16 +86,18 @@ public:
    * the matching is not for styling purposes.
    */
   static PRBool SelectorListMatches(RuleProcessorData& aData,
                                     nsCSSSelectorList* aSelectorList);
 
   // nsIStyleRuleProcessor
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
 
+  NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
+
   NS_IMETHOD RulesMatching(PseudoRuleProcessorData* aData);
 
   NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData,
                                     nsReStyleHint* aResult);
 
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
 
--- a/layout/style/nsHTMLCSSStyleSheet.cpp
+++ b/layout/style/nsHTMLCSSStyleSheet.cpp
@@ -84,16 +84,18 @@ public:
   // style sheet owner info
   NS_IMETHOD GetParentSheet(nsIStyleSheet*& aParent) const;  // will be null
   NS_IMETHOD GetOwningDocument(nsIDocument*& aDocument) const;
   NS_IMETHOD SetOwningDocument(nsIDocument* aDocument);
 
   // nsIStyleRuleProcessor api
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
 
+  NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
+
   NS_IMETHOD RulesMatching(PseudoRuleProcessorData* aData);
 
   NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData,
                                     nsReStyleHint* aResult);
 
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
@@ -152,16 +154,22 @@ HTMLCSSStyleSheetImpl::RulesMatching(Ele
       aData->mRuleWalker->Forward(rule);
 #endif // MOZ_SMIL
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HTMLCSSStyleSheetImpl::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HTMLCSSStyleSheetImpl::RulesMatching(PseudoRuleProcessorData* aData)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLCSSStyleSheetImpl::Init(nsIURI* aURL, nsIDocument* aDocument)
 {
--- a/layout/style/nsHTMLStyleSheet.cpp
+++ b/layout/style/nsHTMLStyleSheet.cpp
@@ -558,16 +558,22 @@ nsHTMLStyleSheet::MediumFeaturesChanged(
                                         PRBool* aRulesChanged)
 {
   *aRulesChanged = PR_FALSE;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
+nsHTMLStyleSheet::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsHTMLStyleSheet::RulesMatching(PseudoRuleProcessorData* aData)
 {
   nsIAtom* pseudoTag = aData->mPseudoTag;
   if (pseudoTag == nsCSSAnonBoxes::tableCol) {
     nsRuleWalker *ruleWalker = aData->mRuleWalker;
     if (ruleWalker) {
       ruleWalker->Forward(mTableColRule);
     }
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -74,16 +74,17 @@ public:
   NS_IMETHOD GetOwningDocument(nsIDocument*& aDocument) const;
   NS_IMETHOD SetOwningDocument(nsIDocument* aDocumemt);
 #ifdef DEBUG
   virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const;
 #endif
 
   // nsIStyleRuleProcessor API
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
+  NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(PseudoRuleProcessorData* aData);
   NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData,
                                     nsReStyleHint* aResult);
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                    PRBool* aRulesChanged);
 
--- a/layout/style/nsIStyleRuleProcessor.h
+++ b/layout/style/nsIStyleRuleProcessor.h
@@ -46,16 +46,17 @@
 #define nsIStyleRuleProcessor_h___
 
 #include "nsISupports.h"
 #include "nsChangeHint.h"
 #include "nsIContent.h"
 
 struct RuleProcessorData;
 struct ElementRuleProcessorData;
+struct PseudoElementRuleProcessorData;
 struct PseudoRuleProcessorData;
 struct StateRuleProcessorData;
 struct AttributeRuleProcessorData;
 class nsPresContext;
 
 // IID for the nsIStyleRuleProcessor interface {a4ec760e-6bfb-4b9f-bd08-9d1c23b700f6}
 #define NS_ISTYLE_RULE_PROCESSOR_IID     \
 { 0xa4ec760e, 0x6bfb, 0x4b9f, \
@@ -84,16 +85,22 @@ public:
    * precedence being farther from the root of the lexicographic tree).
    */
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData) = 0;
 
   /**
    * Just like the previous |RulesMatching|, except for a given content
    * node <em>and pseudo-element</em>.
    */
+  NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData) = 0;
+
+  /**
+   * Just like the previous |RulesMatching|, except for a given content
+   * node <em>and pseudo</em>.
+   */
   NS_IMETHOD RulesMatching(PseudoRuleProcessorData* aData) = 0;
 
   /**
    * Return how (as described by nsReStyleHint) style can depend on a
    * change of the given content state on the given content node.  This
    * test is used for optimization only, and may err on the side of
    * reporting more dependencies than really exist.
    *
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -43,16 +43,17 @@
 
 #ifndef nsRuleProcessorData_h_
 #define nsRuleProcessorData_h_
 
 #include "nsPresContext.h" // for nsCompatability
 #include "nsString.h"
 #include "nsChangeHint.h"
 #include "nsIContent.h"
+#include "nsCSSPseudoElements.h"
 
 class nsIStyleSheet;
 class nsPresContext;
 class nsIAtom;
 class nsICSSPseudoComparator;
 class nsRuleWalker;
 class nsAttrValue;
 
@@ -169,16 +170,34 @@ struct ElementRuleProcessorData : public
   : RuleProcessorData(aPresContext,aContent,aRuleWalker)
   {
     NS_PRECONDITION(aPresContext, "null pointer");
     NS_PRECONDITION(aContent, "null pointer");
     NS_PRECONDITION(aRuleWalker, "null pointer");
   }
 };
 
+struct PseudoElementRuleProcessorData : public RuleProcessorData {
+  PseudoElementRuleProcessorData(nsPresContext* aPresContext,
+                                 nsIContent* aParentContent,
+                                 nsRuleWalker* aRuleWalker,
+                                 nsCSSPseudoElements::Type aPseudoType)
+    : RuleProcessorData(aPresContext, aParentContent, aRuleWalker),
+      mPseudoType(aPseudoType)
+  {
+    NS_PRECONDITION(aPresContext, "null pointer");
+    NS_PRECONDITION(aPseudoType <
+                      nsCSSPseudoElements::ePseudo_PseudoElementCount,
+                    "null pointer");
+    NS_PRECONDITION(aRuleWalker, "null pointer");
+  }
+
+  nsCSSPseudoElements::Type mPseudoType;
+};
+
 struct PseudoRuleProcessorData : public RuleProcessorData {
   PseudoRuleProcessorData(nsPresContext* aPresContext,
                           nsIContent* aParentContent,
                           nsIAtom* aPseudoTag,
                           nsICSSPseudoComparator* aComparator,
                           nsRuleWalker* aRuleWalker)
   : RuleProcessorData(aPresContext, aParentContent, aRuleWalker)
   {
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -794,27 +794,25 @@ nsStyleSet::ResolveStyleForNonElement(ns
                         nsCSSAnonBoxes::mozNonElement,
                         nsCSSPseudoElements::ePseudo_AnonBox).get();
   }
 
   return result;
 }
 
 void
-nsStyleSet::WalkRestrictionRule(nsIAtom* aPseudoType,
+nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType,
                                 nsRuleWalker* aRuleWalker)
 {
   // This needs to match GetPseudoRestriction in nsRuleNode.cpp.
-  if (aPseudoType) {
-    aRuleWalker->SetLevel(eAgentSheet, PR_FALSE, PR_FALSE);
-    if (aPseudoType == nsCSSPseudoElements::firstLetter)
-      aRuleWalker->Forward(mFirstLetterRule);
-    else if (aPseudoType == nsCSSPseudoElements::firstLine)
-      aRuleWalker->Forward(mFirstLineRule);
-  }
+  aRuleWalker->SetLevel(eAgentSheet, PR_FALSE, PR_FALSE);
+  if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter)
+    aRuleWalker->Forward(mFirstLetterRule);
+  else if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLine)
+    aRuleWalker->Forward(mFirstLineRule);
 }
 
 static PRBool
 EnumPseudoRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData)
 {
   PseudoRuleProcessorData* data =
     static_cast<PseudoRuleProcessorData*>(aData);
 
@@ -846,76 +844,109 @@ nsStyleSet::ResolvePseudoStyleFor(nsICon
                "aPseudoTag must be pseudo-element or anonymous box");
   NS_ASSERTION(nsCSSPseudoElements::GetPseudoType(aPseudoTag) == aPseudoType,
                "Incorrect pseudo type");
 
   if (aPseudoTag && presContext) {
     nsRuleWalker ruleWalker(mRuleTree);
     PseudoRuleProcessorData data(presContext, aParentContent, aPseudoTag,
                                  aComparator, &ruleWalker);
-    WalkRestrictionRule(aPseudoTag, &ruleWalker);
     FileRules(EnumPseudoRulesMatching, &data, &ruleWalker);
 
     result = GetContext(presContext, aParentContext,
                         ruleWalker.GetCurrentNode(), aPseudoTag,
                         aPseudoType).get();
   }
 
   return result;
 }
 
+static PRBool
+EnumPseudoElementRulesMatching(nsIStyleRuleProcessor* aProcessor, void* aData)
+{
+  PseudoElementRuleProcessorData* data =
+    static_cast<PseudoElementRuleProcessorData*>(aData);
+
+  aProcessor->RulesMatching(data);
+  return PR_TRUE;
+}
+
+already_AddRefed<nsStyleContext>
+nsStyleSet::ResolvePseudoElementStyle(nsIContent* aParentContent,
+                                      nsCSSPseudoElements::Type aType,
+                                      nsStyleContext* aParentContext)
+{
+  NS_ENSURE_FALSE(mInShutdown, nsnull);
+
+  NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
+               "must have pseudo element type");
+  NS_ASSERTION(aParentContent &&
+               aParentContent->IsNodeOfType(nsINode::eELEMENT),
+               "aParentContent must be element");
+
+  nsRuleWalker ruleWalker(mRuleTree);
+  nsPresContext *presContext = PresContext();
+  PseudoElementRuleProcessorData data(presContext, aParentContent, &ruleWalker,
+                                      aType);
+  WalkRestrictionRule(aType, &ruleWalker);
+  FileRules(EnumPseudoElementRulesMatching, &data, &ruleWalker);
+
+  return GetContext(presContext, aParentContext, ruleWalker.GetCurrentNode(),
+                    nsCSSPseudoElements::GetPseudoAtom(aType), aType);
+}
+
 already_AddRefed<nsStyleContext>
 nsStyleSet::ProbePseudoElementStyle(nsIContent* aParentContent,
                                     nsCSSPseudoElements::Type aType,
                                     nsStyleContext* aParentContext)
 {
   NS_ENSURE_FALSE(mInShutdown, nsnull);
   
-  nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
-
-  nsStyleContext*  result = nsnull;
-  nsPresContext *presContext = PresContext();
-
-  NS_ASSERTION(pseudoTag, "must have pseudo tag");
+  NS_ASSERTION(aType < nsCSSPseudoElements::ePseudo_PseudoElementCount,
+               "must have pseudo element type");
   NS_ASSERTION(aParentContent &&
                aParentContent->IsNodeOfType(nsINode::eELEMENT),
                "aParentContent must be element");
 
-  if (presContext) {
-    nsRuleWalker ruleWalker(mRuleTree);
-    PseudoRuleProcessorData data(presContext, aParentContent, pseudoTag,
-                                 nsnull, &ruleWalker);
-    WalkRestrictionRule(pseudoTag, &ruleWalker);
-    // not the root if there was a restriction rule
-    nsRuleNode *adjustedRoot = ruleWalker.GetCurrentNode();
-    FileRules(EnumPseudoRulesMatching, &data, &ruleWalker);
+  nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(aType);
+
+  nsPresContext *presContext = PresContext();
 
-    nsRuleNode *ruleNode = ruleWalker.GetCurrentNode();
-    if (ruleNode != adjustedRoot)
-      result =
-        GetContext(presContext, aParentContext, ruleNode, pseudoTag, aType).get();
+  nsRuleWalker ruleWalker(mRuleTree);
+  PseudoElementRuleProcessorData data(presContext, aParentContent, &ruleWalker,
+                                      aType);
+  WalkRestrictionRule(aType, &ruleWalker);
+  // not the root if there was a restriction rule
+  nsRuleNode *adjustedRoot = ruleWalker.GetCurrentNode();
+  FileRules(EnumPseudoElementRulesMatching, &data, &ruleWalker);
+
+  nsRuleNode *ruleNode = ruleWalker.GetCurrentNode();
+  if (ruleNode == adjustedRoot) {
+    return nsnull;
   }
 
+  nsRefPtr<nsStyleContext> result =
+    GetContext(presContext, aParentContext, ruleNode, pseudoTag, aType);
+
   // For :before and :after pseudo-elements, having display: none or no
   // 'content' property is equivalent to not having the pseudo-element
   // at all.
   if (result &&
       (pseudoTag == nsCSSPseudoElements::before ||
        pseudoTag == nsCSSPseudoElements::after)) {
     const nsStyleDisplay *display = result->GetStyleDisplay();
     const nsStyleContent *content = result->GetStyleContent();
     // XXXldb What is contentCount for |content: ""|?
     if (display->mDisplay == NS_STYLE_DISPLAY_NONE ||
         content->ContentCount() == 0) {
-      result->Release();
       result = nsnull;
     }
   }
   
-  return result;
+  return result.forget();
 }
 
 PRBool
 nsStyleSet::AppendFontFaceRules(nsPresContext* aPresContext,
                                 nsTArray<nsFontFaceRuleContainer>& aArray)
 {
   NS_ENSURE_FALSE(mInShutdown, PR_FALSE);
 
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -122,22 +122,17 @@ class nsStyleSet
   ResolveStyleForNonElement(nsStyleContext* aParentContext);
 
   // Get a style context for a pseudo-element.  aParentContent must be
   // non-null.  aPseudoID is the nsCSSPseudoElements::Type for the
   // pseudo-element.
   already_AddRefed<nsStyleContext>
   ResolvePseudoElementStyle(nsIContent* aParentContent,
                             nsCSSPseudoElements::Type aType,
-                            nsStyleContext* aParentContext) {
-    return ResolvePseudoStyleFor(aParentContent,
-                                 nsCSSPseudoElements::GetPseudoAtom(aType),
-                                 aType,
-                                 aParentContext);
-  }
+                            nsStyleContext* aParentContext);
 
   // This functions just like ResolvePseudoElementStyle except that it will
   // return nsnull if there are no explicit style rules for that
   // pseudo element.
   already_AddRefed<nsStyleContext>
   ProbePseudoElementStyle(nsIContent* aParentContent,
                           nsCSSPseudoElements::Type aType,
                           nsStyleContext* aParentContext);
@@ -329,17 +324,17 @@ public:
   nsresult GatherRuleProcessors(sheetType aType);
 
   void AddImportantRules(nsRuleNode* aCurrLevelNode,
                          nsRuleNode* aLastPrevLevelNode,
                          nsRuleWalker* aRuleWalker);
 
   // Move aRuleWalker forward by the appropriate rule if we need to add
   // a rule due to property restrictions on pseudo-elements.
-  void WalkRestrictionRule(nsIAtom* aPseudoType,
+  void WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType,
                            nsRuleWalker* aRuleWalker);
 
 #ifdef DEBUG
   // Just like AddImportantRules except it doesn't actually add anything; it
   // just asserts that there are no important rules between aCurrLevelNode and
   // aLastPrevLevelNode.
   void AssertNoImportantRules(nsRuleNode* aCurrLevelNode,
                               nsRuleNode* aLastPrevLevelNode);
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -44,17 +44,16 @@
 #include "mozilla/TimeStamp.h"
 #include "nsRefreshDriver.h"
 #include "nsRuleProcessorData.h"
 #include "nsIStyleRule.h"
 #include "nsRuleWalker.h"
 #include "nsRuleData.h"
 #include "nsSMILKeySpline.h"
 #include "gfxColor.h"
-#include "nsCSSPseudoElements.h"
 #include "nsCSSPropertySet.h"
 #include "nsStyleAnimation.h"
 #include "nsCSSDataBlock.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 /*****************************************************************************
@@ -368,19 +367,20 @@ nsTransitionManager::StyleContextChanged
     return nsnull;
   }      
 
 
   if (aNewStyleContext->PresContext()->IsProcessingAnimationStyleChange()) {
     return nsnull;
   }
   
-  nsIAtom *pseudo = aNewStyleContext->GetPseudo();
-  if (pseudo && (pseudo != nsCSSPseudoElements::before &&
-                 pseudo != nsCSSPseudoElements::after)) {
+  nsCSSPseudoElements::Type pseudoType = aNewStyleContext->GetPseudoType();
+  if (pseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement &&
+      pseudoType != nsCSSPseudoElements::ePseudo_before &&
+      pseudoType != nsCSSPseudoElements::ePseudo_after) {
     return nsnull;
   }
   if (aNewStyleContext->GetParent() &&
       aNewStyleContext->GetParent()->HasPseudoElementData()) {
     // Ignore transitions on things that inherit properties from
     // pseudo-elements.
     // FIXME (Bug 522599): Add tests for this.
     return nsnull;
@@ -393,19 +393,17 @@ nsTransitionManager::StyleContextChanged
   PRBool startedAny = PR_FALSE;
   nsCSSPropertySet whichStarted;
   ElementTransitions *et = nsnull;
   for (PRUint32 i = disp->mTransitionPropertyCount; i-- != 0; ) {
     const nsTransition& t = disp->mTransitions[i];
     // Check delay and duration first, since they default to zero, and
     // when they're both zero, we can ignore the transition.
     if (t.GetDelay() != 0.0f || t.GetDuration() != 0.0f) {
-      et = GetElementTransitions(aElement,
-                                 aNewStyleContext->GetPseudo(),
-                                 PR_FALSE);
+      et = GetElementTransitions(aElement, pseudoType, PR_FALSE);
 
       // We might have something to transition.  See if any of the
       // properties in question changed and are animatable.
       nsCSSProperty property = t.GetProperty();
       if (property == eCSSPropertyExtra_no_properties ||
           property == eCSSProperty_UNKNOWN) {
         // Nothing to do, but need to exclude this from cases below.
       } else if (property == eCSSPropertyExtra_all_properties) {
@@ -609,17 +607,17 @@ nsTransitionManager::ConsiderStartingTra
   pt.mStartTime = rd->MostRecentRefresh() +
                   TimeDuration::FromMilliseconds(delay);
   pt.mDuration = TimeDuration::FromMilliseconds(duration);
   const nsTimingFunction &tf = aTransition.GetTimingFunction();
   pt.mTimingFunction.Init(tf.mX1, tf.mY1, tf.mX2, tf.mY2);
 
   if (!aElementTransitions) {
     aElementTransitions =
-      GetElementTransitions(aElement, aNewStyleContext->GetPseudo(),
+      GetElementTransitions(aElement, aNewStyleContext->GetPseudoType(),
                             PR_TRUE);
     if (!aElementTransitions) {
       NS_WARNING("allocating ElementTransitions failed");
       return;
     }
   }
   
   nsTArray<ElementPropertyTransition> &pts =
@@ -643,26 +641,27 @@ nsTransitionManager::ConsiderStartingTra
   presContext->PresShell()->RestyleForAnimation(aElement);
 
   *aStartedAny = PR_TRUE;
   aWhichStarted->AddProperty(aProperty);
 }
 
 ElementTransitions*
 nsTransitionManager::GetElementTransitions(nsIContent *aElement,
-                                           nsIAtom *aPseudo,
+                                           nsCSSPseudoElements::Type aPseudoType,
                                            PRBool aCreateIfNeeded)
 {
   nsIAtom *propName;
-  if (aPseudo == nsCSSPseudoElements::before) {
+  if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
     propName = nsGkAtoms::transitionsOfBeforeProperty;
-  } else if (aPseudo == nsCSSPseudoElements::after) {
+  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
     propName = nsGkAtoms::transitionsOfAfterProperty;
   } else {
-    NS_ASSERTION(!aPseudo || !aCreateIfNeeded,
+    NS_ASSERTION(aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
+		 !aCreateIfNeeded,
                  "should never try to create transitions for pseudo "
                  "other than :before or :after");
     propName = nsGkAtoms::transitionsProperty;
   }
   ElementTransitions *et = static_cast<ElementTransitions*>(
                              aElement->GetProperty(propName));
   if (!et && aCreateIfNeeded) {
     // FIXME: Consider arena-allocating?
@@ -706,32 +705,32 @@ NS_IMPL_RELEASE_USING_AGGREGATOR(nsTrans
 NS_IMPL_QUERY_INTERFACE1(nsTransitionManager, nsIStyleRuleProcessor)
 
 /*
  * nsIStyleRuleProcessor implementation
  */
 
 nsresult
 nsTransitionManager::WalkTransitionRule(RuleProcessorData* aData,
-                                        nsIAtom *aPseudo)
+                                        nsCSSPseudoElements::Type aPseudoType)
 {
   if (!aData->mPresContext->IsProcessingAnimationStyleChange()) {
     // If we're processing a normal style change rather than one from
     // animation, don't add the transition rule.  This allows us to
     // compute the new style value rather than having the transition
     // override it, so that we can start transitioning differently.
 
     // In most cases, we need to immediately restyle with animation
     // after doing this.  However, ConsiderStartingTransition takes care
     // of that for us.
     return NS_OK;
   }
 
   ElementTransitions *et =
-    GetElementTransitions(aData->mContent, aPseudo, PR_FALSE);
+    GetElementTransitions(aData->mContent, aPseudoType, PR_FALSE);
   if (!et) {
     return NS_OK;
   }
 
   if (!et->EnsureStyleRuleFor(
         aData->mPresContext->RefreshDriver()->MostRecentRefresh())) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -741,28 +740,36 @@ nsTransitionManager::WalkTransitionRule(
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTransitionManager::RulesMatching(ElementRuleProcessorData* aData)
 {
   NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
                     "pres context mismatch");
-  return WalkTransitionRule(aData, nsnull);
+  return WalkTransitionRule(aData,
+			    nsCSSPseudoElements::ePseudo_NotPseudoElement);
+}
+
+NS_IMETHODIMP
+nsTransitionManager::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
+                    "pres context mismatch");
+
+  // Note:  If we're the only thing keeping a pseudo-element frame alive
+  // (per ProbePseudoStyleContext), we still want to keep it alive, so
+  // this is ok.
+  return WalkTransitionRule(aData, aData->mPseudoType);
 }
 
 NS_IMETHODIMP
 nsTransitionManager::RulesMatching(PseudoRuleProcessorData* aData)
 {
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  // Note:  If we're the only thing keeping a pseudo-element frame alive
-  // (per ProbePseudoStyleContext), we still want to keep it alive, so
-  // this is ok.
-  return WalkTransitionRule(aData, aData->mPseudoTag);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTransitionManager::HasStateDependentStyle(StateRuleProcessorData* aData,
                                             nsReStyleHint* aResult)
 {
   *aResult = nsReStyleHint(0);
   return NS_OK;
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -39,16 +39,17 @@
 
 #ifndef nsTransitionManager_h_
 #define nsTransitionManager_h_
 
 #include "prclist.h"
 #include "nsCSSProperty.h"
 #include "nsIStyleRuleProcessor.h"
 #include "nsRefreshDriver.h"
+#include "nsCSSPseudoElements.h"
 
 class nsStyleContext;
 class nsPresContext;
 class nsCSSPropertySet;
 struct nsTransition;
 struct ElementTransitions;
 
 /**
@@ -80,16 +81,17 @@ public:
                         nsStyleContext *aOldStyleContext,
                         nsStyleContext *aNewStyleContext);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIStyleRuleProcessor
   NS_IMETHOD RulesMatching(ElementRuleProcessorData* aData);
+  NS_IMETHOD RulesMatching(PseudoElementRuleProcessorData* aData);
   NS_IMETHOD RulesMatching(PseudoRuleProcessorData* aData);
   NS_IMETHOD HasStateDependentStyle(StateRuleProcessorData* aData,
                                     nsReStyleHint* aResult);
   virtual nsReStyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData);
   NS_IMETHOD MediumFeaturesChanged(nsPresContext* aPresContext,
                                    PRBool* aRulesChanged);
 
@@ -103,19 +105,20 @@ private:
                                   const nsTransition& aTransition,
                                   nsIContent *aElement,
                                   ElementTransitions *&aElementTransitions,
                                   nsStyleContext *aOldStyleContext,
                                   nsStyleContext *aNewStyleContext,
                                   PRBool *aStartedAny,
                                   nsCSSPropertySet *aWhichStarted);
   ElementTransitions* GetElementTransitions(nsIContent *aElement,
-                                            nsIAtom *aPseudo,
+                                            nsCSSPseudoElements::Type aPseudoType,
                                             PRBool aCreateIfNeeded);
   void AddElementTransitions(ElementTransitions* aElementTransitions);
   void TransitionsRemoved();
-  nsresult WalkTransitionRule(RuleProcessorData* aData, nsIAtom *aPseudo);
+  nsresult WalkTransitionRule(RuleProcessorData* aData,
+			      nsCSSPseudoElements::Type aPseudoType);
 
   PRCList mElementTransitions;
   nsPresContext *mPresContext;
 };
 
 #endif /* !defined(nsTransitionManager_h_) */