Back to index

lightning-sunbird  0.9+nobinonly
nsCSSStyleSheet.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 // vim:cindent:tabstop=2:expandtab:shiftwidth=2:
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *   Daniel Glazman <glazman@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsCSSStyleSheet.h"
00042 
00043 #define PL_ARENA_CONST_ALIGN_MASK 7
00044 #define NS_RULEHASH_ARENA_BLOCK_SIZE (256)
00045 #include "plarena.h"
00046 
00047 #include "nsCRT.h"
00048 #include "nsIAtom.h"
00049 #include "nsIURL.h"
00050 #include "nsIServiceManager.h"
00051 #include "nsISupportsArray.h"
00052 #include "pldhash.h"
00053 #include "nsHashtable.h"
00054 #include "nsICSSPseudoComparator.h"
00055 #include "nsCSSRuleProcessor.h"
00056 #include "nsICSSStyleRule.h"
00057 #include "nsICSSNameSpaceRule.h"
00058 #include "nsICSSGroupRule.h"
00059 #include "nsICSSImportRule.h"
00060 #include "nsIMediaList.h"
00061 #include "nsIStyledContent.h"
00062 #include "nsIDocument.h"
00063 #include "nsPresContext.h"
00064 #include "nsIEventStateManager.h"
00065 #include "nsHTMLAtoms.h"
00066 #include "nsLayoutAtoms.h"
00067 #include "nsIFrame.h"
00068 #include "nsString.h"
00069 #include "nsReadableUtils.h"
00070 #include "nsUnicharUtils.h"
00071 #include "nsVoidArray.h"
00072 #include "nsIUnicharInputStream.h"
00073 #include "nsIDOMHTMLInputElement.h"
00074 #include "nsIDOMHTMLOptionElement.h"
00075 #include "nsIDOMStyleSheetList.h"
00076 #include "nsIDOMCSSStyleSheet.h"
00077 #include "nsIDOMCSSRule.h"
00078 #include "nsIDOMCSSImportRule.h"
00079 #include "nsIDOMCSSRuleList.h"
00080 #include "nsIDOMMediaList.h"
00081 #include "nsIDOMNode.h"
00082 #include "nsDOMError.h"
00083 #include "nsIPresShell.h"
00084 #include "nsICSSParser.h"
00085 #include "nsICSSLoader.h"
00086 #include "nsICSSLoaderObserver.h"
00087 #include "nsRuleWalker.h"
00088 #include "nsCSSPseudoClasses.h"
00089 #include "nsINameSpaceManager.h"
00090 #include "nsXMLNameSpaceMap.h"
00091 #include "nsITextContent.h"
00092 #include "prlog.h"
00093 #include "nsCOMPtr.h"
00094 #include "nsHashKeys.h"
00095 #include "nsStyleUtil.h"
00096 #include "nsQuickSort.h"
00097 #include "nsContentUtils.h"
00098 #include "nsIJSContextStack.h"
00099 #include "nsIScriptSecurityManager.h"
00100 #include "nsAttrValue.h"
00101 
00102 struct RuleValue {
00113   RuleValue(nsICSSStyleRule* aRule, nsCSSSelector* aSelector)
00114     : mRule(aRule), mSelector(aSelector) {}
00115 
00116   RuleValue* Add(PRInt32 aBackwardIndex, RuleValue *aNext)
00117   {
00118     mBackwardIndex = aBackwardIndex;
00119     mNext = aNext;
00120     return this;
00121   }
00122     
00123   // CAUTION: ~RuleValue will never get called as RuleValues are arena
00124   // allocated and arena cleanup will take care of deleting memory.
00125   // Add code to RuleHash::~RuleHash to get it to call the destructor
00126   // if any more cleanup needs to happen.
00127   ~RuleValue()
00128   {
00129     // Rule values are arena allocated. No need for any deletion.
00130   }
00131 
00132   // Placement new to arena allocate the RuleValues
00133   void *operator new(size_t aSize, PLArenaPool &aArena) CPP_THROW_NEW {
00134     void *mem;
00135     PL_ARENA_ALLOCATE(mem, &aArena, aSize);
00136     return mem;
00137   }
00138 
00139   nsICSSStyleRule*  mRule;
00140   nsCSSSelector*    mSelector; // which of |mRule|'s selectors
00141   PRInt32           mBackwardIndex; // High index means low weight/order.
00142   RuleValue*        mNext;
00143 };
00144 
00145 // ------------------------------
00146 // Rule hash table
00147 //
00148 
00149 // Uses any of the sets of ops below.
00150 struct RuleHashTableEntry : public PLDHashEntryHdr {
00151   RuleValue *mRules; // linked list of |RuleValue|, null-terminated
00152 };
00153 
00154 PR_STATIC_CALLBACK(PLDHashNumber)
00155 RuleHash_CIHashKey(PLDHashTable *table, const void *key)
00156 {
00157   nsIAtom *atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*, key));
00158 
00159   nsAutoString str;
00160   atom->ToString(str);
00161   ToUpperCase(str);
00162   return HashString(str);
00163 }
00164 
00165 PR_STATIC_CALLBACK(PRBool)
00166 RuleHash_CIMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
00167                       const void *key)
00168 {
00169   nsIAtom *match_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*,
00170                             key));
00171   // Use the |getKey| callback to avoid code duplication.
00172   // XXX Ugh!  Why does |getKey| have different |const|-ness?
00173   nsIAtom *entry_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*,
00174              table->ops->getKey(table, NS_CONST_CAST(PLDHashEntryHdr*, hdr))));
00175 
00176   // Check for case-sensitive match first.
00177   if (match_atom == entry_atom)
00178     return PR_TRUE;
00179 
00180   const char *match_str, *entry_str;
00181   match_atom->GetUTF8String(&match_str);
00182   entry_atom->GetUTF8String(&entry_str);
00183 
00184   return (nsCRT::strcasecmp(entry_str, match_str) == 0);
00185 }
00186 
00187 PR_STATIC_CALLBACK(PRBool)
00188 RuleHash_CSMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
00189                       const void *key)
00190 {
00191   nsIAtom *match_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*,
00192                             key));
00193   // Use the |getKey| callback to avoid code duplication.
00194   // XXX Ugh!  Why does |getKey| have different |const|-ness?
00195   nsIAtom *entry_atom = NS_CONST_CAST(nsIAtom*, NS_STATIC_CAST(const nsIAtom*,
00196              table->ops->getKey(table, NS_CONST_CAST(PLDHashEntryHdr*, hdr))));
00197 
00198   return match_atom == entry_atom;
00199 }
00200 
00201 PR_STATIC_CALLBACK(const void*)
00202 RuleHash_TagTable_GetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00203 {
00204   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*, hdr);
00205   return entry->mRules->mSelector->mTag;
00206 }
00207 
00208 PR_STATIC_CALLBACK(const void*)
00209 RuleHash_ClassTable_GetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00210 {
00211   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*, hdr);
00212   return entry->mRules->mSelector->mClassList->mAtom;
00213 }
00214 
00215 PR_STATIC_CALLBACK(const void*)
00216 RuleHash_IdTable_GetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00217 {
00218   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*, hdr);
00219   return entry->mRules->mSelector->mIDList->mAtom;
00220 }
00221 
00222 PR_STATIC_CALLBACK(const void*)
00223 RuleHash_NameSpaceTable_GetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
00224 {
00225   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*, hdr);
00226   return NS_INT32_TO_PTR(entry->mRules->mSelector->mNameSpace);
00227 }
00228 
00229 PR_STATIC_CALLBACK(PLDHashNumber)
00230 RuleHash_NameSpaceTable_HashKey(PLDHashTable *table, const void *key)
00231 {
00232   return NS_PTR_TO_INT32(key);
00233 }
00234 
00235 PR_STATIC_CALLBACK(PRBool)
00236 RuleHash_NameSpaceTable_MatchEntry(PLDHashTable *table,
00237                                    const PLDHashEntryHdr *hdr,
00238                                    const void *key)
00239 {
00240   const RuleHashTableEntry *entry =
00241     NS_STATIC_CAST(const RuleHashTableEntry*, hdr);
00242 
00243   return NS_PTR_TO_INT32(key) ==
00244          entry->mRules->mSelector->mNameSpace;
00245 }
00246 
00247 static PLDHashTableOps RuleHash_TagTable_Ops = {
00248   PL_DHashAllocTable,
00249   PL_DHashFreeTable,
00250   RuleHash_TagTable_GetKey,
00251   PL_DHashVoidPtrKeyStub,
00252   RuleHash_CSMatchEntry,
00253   PL_DHashMoveEntryStub,
00254   PL_DHashClearEntryStub,
00255   PL_DHashFinalizeStub,
00256   NULL
00257 };
00258 
00259 // Case-sensitive ops.
00260 static PLDHashTableOps RuleHash_ClassTable_CSOps = {
00261   PL_DHashAllocTable,
00262   PL_DHashFreeTable,
00263   RuleHash_ClassTable_GetKey,
00264   PL_DHashVoidPtrKeyStub,
00265   RuleHash_CSMatchEntry,
00266   PL_DHashMoveEntryStub,
00267   PL_DHashClearEntryStub,
00268   PL_DHashFinalizeStub,
00269   NULL
00270 };
00271 
00272 // Case-insensitive ops.
00273 static PLDHashTableOps RuleHash_ClassTable_CIOps = {
00274   PL_DHashAllocTable,
00275   PL_DHashFreeTable,
00276   RuleHash_ClassTable_GetKey,
00277   RuleHash_CIHashKey,
00278   RuleHash_CIMatchEntry,
00279   PL_DHashMoveEntryStub,
00280   PL_DHashClearEntryStub,
00281   PL_DHashFinalizeStub,
00282   NULL
00283 };
00284 
00285 // Case-sensitive ops.
00286 static PLDHashTableOps RuleHash_IdTable_CSOps = {
00287   PL_DHashAllocTable,
00288   PL_DHashFreeTable,
00289   RuleHash_IdTable_GetKey,
00290   PL_DHashVoidPtrKeyStub,
00291   RuleHash_CSMatchEntry,
00292   PL_DHashMoveEntryStub,
00293   PL_DHashClearEntryStub,
00294   PL_DHashFinalizeStub,
00295   NULL
00296 };
00297 
00298 // Case-insensitive ops.
00299 static PLDHashTableOps RuleHash_IdTable_CIOps = {
00300   PL_DHashAllocTable,
00301   PL_DHashFreeTable,
00302   RuleHash_IdTable_GetKey,
00303   RuleHash_CIHashKey,
00304   RuleHash_CIMatchEntry,
00305   PL_DHashMoveEntryStub,
00306   PL_DHashClearEntryStub,
00307   PL_DHashFinalizeStub,
00308   NULL
00309 };
00310 
00311 static PLDHashTableOps RuleHash_NameSpaceTable_Ops = {
00312   PL_DHashAllocTable,
00313   PL_DHashFreeTable,
00314   RuleHash_NameSpaceTable_GetKey,
00315   RuleHash_NameSpaceTable_HashKey,
00316   RuleHash_NameSpaceTable_MatchEntry,
00317   PL_DHashMoveEntryStub,
00318   PL_DHashClearEntryStub,
00319   PL_DHashFinalizeStub,
00320   NULL
00321 };
00322 
00323 #undef RULE_HASH_STATS
00324 #undef PRINT_UNIVERSAL_RULES
00325 
00326 #ifdef DEBUG_dbaron
00327 #define RULE_HASH_STATS
00328 #define PRINT_UNIVERSAL_RULES
00329 #endif
00330 
00331 #ifdef RULE_HASH_STATS
00332 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO ++(var_); PR_END_MACRO
00333 #else
00334 #define RULE_HASH_STAT_INCREMENT(var_) PR_BEGIN_MACRO PR_END_MACRO
00335 #endif
00336 
00337 // Enumerator callback function.
00338 typedef void (*RuleEnumFunc)(nsICSSStyleRule* aRule,
00339                              nsCSSSelector* aSelector,
00340                              void *aData);
00341 
00342 class RuleHash {
00343 public:
00344   RuleHash(PRBool aQuirksMode);
00345   ~RuleHash();
00346   void PrependRule(RuleValue *aRuleInfo);
00347   void EnumerateAllRules(PRInt32 aNameSpace, nsIAtom* aTag, nsIAtom* aID,
00348                          const nsAttrValue* aClassList,
00349                          RuleEnumFunc aFunc, void* aData);
00350   void EnumerateTagRules(nsIAtom* aTag,
00351                          RuleEnumFunc aFunc, void* aData);
00352   PLArenaPool& Arena() { return mArena; }
00353 
00354 protected:
00355   void PrependRuleToTable(PLDHashTable* aTable, const void* aKey,
00356                           RuleValue* aRuleInfo);
00357   void PrependUniversalRule(RuleValue* aRuleInfo);
00358 
00359   // All rule values in these hashtables are arena allocated
00360   PRInt32     mRuleCount;
00361   PLDHashTable mIdTable;
00362   PLDHashTable mClassTable;
00363   PLDHashTable mTagTable;
00364   PLDHashTable mNameSpaceTable;
00365   RuleValue *mUniversalRules;
00366 
00367   RuleValue** mEnumList;
00368   PRInt32     mEnumListSize;
00369 
00370   PLArenaPool mArena;
00371 
00372 #ifdef RULE_HASH_STATS
00373   PRUint32    mUniversalSelectors;
00374   PRUint32    mNameSpaceSelectors;
00375   PRUint32    mTagSelectors;
00376   PRUint32    mClassSelectors;
00377   PRUint32    mIdSelectors;
00378 
00379   PRUint32    mElementsMatched;
00380   PRUint32    mPseudosMatched;
00381 
00382   PRUint32    mElementUniversalCalls;
00383   PRUint32    mElementNameSpaceCalls;
00384   PRUint32    mElementTagCalls;
00385   PRUint32    mElementClassCalls;
00386   PRUint32    mElementIdCalls;
00387 
00388   PRUint32    mPseudoTagCalls;
00389 #endif // RULE_HASH_STATS
00390 };
00391 
00392 RuleHash::RuleHash(PRBool aQuirksMode)
00393   : mRuleCount(0),
00394     mUniversalRules(nsnull),
00395     mEnumList(nsnull), mEnumListSize(0)
00396 #ifdef RULE_HASH_STATS
00397     ,
00398     mUniversalSelectors(0),
00399     mNameSpaceSelectors(0),
00400     mTagSelectors(0),
00401     mClassSelectors(0),
00402     mIdSelectors(0),
00403     mElementsMatched(0),
00404     mPseudosMatched(0),
00405     mElementUniversalCalls(0),
00406     mElementNameSpaceCalls(0),
00407     mElementTagCalls(0),
00408     mElementClassCalls(0),
00409     mElementIdCalls(0),
00410     mPseudoTagCalls(0)
00411 #endif
00412 {
00413   // Initialize our arena
00414   PL_INIT_ARENA_POOL(&mArena, "RuleHashArena", NS_RULEHASH_ARENA_BLOCK_SIZE);
00415 
00416   PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops, nsnull,
00417                     sizeof(RuleHashTableEntry), 64);
00418   PL_DHashTableInit(&mIdTable,
00419                     aQuirksMode ? &RuleHash_IdTable_CIOps
00420                                 : &RuleHash_IdTable_CSOps,
00421                     nsnull, sizeof(RuleHashTableEntry), 16);
00422   PL_DHashTableInit(&mClassTable,
00423                     aQuirksMode ? &RuleHash_ClassTable_CIOps
00424                                 : &RuleHash_ClassTable_CSOps,
00425                     nsnull, sizeof(RuleHashTableEntry), 16);
00426   PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops, nsnull,
00427                     sizeof(RuleHashTableEntry), 16);
00428 }
00429 
00430 RuleHash::~RuleHash()
00431 {
00432 #ifdef RULE_HASH_STATS
00433   printf(
00434 "RuleHash(%p):\n"
00435 "  Selectors: Universal (%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
00436 "  Content Nodes: Elements(%u) Pseudo-Elements(%u)\n"
00437 "  Element Calls: Universal(%u) NameSpace(%u) Tag(%u) Class(%u) Id(%u)\n"
00438 "  Pseudo-Element Calls: Tag(%u)\n",
00439          NS_STATIC_CAST(void*, this),
00440          mUniversalSelectors, mNameSpaceSelectors, mTagSelectors,
00441            mClassSelectors, mIdSelectors,
00442          mElementsMatched,
00443          mPseudosMatched,
00444          mElementUniversalCalls, mElementNameSpaceCalls, mElementTagCalls,
00445            mElementClassCalls, mElementIdCalls,
00446          mPseudoTagCalls);
00447 #ifdef PRINT_UNIVERSAL_RULES
00448   {
00449     RuleValue* value = mUniversalRules;
00450     if (value) {
00451       printf("  Universal rules:\n");
00452       do {
00453         nsAutoString selectorText;
00454         PRUint32 lineNumber = value->mRule->GetLineNumber();
00455         nsCOMPtr<nsIStyleSheet> sheet;
00456         value->mRule->GetStyleSheet(*getter_AddRefs(sheet));
00457         nsCOMPtr<nsICSSStyleSheet> cssSheet = do_QueryInterface(sheet);
00458         value->mSelector->ToString(selectorText, cssSheet);
00459 
00460         printf("    line %d, %s\n",
00461                lineNumber, NS_ConvertUCS2toUTF8(selectorText).get());
00462         value = value->mNext;
00463       } while (value);
00464     }
00465   }
00466 #endif // PRINT_UNIVERSAL_RULES
00467 #endif // RULE_HASH_STATS
00468   // Rule Values are arena allocated no need to delete them. Their destructor
00469   // isn't doing any cleanup. So we dont even bother to enumerate through
00470   // the hash tables and call their destructors.
00471   if (nsnull != mEnumList) {
00472     delete [] mEnumList;
00473   }
00474   // delete arena for strings and small objects
00475   PL_DHashTableFinish(&mIdTable);
00476   PL_DHashTableFinish(&mClassTable);
00477   PL_DHashTableFinish(&mTagTable);
00478   PL_DHashTableFinish(&mNameSpaceTable);
00479   PL_FinishArenaPool(&mArena);
00480 }
00481 
00482 void RuleHash::PrependRuleToTable(PLDHashTable* aTable, const void* aKey,
00483                                   RuleValue* aRuleInfo)
00484 {
00485   // Get a new or existing entry.
00486   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00487       PL_DHashTableOperate(aTable, aKey, PL_DHASH_ADD));
00488   if (!entry)
00489     return;
00490   entry->mRules = aRuleInfo->Add(mRuleCount++, entry->mRules);
00491 }
00492 
00493 void RuleHash::PrependUniversalRule(RuleValue *aRuleInfo)
00494 {
00495   mUniversalRules = aRuleInfo->Add(mRuleCount++, mUniversalRules);
00496 }
00497 
00498 void RuleHash::PrependRule(RuleValue *aRuleInfo)
00499 {
00500   nsCSSSelector *selector = aRuleInfo->mSelector;
00501   if (nsnull != selector->mIDList) {
00502     PrependRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
00503     RULE_HASH_STAT_INCREMENT(mIdSelectors);
00504   }
00505   else if (nsnull != selector->mClassList) {
00506     PrependRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
00507     RULE_HASH_STAT_INCREMENT(mClassSelectors);
00508   }
00509   else if (nsnull != selector->mTag) {
00510     PrependRuleToTable(&mTagTable, selector->mTag, aRuleInfo);
00511     RULE_HASH_STAT_INCREMENT(mTagSelectors);
00512   }
00513   else if (kNameSpaceID_Unknown != selector->mNameSpace) {
00514     PrependRuleToTable(&mNameSpaceTable,
00515                        NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
00516     RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
00517   }
00518   else {  // universal tag selector
00519     PrependUniversalRule(aRuleInfo);
00520     RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
00521   }
00522 }
00523 
00524 // this should cover practically all cases so we don't need to reallocate
00525 #define MIN_ENUM_LIST_SIZE 8
00526 
00527 #ifdef RULE_HASH_STATS
00528 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
00529   do { ++(var_); (list_) = (list_)->mNext; } while (list_)
00530 #else
00531 #define RULE_HASH_STAT_INCREMENT_LIST_COUNT(list_, var_) \
00532   PR_BEGIN_MACRO PR_END_MACRO
00533 #endif
00534 
00535 void RuleHash::EnumerateAllRules(PRInt32 aNameSpace, nsIAtom* aTag,
00536                                  nsIAtom* aID, const nsAttrValue* aClassList,
00537                                  RuleEnumFunc aFunc, void* aData)
00538 {
00539   PRInt32 classCount = aClassList ? aClassList->GetAtomCount() : 0;
00540 
00541   // assume 1 universal, tag, id, and namespace, rather than wasting
00542   // time counting
00543   PRInt32 testCount = classCount + 4;
00544 
00545   if (mEnumListSize < testCount) {
00546     delete [] mEnumList;
00547     mEnumListSize = PR_MAX(testCount, MIN_ENUM_LIST_SIZE);
00548     mEnumList = new RuleValue*[mEnumListSize];
00549   }
00550 
00551   PRInt32 valueCount = 0;
00552   RULE_HASH_STAT_INCREMENT(mElementsMatched);
00553 
00554   { // universal rules
00555     RuleValue* value = mUniversalRules;
00556     if (nsnull != value) {
00557       mEnumList[valueCount++] = value;
00558       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementUniversalCalls);
00559     }
00560   }
00561   // universal rules within the namespace
00562   if (kNameSpaceID_Unknown != aNameSpace) {
00563     RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00564         PL_DHashTableOperate(&mNameSpaceTable, NS_INT32_TO_PTR(aNameSpace),
00565                              PL_DHASH_LOOKUP));
00566     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00567       RuleValue *value = entry->mRules;
00568       mEnumList[valueCount++] = value;
00569       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementNameSpaceCalls);
00570     }
00571   }
00572   if (nsnull != aTag) {
00573     RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00574         PL_DHashTableOperate(&mTagTable, aTag, PL_DHASH_LOOKUP));
00575     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00576       RuleValue *value = entry->mRules;
00577       mEnumList[valueCount++] = value;
00578       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementTagCalls);
00579     }
00580   }
00581   if (nsnull != aID) {
00582     RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00583         PL_DHashTableOperate(&mIdTable, aID, PL_DHASH_LOOKUP));
00584     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00585       RuleValue *value = entry->mRules;
00586       mEnumList[valueCount++] = value;
00587       RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementIdCalls);
00588     }
00589   }
00590   { // extra scope to work around compiler bugs with |for| scoping.
00591     for (PRInt32 index = 0; index < classCount; ++index) {
00592       RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00593         PL_DHashTableOperate(&mClassTable, aClassList->AtomAt(index),
00594                              PL_DHASH_LOOKUP));
00595       if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00596         RuleValue *value = entry->mRules;
00597         mEnumList[valueCount++] = value;
00598         RULE_HASH_STAT_INCREMENT_LIST_COUNT(value, mElementClassCalls);
00599       }
00600     }
00601   }
00602   NS_ASSERTION(valueCount <= testCount, "values exceeded list size");
00603 
00604   if (valueCount > 0) {
00605     // Merge the lists while there are still multiple lists to merge.
00606     while (valueCount > 1) {
00607       PRInt32 valueIndex = 0;
00608       PRInt32 highestRuleIndex = mEnumList[valueIndex]->mBackwardIndex;
00609       for (PRInt32 index = 1; index < valueCount; ++index) {
00610         PRInt32 ruleIndex = mEnumList[index]->mBackwardIndex;
00611         if (ruleIndex > highestRuleIndex) {
00612           valueIndex = index;
00613           highestRuleIndex = ruleIndex;
00614         }
00615       }
00616       RuleValue *cur = mEnumList[valueIndex];
00617       (*aFunc)(cur->mRule, cur->mSelector, aData);
00618       RuleValue *next = cur->mNext;
00619       mEnumList[valueIndex] = next ? next : mEnumList[--valueCount];
00620     }
00621 
00622     // Fast loop over single value.
00623     RuleValue* value = mEnumList[0];
00624     do {
00625       (*aFunc)(value->mRule, value->mSelector, aData);
00626       value = value->mNext;
00627     } while (value);
00628   }
00629 }
00630 
00631 void RuleHash::EnumerateTagRules(nsIAtom* aTag, RuleEnumFunc aFunc, void* aData)
00632 {
00633   RuleHashTableEntry *entry = NS_STATIC_CAST(RuleHashTableEntry*,
00634       PL_DHashTableOperate(&mTagTable, aTag, PL_DHASH_LOOKUP));
00635 
00636   RULE_HASH_STAT_INCREMENT(mPseudosMatched);
00637   if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00638     RuleValue *tagValue = entry->mRules;
00639     do {
00640       RULE_HASH_STAT_INCREMENT(mPseudoTagCalls);
00641       (*aFunc)(tagValue->mRule, tagValue->mSelector, aData);
00642       tagValue = tagValue->mNext;
00643     } while (tagValue);
00644   }
00645 }
00646 
00647 //--------------------------------
00648 
00649 // Attribute selectors hash table.
00650 struct AttributeSelectorEntry : public PLDHashEntryHdr {
00651   nsIAtom *mAttribute;
00652   nsVoidArray *mSelectors;
00653 };
00654 
00655 PR_STATIC_CALLBACK(void)
00656 AttributeSelectorClearEntry(PLDHashTable *table, PLDHashEntryHdr *hdr)
00657 {
00658   AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*, hdr);
00659   delete entry->mSelectors;
00660   memset(entry, 0, table->entrySize);
00661 }
00662 
00663 static PLDHashTableOps AttributeSelectorOps = {
00664   PL_DHashAllocTable,
00665   PL_DHashFreeTable,
00666   PL_DHashGetKeyStub,
00667   PL_DHashVoidPtrKeyStub,
00668   PL_DHashMatchEntryStub,
00669   PL_DHashMoveEntryStub,
00670   AttributeSelectorClearEntry,
00671   PL_DHashFinalizeStub,
00672   NULL
00673 };
00674 
00675 
00676 //--------------------------------
00677 
00678 struct RuleCascadeData {
00679   RuleCascadeData(nsIAtom *aMedium, PRBool aQuirksMode)
00680     : mRuleHash(aQuirksMode),
00681       mStateSelectors(),
00682       mMedium(aMedium),
00683       mNext(nsnull)
00684   {
00685     PL_DHashTableInit(&mAttributeSelectors, &AttributeSelectorOps, nsnull,
00686                       sizeof(AttributeSelectorEntry), 16);
00687   }
00688 
00689   ~RuleCascadeData()
00690   {
00691     PL_DHashTableFinish(&mAttributeSelectors);
00692   }
00693   RuleHash          mRuleHash;
00694   nsVoidArray       mStateSelectors;
00695   nsVoidArray       mClassSelectors;
00696   nsVoidArray       mIDSelectors;
00697   PLDHashTable      mAttributeSelectors; // nsIAtom* -> nsVoidArray*
00698 
00699   // Looks up or creates the appropriate list in |mAttributeSelectors|.
00700   // Returns null only on allocation failure.
00701   nsVoidArray* AttributeListFor(nsIAtom* aAttribute);
00702 
00703   nsCOMPtr<nsIAtom> mMedium;
00704   RuleCascadeData*  mNext; // for a different medium
00705 };
00706 
00707 nsVoidArray*
00708 RuleCascadeData::AttributeListFor(nsIAtom* aAttribute)
00709 {
00710   AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*,
00711       PL_DHashTableOperate(&mAttributeSelectors, aAttribute, PL_DHASH_ADD));
00712   if (!entry)
00713     return nsnull;
00714   if (!entry->mSelectors) {
00715     if (!(entry->mSelectors = new nsVoidArray)) {
00716       PL_DHashTableRawRemove(&mAttributeSelectors, entry);
00717       return nsnull;
00718     }
00719     entry->mAttribute = aAttribute;
00720   }
00721   return entry->mSelectors;
00722 }
00723 
00724 
00725 // -------------------------------
00726 // Style Rule List for the DOM
00727 //
00728 class CSSRuleListImpl : public nsIDOMCSSRuleList
00729 {
00730 public:
00731   CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet);
00732 
00733   NS_DECL_ISUPPORTS
00734 
00735   // nsIDOMCSSRuleList interface
00736   NS_IMETHOD    GetLength(PRUint32* aLength); 
00737   NS_IMETHOD    Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn); 
00738 
00739   void DropReference() { mStyleSheet = nsnull; }
00740 
00741 protected:
00742   virtual ~CSSRuleListImpl();
00743 
00744   nsCSSStyleSheet*  mStyleSheet;
00745 public:
00746   PRBool              mRulesAccessed;
00747 };
00748 
00749 CSSRuleListImpl::CSSRuleListImpl(nsCSSStyleSheet *aStyleSheet)
00750 {
00751   // Not reference counted to avoid circular references.
00752   // The style sheet will tell us when its going away.
00753   mStyleSheet = aStyleSheet;
00754   mRulesAccessed = PR_FALSE;
00755 }
00756 
00757 CSSRuleListImpl::~CSSRuleListImpl()
00758 {
00759 }
00760 
00761 // QueryInterface implementation for CSSRuleList
00762 NS_INTERFACE_MAP_BEGIN(CSSRuleListImpl)
00763   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRuleList)
00764   NS_INTERFACE_MAP_ENTRY(nsISupports)
00765   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSRuleList)
00766 NS_INTERFACE_MAP_END
00767 
00768 
00769 NS_IMPL_ADDREF(CSSRuleListImpl)
00770 NS_IMPL_RELEASE(CSSRuleListImpl)
00771 
00772 
00773 NS_IMETHODIMP    
00774 CSSRuleListImpl::GetLength(PRUint32* aLength)
00775 {
00776   if (nsnull != mStyleSheet) {
00777     PRInt32 count;
00778     mStyleSheet->StyleRuleCount(count);
00779     *aLength = (PRUint32)count;
00780   }
00781   else {
00782     *aLength = 0;
00783   }
00784 
00785   return NS_OK;
00786 }
00787 
00788 NS_IMETHODIMP    
00789 CSSRuleListImpl::Item(PRUint32 aIndex, nsIDOMCSSRule** aReturn)
00790 {
00791   nsresult result = NS_OK;
00792 
00793   *aReturn = nsnull;
00794   if (mStyleSheet) {
00795     result = mStyleSheet->EnsureUniqueInner(); // needed to ensure rules have correct parent
00796     if (NS_SUCCEEDED(result)) {
00797       nsCOMPtr<nsICSSRule> rule;
00798 
00799       result = mStyleSheet->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
00800       if (rule) {
00801         result = rule->GetDOMRule(aReturn);
00802         mRulesAccessed = PR_TRUE; // signal to never share rules again
00803       } else if (result == NS_ERROR_ILLEGAL_VALUE) {
00804         result = NS_OK; // per spec: "Return Value ... null if ... not a valid index."
00805       }
00806     }
00807   }
00808   
00809   return result;
00810 }
00811 
00812 NS_INTERFACE_MAP_BEGIN(nsMediaList)
00813   NS_INTERFACE_MAP_ENTRY(nsIDOMMediaList)
00814   NS_INTERFACE_MAP_ENTRY(nsISupports)
00815   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(MediaList)
00816 NS_INTERFACE_MAP_END
00817 
00818 NS_IMPL_ADDREF(nsMediaList)
00819 NS_IMPL_RELEASE(nsMediaList)
00820 
00821 
00822 nsMediaList::nsMediaList()
00823   : mStyleSheet(nsnull)
00824 {
00825 }
00826 
00827 nsMediaList::~nsMediaList()
00828 {
00829 }
00830 
00831 nsresult
00832 nsMediaList::GetText(nsAString& aMediaText)
00833 {
00834   aMediaText.Truncate();
00835 
00836   for (PRInt32 i = 0, i_end = mArray.Count(); i < i_end; ++i) {
00837     nsIAtom* medium = mArray[i];
00838     NS_ENSURE_TRUE(medium, NS_ERROR_FAILURE);
00839 
00840     nsAutoString buffer;
00841     medium->ToString(buffer);
00842     aMediaText.Append(buffer);
00843     if (i + 1 < i_end) {
00844       aMediaText.AppendLiteral(", ");
00845     }
00846   }
00847 
00848   return NS_OK;
00849 }
00850 
00851 // XXXbz this is so ill-defined in the spec, it's not clear quite what
00852 // it should be doing....
00853 nsresult
00854 nsMediaList::SetText(const nsAString& aMediaText)
00855 {
00856   nsCOMPtr<nsICSSParser> parser;
00857   nsresult rv = NS_NewCSSParser(getter_AddRefs(parser));
00858   NS_ENSURE_SUCCESS(rv, rv);
00859 
00860   PRBool htmlMode = PR_FALSE;
00861   nsCOMPtr<nsIDOMStyleSheet> domSheet =
00862     do_QueryInterface(NS_STATIC_CAST(nsICSSStyleSheet*, mStyleSheet));
00863   if (domSheet) {
00864     nsCOMPtr<nsIDOMNode> node;
00865     domSheet->GetOwnerNode(getter_AddRefs(node));
00866     htmlMode = !!node;
00867   }
00868 
00869   return parser->ParseMediaList(nsString(aMediaText), nsnull, 0,
00870                                 this, htmlMode);
00871 }
00872 
00873 /*
00874  * aMatch is true when we contain the desired medium or contain the
00875  * "all" medium or contain no media at all, which is the same as
00876  * containing "all"
00877  */
00878 PRBool
00879 nsMediaList::Matches(nsPresContext* aPresContext)
00880 {
00881   if (-1 != mArray.IndexOf(aPresContext->Medium()) ||
00882       -1 != mArray.IndexOf(nsLayoutAtoms::all))
00883     return PR_TRUE;
00884   return mArray.Count() == 0;
00885 }
00886 
00887 nsresult
00888 nsMediaList::SetStyleSheet(nsICSSStyleSheet *aSheet)
00889 {
00890   NS_ASSERTION(aSheet == mStyleSheet || !aSheet || !mStyleSheet,
00891                "multiple style sheets competing for one media list");
00892   mStyleSheet = NS_STATIC_CAST(nsCSSStyleSheet*, aSheet);
00893   return NS_OK;
00894 }
00895 
00896 nsresult
00897 nsMediaList::Clone(nsMediaList** aResult)
00898 {
00899   nsRefPtr<nsMediaList> result = new nsMediaList();
00900   if (!result)
00901     return NS_ERROR_OUT_OF_MEMORY;
00902   if (!result->mArray.AppendObjects(mArray))
00903     return NS_ERROR_OUT_OF_MEMORY;
00904   NS_ADDREF(*aResult = result);
00905   return NS_OK;
00906 }
00907 
00908 NS_IMETHODIMP
00909 nsMediaList::GetMediaText(nsAString& aMediaText)
00910 {
00911   return GetText(aMediaText);
00912 }
00913 
00914 // "sheet" should be an nsCSSStyleSheet and "doc" should be an
00915 // nsCOMPtr<nsIDocument>
00916 #define BEGIN_MEDIA_CHANGE(sheet, doc)                         \
00917   if (sheet) {                                                 \
00918     rv = sheet->GetOwningDocument(*getter_AddRefs(doc));       \
00919     NS_ENSURE_SUCCESS(rv, rv);                                 \
00920   }                                                            \
00921   mozAutoDocUpdate updateBatch(doc, UPDATE_STYLE, PR_TRUE);    \
00922   if (sheet) {                                                 \
00923     rv = sheet->WillDirty();                                   \
00924     NS_ENSURE_SUCCESS(rv, rv);                                 \
00925   }
00926 
00927 #define END_MEDIA_CHANGE(sheet, doc)                           \
00928   if (sheet) {                                                 \
00929     sheet->DidDirty();                                         \
00930   }                                                            \
00931   /* XXXldb Pass something meaningful? */                      \
00932   if (doc) {                                                   \
00933     doc->StyleRuleChanged(sheet, nsnull, nsnull);              \
00934   }
00935 
00936 
00937 NS_IMETHODIMP
00938 nsMediaList::SetMediaText(const nsAString& aMediaText)
00939 {
00940   nsresult rv = NS_OK;
00941   nsCOMPtr<nsIDocument> doc;
00942 
00943   BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
00944 
00945   rv = SetText(aMediaText);
00946   if (NS_FAILED(rv))
00947     return rv;
00948   
00949   END_MEDIA_CHANGE(mStyleSheet, doc)
00950 
00951   return rv;
00952 }
00953                                
00954 NS_IMETHODIMP
00955 nsMediaList::GetLength(PRUint32* aLength)
00956 {
00957   NS_ENSURE_ARG_POINTER(aLength);
00958 
00959   *aLength = mArray.Count();
00960   return NS_OK;
00961 }
00962 
00963 NS_IMETHODIMP
00964 nsMediaList::Item(PRUint32 aIndex, nsAString& aReturn)
00965 {
00966   PRInt32 index = aIndex;
00967   if (0 <= index && index < Count()) {
00968     MediumAt(aIndex)->ToString(aReturn);
00969   } else {
00970     SetDOMStringToNull(aReturn);
00971   }
00972 
00973   return NS_OK;
00974 }
00975 
00976 NS_IMETHODIMP
00977 nsMediaList::DeleteMedium(const nsAString& aOldMedium)
00978 {
00979   nsresult rv = NS_OK;
00980   nsCOMPtr<nsIDocument> doc;
00981 
00982   BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
00983   
00984   rv = Delete(aOldMedium);
00985   if (NS_FAILED(rv))
00986     return rv;
00987 
00988   END_MEDIA_CHANGE(mStyleSheet, doc)
00989   
00990   return rv;
00991 }
00992 
00993 NS_IMETHODIMP
00994 nsMediaList::AppendMedium(const nsAString& aNewMedium)
00995 {
00996   nsresult rv = NS_OK;
00997   nsCOMPtr<nsIDocument> doc;
00998 
00999   BEGIN_MEDIA_CHANGE(mStyleSheet, doc)
01000   
01001   rv = Append(aNewMedium);
01002   if (NS_FAILED(rv))
01003     return rv;
01004 
01005   END_MEDIA_CHANGE(mStyleSheet, doc)
01006   
01007   return rv;
01008 }
01009 
01010 nsresult
01011 nsMediaList::Delete(const nsAString& aOldMedium)
01012 {
01013   if (aOldMedium.IsEmpty())
01014     return NS_ERROR_DOM_NOT_FOUND_ERR;
01015 
01016   nsCOMPtr<nsIAtom> old = do_GetAtom(aOldMedium);
01017   NS_ENSURE_TRUE(old, NS_ERROR_OUT_OF_MEMORY);
01018 
01019   PRInt32 indx = mArray.IndexOf(old);
01020 
01021   if (indx < 0) {
01022     return NS_ERROR_DOM_NOT_FOUND_ERR;
01023   }
01024 
01025   mArray.RemoveObjectAt(indx);
01026 
01027   return NS_OK;
01028 }
01029 
01030 nsresult
01031 nsMediaList::Append(const nsAString& aNewMedium)
01032 {
01033   if (aNewMedium.IsEmpty())
01034     return NS_ERROR_DOM_NOT_FOUND_ERR;
01035 
01036   nsCOMPtr<nsIAtom> media = do_GetAtom(aNewMedium);
01037   NS_ENSURE_TRUE(media, NS_ERROR_OUT_OF_MEMORY);
01038 
01039   PRInt32 indx = mArray.IndexOf(media);
01040 
01041   if (indx >= 0) {
01042     mArray.RemoveObjectAt(indx);
01043   }
01044 
01045   mArray.AppendObject(media);
01046 
01047   return NS_OK;
01048 }
01049 
01050 // -------------------------------
01051 // Imports Collection for the DOM
01052 //
01053 class CSSImportsCollectionImpl : public nsIDOMStyleSheetList
01054 {
01055 public:
01056   CSSImportsCollectionImpl(nsICSSStyleSheet *aStyleSheet);
01057 
01058   NS_DECL_ISUPPORTS
01059 
01060   // nsIDOMCSSStyleSheetList interface
01061   NS_IMETHOD    GetLength(PRUint32* aLength); 
01062   NS_IMETHOD    Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn); 
01063 
01064   void DropReference() { mStyleSheet = nsnull; }
01065 
01066 protected:
01067   virtual ~CSSImportsCollectionImpl();
01068 
01069   nsICSSStyleSheet*  mStyleSheet;
01070 };
01071 
01072 CSSImportsCollectionImpl::CSSImportsCollectionImpl(nsICSSStyleSheet *aStyleSheet)
01073 {
01074   // Not reference counted to avoid circular references.
01075   // The style sheet will tell us when its going away.
01076   mStyleSheet = aStyleSheet;
01077 }
01078 
01079 CSSImportsCollectionImpl::~CSSImportsCollectionImpl()
01080 {
01081 }
01082 
01083 
01084 // QueryInterface implementation for CSSImportsCollectionImpl
01085 NS_INTERFACE_MAP_BEGIN(CSSImportsCollectionImpl)
01086   NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheetList)
01087   NS_INTERFACE_MAP_ENTRY(nsISupports)
01088   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(StyleSheetList)
01089 NS_INTERFACE_MAP_END
01090 
01091 
01092 NS_IMPL_ADDREF(CSSImportsCollectionImpl)
01093 NS_IMPL_RELEASE(CSSImportsCollectionImpl)
01094 
01095 
01096 NS_IMETHODIMP
01097 CSSImportsCollectionImpl::GetLength(PRUint32* aLength)
01098 {
01099   if (nsnull != mStyleSheet) {
01100     PRInt32 count;
01101     mStyleSheet->StyleSheetCount(count);
01102     *aLength = (PRUint32)count;
01103   }
01104   else {
01105     *aLength = 0;
01106   }
01107 
01108   return NS_OK;
01109 }
01110 
01111 NS_IMETHODIMP    
01112 CSSImportsCollectionImpl::Item(PRUint32 aIndex, nsIDOMStyleSheet** aReturn)
01113 {
01114   nsresult result = NS_OK;
01115 
01116   *aReturn = nsnull;
01117 
01118   if (mStyleSheet) {
01119     nsCOMPtr<nsICSSStyleSheet> sheet;
01120 
01121     result = mStyleSheet->GetStyleSheetAt(aIndex, *getter_AddRefs(sheet));
01122     if (NS_SUCCEEDED(result)) {
01123       result = CallQueryInterface(sheet, aReturn);
01124     }
01125   }
01126   
01127   return result;
01128 }
01129 
01130 // -------------------------------
01131 // CSS Style Sheet Inner Data Container
01132 //
01133 
01134 
01135 static PRBool SetStyleSheetReference(nsISupports* aElement, void* aSheet)
01136 {
01137   nsICSSRule*  rule = (nsICSSRule*)aElement;
01138 
01139   if (nsnull != rule) {
01140     rule->SetStyleSheet((nsICSSStyleSheet*)aSheet);
01141   }
01142   return PR_TRUE;
01143 }
01144 
01145 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsICSSStyleSheet* aParentSheet)
01146   : mSheets(),
01147     mOrderedRules(nsnull),
01148     mComplete(PR_FALSE)
01149 {
01150   MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
01151   mSheets.AppendElement(aParentSheet);
01152 }
01153 
01154 static PRBool
01155 CloneRuleInto(nsISupports* aRule, void* aArray)
01156 {
01157   nsICSSRule* rule = (nsICSSRule*)aRule;
01158   nsICSSRule* clone = nsnull;
01159   rule->Clone(clone);
01160   if (clone) {
01161     nsISupportsArray* array = (nsISupportsArray*)aArray;
01162     array->AppendElement(clone);
01163     NS_RELEASE(clone);
01164   }
01165   return PR_TRUE;
01166 }
01167 
01168 nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy,
01169                                            nsICSSStyleSheet* aParentSheet)
01170   : mSheets(),
01171     mSheetURI(aCopy.mSheetURI),
01172     mOriginalSheetURI(aCopy.mOriginalSheetURI),
01173     mBaseURI(aCopy.mBaseURI),
01174     mComplete(aCopy.mComplete)
01175 {
01176   MOZ_COUNT_CTOR(nsCSSStyleSheetInner);
01177   mSheets.AppendElement(aParentSheet);
01178   if (aCopy.mOrderedRules) {
01179     NS_NewISupportsArray(&mOrderedRules);
01180     if (mOrderedRules) {
01181       aCopy.mOrderedRules->EnumerateForwards(CloneRuleInto, mOrderedRules);
01182       mOrderedRules->EnumerateForwards(SetStyleSheetReference, aParentSheet);
01183     }
01184   }
01185   else {
01186     mOrderedRules = nsnull;
01187   }
01188   RebuildNameSpaces();
01189 }
01190 
01191 nsCSSStyleSheetInner::~nsCSSStyleSheetInner()
01192 {
01193   MOZ_COUNT_DTOR(nsCSSStyleSheetInner);
01194   if (mOrderedRules) {
01195     mOrderedRules->EnumerateForwards(SetStyleSheetReference, nsnull);
01196     NS_RELEASE(mOrderedRules);
01197   }
01198 }
01199 
01200 nsCSSStyleSheetInner* 
01201 nsCSSStyleSheetInner::CloneFor(nsICSSStyleSheet* aParentSheet)
01202 {
01203   return new nsCSSStyleSheetInner(*this, aParentSheet);
01204 }
01205 
01206 void
01207 nsCSSStyleSheetInner::AddSheet(nsICSSStyleSheet* aParentSheet)
01208 {
01209   mSheets.AppendElement(aParentSheet);
01210 }
01211 
01212 void
01213 nsCSSStyleSheetInner::RemoveSheet(nsICSSStyleSheet* aParentSheet)
01214 {
01215   if (1 == mSheets.Count()) {
01216     NS_ASSERTION(aParentSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0), "bad parent");
01217     delete this;
01218     return;
01219   }
01220   if (aParentSheet == (nsICSSStyleSheet*)mSheets.ElementAt(0)) {
01221     mSheets.RemoveElementAt(0);
01222     NS_ASSERTION(mSheets.Count(), "no parents");
01223     if (mOrderedRules) {
01224       mOrderedRules->EnumerateForwards(SetStyleSheetReference, 
01225                                        (nsICSSStyleSheet*)mSheets.ElementAt(0));
01226     }
01227   }
01228   else {
01229     mSheets.RemoveElement(aParentSheet);
01230   }
01231 }
01232 
01233 static PRBool
01234 CreateNameSpace(nsISupports* aRule, void* aNameSpacePtr)
01235 {
01236   nsICSSRule* rule = (nsICSSRule*)aRule;
01237   PRInt32 type = nsICSSRule::UNKNOWN_RULE;
01238   rule->GetType(type);
01239   if (nsICSSRule::NAMESPACE_RULE == type) {
01240     nsICSSNameSpaceRule*  nameSpaceRule = (nsICSSNameSpaceRule*)rule;
01241     nsXMLNameSpaceMap *nameSpaceMap =
01242       NS_STATIC_CAST(nsXMLNameSpaceMap*, aNameSpacePtr);
01243 
01244     nsIAtom*      prefix = nsnull;
01245     nsAutoString  urlSpec;
01246     nameSpaceRule->GetPrefix(prefix);
01247     nameSpaceRule->GetURLSpec(urlSpec);
01248 
01249     nameSpaceMap->AddPrefix(prefix, urlSpec);
01250     return PR_TRUE;
01251   }
01252   // stop if not namespace, import or charset because namespace can't follow anything else
01253   return (((nsICSSRule::CHARSET_RULE == type) || 
01254            (nsICSSRule::IMPORT_RULE)) ? PR_TRUE : PR_FALSE); 
01255 }
01256 
01257 void 
01258 nsCSSStyleSheetInner::RebuildNameSpaces()
01259 {
01260   if (mNameSpaceMap) {
01261     mNameSpaceMap->Clear();
01262   } else {
01263     mNameSpaceMap = nsXMLNameSpaceMap::Create();
01264     if (!mNameSpaceMap) {
01265       return; // out of memory
01266     }
01267   }
01268 
01269   if (mOrderedRules) {
01270     mOrderedRules->EnumerateForwards(CreateNameSpace, mNameSpaceMap);
01271   }
01272 }
01273 
01274 
01275 // -------------------------------
01276 // CSS Style Sheet
01277 //
01278 
01279 MOZ_DECL_CTOR_COUNTER(nsCSSStyleSheet)
01280 
01281 nsCSSStyleSheet::nsCSSStyleSheet()
01282   : nsICSSStyleSheet_MOZILLA_1_8_BRANCH(),
01283     mRefCnt(0),
01284     mTitle(), 
01285     mMedia(nsnull),
01286     mFirstChild(nsnull), 
01287     mNext(nsnull),
01288     mParent(nsnull),
01289     mOwnerRule(nsnull),
01290     mImportsCollection(nsnull),
01291     mRuleCollection(nsnull),
01292     mDocument(nsnull),
01293     mOwningNode(nsnull),
01294     mDisabled(PR_FALSE),
01295     mDirty(PR_FALSE),
01296     mRuleProcessors(nsnull)
01297 {
01298 
01299   mInner = new nsCSSStyleSheetInner(this);
01300 }
01301 
01302 nsCSSStyleSheet::nsCSSStyleSheet(const nsCSSStyleSheet& aCopy,
01303                                      nsICSSStyleSheet* aParentToUse,
01304                                      nsICSSImportRule* aOwnerRuleToUse,
01305                                      nsIDocument* aDocumentToUse,
01306                                      nsIDOMNode* aOwningNodeToUse)
01307   : nsICSSStyleSheet_MOZILLA_1_8_BRANCH(),
01308     mRefCnt(0),
01309     mTitle(aCopy.mTitle), 
01310     mMedia(nsnull),
01311     mFirstChild(nsnull), 
01312     mNext(nsnull),
01313     mParent(aParentToUse),
01314     mOwnerRule(aOwnerRuleToUse),
01315     mImportsCollection(nsnull), // re-created lazily
01316     mRuleCollection(nsnull), // re-created lazily
01317     mDocument(aDocumentToUse),
01318     mOwningNode(aOwningNodeToUse),
01319     mDisabled(aCopy.mDisabled),
01320     mDirty(PR_FALSE),
01321     mInner(aCopy.mInner),
01322     mRuleProcessors(nsnull)
01323 {
01324 
01325   mInner->AddSheet(this);
01326 
01327   if (aCopy.mRuleCollection && 
01328       aCopy.mRuleCollection->mRulesAccessed) {  // CSSOM's been there, force full copy now
01329     NS_ASSERTION(mInner->mComplete, "Why have rules been accessed on an incomplete sheet?");
01330     EnsureUniqueInner();
01331   }
01332 
01333   if (aCopy.mMedia) {
01334     aCopy.mMedia->Clone(getter_AddRefs(mMedia));
01335   }
01336 
01337   if (aCopy.mFirstChild) {
01338     nsCSSStyleSheet*  otherChild = aCopy.mFirstChild;
01339     nsCSSStyleSheet** ourSlot = &mFirstChild;
01340     do {
01341       // XXX This is wrong; we should be keeping @import rules and
01342       // sheets in sync!
01343       nsCSSStyleSheet* child = new nsCSSStyleSheet(*otherChild,
01344                                                        this,
01345                                                        nsnull,
01346                                                        aDocumentToUse,
01347                                                        nsnull);
01348       if (child) {
01349         NS_ADDREF(child);
01350         (*ourSlot) = child;
01351         ourSlot = &(child->mNext);
01352       }
01353       otherChild = otherChild->mNext;
01354     }
01355     while (otherChild && ourSlot);
01356   }
01357 }
01358 
01359 nsCSSStyleSheet::~nsCSSStyleSheet()
01360 {
01361   if (mFirstChild) {
01362     nsCSSStyleSheet* child = mFirstChild;
01363     do {
01364       child->mParent = nsnull;
01365       child->mDocument = nsnull;
01366       child = child->mNext;
01367     } while (child);
01368     NS_RELEASE(mFirstChild);
01369   }
01370   NS_IF_RELEASE(mNext);
01371   if (nsnull != mRuleCollection) {
01372     mRuleCollection->DropReference();
01373     NS_RELEASE(mRuleCollection);
01374   }
01375   if (nsnull != mImportsCollection) {
01376     mImportsCollection->DropReference();
01377     NS_RELEASE(mImportsCollection);
01378   }
01379   if (mMedia) {
01380     mMedia->SetStyleSheet(nsnull);
01381     mMedia = nsnull;
01382   }
01383   mInner->RemoveSheet(this);
01384   // XXX The document reference is not reference counted and should
01385   // not be released. The document will let us know when it is going
01386   // away.
01387   if (mRuleProcessors) {
01388     NS_ASSERTION(mRuleProcessors->Count() == 0, "destucting sheet with rule processor reference");
01389     delete mRuleProcessors; // weak refs, should be empty here anyway
01390   }
01391 }
01392 
01393 
01394 // QueryInterface implementation for nsCSSStyleSheet
01395 NS_INTERFACE_MAP_BEGIN(nsCSSStyleSheet)
01396   NS_INTERFACE_MAP_ENTRY(nsICSSStyleSheet)
01397   NS_INTERFACE_MAP_ENTRY(nsICSSStyleSheet_MOZILLA_1_8_BRANCH)
01398   NS_INTERFACE_MAP_ENTRY(nsIStyleSheet)
01399   NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheet)
01400   NS_INTERFACE_MAP_ENTRY(nsIDOMCSSStyleSheet)
01401   NS_INTERFACE_MAP_ENTRY(nsICSSLoaderObserver)
01402   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICSSStyleSheet)
01403   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(CSSStyleSheet)
01404 NS_INTERFACE_MAP_END
01405 
01406 
01407 NS_IMPL_ADDREF(nsCSSStyleSheet)
01408 NS_IMPL_RELEASE(nsCSSStyleSheet)
01409 
01410 
01411 NS_IMETHODIMP
01412 nsCSSStyleSheet::AddRuleProcessor(nsCSSRuleProcessor* aProcessor)
01413 {
01414   if (! mRuleProcessors) {
01415     mRuleProcessors = new nsAutoVoidArray();
01416     if (!mRuleProcessors)
01417       return NS_ERROR_OUT_OF_MEMORY;
01418   }
01419   NS_ASSERTION(-1 == mRuleProcessors->IndexOf(aProcessor),
01420                "processor already registered");
01421   mRuleProcessors->AppendElement(aProcessor); // weak ref
01422   return NS_OK;
01423 }
01424 
01425 NS_IMETHODIMP
01426 nsCSSStyleSheet::DropRuleProcessor(nsCSSRuleProcessor* aProcessor)
01427 {
01428   if (!mRuleProcessors)
01429     return NS_ERROR_FAILURE;
01430   return mRuleProcessors->RemoveElement(aProcessor)
01431            ? NS_OK
01432            : NS_ERROR_FAILURE;
01433 }
01434 
01435 
01436 NS_IMETHODIMP
01437 nsCSSStyleSheet::SetURIs(nsIURI* aSheetURI, nsIURI* aBaseURI)
01438 {
01439   return SetURIs18(aSheetURI, nsnull, aBaseURI);
01440 }
01441 
01442 NS_IMETHODIMP
01443 nsCSSStyleSheet::SetURIs18(nsIURI* aSheetURI, nsIURI* aOriginalSheetURI,
01444                            nsIURI* aBaseURI)
01445 {
01446   NS_PRECONDITION(aSheetURI && aBaseURI, "null ptr");
01447 
01448   if (! mInner) {
01449     return NS_ERROR_OUT_OF_MEMORY;
01450   }
01451 
01452   NS_ASSERTION(!mInner->mOrderedRules && !mInner->mComplete,
01453                "Can't call SetURL on sheets that are complete or have rules");
01454 
01455   mInner->mSheetURI = aSheetURI;
01456   mInner->mOriginalSheetURI = aOriginalSheetURI;
01457   mInner->mBaseURI = aBaseURI;
01458   return NS_OK;
01459 }
01460 
01461 NS_IMETHODIMP
01462 nsCSSStyleSheet::GetSheetURI(nsIURI** aSheetURI) const
01463 {
01464   NS_IF_ADDREF(*aSheetURI = (mInner ? mInner->mSheetURI.get() : nsnull));
01465   return NS_OK;
01466 }
01467 
01468 NS_IMETHODIMP
01469 nsCSSStyleSheet::GetBaseURI(nsIURI** aBaseURI) const
01470 {
01471   NS_IF_ADDREF(*aBaseURI = (mInner ? mInner->mBaseURI.get() : nsnull));
01472   return NS_OK;
01473 }
01474 
01475 NS_IMETHODIMP
01476 nsCSSStyleSheet::SetTitle(const nsAString& aTitle)
01477 {
01478   mTitle = aTitle;
01479   return NS_OK;
01480 }
01481 
01482 NS_IMETHODIMP
01483 nsCSSStyleSheet::GetType(nsString& aType) const
01484 {
01485   aType.AssignLiteral("text/css");
01486   return NS_OK;
01487 }
01488 
01489 NS_IMETHODIMP_(PRBool)
01490 nsCSSStyleSheet::UseForMedium(nsPresContext* aPresContext) const
01491 {
01492   if (mMedia) {
01493     return mMedia->Matches(aPresContext);
01494   }
01495   return PR_TRUE;
01496 }
01497 
01498 
01499 NS_IMETHODIMP
01500 nsCSSStyleSheet::SetMedia(nsMediaList* aMedia)
01501 {
01502   mMedia = aMedia;
01503   return NS_OK;
01504 }
01505 
01506 NS_IMETHODIMP_(PRBool)
01507 nsCSSStyleSheet::HasRules() const
01508 {
01509   PRInt32 count;
01510   StyleRuleCount(count);
01511   return count != 0;
01512 }
01513 
01514 NS_IMETHODIMP
01515 nsCSSStyleSheet::GetApplicable(PRBool& aApplicable) const
01516 {
01517   aApplicable = !mDisabled && mInner && mInner->mComplete;
01518   return NS_OK;
01519 }
01520 
01521 NS_IMETHODIMP
01522 nsCSSStyleSheet::SetEnabled(PRBool aEnabled)
01523 {
01524   return nsCSSStyleSheet::SetDisabled(!aEnabled);
01525 }
01526 
01527 NS_IMETHODIMP
01528 nsCSSStyleSheet::GetComplete(PRBool& aComplete) const
01529 {
01530   aComplete = mInner && mInner->mComplete;
01531   return NS_OK;
01532 }
01533 
01534 NS_IMETHODIMP
01535 nsCSSStyleSheet::SetComplete()
01536 {
01537   if (!mInner)
01538     return NS_ERROR_UNEXPECTED;
01539   NS_ASSERTION(!mDirty, "Can't set a dirty sheet complete!");
01540   mInner->mComplete = PR_TRUE;
01541   if (mDocument && !mDisabled) {
01542     // Let the document know
01543     mDocument->BeginUpdate(UPDATE_STYLE);
01544     mDocument->SetStyleSheetApplicableState(this, PR_TRUE);
01545     mDocument->EndUpdate(UPDATE_STYLE);
01546   }
01547   return NS_OK;
01548 }
01549 
01550 NS_IMETHODIMP
01551 nsCSSStyleSheet::GetParentSheet(nsIStyleSheet*& aParent) const
01552 {
01553   aParent = mParent;
01554   NS_IF_ADDREF(aParent);
01555   return NS_OK;
01556 }
01557 
01558 NS_IMETHODIMP
01559 nsCSSStyleSheet::GetOwningDocument(nsIDocument*& aDocument) const
01560 {
01561   aDocument = mDocument;
01562   NS_IF_ADDREF(aDocument);
01563   return NS_OK;
01564 }
01565 
01566 NS_IMETHODIMP
01567 nsCSSStyleSheet::SetOwningDocument(nsIDocument* aDocument)
01568 { // not ref counted
01569   mDocument = aDocument;
01570   // Now set the same document on all our child sheets....
01571   for (nsCSSStyleSheet* child = mFirstChild; child; child = child->mNext) {
01572     child->SetOwningDocument(aDocument);
01573   }
01574   return NS_OK;
01575 }
01576 
01577 NS_IMETHODIMP
01578 nsCSSStyleSheet::SetOwningNode(nsIDOMNode* aOwningNode)
01579 { // not ref counted
01580   mOwningNode = aOwningNode;
01581   return NS_OK;
01582 }
01583 
01584 NS_IMETHODIMP
01585 nsCSSStyleSheet::SetOwnerRule(nsICSSImportRule* aOwnerRule)
01586 { // not ref counted
01587   mOwnerRule = aOwnerRule;
01588   return NS_OK;
01589 }
01590 
01591 NS_IMETHODIMP
01592 nsCSSStyleSheet::GetOwnerRule(nsICSSImportRule** aOwnerRule)
01593 {
01594   *aOwnerRule = mOwnerRule;
01595   NS_IF_ADDREF(*aOwnerRule);
01596   return NS_OK;
01597 }
01598 
01599 NS_IMETHODIMP
01600 nsCSSStyleSheet::ContainsStyleSheet(nsIURI* aURL, PRBool& aContains, nsIStyleSheet** aTheChild /*=nsnull*/)
01601 {
01602   NS_PRECONDITION(nsnull != aURL, "null arg");
01603 
01604   if (!mInner || !mInner->mSheetURI) {
01605     // We're not yet far enough along in our load to know what our URL is (we
01606     // may still get redirected and such).  Assert (caller should really not be
01607     // calling this on us at this stage) and return.
01608     NS_ERROR("ContainsStyleSheet called on a sheet that's still loading");
01609     aContains = PR_FALSE;
01610     return NS_OK;
01611   }
01612   
01613   // first check ourself out
01614   nsresult rv = mInner->mSheetURI->Equals(aURL, &aContains);
01615   if (NS_FAILED(rv)) aContains = PR_FALSE;
01616 
01617   if (aContains) {
01618     // if we found it and the out-param is there, set it and addref
01619     if (aTheChild) {
01620       rv = QueryInterface( NS_GET_IID(nsIStyleSheet), (void **)aTheChild);
01621     }
01622   } else {
01623     nsCSSStyleSheet*  child = mFirstChild;
01624     // now check the chil'ins out (recursively)
01625     while ((PR_FALSE == aContains) && (nsnull != child)) {
01626       child->ContainsStyleSheet(aURL, aContains, aTheChild);
01627       if (aContains) {
01628         break;
01629       } else {
01630         child = child->mNext;
01631       }
01632     }
01633   }
01634 
01635   // NOTE: if there are errors in the above we are handling them locally 
01636   //       and not promoting them to the caller
01637   return NS_OK;
01638 }
01639 
01640 NS_IMETHODIMP
01641 nsCSSStyleSheet::AppendStyleSheet(nsICSSStyleSheet* aSheet)
01642 {
01643   NS_PRECONDITION(nsnull != aSheet, "null arg");
01644 
01645   if (NS_SUCCEEDED(WillDirty())) {
01646     NS_ADDREF(aSheet);
01647     nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet;
01648 
01649     if (! mFirstChild) {
01650       mFirstChild = sheet;
01651     }
01652     else {
01653       nsCSSStyleSheet* child = mFirstChild;
01654       while (child->mNext) {
01655         child = child->mNext;
01656       }
01657       child->mNext = sheet;
01658     }
01659   
01660     // This is not reference counted. Our parent tells us when
01661     // it's going away.
01662     sheet->mParent = this;
01663     sheet->mDocument = mDocument;
01664     DidDirty();
01665   }
01666   return NS_OK;
01667 }
01668 
01669 NS_IMETHODIMP
01670 nsCSSStyleSheet::InsertStyleSheetAt(nsICSSStyleSheet* aSheet, PRInt32 aIndex)
01671 {
01672   NS_PRECONDITION(nsnull != aSheet, "null arg");
01673 
01674   nsresult result = WillDirty();
01675 
01676   if (NS_SUCCEEDED(result)) {
01677     NS_ADDREF(aSheet);
01678     nsCSSStyleSheet* sheet = (nsCSSStyleSheet*)aSheet;
01679     nsCSSStyleSheet* child = mFirstChild;
01680 
01681     if (aIndex && child) {
01682       while ((0 < --aIndex) && child->mNext) {
01683         child = child->mNext;
01684       }
01685       sheet->mNext = child->mNext;
01686       child->mNext = sheet;
01687     }
01688     else {
01689       sheet->mNext = mFirstChild;
01690       mFirstChild = sheet; 
01691     }
01692 
01693     // This is not reference counted. Our parent tells us when
01694     // it's going away.
01695     sheet->mParent = this;
01696     sheet->mDocument = mDocument;
01697     DidDirty();
01698   }
01699   return result;
01700 }
01701 
01702 NS_IMETHODIMP
01703 nsCSSStyleSheet::PrependStyleRule(nsICSSRule* aRule)
01704 {
01705   NS_PRECONDITION(nsnull != aRule, "null arg");
01706 
01707   if (NS_SUCCEEDED(WillDirty())) {
01708     if (! mInner->mOrderedRules) {
01709       NS_NewISupportsArray(&(mInner->mOrderedRules));
01710     }
01711     if (mInner->mOrderedRules) {
01712       mInner->mOrderedRules->InsertElementAt(aRule, 0);
01713       aRule->SetStyleSheet(this);
01714       DidDirty();
01715 
01716       PRInt32 type = nsICSSRule::UNKNOWN_RULE;
01717       aRule->GetType(type);
01718       if (nsICSSRule::NAMESPACE_RULE == type) {
01719         // no api to prepend a namespace (ugh), release old ones and re-create them all
01720         mInner->RebuildNameSpaces();
01721       }
01722     }
01723   }
01724   return NS_OK;
01725 }
01726 
01727 NS_IMETHODIMP
01728 nsCSSStyleSheet::AppendStyleRule(nsICSSRule* aRule)
01729 {
01730   NS_PRECONDITION(nsnull != aRule, "null arg");
01731 
01732   if (NS_SUCCEEDED(WillDirty())) {
01733     if (! mInner->mOrderedRules) {
01734       NS_NewISupportsArray(&(mInner->mOrderedRules));
01735     }
01736     if (mInner->mOrderedRules) {
01737       mInner->mOrderedRules->AppendElement(aRule);
01738       aRule->SetStyleSheet(this);
01739       DidDirty();
01740 
01741       PRInt32 type = nsICSSRule::UNKNOWN_RULE;
01742       aRule->GetType(type);
01743       if (nsICSSRule::NAMESPACE_RULE == type) {
01744         if (!mInner->mNameSpaceMap) {
01745           mInner->mNameSpaceMap = nsXMLNameSpaceMap::Create();
01746           NS_ENSURE_TRUE(mInner->mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
01747         }
01748 
01749         nsCOMPtr<nsICSSNameSpaceRule> nameSpaceRule(do_QueryInterface(aRule));
01750 
01751         nsCOMPtr<nsIAtom> prefix;
01752         nsAutoString  urlSpec;
01753         nameSpaceRule->GetPrefix(*getter_AddRefs(prefix));
01754         nameSpaceRule->GetURLSpec(urlSpec);
01755 
01756         mInner->mNameSpaceMap->AddPrefix(prefix, urlSpec);
01757       }
01758     }
01759   }
01760   return NS_OK;
01761 }
01762 
01763 NS_IMETHODIMP
01764 nsCSSStyleSheet::ReplaceStyleRule(nsICSSRule* aOld, nsICSSRule* aNew)
01765 {
01766   NS_PRECONDITION(mInner->mOrderedRules, "can't have old rule");
01767   NS_PRECONDITION(mInner && mInner->mComplete,
01768                   "No replacing in an incomplete sheet!");
01769 
01770   if (NS_SUCCEEDED(WillDirty())) {
01771     PRInt32 index = mInner->mOrderedRules->IndexOf(aOld);
01772     NS_ENSURE_TRUE(index != -1, NS_ERROR_UNEXPECTED);
01773     mInner->mOrderedRules->ReplaceElementAt(aNew, index);
01774 
01775     aNew->SetStyleSheet(this);
01776     aOld->SetStyleSheet(nsnull);
01777     DidDirty();
01778 #ifdef DEBUG
01779     PRInt32 type = nsICSSRule::UNKNOWN_RULE;
01780     aNew->GetType(type);
01781     NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented");
01782     aOld->GetType(type);
01783     NS_ASSERTION(nsICSSRule::NAMESPACE_RULE != type, "not yet implemented");
01784 #endif
01785   }
01786   return NS_OK;
01787 }
01788 
01789 NS_IMETHODIMP
01790 nsCSSStyleSheet::StyleRuleCount(PRInt32& aCount) const
01791 {
01792   aCount = 0;
01793   if (mInner && mInner->mOrderedRules) {
01794     PRUint32 cnt;
01795     nsresult rv = ((nsCSSStyleSheet*)this)->mInner->mOrderedRules->Count(&cnt);      // XXX bogus cast -- this method should not be const
01796     aCount = (PRInt32)cnt;
01797     return rv;
01798   }
01799   return NS_OK;
01800 }
01801 
01802 NS_IMETHODIMP
01803 nsCSSStyleSheet::GetStyleRuleAt(PRInt32 aIndex, nsICSSRule*& aRule) const
01804 {
01805   // Important: If this function is ever made scriptable, we must add
01806   // a security check here. See GetCSSRules below for an example.
01807   nsresult result = NS_ERROR_ILLEGAL_VALUE;
01808 
01809   if (mInner && mInner->mOrderedRules) {
01810     aRule = (nsICSSRule*)(mInner->mOrderedRules->ElementAt(aIndex));
01811     if (nsnull != aRule) {
01812       result = NS_OK;
01813     }
01814   }
01815   else {
01816     aRule = nsnull;
01817   }
01818   return result;
01819 }
01820 
01821 nsXMLNameSpaceMap*
01822 nsCSSStyleSheet::GetNameSpaceMap() const
01823 {
01824   if (mInner)
01825     return mInner->mNameSpaceMap;
01826 
01827   return nsnull;
01828 }
01829 
01830 NS_IMETHODIMP
01831 nsCSSStyleSheet::StyleSheetCount(PRInt32& aCount) const
01832 {
01833   // XXX Far from an ideal way to do this, but the hope is that
01834   // it won't be done too often. If it is, we might want to 
01835   // consider storing the children in an array.
01836   aCount = 0;
01837 
01838   const nsCSSStyleSheet* child = mFirstChild;
01839   while (child) {
01840     aCount++;
01841     child = child->mNext;
01842   }
01843 
01844   return NS_OK;
01845 }
01846 
01847 NS_IMETHODIMP
01848 nsCSSStyleSheet::GetStyleSheetAt(PRInt32 aIndex, nsICSSStyleSheet*& aSheet) const
01849 {
01850   // XXX Ughh...an O(n^2) method for doing iteration. Again, we hope
01851   // that this isn't done too often. If it is, we need to change the
01852   // underlying storage mechanism
01853   aSheet = nsnull;
01854 
01855   if (mFirstChild) {
01856     const nsCSSStyleSheet* child = mFirstChild;
01857     while ((child) && (0 != aIndex)) {
01858       --aIndex;
01859       child = child->mNext;
01860     }
01861     
01862     aSheet = (nsICSSStyleSheet*)child;
01863     NS_IF_ADDREF(aSheet);
01864   }
01865 
01866   return NS_OK;
01867 }
01868 
01869 nsresult  
01870 nsCSSStyleSheet::EnsureUniqueInner()
01871 {
01872   if (! mInner) {
01873     return NS_ERROR_NOT_INITIALIZED;
01874   }
01875   if (1 < mInner->mSheets.Count()) {
01876     nsCSSStyleSheetInner* clone = mInner->CloneFor(this);
01877     if (clone) {
01878       mInner->RemoveSheet(this);
01879       mInner = clone;
01880     }
01881     else {
01882       return NS_ERROR_OUT_OF_MEMORY;
01883     }
01884   }
01885   return NS_OK;
01886 }
01887 
01888 NS_IMETHODIMP
01889 nsCSSStyleSheet::Clone(nsICSSStyleSheet* aCloneParent,
01890                          nsICSSImportRule* aCloneOwnerRule,
01891                          nsIDocument* aCloneDocument,
01892                          nsIDOMNode* aCloneOwningNode,
01893                          nsICSSStyleSheet** aClone) const
01894 {
01895   NS_PRECONDITION(aClone, "Null out param!");
01896   nsCSSStyleSheet* clone = new nsCSSStyleSheet(*this,
01897                                                    aCloneParent,
01898                                                    aCloneOwnerRule,
01899                                                    aCloneDocument,
01900                                                    aCloneOwningNode);
01901   if (clone) {
01902     *aClone = NS_STATIC_CAST(nsICSSStyleSheet*, clone);
01903     NS_ADDREF(*aClone);
01904   }
01905   return NS_OK;
01906 }
01907 
01908 #ifdef DEBUG
01909 static void
01910 ListRules(nsISupportsArray* aRules, FILE* aOut, PRInt32 aIndent)
01911 {
01912   PRUint32 count;
01913   PRInt32 index;
01914   if (aRules) {
01915     aRules->Count(&count);
01916     for (index = count - 1; index >= 0; --index) {
01917       nsCOMPtr<nsICSSRule> rule = dont_AddRef((nsICSSRule*)aRules->ElementAt(index));
01918       rule->List(aOut, aIndent);
01919     }
01920   }
01921 }
01922 
01923 struct ListEnumData {
01924   ListEnumData(FILE* aOut, PRInt32 aIndent)
01925     : mOut(aOut),
01926       mIndent(aIndent)
01927   {
01928   }
01929   FILE*   mOut;
01930   PRInt32 mIndent;
01931 };
01932 
01933 #if 0
01934 static PRBool ListCascade(nsHashKey* aKey, void* aValue, void* aClosure)
01935 {
01936   AtomKey* key = (AtomKey*)aKey;
01937   RuleCascadeData* cascade = (RuleCascadeData*)aValue;
01938   ListEnumData* data = (ListEnumData*)aClosure;
01939 
01940   fputs("\nRules in cascade order for medium: \"", data->mOut);
01941   nsAutoString  buffer;
01942   key->mAtom->ToString(buffer);
01943   fputs(NS_LossyConvertUCS2toASCII(buffer).get(), data->mOut);
01944   fputs("\"\n", data->mOut);
01945 
01946   ListRules(cascade->mWeightedRules, data->mOut, data->mIndent);
01947   return PR_TRUE;
01948 }
01949 #endif
01950 
01951 
01952 void nsCSSStyleSheet::List(FILE* out, PRInt32 aIndent) const
01953 {
01954 
01955   PRInt32 index;
01956 
01957   // Indent
01958   for (index = aIndent; --index >= 0; ) fputs("  ", out);
01959 
01960   if (! mInner) {
01961     fputs("CSS Style Sheet - without inner data storage - ERROR\n", out);
01962     return;
01963   }
01964 
01965   fputs("CSS Style Sheet: ", out);
01966   nsCAutoString urlSpec;
01967   nsresult rv = mInner->mSheetURI->GetSpec(urlSpec);
01968   if (NS_SUCCEEDED(rv) && !urlSpec.IsEmpty()) {
01969     fputs(urlSpec.get(), out);
01970   }
01971 
01972   if (mMedia) {
01973     fputs(" media: ", out);
01974     nsAutoString  buffer;
01975     mMedia->GetText(buffer);
01976     fputs(NS_ConvertUTF16toUTF8(buffer).get(), out);
01977   }
01978   fputs("\n", out);
01979 
01980   const nsCSSStyleSheet*  child = mFirstChild;
01981   while (nsnull != child) {
01982     child->List(out, aIndent + 1);
01983     child = child->mNext;
01984   }
01985 
01986   fputs("Rules in source order:\n", out);
01987   ListRules(mInner->mOrderedRules, out, aIndent);
01988 }
01989 #endif
01990 
01991 static PRBool PR_CALLBACK
01992 EnumClearRuleCascades(void* aProcessor, void* aData)
01993 {
01994   nsCSSRuleProcessor* processor =
01995     NS_STATIC_CAST(nsCSSRuleProcessor*, aProcessor);
01996   processor->ClearRuleCascades();
01997   return PR_TRUE;
01998 }
01999 
02000 void 
02001 nsCSSStyleSheet::ClearRuleCascades()
02002 {
02003   if (mRuleProcessors) {
02004     mRuleProcessors->EnumerateForwards(EnumClearRuleCascades, nsnull);
02005   }
02006   if (mParent) {
02007     nsCSSStyleSheet* parent = (nsCSSStyleSheet*)mParent;
02008     parent->ClearRuleCascades();
02009   }
02010 }
02011 
02012 nsresult
02013 nsCSSStyleSheet::WillDirty()
02014 {
02015   if (mInner && !mInner->mComplete) {
02016     // Do nothing
02017     return NS_OK;
02018   }
02019   
02020   return EnsureUniqueInner();
02021 }
02022 
02023 void
02024 nsCSSStyleSheet::DidDirty()
02025 {
02026   ClearRuleCascades();
02027   mDirty = PR_TRUE;
02028 }
02029 
02030 NS_IMETHODIMP 
02031 nsCSSStyleSheet::IsModified(PRBool* aSheetModified) const
02032 {
02033   *aSheetModified = mDirty;
02034   return NS_OK;
02035 }
02036 
02037 NS_IMETHODIMP
02038 nsCSSStyleSheet::SetModified(PRBool aModified)
02039 {
02040   mDirty = aModified;
02041   return NS_OK;
02042 }
02043 
02044   // nsIDOMStyleSheet interface
02045 NS_IMETHODIMP    
02046 nsCSSStyleSheet::GetType(nsAString& aType)
02047 {
02048   aType.AssignLiteral("text/css");
02049   return NS_OK;
02050 }
02051 
02052 NS_IMETHODIMP    
02053 nsCSSStyleSheet::GetDisabled(PRBool* aDisabled)
02054 {
02055   *aDisabled = mDisabled;
02056   return NS_OK;
02057 }
02058 
02059 NS_IMETHODIMP    
02060 nsCSSStyleSheet::SetDisabled(PRBool aDisabled)
02061 {
02062   PRBool oldDisabled = mDisabled;
02063   mDisabled = aDisabled;
02064 
02065   if (mDocument && mInner && mInner->mComplete && oldDisabled != mDisabled) {
02066     ClearRuleCascades();
02067 
02068     mDocument->BeginUpdate(UPDATE_STYLE);
02069     mDocument->SetStyleSheetApplicableState(this, !mDisabled);
02070     mDocument->EndUpdate(UPDATE_STYLE);
02071   }
02072 
02073   return NS_OK;
02074 }
02075 
02076 NS_IMETHODIMP
02077 nsCSSStyleSheet::GetOwnerNode(nsIDOMNode** aOwnerNode)
02078 {
02079   *aOwnerNode = mOwningNode;
02080   NS_IF_ADDREF(*aOwnerNode);
02081   return NS_OK;
02082 }
02083 
02084 NS_IMETHODIMP
02085 nsCSSStyleSheet::GetParentStyleSheet(nsIDOMStyleSheet** aParentStyleSheet)
02086 {
02087   NS_ENSURE_ARG_POINTER(aParentStyleSheet);
02088 
02089   nsresult rv = NS_OK;
02090 
02091   if (mParent) {
02092     rv =  mParent->QueryInterface(NS_GET_IID(nsIDOMStyleSheet),
02093                                   (void **)aParentStyleSheet);
02094   } else {
02095     *aParentStyleSheet = nsnull;
02096   }
02097 
02098   return rv;
02099 }
02100 
02101 NS_IMETHODIMP
02102 nsCSSStyleSheet::GetHref(nsAString& aHref)
02103 {
02104   if (mInner && mInner->mOriginalSheetURI) {
02105     nsCAutoString str;
02106     mInner->mOriginalSheetURI->GetSpec(str);
02107     CopyUTF8toUTF16(str, aHref);
02108   } else {
02109     SetDOMStringToNull(aHref);
02110   }
02111 
02112   return NS_OK;
02113 }
02114 
02115 NS_IMETHODIMP
02116 nsCSSStyleSheet::GetTitle(nsString& aTitle) const
02117 {
02118   aTitle = mTitle;
02119   return NS_OK;
02120 }
02121 
02122 NS_IMETHODIMP
02123 nsCSSStyleSheet::GetTitle(nsAString& aTitle)
02124 {
02125   aTitle.Assign(mTitle);
02126   return NS_OK;
02127 }
02128 
02129 NS_IMETHODIMP
02130 nsCSSStyleSheet::GetMedia(nsIDOMMediaList** aMedia)
02131 {
02132   NS_ENSURE_ARG_POINTER(aMedia);
02133   *aMedia = nsnull;
02134 
02135   if (!mMedia) {
02136     mMedia = new nsMediaList();
02137     NS_ENSURE_TRUE(mMedia, NS_ERROR_OUT_OF_MEMORY);
02138     mMedia->SetStyleSheet(this);
02139   }
02140 
02141   *aMedia = mMedia;
02142   NS_ADDREF(*aMedia);
02143 
02144   return NS_OK;
02145 }
02146 
02147 NS_IMETHODIMP    
02148 nsCSSStyleSheet::GetOwnerRule(nsIDOMCSSRule** aOwnerRule)
02149 {
02150   if (mOwnerRule) {
02151     return mOwnerRule->GetDOMRule(aOwnerRule);
02152   }
02153 
02154   *aOwnerRule = nsnull;
02155   return NS_OK;    
02156 }
02157 
02158 NS_IMETHODIMP    
02159 nsCSSStyleSheet::GetCssRules(nsIDOMCSSRuleList** aCssRules)
02160 {
02161   // No doing this on incomplete sheets!
02162   PRBool complete;
02163   GetComplete(complete);
02164   if (!complete) {
02165     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
02166   }
02167   
02168   //-- Security check: Only scripts from the same origin as the
02169   //   style sheet can access rule collections
02170 
02171   // Get JSContext from stack
02172   nsCOMPtr<nsIJSContextStack> stack =
02173     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
02174   NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE);
02175 
02176   JSContext *cx = nsnull;
02177   nsresult rv = NS_OK;
02178 
02179   rv = stack->Peek(&cx);
02180   NS_ENSURE_SUCCESS(rv, rv);
02181   if (!cx)
02182     return NS_ERROR_FAILURE;
02183 
02184   // Get the security manager and do the same-origin check
02185   rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx,
02186                                                              mInner->mSheetURI);
02187   if (NS_FAILED(rv)) {
02188     return rv;
02189   }
02190 
02191   // OK, security check passed, so get the rule collection
02192   if (nsnull == mRuleCollection) {
02193     mRuleCollection = new CSSRuleListImpl(this);
02194     if (nsnull == mRuleCollection) {
02195       return NS_ERROR_OUT_OF_MEMORY;
02196     }
02197     NS_ADDREF(mRuleCollection);
02198   }
02199 
02200   *aCssRules = mRuleCollection;
02201   NS_ADDREF(mRuleCollection);
02202 
02203   return NS_OK;
02204 }
02205 
02206 NS_IMETHODIMP    
02207 nsCSSStyleSheet::InsertRule(const nsAString& aRule, 
02208                             PRUint32 aIndex, 
02209                             PRUint32* aReturn)
02210 {
02211   NS_ENSURE_TRUE(mInner, NS_ERROR_FAILURE);
02212   // No doing this if the sheet is not complete!
02213   PRBool complete;
02214   GetComplete(complete);
02215   if (!complete) {
02216     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
02217   }
02218 
02219   if (aRule.IsEmpty()) {
02220     // Nothing to do here
02221     return NS_OK;
02222   }
02223   
02224   nsresult result;
02225   result = WillDirty();
02226   if (NS_FAILED(result))
02227     return result;
02228   
02229   if (! mInner->mOrderedRules) {
02230     result = NS_NewISupportsArray(&(mInner->mOrderedRules));
02231   }
02232   if (NS_FAILED(result))
02233     return result;
02234 
02235   PRUint32 count;
02236   mInner->mOrderedRules->Count(&count);
02237   if (aIndex > count)
02238     return NS_ERROR_DOM_INDEX_SIZE_ERR;
02239   
02240   // Hold strong ref to the CSSLoader in case the document update
02241   // kills the document
02242   nsCOMPtr<nsICSSLoader> loader;
02243   if (mDocument) {
02244     loader = mDocument->CSSLoader();
02245     NS_ASSERTION(loader, "Document with no CSS loader!");
02246   }
02247   
02248   nsCOMPtr<nsICSSParser> css;
02249   if (loader) {
02250     result = loader->GetParserFor(this, getter_AddRefs(css));
02251   }
02252   else {
02253     result = NS_NewCSSParser(getter_AddRefs(css));
02254     if (css) {
02255       css->SetStyleSheet(this);
02256     }
02257   }
02258   if (NS_FAILED(result))
02259     return result;
02260 
02261   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
02262 
02263   nsCOMPtr<nsISupportsArray> rules;
02264   result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
02265                           getter_AddRefs(rules));
02266   if (NS_FAILED(result))
02267     return result;
02268   
02269   PRUint32 rulecount = 0;
02270   rules->Count(&rulecount);  
02271   if (rulecount == 0) {
02272     // Since we know aRule was not an empty string, just throw
02273     return NS_ERROR_DOM_SYNTAX_ERR;
02274   }
02275   
02276   // Hierarchy checking.  Just check the first and last rule in the list.
02277   
02278   // check that we're not inserting before a charset rule
02279   nsCOMPtr<nsICSSRule> nextRule;
02280   PRInt32 nextType = nsICSSRule::UNKNOWN_RULE;
02281   nextRule = dont_AddRef((nsICSSRule*)mInner->mOrderedRules->ElementAt(aIndex));
02282   if (nextRule) {
02283     nextRule->GetType(nextType);
02284     if (nextType == nsICSSRule::CHARSET_RULE) {
02285       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02286     }
02287 
02288     // check last rule in list
02289     nsCOMPtr<nsICSSRule> lastRule = dont_AddRef((nsICSSRule*)rules->ElementAt(rulecount-1));
02290     PRInt32 lastType = nsICSSRule::UNKNOWN_RULE;
02291     lastRule->GetType(lastType);
02292     
02293     if (nextType == nsICSSRule::IMPORT_RULE &&
02294         lastType != nsICSSRule::CHARSET_RULE &&
02295         lastType != nsICSSRule::IMPORT_RULE) {
02296       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02297     }
02298     
02299     if (nextType == nsICSSRule::NAMESPACE_RULE &&
02300         lastType != nsICSSRule::CHARSET_RULE &&
02301         lastType != nsICSSRule::IMPORT_RULE &&
02302         lastType != nsICSSRule::NAMESPACE_RULE) {
02303       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02304     } 
02305   }
02306   
02307   // check first rule in list
02308   nsCOMPtr<nsICSSRule> firstRule = dont_AddRef((nsICSSRule*)rules->ElementAt(0));
02309   PRInt32 firstType = nsICSSRule::UNKNOWN_RULE;
02310   firstRule->GetType(firstType);
02311   if (aIndex != 0) {
02312     if (firstType == nsICSSRule::CHARSET_RULE) { // no inserting charset at nonzero position
02313       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02314     }
02315   
02316     nsCOMPtr<nsICSSRule> prevRule = dont_AddRef((nsICSSRule*)mInner->mOrderedRules->ElementAt(aIndex-1));
02317     PRInt32 prevType = nsICSSRule::UNKNOWN_RULE;
02318     prevRule->GetType(prevType);
02319 
02320     if (firstType == nsICSSRule::IMPORT_RULE &&
02321         prevType != nsICSSRule::CHARSET_RULE &&
02322         prevType != nsICSSRule::IMPORT_RULE) {
02323       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02324     }
02325 
02326     if (firstType == nsICSSRule::NAMESPACE_RULE &&
02327         prevType != nsICSSRule::CHARSET_RULE &&
02328         prevType != nsICSSRule::IMPORT_RULE &&
02329         prevType != nsICSSRule::NAMESPACE_RULE) {
02330       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02331     }
02332   }
02333   
02334   PRBool insertResult = mInner->mOrderedRules->InsertElementsAt(rules, aIndex);
02335   NS_ENSURE_TRUE(insertResult, NS_ERROR_OUT_OF_MEMORY);
02336   DidDirty();
02337 
02338   nsCOMPtr<nsICSSRule> cssRule;
02339   PRUint32 counter;
02340   for (counter = 0; counter < rulecount; counter++) {
02341     cssRule = dont_AddRef((nsICSSRule*)rules->ElementAt(counter));
02342     cssRule->SetStyleSheet(this);
02343     
02344     PRInt32 type = nsICSSRule::UNKNOWN_RULE;
02345     cssRule->GetType(type);
02346     if (type == nsICSSRule::NAMESPACE_RULE) {
02347       if (!mInner->mNameSpaceMap) {
02348         mInner->mNameSpaceMap = nsXMLNameSpaceMap::Create();
02349         NS_ENSURE_TRUE(mInner->mNameSpaceMap, NS_ERROR_OUT_OF_MEMORY);
02350       }
02351 
02352       nsCOMPtr<nsICSSNameSpaceRule> nameSpaceRule(do_QueryInterface(cssRule));
02353     
02354       nsCOMPtr<nsIAtom> prefix;
02355       nsAutoString urlSpec;
02356       nameSpaceRule->GetPrefix(*getter_AddRefs(prefix));
02357       nameSpaceRule->GetURLSpec(urlSpec);
02358 
02359       mInner->mNameSpaceMap->AddPrefix(prefix, urlSpec);
02360     }
02361 
02362     // We don't notify immediately for @import rules, but rather when
02363     // the sheet the rule is importing is loaded
02364     PRBool notify = PR_TRUE;
02365     if (type == nsICSSRule::IMPORT_RULE) {
02366       nsCOMPtr<nsIDOMCSSImportRule> importRule(do_QueryInterface(cssRule));
02367       NS_ASSERTION(importRule, "Rule which has type IMPORT_RULE and does not implement nsIDOMCSSImportRule!");
02368       nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
02369       importRule->GetStyleSheet(getter_AddRefs(childSheet));
02370       if (!childSheet) {
02371         notify = PR_FALSE;
02372       }
02373     }
02374     if (mDocument && notify) {
02375       mDocument->StyleRuleAdded(this, cssRule);
02376     }
02377   }
02378   
02379   if (loader) {
02380     loader->RecycleParser(css);
02381   }
02382   
02383   *aReturn = aIndex;
02384   return NS_OK;
02385 }
02386 
02387 NS_IMETHODIMP    
02388 nsCSSStyleSheet::DeleteRule(PRUint32 aIndex)
02389 {
02390   nsresult result = NS_ERROR_DOM_INDEX_SIZE_ERR;
02391   // No doing this if the sheet is not complete!
02392   PRBool complete;
02393   GetComplete(complete);
02394   if (!complete) {
02395     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
02396   }
02397 
02398   // XXX TBI: handle @rule types
02399   if (mInner && mInner->mOrderedRules) {
02400     mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
02401     
02402     result = WillDirty();
02403 
02404     if (NS_SUCCEEDED(result)) {
02405       PRUint32 count;
02406       mInner->mOrderedRules->Count(&count);
02407       if (aIndex >= count)
02408         return NS_ERROR_DOM_INDEX_SIZE_ERR;
02409 
02410       nsCOMPtr<nsICSSRule> rule =
02411         dont_AddRef((nsICSSRule*)mInner->mOrderedRules->ElementAt(aIndex));
02412       if (rule) {
02413         mInner->mOrderedRules->RemoveElementAt(aIndex);
02414         rule->SetStyleSheet(nsnull);
02415         DidDirty();
02416 
02417         if (mDocument) {
02418           mDocument->StyleRuleRemoved(this, rule);
02419         }
02420       }
02421     }
02422   }
02423 
02424   return result;
02425 }
02426 
02427 NS_IMETHODIMP
02428 nsCSSStyleSheet::DeleteRuleFromGroup(nsICSSGroupRule* aGroup, PRUint32 aIndex)
02429 {
02430   NS_ENSURE_ARG_POINTER(aGroup);
02431   NS_ASSERTION(mInner && mInner->mComplete,
02432                "No deleting from an incomplete sheet!");
02433   nsresult result;
02434   nsCOMPtr<nsICSSRule> rule;
02435   result = aGroup->GetStyleRuleAt(aIndex, *getter_AddRefs(rule));
02436   NS_ENSURE_SUCCESS(result, result);
02437   
02438   // check that the rule actually belongs to this sheet!
02439   nsCOMPtr<nsIStyleSheet> ruleSheet;
02440   rule->GetStyleSheet(*getter_AddRefs(ruleSheet));
02441   if (this != ruleSheet) {
02442     return NS_ERROR_INVALID_ARG;
02443   }
02444 
02445   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
02446   
02447   result = WillDirty();
02448   NS_ENSURE_SUCCESS(result, result);
02449 
02450   result = aGroup->DeleteStyleRuleAt(aIndex);
02451   NS_ENSURE_SUCCESS(result, result);
02452   
02453   rule->SetStyleSheet(nsnull);
02454   
02455   DidDirty();
02456 
02457   if (mDocument) {
02458     mDocument->StyleRuleRemoved(this, rule);
02459   }
02460 
02461   return NS_OK;
02462 }
02463 
02464 NS_IMETHODIMP
02465 nsCSSStyleSheet::InsertRuleIntoGroup(const nsAString & aRule,
02466                                      nsICSSGroupRule* aGroup,
02467                                      PRUint32 aIndex,
02468                                      PRUint32* _retval)
02469 {
02470   nsresult result;
02471   NS_ASSERTION(mInner && mInner->mComplete,
02472                "No inserting into an incomplete sheet!");
02473   // check that the group actually belongs to this sheet!
02474   nsCOMPtr<nsIStyleSheet> groupSheet;
02475   aGroup->GetStyleSheet(*getter_AddRefs(groupSheet));
02476   if (this != groupSheet) {
02477     return NS_ERROR_INVALID_ARG;
02478   }
02479 
02480   if (aRule.IsEmpty()) {
02481     // Nothing to do here
02482     return NS_OK;
02483   }
02484   
02485   // Hold strong ref to the CSSLoader in case the document update
02486   // kills the document
02487   nsCOMPtr<nsICSSLoader> loader;
02488   if (mDocument) {
02489     loader = mDocument->CSSLoader();
02490     NS_ASSERTION(loader, "Document with no CSS loader!");
02491   }
02492 
02493   nsCOMPtr<nsICSSParser> css;
02494   if (loader) {
02495     result = loader->GetParserFor(this, getter_AddRefs(css));
02496   }
02497   else {
02498     result = NS_NewCSSParser(getter_AddRefs(css));
02499     if (css) {
02500       css->SetStyleSheet(this);
02501     }
02502   }
02503   NS_ENSURE_SUCCESS(result, result);
02504 
02505   // parse and grab the rule
02506   mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
02507 
02508   result = WillDirty();
02509   NS_ENSURE_SUCCESS(result, result);
02510 
02511   nsCOMPtr<nsISupportsArray> rules;
02512   result = css->ParseRule(aRule, mInner->mSheetURI, mInner->mBaseURI,
02513                           getter_AddRefs(rules));
02514   NS_ENSURE_SUCCESS(result, result);
02515 
02516   PRUint32 rulecount = 0;
02517   rules->Count(&rulecount);
02518   if (rulecount == 0) {
02519     // Since we know aRule was not an empty string, just throw
02520     return NS_ERROR_DOM_SYNTAX_ERR;
02521   }
02522 
02523   PRUint32 counter;
02524   nsCOMPtr<nsICSSRule> rule;
02525   for (counter = 0; counter < rulecount; counter++) {
02526     // Only rulesets are allowed in a group as of CSS2
02527     PRInt32 type = nsICSSRule::UNKNOWN_RULE;
02528     rule = dont_AddRef((nsICSSRule*)rules->ElementAt(counter));
02529     rule->GetType(type);
02530     if (type != nsICSSRule::STYLE_RULE) {
02531       return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
02532     }
02533   }
02534   
02535   result = aGroup->InsertStyleRulesAt(aIndex, rules);
02536   NS_ENSURE_SUCCESS(result, result);
02537   DidDirty();
02538   for (counter = 0; counter < rulecount; counter++) {
02539     rule = dont_AddRef((nsICSSRule*)rules->ElementAt(counter));
02540   
02541     if (mDocument) {
02542       mDocument->StyleRuleAdded(this, rule);
02543     }
02544   }
02545 
02546   if (loader) {
02547     loader->RecycleParser(css);
02548   }
02549 
02550   *_retval = aIndex;
02551   return NS_OK;
02552 }
02553 
02554 NS_IMETHODIMP
02555 nsCSSStyleSheet::ReplaceRuleInGroup(nsICSSGroupRule* aGroup,
02556                                       nsICSSRule* aOld, nsICSSRule* aNew)
02557 {
02558   nsresult result;
02559   NS_PRECONDITION(mInner && mInner->mComplete,
02560                   "No replacing in an incomplete sheet!");
02561 #ifdef DEBUG
02562   {
02563     nsCOMPtr<nsIStyleSheet> groupSheet;
02564     aGroup->GetStyleSheet(*getter_AddRefs(groupSheet));
02565     NS_ASSERTION(this == groupSheet, "group doesn't belong to this sheet");
02566   }
02567 #endif
02568   result = WillDirty();
02569   NS_ENSURE_SUCCESS(result, result);
02570 
02571   result = aGroup->ReplaceStyleRule(aOld, aNew);
02572   DidDirty();
02573   return result;
02574 }
02575 
02576 // nsICSSLoaderObserver implementation
02577 NS_IMETHODIMP
02578 nsCSSStyleSheet::StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify)
02579 {
02580 #ifdef DEBUG
02581   nsCOMPtr<nsIStyleSheet> styleSheet(do_QueryInterface(aSheet));
02582   NS_ASSERTION(styleSheet, "Sheet not implementing nsIStyleSheet!\n");
02583   nsCOMPtr<nsIStyleSheet> parentSheet;
02584   aSheet->GetParentSheet(*getter_AddRefs(parentSheet));
02585   nsCOMPtr<nsIStyleSheet> thisSheet;
02586   QueryInterface(NS_GET_IID(nsIStyleSheet), getter_AddRefs(thisSheet));
02587   NS_ASSERTION(thisSheet == parentSheet, "We are being notified of a sheet load for a sheet that is not our child!\n");
02588 #endif
02589   
02590   if (mDocument && aNotify) {
02591     nsCOMPtr<nsICSSImportRule> ownerRule;
02592     aSheet->GetOwnerRule(getter_AddRefs(ownerRule));
02593     
02594     mozAutoDocUpdate updateBatch(mDocument, UPDATE_STYLE, PR_TRUE);
02595 
02596     // XXXldb @import rules shouldn't even implement nsIStyleRule (but
02597     // they do)!
02598     nsCOMPtr<nsIStyleRule> styleRule(do_QueryInterface(ownerRule));
02599     
02600     mDocument->StyleRuleAdded(this, styleRule);
02601   }
02602 
02603   return NS_OK;
02604 }
02605 
02606 nsresult
02607 NS_NewCSSStyleSheet(nsICSSStyleSheet** aInstancePtrResult)
02608 {
02609   nsCSSStyleSheet  *it = new nsCSSStyleSheet();
02610 
02611   if (nsnull == it) {
02612     return NS_ERROR_OUT_OF_MEMORY;
02613   }
02614 
02615   NS_ADDREF(it);
02616   *aInstancePtrResult = it;
02617   return NS_OK;
02618 }
02619 
02620 
02621 // -------------------------------
02622 // CSS Style rule processor implementation
02623 //
02624 
02625 nsCSSRuleProcessor::nsCSSRuleProcessor(const nsCOMArray<nsICSSStyleSheet>& aSheets)
02626   : mSheets(aSheets),
02627     mRuleCascades(nsnull)
02628 {
02629   for (PRInt32 i = mSheets.Count() - 1; i >= 0; --i)
02630     mSheets[i]->AddRuleProcessor(this);
02631 }
02632 
02633 nsCSSRuleProcessor::~nsCSSRuleProcessor()
02634 {
02635   for (PRInt32 i = mSheets.Count() - 1; i >= 0; --i)
02636     mSheets[i]->DropRuleProcessor(this);
02637   mSheets.Clear();
02638   ClearRuleCascades();
02639 }
02640 
02641 NS_IMPL_ISUPPORTS1(nsCSSRuleProcessor, nsIStyleRuleProcessor)
02642 
02643 MOZ_DECL_CTOR_COUNTER(RuleProcessorData)
02644 
02645 RuleProcessorData::RuleProcessorData(nsPresContext* aPresContext,
02646                                      nsIContent* aContent, 
02647                                      nsRuleWalker* aRuleWalker,
02648                                      nsCompatibility* aCompat /*= nsnull*/)
02649 {
02650   MOZ_COUNT_CTOR(RuleProcessorData);
02651 
02652   NS_PRECONDITION(aPresContext, "null pointer");
02653   NS_ASSERTION(!aContent || aContent->IsContentOfType(nsIContent::eELEMENT),
02654                "non-element leaked into SelectorMatches");
02655 
02656   mPresContext = aPresContext;
02657   mContent = aContent;
02658   mParentContent = nsnull;
02659   mRuleWalker = aRuleWalker;
02660   mScopedRoot = nsnull;
02661 
02662   mContentTag = nsnull;
02663   mContentID = nsnull;
02664   mStyledContent = nsnull;
02665   mIsHTMLContent = PR_FALSE;
02666   mIsHTMLLink = PR_FALSE;
02667   mIsSimpleXLink = PR_FALSE;
02668   mLinkState = eLinkState_Unknown;
02669   mEventState = 0;
02670   mNameSpaceID = kNameSpaceID_Unknown;
02671   mPreviousSiblingData = nsnull;
02672   mParentData = nsnull;
02673   mLanguage = nsnull;
02674 
02675   // get the compat. mode (unless it is provided)
02676   if(!aCompat) {
02677     mCompatMode = mPresContext->CompatibilityMode();
02678   } else {
02679     mCompatMode = *aCompat;
02680   }
02681 
02682 
02683   if(aContent){
02684     // we hold no ref to the content...
02685     mContent = aContent;
02686 
02687     // get the tag and parent
02688     mContentTag = aContent->Tag();
02689     mParentContent = aContent->GetParent();
02690 
02691     // get the event state
02692     mPresContext->EventStateManager()->GetContentState(aContent, mEventState);
02693 
02694     // get the styledcontent interface and the ID
02695     if (NS_SUCCEEDED(aContent->QueryInterface(NS_GET_IID(nsIStyledContent), (void**)&mStyledContent))) {
02696       NS_ASSERTION(mStyledContent, "Succeeded but returned null");
02697       mContentID = mStyledContent->GetID();
02698     }
02699 
02700     // see if there are attributes for the content
02701     mHasAttributes = aContent->GetAttrCount() > 0;
02702 
02703     // check for HTMLContent and Link status
02704     if (aContent->IsContentOfType(nsIContent::eHTML)) {
02705       mIsHTMLContent = PR_TRUE;
02706       // Note that we want to treat non-XML HTML content as XHTML for namespace
02707       // purposes, since html.css has that namespace declared.
02708       mNameSpaceID = kNameSpaceID_XHTML;
02709     } else {
02710       // get the namespace
02711       mNameSpaceID = aContent->GetNameSpaceID();
02712     }
02713 
02714 
02715 
02716     // if HTML content and it has some attributes, check for an HTML link
02717     // NOTE: optimization: cannot be a link if no attributes (since it needs an href)
02718     if (mIsHTMLContent && mHasAttributes) {
02719       // check if it is an HTML Link
02720       if(nsStyleUtil::IsHTMLLink(aContent, mContentTag, mPresContext, &mLinkState)) {
02721         mIsHTMLLink = PR_TRUE;
02722       }
02723     } 
02724 
02725     // if not an HTML link, check for a simple xlink (cannot be both HTML link and xlink)
02726     // NOTE: optimization: cannot be an XLink if no attributes (since it needs an 
02727     if(!mIsHTMLLink &&
02728        mHasAttributes && 
02729        !(mIsHTMLContent || aContent->IsContentOfType(nsIContent::eXUL)) && 
02730        nsStyleUtil::IsSimpleXlink(aContent, mPresContext, &mLinkState)) {
02731       mIsSimpleXLink = PR_TRUE;
02732     } 
02733   }
02734 }
02735 
02736 RuleProcessorData::~RuleProcessorData()
02737 {
02738   MOZ_COUNT_DTOR(RuleProcessorData);
02739 
02740   // Destroy potentially long chains of previous sibling and parent data
02741   // without more than one level of recursion.
02742   if (mPreviousSiblingData || mParentData) {
02743     nsAutoVoidArray destroyQueue;
02744     destroyQueue.AppendElement(this);
02745 
02746     do {
02747       RuleProcessorData *d = NS_STATIC_CAST(RuleProcessorData*,
02748         destroyQueue.FastElementAt(destroyQueue.Count() - 1));
02749       destroyQueue.RemoveElementAt(destroyQueue.Count() - 1);
02750 
02751       if (d->mPreviousSiblingData) {
02752         destroyQueue.AppendElement(d->mPreviousSiblingData);
02753         d->mPreviousSiblingData = nsnull;
02754       }
02755       if (d->mParentData) {
02756         destroyQueue.AppendElement(d->mParentData);
02757         d->mParentData = nsnull;
02758       }
02759 
02760       if (d != this)
02761         d->Destroy(mPresContext);
02762     } while (destroyQueue.Count());
02763   }
02764 
02765   NS_IF_RELEASE(mStyledContent);
02766 
02767   delete mLanguage;
02768 }
02769 
02770 const nsString* RuleProcessorData::GetLang()
02771 {
02772   if (!mLanguage) {
02773     mLanguage = new nsAutoString();
02774     if (!mLanguage)
02775       return nsnull;
02776     for (nsIContent* content = mContent; content;
02777          content = content->GetParent()) {
02778       if (content->GetAttrCount() > 0) {
02779         // xml:lang has precedence over lang on HTML elements (see
02780         // XHTML1 section C.7).
02781         nsAutoString value;
02782         nsresult attrState = content->GetAttr(kNameSpaceID_XML,
02783                                               nsHTMLAtoms::lang, value);
02784         if (attrState != NS_CONTENT_ATTR_HAS_VALUE &&
02785             content->IsContentOfType(nsIContent::eHTML)) {
02786           attrState = content->GetAttr(kNameSpaceID_None,
02787                                        nsHTMLAtoms::lang, value);
02788         }
02789         if (attrState == NS_CONTENT_ATTR_HAS_VALUE) {
02790           *mLanguage = value;
02791           break;
02792         }
02793       }
02794     }
02795   }
02796   return mLanguage;
02797 }
02798 
02799 static const PRUnichar kNullCh = PRUnichar('\0');
02800 
02801 static PRBool ValueIncludes(const nsSubstring& aValueList,
02802                             const nsSubstring& aValue,
02803                             const nsStringComparator& aComparator)
02804 {
02805   const PRUnichar *p = aValueList.BeginReading(),
02806               *p_end = aValueList.EndReading();
02807 
02808   while (p < p_end) {
02809     // skip leading space
02810     while (p != p_end && nsCRT::IsAsciiSpace(*p))
02811       ++p;
02812 
02813     const PRUnichar *val_start = p;
02814 
02815     // look for space or end
02816     while (p != p_end && !nsCRT::IsAsciiSpace(*p))
02817       ++p;
02818 
02819     const PRUnichar *val_end = p;
02820 
02821     if (val_start < val_end &&
02822         aValue.Equals(Substring(val_start, val_end), aComparator))
02823       return PR_TRUE;
02824 
02825     ++p; // we know the next character is not whitespace
02826   }
02827   return PR_FALSE;
02828 }
02829 
02830 inline PRBool IsEventPseudo(nsIAtom* aAtom)
02831 {
02832   return nsCSSPseudoClasses::active == aAtom || 
02833          nsCSSPseudoClasses::mozDragOver == aAtom || 
02834          nsCSSPseudoClasses::focus == aAtom || 
02835          nsCSSPseudoClasses::hover == aAtom ||
02836          nsCSSPseudoClasses::target == aAtom; 
02837          // XXX selected, enabled, disabled, selection?
02838 }
02839 
02840 inline PRBool IsLinkPseudo(nsIAtom* aAtom)
02841 {
02842   return PRBool ((nsCSSPseudoClasses::link == aAtom) || 
02843                  (nsCSSPseudoClasses::visited == aAtom) ||
02844                  (nsCSSPseudoClasses::mozAnyLink == aAtom));
02845 }
02846 
02847 // Return whether we should apply a "global" (i.e., universal-tag)
02848 // selector for event states in quirks mode.  Note that
02849 // |data.mIsHTMLLink| is checked separately by the caller, so we return
02850 // false for |nsHTMLAtoms::a|, which here means a named anchor.
02851 inline PRBool IsQuirkEventSensitive(nsIAtom *aContentTag)
02852 {
02853   return PRBool ((nsHTMLAtoms::button == aContentTag) ||
02854                  (nsHTMLAtoms::img == aContentTag)    ||
02855                  (nsHTMLAtoms::input == aContentTag)  ||
02856                  (nsHTMLAtoms::label == aContentTag)  ||
02857                  (nsHTMLAtoms::select == aContentTag) ||
02858                  (nsHTMLAtoms::textarea == aContentTag));
02859 }
02860 
02861 
02862 static PRBool IsSignificantChild(nsIContent* aChild, PRBool aTextIsSignificant, PRBool aWhitespaceIsSignificant)
02863 {
02864   NS_ASSERTION(!aWhitespaceIsSignificant || aTextIsSignificant,
02865                "Nonsensical arguments");
02866 
02867   PRBool isText = aChild->IsContentOfType(nsIContent::eTEXT);
02868 
02869   if (!isText && !aChild->IsContentOfType(nsIContent::eCOMMENT) &&
02870       !aChild->IsContentOfType(nsIContent::ePROCESSING_INSTRUCTION)) {
02871     return PR_TRUE;
02872   }
02873 
02874   if (aTextIsSignificant && isText) {
02875     if (!aWhitespaceIsSignificant) {
02876        nsCOMPtr<nsITextContent> text = do_QueryInterface(aChild);
02877       
02878        if (text && !text->IsOnlyWhitespace())
02879          return PR_TRUE;
02880     }
02881     else {
02882       return PR_TRUE;
02883     }
02884   }
02885 
02886   return PR_FALSE;
02887 }
02888 
02889 // This function is to be called once we have fetched a value for an attribute
02890 // whose namespace and name match those of aAttrSelector.  This function
02891 // performs comparisons on the value only, based on aAttrSelector->mFunction.
02892 static PRBool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
02893                                const nsString& aValue)
02894 {
02895   NS_PRECONDITION(aAttrSelector, "Must have an attribute selector");
02896   const nsDefaultStringComparator defaultComparator;
02897   const nsCaseInsensitiveStringComparator ciComparator;
02898   const nsStringComparator& comparator = aAttrSelector->mCaseSensitive
02899                 ? NS_STATIC_CAST(const nsStringComparator&, defaultComparator)
02900                 : NS_STATIC_CAST(const nsStringComparator&, ciComparator);
02901   switch (aAttrSelector->mFunction) {
02902     case NS_ATTR_FUNC_EQUALS: 
02903       return aValue.Equals(aAttrSelector->mValue, comparator);
02904     case NS_ATTR_FUNC_INCLUDES: 
02905       return ValueIncludes(aValue, aAttrSelector->mValue, comparator);
02906     case NS_ATTR_FUNC_DASHMATCH: 
02907       return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator);
02908     case NS_ATTR_FUNC_ENDSMATCH:
02909       return StringEndsWith(aValue, aAttrSelector->mValue, comparator);
02910     case NS_ATTR_FUNC_BEGINSMATCH:
02911       return StringBeginsWith(aValue, aAttrSelector->mValue, comparator);
02912     case NS_ATTR_FUNC_CONTAINSMATCH:
02913       return FindInReadable(aAttrSelector->mValue, aValue, comparator);
02914     default:
02915       NS_NOTREACHED("Shouldn't be ending up here");
02916       return PR_FALSE;
02917   }
02918 }
02919 
02920 #define STATE_CHECK(_state)                                  \
02921   ((aStateMask & (_state)) ||                                \
02922    (localTrue == (0 != (data.mEventState & (_state)))))
02923 
02924 // NOTE:  The |aStateMask| code isn't going to work correctly anymore if
02925 // we start batching style changes, because if multiple states change in
02926 // separate notifications then we might determine the style is not
02927 // state-dependent when it really is (e.g., determining that a
02928 // :hover:active rule no longer matches when both states are unset).
02929 static PRBool SelectorMatches(RuleProcessorData &data,
02930                               nsCSSSelector* aSelector,
02931                               PRInt32 aStateMask, // states NOT to test
02932                               nsIAtom* aAttribute, // attribute NOT to test
02933                               PRInt8 aNegationIndex) 
02934 
02935 {
02936   // if we are dealing with negations, reverse the values of PR_TRUE and PR_FALSE
02937   PRBool localFalse = 0 < aNegationIndex;
02938   PRBool localTrue = 0 == aNegationIndex;
02939   PRBool result = localTrue;
02940 
02941   // Do not perform the test if aNegationIndex==1
02942   // because it then contains only negated IDs, classes, attributes and pseudo-
02943   // classes
02944   if (1 != aNegationIndex) {
02945     if (kNameSpaceID_Unknown != aSelector->mNameSpace) {
02946       if (data.mNameSpaceID != aSelector->mNameSpace) {
02947         result = localFalse;
02948       }
02949     }
02950     if (localTrue == result) {
02951       if ((nsnull != aSelector->mTag) && (aSelector->mTag != data.mContentTag)) {
02952         result = localFalse;
02953       }
02954     }
02955     // optimization : bail out early if we can
02956     if (!result) {
02957       return PR_FALSE;
02958     }
02959   }
02960 
02961   result = PR_TRUE;
02962 
02963   // test for pseudo class match
02964   // first-child, root, lang, active, focus, hover, link, visited...
02965   // XXX disabled, enabled, selected, selection
02966   for (nsAtomStringList* pseudoClass = aSelector->mPseudoClassList;
02967        pseudoClass && result; pseudoClass = pseudoClass->mNext) {
02968     if ((nsCSSPseudoClasses::firstChild == pseudoClass->mAtom) ||
02969         (nsCSSPseudoClasses::firstNode == pseudoClass->mAtom) ) {
02970       nsIContent *firstChild = nsnull;
02971       nsIContent *parent = data.mParentContent;
02972       if (parent) {
02973         PRBool acceptNonWhitespace =
02974           nsCSSPseudoClasses::firstNode == pseudoClass->mAtom;
02975         PRInt32 index = -1;
02976         do {
02977           firstChild = parent->GetChildAt(++index);
02978           // stop at first non-comment and non-whitespace node (and
02979           // non-text node for firstChild)
02980         } while (firstChild &&
02981                  !IsSignificantChild(firstChild, acceptNonWhitespace, PR_FALSE));
02982       }
02983       result = localTrue == (data.mContent == firstChild);
02984     }
02985     else if ((nsCSSPseudoClasses::lastChild == pseudoClass->mAtom) ||
02986              (nsCSSPseudoClasses::lastNode == pseudoClass->mAtom)) {
02987       nsIContent *lastChild = nsnull;
02988       nsIContent *parent = data.mParentContent;
02989       if (parent) {
02990         PRBool acceptNonWhitespace =
02991           nsCSSPseudoClasses::lastNode == pseudoClass->mAtom;
02992         PRUint32 index = parent->GetChildCount();
02993         do {
02994           lastChild = parent->GetChildAt(--index);
02995           // stop at first non-comment and non-whitespace node (and
02996           // non-text node for lastChild)
02997         } while (lastChild &&
02998                  !IsSignificantChild(lastChild, acceptNonWhitespace, PR_FALSE));
02999       }
03000       result = localTrue == (data.mContent == lastChild);
03001     }
03002     else if (nsCSSPseudoClasses::onlyChild == pseudoClass->mAtom) {
03003       nsIContent *onlyChild = nsnull;
03004       nsIContent *moreChild = nsnull;
03005       nsIContent *parent = data.mParentContent;
03006       if (parent) {
03007         PRInt32 index = -1;
03008         do {
03009           onlyChild = parent->GetChildAt(++index);
03010           // stop at first non-comment, non-whitespace and non-text node
03011         } while (onlyChild &&
03012                  !IsSignificantChild(onlyChild, PR_FALSE, PR_FALSE));
03013         if (data.mContent == onlyChild) {
03014           // see if there's any more
03015           do {
03016             moreChild = parent->GetChildAt(++index);
03017           } while (moreChild && !IsSignificantChild(moreChild, PR_FALSE, PR_FALSE));
03018         }
03019       }
03020       result = localTrue == (data.mContent == onlyChild &&
03021                              moreChild == nsnull);
03022     }
03023     else if (nsCSSPseudoClasses::empty == pseudoClass->mAtom ||
03024              nsCSSPseudoClasses::mozOnlyWhitespace == pseudoClass->mAtom) {
03025       nsIContent *child = nsnull;
03026       nsIContent *element = data.mContent;
03027       PRBool isWhitespaceSignificant =
03028         nsCSSPseudoClasses::empty == pseudoClass->mAtom;
03029       PRInt32 index = -1;
03030 
03031       do {
03032         child = element->GetChildAt(++index);
03033         // stop at first non-comment (and non-whitespace for
03034         // :-moz-only-whitespace) node        
03035       } while (child && !IsSignificantChild(child, PR_TRUE, isWhitespaceSignificant));
03036       result = localTrue == (child == nsnull);
03037     }
03038     else if (nsCSSPseudoClasses::root == pseudoClass->mAtom) {
03039       if (data.mParentContent) {
03040         result = localFalse;
03041       }
03042       else {
03043         result = localTrue;
03044       }
03045     }
03046     else if (nsCSSPseudoClasses::mozBoundElement == pseudoClass->mAtom) {
03047       // XXXldb How do we know where the selector came from?  And what
03048       // if there are multiple bindings, and we should be matching the
03049       // outer one?
03050       result = (data.mScopedRoot && data.mScopedRoot == data.mContent)
03051                  ? localTrue : localFalse;
03052     }
03053     else if (nsCSSPseudoClasses::lang == pseudoClass->mAtom) {
03054       NS_ASSERTION(nsnull != pseudoClass->mString, "null lang parameter");
03055       result = localFalse;
03056       if (pseudoClass->mString && *pseudoClass->mString) {
03057         // We have to determine the language of the current element.  Since
03058         // this is currently no property and since the language is inherited
03059         // from the parent we have to be prepared to look at all parent
03060         // nodes.  The language itself is encoded in the LANG attribute.
03061         const nsString* lang = data.GetLang();
03062         if (lang && !lang->IsEmpty()) { // null check for out-of-memory
03063           result = localTrue == nsStyleUtil::DashMatchCompare(*lang,
03064                                     nsDependentString(pseudoClass->mString), 
03065                                     nsCaseInsensitiveStringComparator());
03066         }
03067         else {
03068           nsIDocument* doc = data.mContent->GetDocument();
03069           if (doc) {
03070             // Try to get the language from the HTTP header or if this
03071             // is missing as well from the preferences.
03072             // The content language can be a comma-separated list of
03073             // language codes.
03074             nsAutoString language;
03075             doc->GetContentLanguage(language);
03076 
03077             nsDependentString langString(pseudoClass->mString);
03078             language.StripWhitespace();
03079             PRInt32 begin = 0;
03080             PRInt32 len = language.Length();
03081             while (begin < len) {
03082               PRInt32 end = language.FindChar(PRUnichar(','), begin);
03083               if (end == kNotFound) {
03084                 end = len;
03085               }
03086               if (nsStyleUtil::DashMatchCompare(Substring(language, begin, end-begin),
03087                                    langString,
03088                                    nsCaseInsensitiveStringComparator())) {
03089                 result = localTrue;
03090                 break;
03091               }
03092               begin = end + 1;
03093             }
03094           }
03095         }
03096       }
03097     }
03098     else if (IsEventPseudo(pseudoClass->mAtom)) {
03099       // check if the element is event-sensitive
03100       if (data.mCompatMode == eCompatibility_NavQuirks &&
03101           // global selector (but don't check .class):
03102           !aSelector->mTag && !aSelector->mIDList && !aSelector->mAttrList &&
03103           // This (or the other way around) both make :not() asymmetric
03104           // in quirks mode (and it's hard to work around since we're
03105           // testing the current mNegations, not the first
03106           // (unnegated)). This at least makes it closer to the spec.
03107           aNegationIndex == 0 &&
03108           // :hover or :active
03109           (nsCSSPseudoClasses::active == pseudoClass->mAtom ||
03110            nsCSSPseudoClasses::hover == pseudoClass->mAtom) &&
03111           // important for |IsQuirkEventSensitive|:
03112           data.mIsHTMLContent && !data.mIsHTMLLink &&
03113           !IsQuirkEventSensitive(data.mContentTag)) {
03114         // In quirks mode, only make certain elements sensitive to
03115         // selectors ":hover" and ":active".
03116         result = localFalse;
03117       } else {
03118         if (nsCSSPseudoClasses::active == pseudoClass->mAtom) {
03119           result = STATE_CHECK(NS_EVENT_STATE_ACTIVE);
03120         }
03121         else if (nsCSSPseudoClasses::focus == pseudoClass->mAtom) {
03122           result = STATE_CHECK(NS_EVENT_STATE_FOCUS);
03123         }
03124         else if (nsCSSPseudoClasses::hover == pseudoClass->mAtom) {
03125           result = STATE_CHECK(NS_EVENT_STATE_HOVER);
03126         }
03127         else if (nsCSSPseudoClasses::mozDragOver == pseudoClass->mAtom) {
03128           result = STATE_CHECK(NS_EVENT_STATE_DRAGOVER);
03129         }
03130         else if (nsCSSPseudoClasses::target == pseudoClass->mAtom) {
03131           result = STATE_CHECK(NS_EVENT_STATE_URLTARGET);
03132         }
03133       } 
03134     }
03135     else if (IsLinkPseudo(pseudoClass->mAtom)) {
03136       if (data.mIsHTMLLink || data.mIsSimpleXLink) {
03137         if (nsCSSPseudoClasses::mozAnyLink == pseudoClass->mAtom) {
03138           result = localTrue;
03139         }
03140         else if (nsCSSPseudoClasses::link == pseudoClass->mAtom) {
03141           result = (aStateMask & NS_EVENT_STATE_VISITED) ||
03142             localTrue == (eLinkState_Unvisited == data.mLinkState);
03143         }
03144         else if (nsCSSPseudoClasses::visited == pseudoClass->mAtom) {
03145           result = (aStateMask & NS_EVENT_STATE_VISITED) ||
03146             localTrue == (eLinkState_Visited == data.mLinkState);
03147         }
03148       }
03149       else {
03150         result = localFalse;  // not a link
03151       }
03152     }
03153     else if (nsCSSPseudoClasses::checked == pseudoClass->mAtom) {
03154       // This pseudoclass matches the selected state on the following elements:
03155       //  <option>
03156       //  <input type=checkbox>
03157       //  <input type=radio>
03158       result = STATE_CHECK(NS_EVENT_STATE_CHECKED);
03159     }
03160     else if (nsCSSPseudoClasses::enabled == pseudoClass->mAtom) {
03161       result = STATE_CHECK(NS_EVENT_STATE_ENABLED);
03162     }
03163     else if (nsCSSPseudoClasses::disabled == pseudoClass->mAtom) {
03164       result = STATE_CHECK(NS_EVENT_STATE_DISABLED);
03165     }
03166     else if (nsCSSPseudoClasses::required == pseudoClass->mAtom) {
03167       result = STATE_CHECK(NS_EVENT_STATE_REQUIRED);
03168     }
03169     else if (nsCSSPseudoClasses::optional == pseudoClass->mAtom) {
03170       result = STATE_CHECK(NS_EVENT_STATE_OPTIONAL);
03171     }
03172     else if (nsCSSPseudoClasses::valid == pseudoClass->mAtom) {
03173       result = STATE_CHECK(NS_EVENT_STATE_VALID);
03174     }
03175     else if (nsCSSPseudoClasses::invalid == pseudoClass->mAtom) {
03176       result = STATE_CHECK(NS_EVENT_STATE_INVALID);
03177     }
03178     else if (nsCSSPseudoClasses::inRange == pseudoClass->mAtom) {
03179       result = STATE_CHECK(NS_EVENT_STATE_INRANGE);
03180     }
03181     else if (nsCSSPseudoClasses::outOfRange == pseudoClass->mAtom) {
03182       result = STATE_CHECK(NS_EVENT_STATE_OUTOFRANGE);
03183     }
03184     else if (nsCSSPseudoClasses::mozReadOnly == pseudoClass->mAtom) {
03185       result = STATE_CHECK(NS_EVENT_STATE_MOZ_READONLY);
03186     }
03187     else if (nsCSSPseudoClasses::mozReadWrite == pseudoClass->mAtom) {
03188       result = STATE_CHECK(NS_EVENT_STATE_MOZ_READWRITE);
03189     }
03190     else if (nsCSSPseudoClasses::mozIsHTML == pseudoClass->mAtom) {
03191       result =
03192         (data.mIsHTMLContent &&
03193          data.mContent->GetNameSpaceID() == kNameSpaceID_None) ? localTrue :
03194                                                                  localFalse;
03195     }
03196     else {
03197       NS_ERROR("CSS parser parsed a pseudo-class that we do not handle");
03198       result = PR_FALSE;  // unknown pseudo class
03199     }
03200   }
03201 
03202   // namespace/tag match
03203 
03204   if (result && aSelector->mAttrList) {
03205     // test for attribute match
03206     if (!data.mHasAttributes && !aAttribute) {
03207       // if no attributes on the content, no match
03208       result = localFalse;
03209     } else {
03210       result = localTrue;
03211       nsAttrSelector* attr = aSelector->mAttrList;
03212       do {
03213         if (attr->mAttr == aAttribute) {
03214           // XXX we should really have a namespace, not just an attr
03215           // name, in HasAttributeDependentStyle!
03216           result = PR_TRUE;
03217         }
03218         else if (attr->mNameSpace == kNameSpaceID_Unknown) {
03219           // Attr selector with a wildcard namespace.  We have to examine all
03220           // the attributes on our content node....  This sort of selector is
03221           // essentially a boolean OR, over all namespaces, of equivalent attr
03222           // selectors with those namespaces.  So to evaluate whether it
03223           // matches, evaluate for each namespace (the only namespaces that
03224           // have a chance at matching, of course, are ones that the element
03225           // actually has attributes in), short-circuiting if we ever match.
03226           // Then deal with the localFalse/localTrue stuff.
03227           PRUint32 attrCount = data.mContent->GetAttrCount();
03228           PRInt32 nameSpaceID;
03229           nsCOMPtr<nsIAtom> name;
03230           nsCOMPtr<nsIAtom> prefix;
03231           PRBool attrSelectorMatched = PR_FALSE;
03232           for (PRUint32 i = 0; i < attrCount; ++i) {
03233 #ifdef DEBUG
03234             nsresult attrState =
03235 #endif
03236               data.mContent->GetAttrNameAt(i, &nameSpaceID,
03237                                            getter_AddRefs(name),
03238                                            getter_AddRefs(prefix));
03239             NS_ASSERTION(NS_SUCCEEDED(attrState),
03240                          "GetAttrCount lied or GetAttrNameAt failed");
03241             if (name != attr->mAttr) {
03242               continue;
03243             }
03244             if (attr->mFunction == NS_ATTR_FUNC_SET) {
03245               attrSelectorMatched = PR_TRUE;
03246             } else {
03247               nsAutoString value;
03248 #ifdef DEBUG
03249               attrState =
03250 #endif
03251                 data.mContent->GetAttr(nameSpaceID, name, value);
03252               NS_ASSERTION(NS_SUCCEEDED(attrState) &&
03253                            NS_CONTENT_ATTR_NOT_THERE != attrState,
03254                            "GetAttrNameAt lied or GetAttr failed");
03255               attrSelectorMatched = AttrMatchesValue(attr, value);
03256             }
03257 
03258             // At this point |attrSelectorMatched| has been set by us
03259             // explicitly in this loop.  If it's PR_FALSE, we may still match
03260             // -- the content may have another attribute with the same name but
03261             // in a different namespace.  But if it's PR_TRUE, we are done (we
03262             // can short-circuit the boolean OR described above).
03263             if (attrSelectorMatched) {
03264               break;
03265             }
03266           }
03267           
03268           // Now adjust for a possible negation
03269           result = localTrue == attrSelectorMatched;
03270         }
03271         else if (!data.mContent->HasAttr(attr->mNameSpace, attr->mAttr)) {
03272           result = localFalse;
03273         }
03274         else if (attr->mFunction != NS_ATTR_FUNC_SET) {
03275           nsAutoString value;
03276 #ifdef DEBUG
03277           nsresult attrState =
03278 #endif
03279               data.mContent->GetAttr(attr->mNameSpace, attr->mAttr, value);
03280           NS_ASSERTION(NS_SUCCEEDED(attrState) &&
03281                        NS_CONTENT_ATTR_NOT_THERE != attrState,
03282                        "HasAttr lied or GetAttr failed");
03283           result = localTrue == AttrMatchesValue(attr, value);
03284         }
03285         
03286         attr = attr->mNext;
03287       } while (attr && result);
03288     }
03289   }
03290   if (result && (aSelector->mIDList || aSelector->mClassList)) {
03291     // test for ID & class match
03292     result = localFalse;
03293     if (data.mStyledContent) {
03294       // case sensitivity: bug 93371
03295       PRBool isCaseSensitive = data.mCompatMode != eCompatibility_NavQuirks;
03296       nsAtomList* IDList = aSelector->mIDList;
03297       if (nsnull == IDList ||
03298           (aAttribute && aAttribute == data.mContent->GetIDAttributeName())) {
03299         result = PR_TRUE;
03300       }
03301       else if (nsnull != data.mContentID) {
03302         result = PR_TRUE;
03303         if (isCaseSensitive) {
03304           do {
03305             if (localTrue == (IDList->mAtom != data.mContentID)) {
03306               result = PR_FALSE;
03307               break;
03308             }
03309             IDList = IDList->mNext;
03310           } while (IDList);
03311         } else {
03312           const char* id1Str;
03313           data.mContentID->GetUTF8String(&id1Str);
03314           nsDependentCString id1(id1Str);
03315           do {
03316             const char* id2Str;
03317             IDList->mAtom->GetUTF8String(&id2Str);
03318             nsDependentCString id2(id2Str);
03319             if (localTrue !=
03320                 id1.Equals(id2, nsCaseInsensitiveCStringComparator())) {
03321               result = PR_FALSE;
03322               break;
03323             }
03324             IDList = IDList->mNext;
03325           } while (IDList);
03326         }
03327       }
03328       
03329       if (result &&
03330           (!aAttribute || aAttribute != data.mStyledContent->GetClassAttributeName())) {
03331         nsAtomList* classList = aSelector->mClassList;
03332         while (nsnull != classList) {
03333           if (localTrue == (!data.mStyledContent->HasClass(classList->mAtom, isCaseSensitive))) {
03334             result = PR_FALSE;
03335             break;
03336           }
03337           classList = classList->mNext;
03338         }
03339       }
03340     }
03341   }
03342   
03343   // apply SelectorMatches to the negated selectors in the chain
03344   if (result && (nsnull != aSelector->mNegations)) {
03345     result = SelectorMatches(data, aSelector->mNegations, aStateMask,
03346                              aAttribute, aNegationIndex+1);
03347   }
03348   return result;
03349 }
03350 
03351 #undef STATE_CHECK
03352 
03353 // Right now, there are four operators:
03354 //   PRUnichar(0), the descendent combinator, is greedy
03355 //   '~', the indirect adjacent sibling combinator, is greedy
03356 //   '+' and '>', the direct adjacent sibling and child combinators, are not
03357 #define NS_IS_GREEDY_OPERATOR(ch) (ch == PRUnichar(0) || ch == PRUnichar('~'))
03358 
03359 static PRBool SelectorMatchesTree(RuleProcessorData& aPrevData,
03360                                   nsCSSSelector* aSelector) 
03361 {
03362   nsCSSSelector* selector = aSelector;
03363   RuleProcessorData* prevdata = &aPrevData;
03364   while (selector) { // check compound selectors
03365     // If we don't already have a RuleProcessorData for the next
03366     // appropriate content (whether parent or previous sibling), create
03367     // one.
03368 
03369     // for adjacent sibling combinators, the content to test against the
03370     // selector is the previous sibling *element*
03371     RuleProcessorData* data;
03372     if (PRUnichar('+') == selector->mOperator ||
03373         PRUnichar('~') == selector->mOperator) {
03374       data = prevdata->mPreviousSiblingData;
03375       if (!data) {
03376         nsIContent* content = prevdata->mContent;
03377         nsIContent* parent = content->GetParent();
03378         if (parent) {
03379           PRInt32 index = parent->IndexOf(content);
03380           while (0 <= --index) {
03381             content = parent->GetChildAt(index);
03382             if (content->IsContentOfType(nsIContent::eELEMENT)) {
03383               data = new (prevdata->mPresContext)
03384                           RuleProcessorData(prevdata->mPresContext, content,
03385                                             prevdata->mRuleWalker,
03386                                             &prevdata->mCompatMode);
03387               prevdata->mPreviousSiblingData = data;    
03388               break;
03389             }
03390           }
03391         }
03392       }
03393     }
03394     // for descendant combinators and child combinators, the content
03395     // to test against is the parent
03396     else {
03397       data = prevdata->mParentData;
03398       if (!data) {
03399         nsIContent *content = prevdata->mContent->GetParent();
03400         if (content) {
03401           data = new (prevdata->mPresContext)
03402                       RuleProcessorData(prevdata->mPresContext, content,
03403                                         prevdata->mRuleWalker,
03404                                         &prevdata->mCompatMode);
03405           prevdata->mParentData = data;    
03406         }
03407       }
03408     }
03409     if (! data) {
03410       return PR_FALSE;
03411     }
03412     if (SelectorMatches(*data, selector, 0, nsnull, 0)) {
03413       // to avoid greedy matching, we need to recur if this is a
03414       // descendant combinator and the next combinator is not
03415       if ((NS_IS_GREEDY_OPERATOR(selector->mOperator)) &&
03416           (selector->mNext) &&
03417           (!NS_IS_GREEDY_OPERATOR(selector->mNext->mOperator))) {
03418 
03419         // pretend the selector didn't match, and step through content
03420         // while testing the same selector
03421 
03422         // This approach is slightly strange in that when it recurs
03423         // it tests from the top of the content tree, down.  This
03424         // doesn't matter much for performance since most selectors
03425         // don't match.  (If most did, it might be faster...)
03426         if (SelectorMatchesTree(*data, selector)) {
03427           return PR_TRUE;
03428         }
03429       }
03430       selector = selector->mNext;
03431     }
03432     else {
03433       // for adjacent sibling and child combinators, if we didn't find
03434       // a match, we're done
03435       if (!NS_IS_GREEDY_OPERATOR(selector->mOperator)) {
03436         return PR_FALSE;  // parent was required to match
03437       }
03438     }
03439     prevdata = data;
03440   }
03441   return PR_TRUE; // all the selectors matched.
03442 }
03443 
03444 static void ContentEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
03445                             void* aData)
03446 {
03447   ElementRuleProcessorData* data = (ElementRuleProcessorData*)aData;
03448 
03449   if (SelectorMatches(*data, aSelector, 0, nsnull, 0)) {
03450     nsCSSSelector *next = aSelector->mNext;
03451     if (!next || SelectorMatchesTree(*data, next)) {
03452       // for performance, require that every implementation of
03453       // nsICSSStyleRule return the same pointer for nsIStyleRule (why
03454       // would anything multiply inherit nsIStyleRule anyway?)
03455 #ifdef DEBUG
03456       nsCOMPtr<nsIStyleRule> iRule = do_QueryInterface(aRule);
03457       NS_ASSERTION(NS_STATIC_CAST(nsIStyleRule*, aRule) == iRule.get(),
03458                    "Please fix QI so this performance optimization is valid");
03459 #endif
03460       data->mRuleWalker->Forward(NS_STATIC_CAST(nsIStyleRule*, aRule));
03461       // nsStyleSet will deal with the !important rule
03462     }
03463   }
03464 }
03465 
03466 NS_IMETHODIMP
03467 nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
03468 {
03469   NS_PRECONDITION(aData->mContent->IsContentOfType(nsIContent::eELEMENT),
03470                   "content must be element");
03471 
03472   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
03473 
03474   if (cascade) {
03475     nsIStyledContent* styledContent = aData->mStyledContent;
03476     const nsAttrValue* classes = nsnull;
03477     if (styledContent)
03478       classes = styledContent->GetClasses();
03479     
03480     cascade->mRuleHash.EnumerateAllRules(aData->mNameSpaceID,
03481                                          aData->mContentTag,
03482                                          aData->mContentID,
03483                                          classes,
03484                                          ContentEnumFunc,
03485                                          aData);
03486   }
03487   return NS_OK;
03488 }
03489 
03490 static void PseudoEnumFunc(nsICSSStyleRule* aRule, nsCSSSelector* aSelector,
03491                            void* aData)
03492 {
03493   PseudoRuleProcessorData* data = (PseudoRuleProcessorData*)aData;
03494 
03495   NS_ASSERTION(aSelector->mTag == data->mPseudoTag, "RuleHash failure");
03496   PRBool matches = PR_TRUE;
03497   if (data->mComparator)
03498     data->mComparator->PseudoMatches(data->mPseudoTag, aSelector, &matches);
03499 
03500   if (matches) {
03501     nsCSSSelector *selector = aSelector->mNext;
03502 
03503     if (selector) { // test next selector specially
03504       if (PRUnichar('+') == selector->mOperator) {
03505         return; // not valid here, can't match
03506       }
03507       if (SelectorMatches(*data, selector, 0, nsnull, 0)) {
03508         selector = selector->mNext;
03509       }
03510       else {
03511         if (PRUnichar('>') == selector->mOperator) {
03512           return; // immediate parent didn't match
03513         }
03514       }
03515     }
03516 
03517     if (selector && 
03518         (! SelectorMatchesTree(*data, selector))) {
03519       return; // remaining selectors didn't match
03520     }
03521 
03522     // for performance, require that every implementation of
03523     // nsICSSStyleRule return the same pointer for nsIStyleRule (why
03524     // would anything multiply inherit nsIStyleRule anyway?)
03525 #ifdef DEBUG
03526     nsCOMPtr<nsIStyleRule> iRule = do_QueryInterface(aRule);
03527     NS_ASSERTION(NS_STATIC_CAST(nsIStyleRule*, aRule) == iRule.get(),
03528                  "Please fix QI so this performance optimization is valid");
03529 #endif
03530     data->mRuleWalker->Forward(NS_STATIC_CAST(nsIStyleRule*, aRule));
03531     // nsStyleSet will deal with the !important rule
03532   }
03533 }
03534 
03535 NS_IMETHODIMP
03536 nsCSSRuleProcessor::RulesMatching(PseudoRuleProcessorData* aData)
03537 {
03538   NS_PRECONDITION(!aData->mContent ||
03539                   aData->mContent->IsContentOfType(nsIContent::eELEMENT),
03540                   "content (if present) must be element");
03541 
03542   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
03543 
03544   if (cascade) {
03545     cascade->mRuleHash.EnumerateTagRules(aData->mPseudoTag,
03546                                          PseudoEnumFunc, aData);
03547   }
03548   return NS_OK;
03549 }
03550 
03551 inline PRBool
03552 IsSiblingOperator(PRUnichar oper)
03553 {
03554   return oper == PRUnichar('+') || oper == PRUnichar('~');
03555 }
03556 
03557 struct StateEnumData {
03558   StateEnumData(StateRuleProcessorData *aData)
03559     : data(aData), change(nsReStyleHint(0)) {}
03560 
03561   StateRuleProcessorData *data;
03562   nsReStyleHint change;
03563 };
03564 
03565 PR_STATIC_CALLBACK(PRBool) StateEnumFunc(void* aSelector, void* aData)
03566 {
03567   StateEnumData *enumData = NS_STATIC_CAST(StateEnumData*, aData);
03568   StateRuleProcessorData *data = enumData->data;
03569   nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector);
03570 
03571   if (SelectorMatches(*data, selector, data->mStateMask, nsnull, 0) &&
03572       SelectorMatchesTree(*data, selector->mNext)) {
03573     if (IsSiblingOperator(selector->mOperator))
03574       enumData->change = nsReStyleHint(enumData->change | eReStyle_LaterSiblings);
03575     else
03576       enumData->change = nsReStyleHint(enumData->change | eReStyle_Self);
03577   }
03578 
03579   return PR_TRUE;
03580 }
03581 
03582 NS_IMETHODIMP
03583 nsCSSRuleProcessor::HasStateDependentStyle(StateRuleProcessorData* aData,
03584                                            nsReStyleHint* aResult)
03585 {
03586   NS_PRECONDITION(aData->mContent->IsContentOfType(nsIContent::eELEMENT),
03587                   "content must be element");
03588 
03589   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
03590 
03591   // Look up the content node in the state rule list, which points to
03592   // any (CSS2 definition) simple selector (whether or not it is the
03593   // subject) that has a state pseudo-class on it.  This means that this
03594   // code will be matching selectors that aren't real selectors in any
03595   // stylesheet (e.g., if there is a selector "body > p:hover > a", then
03596   // "body > p:hover" will be in |cascade->mStateSelectors|).  Note that
03597   // |IsStateSelector| below determines which selectors are in
03598   // |cascade->mStateSelectors|.
03599   StateEnumData data(aData);
03600   if (cascade)
03601     cascade->mStateSelectors.EnumerateForwards(StateEnumFunc, &data);
03602   *aResult = data.change;
03603   return NS_OK;
03604 }
03605 
03606 struct AttributeEnumData {
03607   AttributeEnumData(AttributeRuleProcessorData *aData)
03608     : data(aData), change(nsReStyleHint(0)) {}
03609 
03610   AttributeRuleProcessorData *data;
03611   nsReStyleHint change;
03612 };
03613 
03614 
03615 PR_STATIC_CALLBACK(PRBool) AttributeEnumFunc(void* aSelector, void* aData)
03616 {
03617   AttributeEnumData *enumData = NS_STATIC_CAST(AttributeEnumData*, aData);
03618   AttributeRuleProcessorData *data = enumData->data;
03619   nsCSSSelector* selector = NS_STATIC_CAST(nsCSSSelector*, aSelector);
03620 
03621   if (SelectorMatches(*data, selector, 0, data->mAttribute, 0) &&
03622       SelectorMatchesTree(*data, selector->mNext)) {
03623     if (IsSiblingOperator(selector->mOperator))
03624       enumData->change = nsReStyleHint(enumData->change | eReStyle_LaterSiblings);
03625     else
03626       enumData->change = nsReStyleHint(enumData->change | eReStyle_Self);
03627   }
03628 
03629   return PR_TRUE;
03630 }
03631 
03632 NS_IMETHODIMP
03633 nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData,
03634                                                nsReStyleHint* aResult)
03635 {
03636   NS_PRECONDITION(aData->mContent->IsContentOfType(nsIContent::eELEMENT),
03637                   "content must be element");
03638   NS_ASSERTION(aData->mStyledContent,
03639                "elements must implement nsIStyledContent");
03640 
03641   AttributeEnumData data(aData);
03642 
03643   // Since we always have :-moz-any-link (and almost always have :link
03644   // and :visited rules from prefs), rather than hacking AddRule below
03645   // to add |href| to the hash, we'll just handle it here.
03646   if (aData->mAttribute == nsHTMLAtoms::href &&
03647       aData->mIsHTMLContent &&
03648       (aData->mContentTag == nsHTMLAtoms::a ||
03649        aData->mContentTag == nsHTMLAtoms::area ||
03650        aData->mContentTag == nsHTMLAtoms::link)) {
03651     data.change = nsReStyleHint(data.change | eReStyle_Self);
03652   }
03653   // XXX What about XLinks?
03654 
03655   RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext);
03656 
03657   // We do the same thing for attributes that we do for state selectors
03658   // (see HasStateDependentStyle), except that instead of one big list
03659   // we have a hashtable with a per-attribute list.
03660 
03661   if (cascade) {
03662     if (aData->mAttribute == aData->mContent->GetIDAttributeName()) {
03663       cascade->mIDSelectors.EnumerateForwards(AttributeEnumFunc, &data);
03664     }
03665     
03666     if (aData->mAttribute == aData->mStyledContent->GetClassAttributeName()) {
03667       cascade->mClassSelectors.EnumerateForwards(AttributeEnumFunc, &data);
03668     }
03669 
03670     AttributeSelectorEntry *entry = NS_STATIC_CAST(AttributeSelectorEntry*,
03671         PL_DHashTableOperate(&cascade->mAttributeSelectors, aData->mAttribute,
03672                              PL_DHASH_LOOKUP));
03673     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
03674       entry->mSelectors->EnumerateForwards(AttributeEnumFunc, &data);
03675     }
03676   }
03677 
03678   *aResult = data.change;
03679   return NS_OK;
03680 }
03681 
03682 nsresult
03683 nsCSSRuleProcessor::ClearRuleCascades()
03684 {
03685   RuleCascadeData *data = mRuleCascades;
03686   mRuleCascades = nsnull;
03687   while (data) {
03688     RuleCascadeData *next = data->mNext;
03689     delete data;
03690     data = next;
03691   }
03692   return NS_OK;
03693 }
03694 
03695 
03696 // This function should return true only for selectors that need to be
03697 // checked by |HasStateDependentStyle|.
03698 inline
03699 PRBool IsStateSelector(nsCSSSelector& aSelector)
03700 {
03701   for (nsAtomStringList* pseudoClass = aSelector.mPseudoClassList;
03702        pseudoClass; pseudoClass = pseudoClass->mNext) {
03703     if ((pseudoClass->mAtom == nsCSSPseudoClasses::active) ||
03704         (pseudoClass->mAtom == nsCSSPseudoClasses::checked) ||
03705         (pseudoClass->mAtom == nsCSSPseudoClasses::mozDragOver) || 
03706         (pseudoClass->mAtom == nsCSSPseudoClasses::focus) || 
03707         (pseudoClass->mAtom == nsCSSPseudoClasses::hover) ||
03708         (pseudoClass->mAtom == nsCSSPseudoClasses::target) ||
03709         (pseudoClass->mAtom == nsCSSPseudoClasses::link) ||
03710         (pseudoClass->mAtom == nsCSSPseudoClasses::visited) ||
03711         (pseudoClass->mAtom == nsCSSPseudoClasses::enabled) ||
03712         (pseudoClass->mAtom == nsCSSPseudoClasses::disabled) ||
03713         (pseudoClass->mAtom == nsCSSPseudoClasses::required) ||
03714         (pseudoClass->mAtom == nsCSSPseudoClasses::optional) ||
03715         (pseudoClass->mAtom == nsCSSPseudoClasses::valid) ||
03716         (pseudoClass->mAtom == nsCSSPseudoClasses::invalid) ||
03717         (pseudoClass->mAtom == nsCSSPseudoClasses::inRange) ||
03718         (pseudoClass->mAtom == nsCSSPseudoClasses::outOfRange) ||
03719         (pseudoClass->mAtom == nsCSSPseudoClasses::mozReadOnly) ||
03720         (pseudoClass->mAtom == nsCSSPseudoClasses::mozReadWrite)) {
03721       return PR_TRUE;
03722     }
03723   }
03724   return PR_FALSE;
03725 }
03726 
03727 PR_STATIC_CALLBACK(PRBool)
03728 AddRule(void* aRuleInfo, void* aCascade)
03729 {
03730   RuleValue* ruleInfo = NS_STATIC_CAST(RuleValue*, aRuleInfo);
03731   RuleCascadeData *cascade = NS_STATIC_CAST(RuleCascadeData*, aCascade);
03732 
03733   // Build the rule hash.
03734   cascade->mRuleHash.PrependRule(ruleInfo);
03735 
03736   nsVoidArray* stateArray = &cascade->mStateSelectors;
03737   nsVoidArray* classArray = &cascade->mClassSelectors;
03738   nsVoidArray* idArray = &cascade->mIDSelectors;
03739   
03740   for (nsCSSSelector* selector = ruleInfo->mSelector;
03741            selector; selector = selector->mNext) {
03742     // It's worth noting that this loop over negations isn't quite
03743     // optimal for two reasons.  One, we could add something to one of
03744     // these lists twice, which means we'll check it twice, but I don't
03745     // think that's worth worrying about.   (We do the same for multiple
03746     // attribute selectors on the same attribute.)  Two, we don't really
03747     // need to check negations past the first in the current
03748     // implementation (and they're rare as well), but that might change
03749     // in the future if :not() is extended. 
03750     for (nsCSSSelector* negation = selector; negation;
03751          negation = negation->mNegations) {
03752       // Build mStateSelectors.
03753       if (IsStateSelector(*negation))
03754         stateArray->AppendElement(selector);
03755 
03756       // Build mIDSelectors
03757       if (negation->mIDList) {
03758         idArray->AppendElement(selector);
03759       }
03760       
03761       // Build mClassSelectors
03762       if (negation->mClassList) {
03763         classArray->AppendElement(selector);
03764       }
03765 
03766       // Build mAttributeSelectors.
03767       for (nsAttrSelector *attr = negation->mAttrList; attr;
03768            attr = attr->mNext) {
03769         nsVoidArray *array = cascade->AttributeListFor(attr->mAttr);
03770         if (!array)
03771           return PR_FALSE;
03772         array->AppendElement(selector);
03773       }
03774     }
03775   }
03776 
03777   return PR_TRUE;
03778 }
03779 
03780 PR_STATIC_CALLBACK(PRIntn)
03781 RuleArraysDestroy(nsHashKey *aKey, void *aData, void *aClosure)
03782 {
03783   delete NS_STATIC_CAST(nsAutoVoidArray*, aData);
03784   return PR_TRUE;
03785 }
03786 
03787 struct CascadeEnumData {
03788   CascadeEnumData(nsPresContext* aPresContext, PLArenaPool& aArena)
03789     : mPresContext(aPresContext),
03790       mRuleArrays(nsnull, nsnull, RuleArraysDestroy, nsnull, 64),
03791       mArena(aArena)
03792   {
03793   }
03794 
03795   nsPresContext* mPresContext;
03796   nsObjectHashtable mRuleArrays; // of nsAutoVoidArray
03797   PLArenaPool& mArena;
03798 };
03799 
03800 static PRBool
03801 InsertRuleByWeight(nsISupports* aRule, void* aData)
03802 {
03803   nsICSSRule* rule = (nsICSSRule*)aRule;
03804   CascadeEnumData* data = (CascadeEnumData*)aData;
03805   PRInt32 type = nsICSSRule::UNKNOWN_RULE;
03806   rule->GetType(type);
03807 
03808   if (nsICSSRule::STYLE_RULE == type) {
03809     nsICSSStyleRule* styleRule = (nsICSSStyleRule*)rule;
03810 
03811     for (nsCSSSelectorList *sel = styleRule->Selector();
03812          sel; sel = sel->mNext) {
03813       PRInt32 weight = sel->mWeight;
03814       nsPRUint32Key key(weight);
03815       nsAutoVoidArray *rules =
03816         NS_STATIC_CAST(nsAutoVoidArray*, data->mRuleArrays.Get(&key));
03817       if (!rules) {
03818         rules = new nsAutoVoidArray();
03819         if (!rules) return PR_FALSE; // out of memory
03820         data->mRuleArrays.Put(&key, rules);
03821       }
03822       RuleValue *info =
03823         new (data->mArena) RuleValue(styleRule, sel->mSelectors);
03824       rules->AppendElement(info);
03825     }
03826   }
03827   else if (nsICSSRule::MEDIA_RULE == type ||
03828            nsICSSRule::DOCUMENT_RULE == type) {
03829     nsICSSGroupRule* groupRule = (nsICSSGroupRule*)rule;
03830     if (groupRule->UseForPresentation(data->mPresContext))
03831       groupRule->EnumerateRulesForwards(InsertRuleByWeight, aData);
03832   }
03833   return PR_TRUE;
03834 }
03835 
03836 
03837 static PRBool
03838 CascadeSheetRulesInto(nsICSSStyleSheet* aSheet, void* aData)
03839 {
03840   nsCSSStyleSheet*  sheet = NS_STATIC_CAST(nsCSSStyleSheet*, aSheet);
03841   CascadeEnumData* data = NS_STATIC_CAST(CascadeEnumData*, aData);
03842   PRBool bSheetApplicable = PR_TRUE;
03843   sheet->GetApplicable(bSheetApplicable);
03844 
03845   if (bSheetApplicable && sheet->UseForMedium(data->mPresContext)) {
03846     nsCSSStyleSheet* child = sheet->mFirstChild;
03847     while (child) {
03848       CascadeSheetRulesInto(child, data);
03849       child = child->mNext;
03850     }
03851 
03852     if (sheet->mInner && sheet->mInner->mOrderedRules) {
03853       sheet->mInner->mOrderedRules->EnumerateForwards(InsertRuleByWeight, data);
03854     }
03855   }
03856   return PR_TRUE;
03857 }
03858 
03859 struct RuleArrayData {
03860   PRInt32 mWeight;
03861   nsVoidArray* mRuleArray;
03862 };
03863 
03864 PR_STATIC_CALLBACK(int) CompareArrayData(const void* aArg1, const void* aArg2,
03865                                          void* closure)
03866 {
03867   const RuleArrayData* arg1 = NS_STATIC_CAST(const RuleArrayData*, aArg1);
03868   const RuleArrayData* arg2 = NS_STATIC_CAST(const RuleArrayData*, aArg2);
03869   return arg1->mWeight - arg2->mWeight; // put lower weight first
03870 }
03871 
03872 
03873 struct FillArrayData {
03874   FillArrayData(RuleArrayData* aArrayData) :
03875     mIndex(0),
03876     mArrayData(aArrayData)
03877   {
03878   }
03879   PRInt32 mIndex;
03880   RuleArrayData* mArrayData;
03881 };
03882 
03883 PR_STATIC_CALLBACK(PRBool)
03884 FillArray(nsHashKey* aKey, void* aData, void* aClosure)
03885 {
03886   nsPRUint32Key* key = NS_STATIC_CAST(nsPRUint32Key*, aKey);
03887   nsVoidArray* weightArray = NS_STATIC_CAST(nsVoidArray*, aData);
03888   FillArrayData* data = NS_STATIC_CAST(FillArrayData*, aClosure);
03889 
03890   RuleArrayData& ruleData = data->mArrayData[data->mIndex++];
03891   ruleData.mRuleArray = weightArray;
03892   ruleData.mWeight = key->GetValue();
03893 
03894   return PR_TRUE;
03895 }
03896 
03902 static void PutRulesInList(nsObjectHashtable* aRuleArrays,
03903                            nsVoidArray* aWeightedRules)
03904 {
03905   PRInt32 arrayCount = aRuleArrays->Count();
03906   RuleArrayData* arrayData = new RuleArrayData[arrayCount];
03907   FillArrayData faData(arrayData);
03908   aRuleArrays->Enumerate(FillArray, &faData);
03909   NS_QuickSort(arrayData, arrayCount, sizeof(RuleArrayData),
03910                CompareArrayData, nsnull);
03911   for (PRInt32 i = 0; i < arrayCount; ++i)
03912     aWeightedRules->AppendElements(*arrayData[i].mRuleArray);
03913 
03914   delete [] arrayData;
03915 }
03916 
03917 RuleCascadeData*
03918 nsCSSRuleProcessor::GetRuleCascade(nsPresContext* aPresContext)
03919 {
03920   // Having RuleCascadeData objects be per-medium works for now since
03921   // nsCSSRuleProcessor objects are per-document.  (For a given set
03922   // of stylesheets they can vary based on medium (@media) or document
03923   // (@-moz-document).)  Things will get a little more complicated if
03924   // we implement media queries, though.
03925 
03926   RuleCascadeData **cascadep = &mRuleCascades;
03927   RuleCascadeData *cascade;
03928   nsIAtom *medium = aPresContext->Medium();
03929   while ((cascade = *cascadep)) {
03930     if (cascade->mMedium == medium)
03931       return cascade;
03932     cascadep = &cascade->mNext;
03933   }
03934 
03935   if (mSheets.Count() != 0) {
03936     cascade = new RuleCascadeData(medium,
03937                                   eCompatibility_NavQuirks == aPresContext->CompatibilityMode());
03938     if (cascade) {
03939       CascadeEnumData data(aPresContext, cascade->mRuleHash.Arena());
03940       mSheets.EnumerateForwards(CascadeSheetRulesInto, &data);
03941       nsVoidArray weightedRules;
03942       PutRulesInList(&data.mRuleArrays, &weightedRules);
03943 
03944       // Put things into the rule hash backwards because it's easier to
03945       // build a singly linked list lowest-first that way.
03946       if (!weightedRules.EnumerateBackwards(AddRule, cascade)) {
03947         delete cascade;
03948         cascade = nsnull;
03949       }
03950 
03951       *cascadep = cascade;
03952     }
03953   }
03954   return cascade;
03955 }