content/base/src/nsContentList.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=78: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     8  * nsBaseContentList is a basic list of content nodes; nsContentList
     9  * is a commonly used NodeList implementation (used for
    10  * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc).
    11  */
    13 #include "nsContentList.h"
    14 #include "nsIContent.h"
    15 #include "nsIDOMNode.h"
    16 #include "nsIDocument.h"
    17 #include "mozilla/dom/Element.h"
    18 #include "nsWrapperCacheInlines.h"
    19 #include "nsContentUtils.h"
    20 #include "nsCCUncollectableMarker.h"
    21 #include "nsGkAtoms.h"
    22 #include "mozilla/dom/HTMLCollectionBinding.h"
    23 #include "mozilla/dom/NodeListBinding.h"
    24 #include "mozilla/Likely.h"
    25 #include "nsGenericHTMLElement.h"
    26 #include "jsfriendapi.h"
    27 #include <algorithm>
    29 // Form related includes
    30 #include "nsIDOMHTMLFormElement.h"
    32 #include "pldhash.h"
    34 #ifdef DEBUG_CONTENT_LIST
    35 #include "nsIContentIterator.h"
    36 #define ASSERT_IN_SYNC AssertInSync()
    37 #else
    38 #define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO
    39 #endif
    41 using namespace mozilla;
    42 using namespace mozilla::dom;
    44 nsBaseContentList::~nsBaseContentList()
    45 {
    46 }
    48 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList)
    49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
    50   NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
    51   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    52   tmp->RemoveFromCaches();
    53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
    55   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
    56   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
    57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    58 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsBaseContentList)
    60 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList)
    61   if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
    62     for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) {
    63       nsIContent* c = tmp->mElements[i];
    64       if (c->IsPurple()) {
    65         c->RemovePurple();
    66       }
    67       Element::MarkNodeChildren(c);
    68     }
    69     return true;
    70   }
    71 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
    73 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList)
    74   return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
    75 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
    77 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList)
    78   return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
    79 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
    81 #define NS_CONTENT_LIST_INTERFACES(_class)                                    \
    82     NS_INTERFACE_TABLE_ENTRY(_class, nsINodeList)                             \
    83     NS_INTERFACE_TABLE_ENTRY(_class, nsIDOMNodeList)
    85 // QueryInterface implementation for nsBaseContentList
    86 NS_INTERFACE_TABLE_HEAD(nsBaseContentList)
    87   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    88   NS_INTERFACE_TABLE(nsBaseContentList, nsINodeList, nsIDOMNodeList)
    89   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList)
    90 NS_INTERFACE_MAP_END
    93 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList)
    94 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBaseContentList)
    97 NS_IMETHODIMP
    98 nsBaseContentList::GetLength(uint32_t* aLength)
    99 {
   100   *aLength = mElements.Length();
   102   return NS_OK;
   103 }
   105 NS_IMETHODIMP
   106 nsBaseContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
   107 {
   108   nsISupports *tmp = Item(aIndex);
   110   if (!tmp) {
   111     *aReturn = nullptr;
   113     return NS_OK;
   114   }
   116   return CallQueryInterface(tmp, aReturn);
   117 }
   119 nsIContent*
   120 nsBaseContentList::Item(uint32_t aIndex)
   121 {
   122   return mElements.SafeElementAt(aIndex);
   123 }
   126 int32_t
   127 nsBaseContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
   128 {
   129   return mElements.IndexOf(aContent);
   130 }
   132 int32_t
   133 nsBaseContentList::IndexOf(nsIContent* aContent)
   134 {
   135   return IndexOf(aContent, true);
   136 }
   138 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList, nsBaseContentList,
   139                                    mRoot)
   141 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsSimpleContentList)
   142 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
   145 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
   146 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
   148 JSObject*
   149 nsSimpleContentList::WrapObject(JSContext *cx)
   150 {
   151   return NodeListBinding::Wrap(cx, this);
   152 }
   154 // Hashtable for storing nsContentLists
   155 static PLDHashTable gContentListHashTable;
   157 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
   158 static nsContentList*
   159   sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {};
   161 static MOZ_ALWAYS_INLINE uint32_t
   162 RecentlyUsedCacheIndex(const nsContentListKey& aKey)
   163 {
   164   return aKey.GetHash() % RECENTLY_USED_CONTENT_LIST_CACHE_SIZE;
   165 }
   167 struct ContentListHashEntry : public PLDHashEntryHdr
   168 {
   169   nsContentList* mContentList;
   170 };
   172 static PLDHashNumber
   173 ContentListHashtableHashKey(PLDHashTable *table, const void *key)
   174 {
   175   const nsContentListKey* list = static_cast<const nsContentListKey *>(key);
   176   return list->GetHash();
   177 }
   179 static bool
   180 ContentListHashtableMatchEntry(PLDHashTable *table,
   181                                const PLDHashEntryHdr *entry,
   182                                const void *key)
   183 {
   184   const ContentListHashEntry *e =
   185     static_cast<const ContentListHashEntry *>(entry);
   186   const nsContentList* list = e->mContentList;
   187   const nsContentListKey* ourKey = static_cast<const nsContentListKey *>(key);
   189   return list->MatchesKey(*ourKey);
   190 }
   192 already_AddRefed<nsContentList>
   193 NS_GetContentList(nsINode* aRootNode, 
   194                   int32_t  aMatchNameSpaceId,
   195                   const nsAString& aTagname)
   196 {
   197   NS_ASSERTION(aRootNode, "content list has to have a root");
   199   nsRefPtr<nsContentList> list;
   200   nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname);
   201   uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(hashKey);
   202   nsContentList* cachedList = sRecentlyUsedContentLists[recentlyUsedCacheIndex];
   203   if (cachedList && cachedList->MatchesKey(hashKey)) {
   204     list = cachedList;
   205     return list.forget();
   206   }
   208   static const PLDHashTableOps hash_table_ops =
   209   {
   210     PL_DHashAllocTable,
   211     PL_DHashFreeTable,
   212     ContentListHashtableHashKey,
   213     ContentListHashtableMatchEntry,
   214     PL_DHashMoveEntryStub,
   215     PL_DHashClearEntryStub,
   216     PL_DHashFinalizeStub
   217   };
   219   // Initialize the hashtable if needed.
   220   if (!gContentListHashTable.ops) {
   221     PL_DHashTableInit(&gContentListHashTable,
   222                       &hash_table_ops, nullptr,
   223                       sizeof(ContentListHashEntry),
   224                       16);
   225   }
   227   ContentListHashEntry *entry = nullptr;
   228   // First we look in our hashtable.  Then we create a content list if needed
   229   if (gContentListHashTable.ops) {
   231     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
   232     // when the entry is already in the hashtable.
   233     entry = static_cast<ContentListHashEntry *>
   234                        (PL_DHashTableOperate(&gContentListHashTable,
   235                                              &hashKey,
   236                                              PL_DHASH_ADD));
   237     if (entry)
   238       list = entry->mContentList;
   239   }
   241   if (!list) {
   242     // We need to create a ContentList and add it to our new entry, if
   243     // we have an entry
   244     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
   245     nsCOMPtr<nsIAtom> htmlAtom;
   246     if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
   247       nsAutoString lowercaseName;
   248       nsContentUtils::ASCIIToLower(aTagname, lowercaseName);
   249       htmlAtom = do_GetAtom(lowercaseName);
   250     } else {
   251       htmlAtom = xmlAtom;
   252     }
   253     list = new nsContentList(aRootNode, aMatchNameSpaceId,
   254                              htmlAtom, xmlAtom);
   255     if (entry) {
   256       entry->mContentList = list;
   257     }
   258   }
   260   sRecentlyUsedContentLists[recentlyUsedCacheIndex] = list;
   261   return list.forget();
   262 }
   264 #ifdef DEBUG
   265 const nsCacheableFuncStringContentList::ContentListType
   266   nsCacheableFuncStringNodeList::sType = nsCacheableFuncStringContentList::eNodeList;
   267 const nsCacheableFuncStringContentList::ContentListType
   268   nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
   269 #endif
   271 JSObject*
   272 nsCacheableFuncStringNodeList::WrapObject(JSContext *cx)
   273 {
   274   return NodeListBinding::Wrap(cx, this);
   275 }
   278 JSObject*
   279 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx)
   280 {
   281   return HTMLCollectionBinding::Wrap(cx, this);
   282 }
   284 // Hashtable for storing nsCacheableFuncStringContentList
   285 static PLDHashTable gFuncStringContentListHashTable;
   287 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
   288 {
   289   nsCacheableFuncStringContentList* mContentList;
   290 };
   292 static PLDHashNumber
   293 FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
   294 {
   295   const nsFuncStringCacheKey* funcStringKey =
   296     static_cast<const nsFuncStringCacheKey *>(key);
   297   return funcStringKey->GetHash();
   298 }
   300 static bool
   301 FuncStringContentListHashtableMatchEntry(PLDHashTable *table,
   302                                const PLDHashEntryHdr *entry,
   303                                const void *key)
   304 {
   305   const FuncStringContentListHashEntry *e =
   306     static_cast<const FuncStringContentListHashEntry *>(entry);
   307   const nsFuncStringCacheKey* ourKey =
   308     static_cast<const nsFuncStringCacheKey *>(key);
   310   return e->mContentList->Equals(ourKey);
   311 }
   313 template<class ListType>
   314 already_AddRefed<nsContentList>
   315 GetFuncStringContentList(nsINode* aRootNode,
   316                          nsContentListMatchFunc aFunc,
   317                          nsContentListDestroyFunc aDestroyFunc,
   318                          nsFuncStringContentListDataAllocator aDataAllocator,
   319                          const nsAString& aString)
   320 {
   321   NS_ASSERTION(aRootNode, "content list has to have a root");
   323   nsRefPtr<nsCacheableFuncStringContentList> list;
   325   static const PLDHashTableOps hash_table_ops =
   326   {
   327     PL_DHashAllocTable,
   328     PL_DHashFreeTable,
   329     FuncStringContentListHashtableHashKey,
   330     FuncStringContentListHashtableMatchEntry,
   331     PL_DHashMoveEntryStub,
   332     PL_DHashClearEntryStub,
   333     PL_DHashFinalizeStub
   334   };
   336   // Initialize the hashtable if needed.
   337   if (!gFuncStringContentListHashTable.ops) {
   338     PL_DHashTableInit(&gFuncStringContentListHashTable,
   339                       &hash_table_ops, nullptr,
   340                       sizeof(FuncStringContentListHashEntry),
   341                       16);
   342   }
   344   FuncStringContentListHashEntry *entry = nullptr;
   345   // First we look in our hashtable.  Then we create a content list if needed
   346   if (gFuncStringContentListHashTable.ops) {
   347     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
   349     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
   350     // when the entry is already in the hashtable.
   351     entry = static_cast<FuncStringContentListHashEntry *>
   352                        (PL_DHashTableOperate(&gFuncStringContentListHashTable,
   353                                              &hashKey,
   354                                              PL_DHASH_ADD));
   355     if (entry) {
   356       list = entry->mContentList;
   357 #ifdef DEBUG
   358       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
   359 #endif
   360     }
   361   }
   363   if (!list) {
   364     // We need to create a ContentList and add it to our new entry, if
   365     // we have an entry
   366     list = new ListType(aRootNode, aFunc, aDestroyFunc, aDataAllocator,
   367                         aString);
   368     if (entry) {
   369       entry->mContentList = list;
   370     }
   371   }
   373   // Don't cache these lists globally
   375   return list.forget();
   376 }
   378 already_AddRefed<nsContentList>
   379 NS_GetFuncStringNodeList(nsINode* aRootNode,
   380                          nsContentListMatchFunc aFunc,
   381                          nsContentListDestroyFunc aDestroyFunc,
   382                          nsFuncStringContentListDataAllocator aDataAllocator,
   383                          const nsAString& aString)
   384 {
   385   return GetFuncStringContentList<nsCacheableFuncStringNodeList>(aRootNode,
   386                                                                  aFunc,
   387                                                                  aDestroyFunc,
   388                                                                  aDataAllocator,
   389                                                                  aString);
   390 }
   392 already_AddRefed<nsContentList>
   393 NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
   394                                nsContentListMatchFunc aFunc,
   395                                nsContentListDestroyFunc aDestroyFunc,
   396                                nsFuncStringContentListDataAllocator aDataAllocator,
   397                                const nsAString& aString)
   398 {
   399   return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(aRootNode,
   400                                                                        aFunc,
   401                                                                        aDestroyFunc,
   402                                                                        aDataAllocator,
   403                                                                        aString);
   404 }
   406 // nsContentList implementation
   408 nsContentList::nsContentList(nsINode* aRootNode,
   409                              int32_t aMatchNameSpaceId,
   410                              nsIAtom* aHTMLMatchAtom,
   411                              nsIAtom* aXMLMatchAtom,
   412                              bool aDeep)
   413   : nsBaseContentList(),
   414     mRootNode(aRootNode),
   415     mMatchNameSpaceId(aMatchNameSpaceId),
   416     mHTMLMatchAtom(aHTMLMatchAtom),
   417     mXMLMatchAtom(aXMLMatchAtom),
   418     mFunc(nullptr),
   419     mDestroyFunc(nullptr),
   420     mData(nullptr),
   421     mState(LIST_DIRTY),
   422     mDeep(aDeep),
   423     mFuncMayDependOnAttr(false)
   424 {
   425   NS_ASSERTION(mRootNode, "Must have root");
   426   if (nsGkAtoms::_asterix == mHTMLMatchAtom) {
   427     NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterix, "HTML atom and XML atom are not both asterix?");
   428     mMatchAll = true;
   429   }
   430   else {
   431     mMatchAll = false;
   432   }
   433   mRootNode->AddMutationObserver(this);
   435   // We only need to flush if we're in an non-HTML document, since the
   436   // HTML5 parser doesn't need flushing.  Further, if we're not in a
   437   // document at all right now (in the GetCurrentDoc() sense), we're
   438   // not parser-created and don't need to be flushing stuff under us
   439   // to get our kids right.
   440   nsIDocument* doc = mRootNode->GetCurrentDoc();
   441   mFlushesNeeded = doc && !doc->IsHTML();
   442 }
   444 nsContentList::nsContentList(nsINode* aRootNode,
   445                              nsContentListMatchFunc aFunc,
   446                              nsContentListDestroyFunc aDestroyFunc,
   447                              void* aData,
   448                              bool aDeep,
   449                              nsIAtom* aMatchAtom,
   450                              int32_t aMatchNameSpaceId,
   451                              bool aFuncMayDependOnAttr)
   452   : nsBaseContentList(),
   453     mRootNode(aRootNode),
   454     mMatchNameSpaceId(aMatchNameSpaceId),
   455     mHTMLMatchAtom(aMatchAtom),
   456     mXMLMatchAtom(aMatchAtom),
   457     mFunc(aFunc),
   458     mDestroyFunc(aDestroyFunc),
   459     mData(aData),
   460     mState(LIST_DIRTY),
   461     mMatchAll(false),
   462     mDeep(aDeep),
   463     mFuncMayDependOnAttr(aFuncMayDependOnAttr)
   464 {
   465   NS_ASSERTION(mRootNode, "Must have root");
   466   mRootNode->AddMutationObserver(this);
   468   // We only need to flush if we're in an non-HTML document, since the
   469   // HTML5 parser doesn't need flushing.  Further, if we're not in a
   470   // document at all right now (in the GetCurrentDoc() sense), we're
   471   // not parser-created and don't need to be flushing stuff under us
   472   // to get our kids right.
   473   nsIDocument* doc = mRootNode->GetCurrentDoc();
   474   mFlushesNeeded = doc && !doc->IsHTML();
   475 }
   477 nsContentList::~nsContentList()
   478 {
   479   RemoveFromHashtable();
   480   if (mRootNode) {
   481     mRootNode->RemoveMutationObserver(this);
   482   }
   484   if (mDestroyFunc) {
   485     // Clean up mData
   486     (*mDestroyFunc)(mData);
   487   }
   488 }
   490 JSObject*
   491 nsContentList::WrapObject(JSContext *cx)
   492 {
   493   return HTMLCollectionBinding::Wrap(cx, this);
   494 }
   496 NS_IMPL_ISUPPORTS_INHERITED(nsContentList, nsBaseContentList,
   497                             nsIHTMLCollection, nsIDOMHTMLCollection,
   498                             nsIMutationObserver)
   500 uint32_t
   501 nsContentList::Length(bool aDoFlush)
   502 {
   503   BringSelfUpToDate(aDoFlush);
   505   return mElements.Length();
   506 }
   508 nsIContent *
   509 nsContentList::Item(uint32_t aIndex, bool aDoFlush)
   510 {
   511   if (mRootNode && aDoFlush && mFlushesNeeded) {
   512     // XXX sXBL/XBL2 issue
   513     nsIDocument* doc = mRootNode->GetCurrentDoc();
   514     if (doc) {
   515       // Flush pending content changes Bug 4891.
   516       doc->FlushPendingNotifications(Flush_ContentAndNotify);
   517     }
   518   }
   520   if (mState != LIST_UP_TO_DATE)
   521     PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1);
   523   ASSERT_IN_SYNC;
   524   NS_ASSERTION(!mRootNode || mState != LIST_DIRTY,
   525                "PopulateSelf left the list in a dirty (useless) state!");
   527   return mElements.SafeElementAt(aIndex);
   528 }
   530 Element*
   531 nsContentList::NamedItem(const nsAString& aName, bool aDoFlush)
   532 {
   533   BringSelfUpToDate(aDoFlush);
   535   uint32_t i, count = mElements.Length();
   537   // Typically IDs and names are atomized
   538   nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
   539   NS_ENSURE_TRUE(name, nullptr);
   541   for (i = 0; i < count; i++) {
   542     nsIContent *content = mElements[i];
   543     // XXX Should this pass eIgnoreCase?
   544     if (content &&
   545         (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
   546                               name, eCaseMatters) ||
   547          content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
   548                               name, eCaseMatters))) {
   549       return content->AsElement();
   550     }
   551   }
   553   return nullptr;
   554 }
   556 void
   557 nsContentList::GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames)
   558 {
   559   if (!(aFlags & JSITER_HIDDEN)) {
   560     return;
   561   }
   563   BringSelfUpToDate(true);
   565   nsAutoTArray<nsIAtom*, 8> atoms;
   566   for (uint32_t i = 0; i < mElements.Length(); ++i) {
   567     nsIContent *content = mElements.ElementAt(i);
   568     nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(content);
   569     if (el) {
   570       // XXXbz should we be checking for particular tags here?  How
   571       // stable is this part of the spec?
   572       // Note: nsINode::HasName means the name is exposed on the document,
   573       // which is false for options, so we don't check it here.
   574       const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
   575       if (val && val->Type() == nsAttrValue::eAtom) {
   576         nsIAtom* name = val->GetAtomValue();
   577         if (!atoms.Contains(name)) {
   578           atoms.AppendElement(name);
   579         }
   580       }
   581     }
   582     if (content->HasID()) {
   583       nsIAtom* id = content->GetID();
   584       if (!atoms.Contains(id)) {
   585         atoms.AppendElement(id);
   586       }
   587     }
   588   }
   590   aNames.SetCapacity(atoms.Length());
   591   for (uint32_t i = 0; i < atoms.Length(); ++i) {
   592     aNames.AppendElement(nsDependentAtomString(atoms[i]));
   593   }
   594 }
   596 int32_t
   597 nsContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
   598 {
   599   BringSelfUpToDate(aDoFlush);
   601   return mElements.IndexOf(aContent);
   602 }
   604 int32_t
   605 nsContentList::IndexOf(nsIContent* aContent)
   606 {
   607   return IndexOf(aContent, true);
   608 }
   610 void
   611 nsContentList::NodeWillBeDestroyed(const nsINode* aNode)
   612 {
   613   // We shouldn't do anything useful from now on
   615   RemoveFromCaches();
   616   mRootNode = nullptr;
   618   // We will get no more updates, so we can never know we're up to
   619   // date
   620   SetDirty();
   621 }
   623 NS_IMETHODIMP
   624 nsContentList::GetLength(uint32_t* aLength)
   625 {
   626   *aLength = Length(true);
   628   return NS_OK;
   629 }
   631 NS_IMETHODIMP
   632 nsContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
   633 {
   634   nsINode* node = Item(aIndex);
   636   if (node) {
   637     return CallQueryInterface(node, aReturn);
   638   }
   640   *aReturn = nullptr;
   642   return NS_OK;
   643 }
   645 NS_IMETHODIMP
   646 nsContentList::NamedItem(const nsAString& aName, nsIDOMNode** aReturn)
   647 {
   648   nsIContent *content = NamedItem(aName, true);
   650   if (content) {
   651     return CallQueryInterface(content, aReturn);
   652   }
   654   *aReturn = nullptr;
   656   return NS_OK;
   657 }
   659 Element*
   660 nsContentList::GetElementAt(uint32_t aIndex)
   661 {
   662   return static_cast<Element*>(Item(aIndex, true));
   663 }
   665 nsIContent*
   666 nsContentList::Item(uint32_t aIndex)
   667 {
   668   return GetElementAt(aIndex);
   669 }
   671 void
   672 nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement,
   673                                 int32_t aNameSpaceID, nsIAtom* aAttribute,
   674                                 int32_t aModType)
   675 {
   676   NS_PRECONDITION(aElement, "Must have a content node to work with");
   678   if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
   679       !MayContainRelevantNodes(aElement->GetParentNode()) ||
   680       !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
   681     // Either we're already dirty or this notification doesn't affect
   682     // whether we might match aElement.
   683     return;
   684   }
   686   if (Match(aElement)) {
   687     if (mElements.IndexOf(aElement) == mElements.NoIndex) {
   688       // We match aElement now, and it's not in our list already.  Just dirty
   689       // ourselves; this is simpler than trying to figure out where to insert
   690       // aElement.
   691       SetDirty();
   692     }
   693   } else {
   694     // We no longer match aElement.  Remove it from our list.  If it's
   695     // already not there, this is a no-op (though a potentially
   696     // expensive one).  Either way, no change of mState is required
   697     // here.
   698     mElements.RemoveElement(aElement);
   699   }
   700 }
   702 void
   703 nsContentList::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
   704                                nsIContent* aFirstNewContent,
   705                                int32_t aNewIndexInContainer)
   706 {
   707   NS_PRECONDITION(aContainer, "Can't get at the new content if no container!");
   709   /*
   710    * If the state is LIST_DIRTY then we have no useful information in our list
   711    * and we want to put off doing work as much as possible.
   712    *
   713    * Also, if aContainer is anonymous from our point of view, we know that we
   714    * can't possibly be matching any of the kids.
   715    *
   716    * Optimize out also the common case when just one new node is appended and
   717    * it doesn't match us.
   718    */
   719   if (mState == LIST_DIRTY ||
   720       !nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer) ||
   721       !MayContainRelevantNodes(aContainer) ||
   722       (!aFirstNewContent->HasChildren() &&
   723        !aFirstNewContent->GetNextSibling() &&
   724        !MatchSelf(aFirstNewContent))) {
   725     return;
   726   }
   728   /*
   729    * We want to handle the case of ContentAppended by sometimes
   730    * appending the content to our list, not just setting state to
   731    * LIST_DIRTY, since most of our ContentAppended notifications
   732    * should come during pageload and be at the end of the document.
   733    * Do a bit of work to see whether we could just append to what we
   734    * already have.
   735    */
   737   int32_t count = aContainer->GetChildCount();
   739   if (count > 0) {
   740     uint32_t ourCount = mElements.Length();
   741     bool appendToList = false;
   742     if (ourCount == 0) {
   743       appendToList = true;
   744     } else {
   745       nsIContent* ourLastContent = mElements[ourCount - 1];
   746       /*
   747        * We want to append instead of invalidating if the first thing
   748        * that got appended comes after ourLastContent.
   749        */
   750       if (nsContentUtils::PositionIsBefore(ourLastContent, aFirstNewContent)) {
   751         appendToList = true;
   752       }
   753     }
   756     if (!appendToList) {
   757       // The new stuff is somewhere in the middle of our list; check
   758       // whether we need to invalidate
   759       for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
   760         if (MatchSelf(cur)) {
   761           // Uh-oh.  We're gonna have to add elements into the middle
   762           // of our list. That's not worth the effort.
   763           SetDirty();
   764           break;
   765         }
   766       }
   768       ASSERT_IN_SYNC;
   769       return;
   770     }
   772     /*
   773      * At this point we know we could append.  If we're not up to
   774      * date, however, that would be a bad idea -- it could miss some
   775      * content that we never picked up due to being lazy.  Further, we
   776      * may never get asked for this content... so don't grab it yet.
   777      */
   778     if (mState == LIST_LAZY) // be lazy
   779       return;
   781     /*
   782      * We're up to date.  That means someone's actively using us; we
   783      * may as well grab this content....
   784      */
   785     if (mDeep) {
   786       for (nsIContent* cur = aFirstNewContent;
   787            cur;
   788            cur = cur->GetNextNode(aContainer)) {
   789         if (cur->IsElement() && Match(cur->AsElement())) {
   790           mElements.AppendElement(cur);
   791         }
   792       }
   793     } else {
   794       for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
   795         if (cur->IsElement() && Match(cur->AsElement())) {
   796           mElements.AppendElement(cur);
   797         }
   798       }
   799     }
   801     ASSERT_IN_SYNC;
   802   }
   803 }
   805 void
   806 nsContentList::ContentInserted(nsIDocument *aDocument,
   807                                nsIContent* aContainer,
   808                                nsIContent* aChild,
   809                                int32_t aIndexInContainer)
   810 {
   811   // Note that aContainer can be null here if we are inserting into
   812   // the document itself; any attempted optimizations to this method
   813   // should deal with that.
   814   if (mState != LIST_DIRTY &&
   815       MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
   816       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
   817       MatchSelf(aChild)) {
   818     SetDirty();
   819   }
   821   ASSERT_IN_SYNC;
   822 }
   824 void
   825 nsContentList::ContentRemoved(nsIDocument *aDocument,
   826                               nsIContent* aContainer,
   827                               nsIContent* aChild,
   828                               int32_t aIndexInContainer,
   829                               nsIContent* aPreviousSibling)
   830 {
   831   // Note that aContainer can be null here if we are removing from
   832   // the document itself; any attempted optimizations to this method
   833   // should deal with that.
   834   if (mState != LIST_DIRTY &&
   835       MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
   836       nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
   837       MatchSelf(aChild)) {
   838     SetDirty();
   839   }
   841   ASSERT_IN_SYNC;
   842 }
   844 bool
   845 nsContentList::Match(Element *aElement)
   846 {
   847   if (mFunc) {
   848     return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData);
   849   }
   851   if (!mXMLMatchAtom)
   852     return false;
   854   nsINodeInfo *ni = aElement->NodeInfo();
   856   bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
   857   bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
   858   bool toReturn = mMatchAll;
   859   if (!unknown && !wildcard)
   860     toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
   862   if (toReturn)
   863     return toReturn;
   865   bool matchHTML = aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
   866     aElement->OwnerDoc()->IsHTML();
   868   if (unknown) {
   869     return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) :
   870                        ni->QualifiedNameEquals(mXMLMatchAtom);
   871   }
   873   if (wildcard) {
   874     return matchHTML ? ni->Equals(mHTMLMatchAtom) :
   875                        ni->Equals(mXMLMatchAtom);
   876   }
   878   return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId) :
   879                      ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
   880 }
   882 bool 
   883 nsContentList::MatchSelf(nsIContent *aContent)
   884 {
   885   NS_PRECONDITION(aContent, "Can't match null stuff, you know");
   886   NS_PRECONDITION(mDeep || aContent->GetParentNode() == mRootNode,
   887                   "MatchSelf called on a node that we can't possibly match");
   889   if (!aContent->IsElement()) {
   890     return false;
   891   }
   893   if (Match(aContent->AsElement()))
   894     return true;
   896   if (!mDeep)
   897     return false;
   899   for (nsIContent* cur = aContent->GetFirstChild();
   900        cur;
   901        cur = cur->GetNextNode(aContent)) {
   902     if (cur->IsElement() && Match(cur->AsElement())) {
   903       return true;
   904     }
   905   }
   907   return false;
   908 }
   910 void 
   911 nsContentList::PopulateSelf(uint32_t aNeededLength)
   912 {
   913   if (!mRootNode) {
   914     return;
   915   }
   917   ASSERT_IN_SYNC;
   919   uint32_t count = mElements.Length();
   920   NS_ASSERTION(mState != LIST_DIRTY || count == 0,
   921                "Reset() not called when setting state to LIST_DIRTY?");
   923   if (count >= aNeededLength) // We're all set
   924     return;
   926   uint32_t elementsToAppend = aNeededLength - count;
   927 #ifdef DEBUG
   928   uint32_t invariant = elementsToAppend + mElements.Length();
   929 #endif
   931   if (mDeep) {
   932     // If we already have nodes start searching at the last one, otherwise
   933     // start searching at the root.
   934     nsINode* cur = count ? mElements[count - 1] : mRootNode;
   935     do {
   936       cur = cur->GetNextNode(mRootNode);
   937       if (!cur) {
   938         break;
   939       }
   940       if (cur->IsElement() && Match(cur->AsElement())) {
   941         // Append AsElement() to get nsIContent instead of nsINode
   942         mElements.AppendElement(cur->AsElement());
   943         --elementsToAppend;
   944       }
   945     } while (elementsToAppend);
   946   } else {
   947     nsIContent* cur =
   948       count ? mElements[count-1]->GetNextSibling() : mRootNode->GetFirstChild();
   949     for ( ; cur && elementsToAppend; cur = cur->GetNextSibling()) {
   950       if (cur->IsElement() && Match(cur->AsElement())) {
   951         mElements.AppendElement(cur);
   952         --elementsToAppend;
   953       }
   954     }
   955   }
   957   NS_ASSERTION(elementsToAppend + mElements.Length() == invariant,
   958                "Something is awry!");
   960   if (elementsToAppend != 0)
   961     mState = LIST_UP_TO_DATE;
   962   else
   963     mState = LIST_LAZY;
   965   ASSERT_IN_SYNC;
   966 }
   968 void
   969 nsContentList::RemoveFromHashtable()
   970 {
   971   if (mFunc) {
   972     // This can't be in the table anyway
   973     return;
   974   }
   976   nsDependentAtomString str(mXMLMatchAtom);
   977   nsContentListKey key(mRootNode, mMatchNameSpaceId, str);
   978   uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
   979   if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
   980     sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
   981   }
   983   if (!gContentListHashTable.ops)
   984     return;
   986   PL_DHashTableOperate(&gContentListHashTable,
   987                        &key,
   988                        PL_DHASH_REMOVE);
   990   if (gContentListHashTable.entryCount == 0) {
   991     PL_DHashTableFinish(&gContentListHashTable);
   992     gContentListHashTable.ops = nullptr;
   993   }
   994 }
   996 void
   997 nsContentList::BringSelfUpToDate(bool aDoFlush)
   998 {
   999   if (mRootNode && aDoFlush && mFlushesNeeded) {
  1000     // XXX sXBL/XBL2 issue
  1001     nsIDocument* doc = mRootNode->GetCurrentDoc();
  1002     if (doc) {
  1003       // Flush pending content changes Bug 4891.
  1004       doc->FlushPendingNotifications(Flush_ContentAndNotify);
  1008   if (mState != LIST_UP_TO_DATE)
  1009     PopulateSelf(uint32_t(-1));
  1011   ASSERT_IN_SYNC;
  1012   NS_ASSERTION(!mRootNode || mState == LIST_UP_TO_DATE,
  1013                "PopulateSelf dod not bring content list up to date!");
  1016 nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
  1018   RemoveFromFuncStringHashtable();
  1021 void
  1022 nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
  1024   if (!gFuncStringContentListHashTable.ops) {
  1025     return;
  1028   nsFuncStringCacheKey key(mRootNode, mFunc, mString);
  1029   PL_DHashTableOperate(&gFuncStringContentListHashTable,
  1030                        &key,
  1031                        PL_DHASH_REMOVE);
  1033   if (gFuncStringContentListHashTable.entryCount == 0) {
  1034     PL_DHashTableFinish(&gFuncStringContentListHashTable);
  1035     gFuncStringContentListHashTable.ops = nullptr;
  1039 #ifdef DEBUG_CONTENT_LIST
  1040 void
  1041 nsContentList::AssertInSync()
  1043   if (mState == LIST_DIRTY) {
  1044     return;
  1047   if (!mRootNode) {
  1048     NS_ASSERTION(mElements.Length() == 0 && mState == LIST_DIRTY,
  1049                  "Empty iterator isn't quite empty?");
  1050     return;
  1053   // XXX This code will need to change if nsContentLists can ever match
  1054   // elements that are outside of the document element.
  1055   nsIContent *root;
  1056   if (mRootNode->IsNodeOfType(nsINode::eDOCUMENT)) {
  1057     root = static_cast<nsIDocument*>(mRootNode)->GetRootElement();
  1059   else {
  1060     root = static_cast<nsIContent*>(mRootNode);
  1063   nsCOMPtr<nsIContentIterator> iter;
  1064   if (mDeep) {
  1065     iter = NS_NewPreContentIterator();
  1066     iter->Init(root);
  1067     iter->First();
  1070   uint32_t cnt = 0, index = 0;
  1071   while (true) {
  1072     if (cnt == mElements.Length() && mState == LIST_LAZY) {
  1073       break;
  1076     nsIContent *cur = mDeep ? iter->GetCurrentNode() :
  1077                               mRootNode->GetChildAt(index++);
  1078     if (!cur) {
  1079       break;
  1082     if (cur->IsElement() && Match(cur->AsElement())) {
  1083       NS_ASSERTION(cnt < mElements.Length() && mElements[cnt] == cur,
  1084                    "Elements is out of sync");
  1085       ++cnt;
  1088     if (mDeep) {
  1089       iter->Next();
  1093   NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
  1095 #endif

mercurial