tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsContentList.cpp (37341B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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/. */
      6 
      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 HTMLDocument/Document, etc).
     11 */
     12 
     13 #include "nsContentList.h"
     14 
     15 #include <algorithm>
     16 
     17 #include "PLDHashTable.h"
     18 #include "jsfriendapi.h"
     19 #include "mozilla/ContentIterator.h"
     20 #include "mozilla/MruCache.h"
     21 #include "mozilla/StaticPtr.h"
     22 #include "mozilla/dom/Document.h"
     23 #include "mozilla/dom/Element.h"
     24 #include "mozilla/dom/HTMLCollectionBinding.h"
     25 #include "mozilla/dom/NodeInfoInlines.h"
     26 #include "mozilla/dom/NodeListBinding.h"
     27 #include "nsCCUncollectableMarker.h"
     28 #include "nsContentUtils.h"
     29 #include "nsGenericHTMLElement.h"
     30 #include "nsGkAtoms.h"
     31 #include "nsIContent.h"
     32 #include "nsTHashtable.h"
     33 #include "nsWrapperCacheInlines.h"
     34 
     35 #ifdef DEBUG_CONTENT_LIST
     36 #  define ASSERT_IN_SYNC AssertInSync()
     37 #else
     38 #  define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO
     39 #endif
     40 
     41 using namespace mozilla;
     42 using namespace mozilla::dom;
     43 
     44 nsBaseContentList::~nsBaseContentList() = default;
     45 
     46 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(nsBaseContentList)
     47 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
     48  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
     49  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
     50  tmp->RemoveFromCaches();
     51 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     52 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
     53  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
     54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     55 
     56 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList)
     57  if (nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper()) {
     58    for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) {
     59      nsIContent* c = tmp->mElements[i];
     60      if (c->IsPurple()) {
     61        c->RemovePurple();
     62      }
     63      Element::MarkNodeChildren(c);
     64    }
     65    return true;
     66  }
     67 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
     68 
     69 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList)
     70  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
     71 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
     72 
     73 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList)
     74  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
     75 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
     76 
     77 // QueryInterface implementation for nsBaseContentList
     78 NS_INTERFACE_TABLE_HEAD(nsBaseContentList)
     79  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
     80  NS_INTERFACE_TABLE(nsBaseContentList, nsINodeList)
     81  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList)
     82 NS_INTERFACE_MAP_END
     83 
     84 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList)
     85 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsBaseContentList,
     86                                                   LastRelease())
     87 
     88 nsIContent* nsBaseContentList::Item(uint32_t aIndex) {
     89  return mElements.SafeElementAt(aIndex);
     90 }
     91 
     92 int32_t nsBaseContentList::IndexOf(nsIContent* aContent, bool aDoFlush) {
     93  return mElements.IndexOf(aContent);
     94 }
     95 
     96 int32_t nsBaseContentList::IndexOf(nsIContent* aContent) {
     97  return IndexOf(aContent, true);
     98 }
     99 
    100 size_t nsBaseContentList::SizeOfIncludingThis(
    101    MallocSizeOf aMallocSizeOf) const {
    102  size_t n = aMallocSizeOf(this);
    103  n += mElements.ShallowSizeOfExcludingThis(aMallocSizeOf);
    104  return n;
    105 }
    106 
    107 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList, nsBaseContentList,
    108                                   mRoot)
    109 
    110 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSimpleContentList)
    111 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
    112 
    113 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
    114 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
    115 
    116 JSObject* nsSimpleContentList::WrapObject(JSContext* cx,
    117                                          JS::Handle<JSObject*> aGivenProto) {
    118  return NodeList_Binding::Wrap(cx, this, aGivenProto);
    119 }
    120 
    121 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsEmptyContentList, nsBaseContentList, mRoot)
    122 
    123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEmptyContentList)
    124  NS_INTERFACE_MAP_ENTRY(nsIHTMLCollection)
    125 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
    126 
    127 NS_IMPL_ADDREF_INHERITED(nsEmptyContentList, nsBaseContentList)
    128 NS_IMPL_RELEASE_INHERITED(nsEmptyContentList, nsBaseContentList)
    129 
    130 JSObject* nsEmptyContentList::WrapObject(JSContext* cx,
    131                                         JS::Handle<JSObject*> aGivenProto) {
    132  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
    133 }
    134 
    135 mozilla::dom::Element* nsEmptyContentList::GetElementAt(uint32_t index) {
    136  return nullptr;
    137 }
    138 
    139 mozilla::dom::Element* nsEmptyContentList::GetFirstNamedElement(
    140    const nsAString& aName, bool& aFound) {
    141  aFound = false;
    142  return nullptr;
    143 }
    144 
    145 void nsEmptyContentList::GetSupportedNames(nsTArray<nsString>& aNames) {}
    146 
    147 nsIContent* nsEmptyContentList::Item(uint32_t aIndex) { return nullptr; }
    148 
    149 struct ContentListCache
    150    : public MruCache<nsContentListKey, nsContentList*, ContentListCache> {
    151  static HashNumber Hash(const nsContentListKey& aKey) {
    152    return aKey.GetHash();
    153  }
    154  static bool Match(const nsContentListKey& aKey, const nsContentList* aVal) {
    155    return aVal->MatchesKey(aKey);
    156  }
    157 };
    158 
    159 static ContentListCache sRecentlyUsedContentLists;
    160 
    161 class nsContentList::HashEntry : public PLDHashEntryHdr {
    162 public:
    163  using KeyType = const nsContentListKey*;
    164  using KeyTypePointer = KeyType;
    165 
    166  // Note that this is creating a blank entry, so you'll have to manually
    167  // initialize it after it has been inserted into the hash table.
    168  explicit HashEntry(KeyTypePointer aKey) : mContentList(nullptr) {}
    169 
    170  HashEntry(HashEntry&& aEnt) : mContentList(std::move(aEnt.mContentList)) {}
    171 
    172  ~HashEntry() {
    173    if (mContentList) {
    174      MOZ_RELEASE_ASSERT(mContentList->mInHashtable);
    175      mContentList->mInHashtable = false;
    176    }
    177  }
    178 
    179  bool KeyEquals(KeyTypePointer aKey) const {
    180    return mContentList->MatchesKey(*aKey);
    181  }
    182 
    183  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
    184 
    185  static PLDHashNumber HashKey(KeyTypePointer aKey) { return aKey->GetHash(); }
    186 
    187  nsContentList* GetContentList() const { return mContentList; }
    188  void SetContentList(nsContentList* aContentList) {
    189    MOZ_RELEASE_ASSERT(!mContentList);
    190    MOZ_ASSERT(aContentList);
    191    MOZ_RELEASE_ASSERT(!aContentList->mInHashtable);
    192    mContentList = aContentList;
    193    mContentList->mInHashtable = true;
    194  }
    195 
    196  enum { ALLOW_MEMMOVE = true };
    197 
    198 private:
    199  nsContentList* MOZ_UNSAFE_REF(
    200      "This entry will be removed in nsContentList::RemoveFromHashtable "
    201      "before mContentList is destroyed") mContentList;
    202 };
    203 
    204 // Hashtable for storing nsContentLists
    205 static StaticAutoPtr<nsTHashtable<nsContentList::HashEntry>>
    206    gContentListHashTable;
    207 
    208 already_AddRefed<nsContentList> NS_GetContentList(nsINode* aRootNode,
    209                                                  int32_t aMatchNameSpaceId,
    210                                                  const nsAString& aTagname) {
    211  NS_ASSERTION(aRootNode, "content list has to have a root");
    212 
    213  RefPtr<nsContentList> list;
    214  nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname,
    215                           aRootNode->OwnerDoc()->IsHTMLDocument());
    216  auto p = sRecentlyUsedContentLists.Lookup(hashKey);
    217  if (p) {
    218    list = p.Data();
    219    return list.forget();
    220  }
    221 
    222  // Initialize the hashtable if needed.
    223  if (!gContentListHashTable) {
    224    gContentListHashTable = new nsTHashtable<nsContentList::HashEntry>();
    225  }
    226 
    227  // First we look in our hashtable.  Then we create a content list if needed
    228  auto entry = gContentListHashTable->PutEntry(&hashKey, fallible);
    229  if (entry) {
    230    list = entry->GetContentList();
    231  }
    232 
    233  if (!list) {
    234    // We need to create a ContentList and add it to our new entry, if
    235    // we have an entry
    236    RefPtr<nsAtom> xmlAtom = NS_Atomize(aTagname);
    237    RefPtr<nsAtom> htmlAtom;
    238    if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
    239      nsAutoString lowercaseName;
    240      nsContentUtils::ASCIIToLower(aTagname, lowercaseName);
    241      htmlAtom = NS_Atomize(lowercaseName);
    242    } else {
    243      htmlAtom = xmlAtom;
    244    }
    245    list = new nsContentList(aRootNode, aMatchNameSpaceId, htmlAtom, xmlAtom);
    246    if (entry) {
    247      entry->SetContentList(list);
    248    }
    249  }
    250 
    251  p.Set(list);
    252  return list.forget();
    253 }
    254 
    255 #ifdef DEBUG
    256 const nsCacheableFuncStringContentList::ContentListType
    257    nsCachableElementsByNameNodeList::sType =
    258        nsCacheableFuncStringContentList::eNodeList;
    259 const nsCacheableFuncStringContentList::ContentListType
    260    nsCacheableFuncStringHTMLCollection::sType =
    261        nsCacheableFuncStringContentList::eHTMLCollection;
    262 #endif
    263 
    264 class nsCacheableFuncStringContentList::HashEntry : public PLDHashEntryHdr {
    265 public:
    266  using KeyType = const nsFuncStringCacheKey*;
    267  using KeyTypePointer = KeyType;
    268 
    269  // Note that this is creating a blank entry, so you'll have to manually
    270  // initialize it after it has been inserted into the hash table.
    271  explicit HashEntry(KeyTypePointer aKey) : mContentList(nullptr) {}
    272 
    273  HashEntry(HashEntry&& aEnt) : mContentList(std::move(aEnt.mContentList)) {}
    274 
    275  ~HashEntry() {
    276    if (mContentList) {
    277      MOZ_RELEASE_ASSERT(mContentList->mInHashtable);
    278      mContentList->mInHashtable = false;
    279    }
    280  }
    281 
    282  bool KeyEquals(KeyTypePointer aKey) const {
    283    return mContentList->Equals(aKey);
    284  }
    285 
    286  static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
    287 
    288  static PLDHashNumber HashKey(KeyTypePointer aKey) { return aKey->GetHash(); }
    289 
    290  nsCacheableFuncStringContentList* GetContentList() const {
    291    return mContentList;
    292  }
    293  void SetContentList(nsCacheableFuncStringContentList* aContentList) {
    294    MOZ_RELEASE_ASSERT(!mContentList);
    295    MOZ_ASSERT(aContentList);
    296    MOZ_RELEASE_ASSERT(!aContentList->mInHashtable);
    297    mContentList = aContentList;
    298    mContentList->mInHashtable = true;
    299  }
    300 
    301  enum { ALLOW_MEMMOVE = true };
    302 
    303 private:
    304  nsCacheableFuncStringContentList* MOZ_UNSAFE_REF(
    305      "This entry will be removed in "
    306      "nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable "
    307      "before mContentList is destroyed") mContentList;
    308 };
    309 
    310 // Hashtable for storing nsCacheableFuncStringContentList
    311 static StaticAutoPtr<nsTHashtable<nsCacheableFuncStringContentList::HashEntry>>
    312    gFuncStringContentListHashTable;
    313 
    314 template <class ListType>
    315 already_AddRefed<nsContentList> GetFuncStringContentList(
    316    nsINode* aRootNode, nsContentListMatchFunc aFunc,
    317    nsContentListDestroyFunc aDestroyFunc,
    318    nsFuncStringContentListDataAllocator aDataAllocator,
    319    const nsAString& aString) {
    320  NS_ASSERTION(aRootNode, "content list has to have a root");
    321 
    322  RefPtr<nsCacheableFuncStringContentList> list;
    323 
    324  // Initialize the hashtable if needed.
    325  if (!gFuncStringContentListHashTable) {
    326    gFuncStringContentListHashTable =
    327        new nsTHashtable<nsCacheableFuncStringContentList::HashEntry>();
    328  }
    329 
    330  nsCacheableFuncStringContentList::HashEntry* entry = nullptr;
    331  // First we look in our hashtable.  Then we create a content list if needed
    332  if (gFuncStringContentListHashTable) {
    333    nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
    334 
    335    entry = gFuncStringContentListHashTable->PutEntry(&hashKey, fallible);
    336    if (entry) {
    337      list = entry->GetContentList();
    338 #ifdef DEBUG
    339      MOZ_ASSERT_IF(list, list->mType == ListType::sType);
    340 #endif
    341    }
    342  }
    343 
    344  if (!list) {
    345    // We need to create a ContentList and add it to our new entry, if
    346    // we have an entry
    347    list =
    348        new ListType(aRootNode, aFunc, aDestroyFunc, aDataAllocator, aString);
    349    if (entry) {
    350      entry->SetContentList(list);
    351    }
    352  }
    353 
    354  // Don't cache these lists globally
    355 
    356  return list.forget();
    357 }
    358 
    359 // Explicit instantiations to avoid link errors
    360 template already_AddRefed<nsContentList>
    361 GetFuncStringContentList<nsCachableElementsByNameNodeList>(
    362    nsINode* aRootNode, nsContentListMatchFunc aFunc,
    363    nsContentListDestroyFunc aDestroyFunc,
    364    nsFuncStringContentListDataAllocator aDataAllocator,
    365    const nsAString& aString);
    366 template already_AddRefed<nsContentList>
    367 GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
    368    nsINode* aRootNode, nsContentListMatchFunc aFunc,
    369    nsContentListDestroyFunc aDestroyFunc,
    370    nsFuncStringContentListDataAllocator aDataAllocator,
    371    const nsAString& aString);
    372 
    373 //-----------------------------------------------------
    374 // nsContentList implementation
    375 
    376 nsContentList::nsContentList(nsINode* aRootNode, int32_t aMatchNameSpaceId,
    377                             nsAtom* aHTMLMatchAtom, nsAtom* aXMLMatchAtom,
    378                             bool aDeep, bool aLiveList)
    379    : nsBaseContentList(),
    380      mRootNode(aRootNode),
    381      mMatchNameSpaceId(aMatchNameSpaceId),
    382      mHTMLMatchAtom(aHTMLMatchAtom),
    383      mXMLMatchAtom(aXMLMatchAtom),
    384      mState(State::Dirty),
    385      mDeep(aDeep),
    386      mFuncMayDependOnAttr(false),
    387      mIsHTMLDocument(aRootNode->OwnerDoc()->IsHTMLDocument()),
    388      mNamedItemsCacheValid(false),
    389      mIsLiveList(aLiveList),
    390      mInHashtable(false) {
    391  NS_ASSERTION(mRootNode, "Must have root");
    392  if (nsGkAtoms::_asterisk == mHTMLMatchAtom) {
    393    NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterisk,
    394                 "HTML atom and XML atom are not both asterisk?");
    395    mMatchAll = true;
    396  } else {
    397    mMatchAll = false;
    398  }
    399  // This is aLiveList instead of mIsLiveList to avoid Valgrind errors.
    400  if (aLiveList) {
    401    SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
    402    mRootNode->AddMutationObserver(this);
    403  }
    404 
    405  // We only need to flush if we're in an non-HTML document, since the
    406  // HTML5 parser doesn't need flushing.  Further, if we're not in a
    407  // document at all right now (in the GetUncomposedDoc() sense), we're
    408  // not parser-created and don't need to be flushing stuff under us
    409  // to get our kids right.
    410  Document* doc = mRootNode->GetUncomposedDoc();
    411  mFlushesNeeded = doc && !doc->IsHTMLDocument();
    412 }
    413 
    414 nsContentList::nsContentList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
    415                             nsContentListDestroyFunc aDestroyFunc, void* aData,
    416                             bool aDeep, nsAtom* aMatchAtom,
    417                             int32_t aMatchNameSpaceId,
    418                             bool aFuncMayDependOnAttr, bool aLiveList)
    419    : nsBaseContentList(),
    420      mRootNode(aRootNode),
    421      mMatchNameSpaceId(aMatchNameSpaceId),
    422      mHTMLMatchAtom(aMatchAtom),
    423      mXMLMatchAtom(aMatchAtom),
    424      mFunc(aFunc),
    425      mDestroyFunc(aDestroyFunc),
    426      mData(aData),
    427      mState(State::Dirty),
    428      mMatchAll(false),
    429      mDeep(aDeep),
    430      mFuncMayDependOnAttr(aFuncMayDependOnAttr),
    431      mIsHTMLDocument(false),
    432      mNamedItemsCacheValid(false),
    433      mIsLiveList(aLiveList),
    434      mInHashtable(false) {
    435  NS_ASSERTION(mRootNode, "Must have root");
    436  // This is aLiveList instead of mIsLiveList to avoid Valgrind errors.
    437  if (aLiveList) {
    438    SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
    439    mRootNode->AddMutationObserver(this);
    440  }
    441 
    442  // We only need to flush if we're in an non-HTML document, since the
    443  // HTML5 parser doesn't need flushing.  Further, if we're not in a
    444  // document at all right now (in the GetUncomposedDoc() sense), we're
    445  // not parser-created and don't need to be flushing stuff under us
    446  // to get our kids right.
    447  Document* doc = mRootNode->GetUncomposedDoc();
    448  mFlushesNeeded = doc && !doc->IsHTMLDocument();
    449 }
    450 
    451 nsContentList::~nsContentList() {
    452  RemoveFromHashtable();
    453  if (mIsLiveList && mRootNode) {
    454    mRootNode->RemoveMutationObserver(this);
    455  }
    456 
    457  if (mDestroyFunc) {
    458    // Clean up mData
    459    (*mDestroyFunc)(mData);
    460  }
    461 }
    462 
    463 JSObject* nsContentList::WrapObject(JSContext* cx,
    464                                    JS::Handle<JSObject*> aGivenProto) {
    465  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
    466 }
    467 
    468 NS_IMPL_ISUPPORTS_INHERITED(nsContentList, nsBaseContentList, nsIHTMLCollection,
    469                            nsIMutationObserver)
    470 
    471 uint32_t nsContentList::Length(bool aDoFlush) {
    472  BringSelfUpToDate(aDoFlush);
    473 
    474  return mElements.Length();
    475 }
    476 
    477 nsIContent* nsContentList::Item(uint32_t aIndex, bool aDoFlush) {
    478  if (mRootNode && aDoFlush && mFlushesNeeded) {
    479    // XXX sXBL/XBL2 issue
    480    Document* doc = mRootNode->GetUncomposedDoc();
    481    if (doc) {
    482      // Flush pending content changes Bug 4891.
    483      doc->FlushPendingNotifications(FlushType::ContentAndNotify);
    484    }
    485  }
    486 
    487  if (mState != State::UpToDate) {
    488    PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1);
    489  }
    490 
    491  ASSERT_IN_SYNC;
    492  NS_ASSERTION(!mRootNode || mState != State::Dirty,
    493               "PopulateSelf left the list in a dirty (useless) state!");
    494 
    495  return mElements.SafeElementAt(aIndex);
    496 }
    497 
    498 inline void nsContentList::InsertElementInNamedItemsCache(
    499    nsIContent& aContent) {
    500  const bool hasName = aContent.HasName();
    501  const bool hasId = aContent.HasID();
    502  if (!hasName && !hasId) {
    503    return;
    504  }
    505 
    506  Element* el = aContent.AsElement();
    507  MOZ_ASSERT_IF(hasName, el->IsHTMLElement());
    508 
    509  uint32_t i = 0;
    510  while (BorrowedAttrInfo info = el->GetAttrInfoAt(i++)) {
    511    const bool valid = (info.mName->Equals(nsGkAtoms::name) && hasName) ||
    512                       (info.mName->Equals(nsGkAtoms::id) && hasId);
    513    if (!valid) {
    514      continue;
    515    }
    516 
    517    if (!mNamedItemsCache) {
    518      mNamedItemsCache = MakeUnique<NamedItemsCache>();
    519    }
    520 
    521    nsAtom* name = info.mValue->GetAtomValue();
    522    // NOTE: LookupOrInsert makes sure we keep the first element we find for a
    523    // given name.
    524    mNamedItemsCache->LookupOrInsert(name, el);
    525  }
    526 }
    527 
    528 inline void nsContentList::InvalidateNamedItemsCacheForAttributeChange(
    529    int32_t aNamespaceID, nsAtom* aAttribute) {
    530  if (!mNamedItemsCacheValid) {
    531    return;
    532  }
    533  if ((aAttribute == nsGkAtoms::id || aAttribute == nsGkAtoms::name) &&
    534      aNamespaceID == kNameSpaceID_None) {
    535    InvalidateNamedItemsCache();
    536  }
    537 }
    538 
    539 inline void nsContentList::InvalidateNamedItemsCacheForInsertion(
    540    Element& aElement) {
    541  if (!mNamedItemsCacheValid) {
    542    return;
    543  }
    544 
    545  InsertElementInNamedItemsCache(aElement);
    546 }
    547 
    548 inline void nsContentList::InvalidateNamedItemsCacheForDeletion(
    549    Element& aElement) {
    550  if (!mNamedItemsCacheValid) {
    551    return;
    552  }
    553  if (aElement.HasName() || aElement.HasID()) {
    554    InvalidateNamedItemsCache();
    555  }
    556 }
    557 
    558 void nsContentList::EnsureNamedItemsCacheValid(bool aDoFlush) {
    559  BringSelfUpToDate(aDoFlush);
    560 
    561  if (mNamedItemsCacheValid) {
    562    return;
    563  }
    564 
    565  MOZ_ASSERT(!mNamedItemsCache);
    566 
    567  // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem-key
    568  // XXX: Blink/WebKit don't follow the spec here, and searches first-by-id,
    569  // then by name.
    570  for (const nsCOMPtr<nsIContent>& content : mElements) {
    571    InsertElementInNamedItemsCache(*content);
    572  }
    573 
    574  mNamedItemsCacheValid = true;
    575 }
    576 
    577 Element* nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) {
    578  if (aName.IsEmpty()) {
    579    return nullptr;
    580  }
    581 
    582  EnsureNamedItemsCacheValid(aDoFlush);
    583 
    584  if (!mNamedItemsCache) {
    585    return nullptr;
    586  }
    587 
    588  // Typically IDs and names are atomized
    589  RefPtr<nsAtom> name = NS_Atomize(aName);
    590  NS_ENSURE_TRUE(name, nullptr);
    591 
    592  return mNamedItemsCache->Get(name);
    593 }
    594 
    595 void nsContentList::GetSupportedNames(nsTArray<nsString>& aNames) {
    596  BringSelfUpToDate(true);
    597 
    598  AutoTArray<nsAtom*, 8> atoms;
    599  for (uint32_t i = 0; i < mElements.Length(); ++i) {
    600    nsIContent* content = mElements.ElementAt(i);
    601    if (content->HasID()) {
    602      nsAtom* id = content->GetID();
    603      MOZ_ASSERT(id != nsGkAtoms::_empty, "Empty ids don't get atomized");
    604      if (!atoms.Contains(id)) {
    605        atoms.AppendElement(id);
    606      }
    607    }
    608 
    609    nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(content);
    610    if (el) {
    611      // XXXbz should we be checking for particular tags here?  How
    612      // stable is this part of the spec?
    613      // Note: nsINode::HasName means the name is exposed on the document,
    614      // which is false for options, so we don't check it here.
    615      const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
    616      if (val && val->Type() == nsAttrValue::eAtom) {
    617        nsAtom* name = val->GetAtomValue();
    618        MOZ_ASSERT(name != nsGkAtoms::_empty, "Empty names don't get atomized");
    619        if (!atoms.Contains(name)) {
    620          atoms.AppendElement(name);
    621        }
    622      }
    623    }
    624  }
    625 
    626  uint32_t atomsLen = atoms.Length();
    627  nsString* names = aNames.AppendElements(atomsLen);
    628  for (uint32_t i = 0; i < atomsLen; ++i) {
    629    atoms[i]->ToString(names[i]);
    630  }
    631 }
    632 
    633 int32_t nsContentList::IndexOf(nsIContent* aContent, bool aDoFlush) {
    634  BringSelfUpToDate(aDoFlush);
    635 
    636  return mElements.IndexOf(aContent);
    637 }
    638 
    639 int32_t nsContentList::IndexOf(nsIContent* aContent) {
    640  return IndexOf(aContent, true);
    641 }
    642 
    643 void nsContentList::NodeWillBeDestroyed(nsINode* aNode) {
    644  // We shouldn't do anything useful from now on
    645 
    646  RemoveFromCaches();
    647  mRootNode = nullptr;
    648 
    649  // We will get no more updates, so we can never know we're up to
    650  // date
    651  SetDirty();
    652 }
    653 
    654 void nsContentList::LastRelease() {
    655  RemoveFromCaches();
    656  if (mIsLiveList && mRootNode) {
    657    mRootNode->RemoveMutationObserver(this);
    658    mRootNode = nullptr;
    659  }
    660  SetDirty();
    661 }
    662 
    663 Element* nsContentList::GetElementAt(uint32_t aIndex) {
    664  return static_cast<Element*>(Item(aIndex, true));
    665 }
    666 
    667 nsIContent* nsContentList::Item(uint32_t aIndex) {
    668  return GetElementAt(aIndex);
    669 }
    670 
    671 void nsContentList::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
    672                                     nsAtom* aAttribute, AttrModType,
    673                                     const nsAttrValue* aOldValue) {
    674  MOZ_ASSERT(aElement, "Must have a content node to work with");
    675 
    676  if (mState == State::Dirty ||
    677      !MayContainRelevantNodes(aElement->GetParentNode()) ||
    678      !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
    679    // Either we're already dirty or aElement will never match us.
    680    return;
    681  }
    682 
    683  InvalidateNamedItemsCacheForAttributeChange(aNameSpaceID, aAttribute);
    684 
    685  if (!mFunc || !mFuncMayDependOnAttr) {
    686    // aElement might be relevant but the attribute change doesn't affect
    687    // whether we match it.
    688    return;
    689  }
    690 
    691  if (Match(aElement)) {
    692    if (mElements.IndexOf(aElement) == mElements.NoIndex) {
    693      // We match aElement now, and it's not in our list already.  Just dirty
    694      // ourselves; this is simpler than trying to figure out where to insert
    695      // aElement.
    696      SetDirty();
    697    }
    698  } else {
    699    // We no longer match aElement.  Remove it from our list.  If it's
    700    // already not there, this is a no-op (though a potentially
    701    // expensive one).  Either way, no change of mState is required
    702    // here.
    703    if (mElements.RemoveElement(aElement)) {
    704      InvalidateNamedItemsCacheForDeletion(*aElement);
    705    }
    706  }
    707 }
    708 
    709 void nsContentList::ContentAppended(nsIContent* aFirstNewContent,
    710                                    const ContentAppendInfo&) {
    711  nsIContent* container = aFirstNewContent->GetParent();
    712  MOZ_ASSERT(container, "Can't get at the new content if no container!");
    713 
    714  /*
    715   * If the state is State::Dirty then we have no useful information in our list
    716   * and we want to put off doing work as much as possible.
    717   *
    718   * Also, if container is anonymous from our point of view, we know that we
    719   * can't possibly be matching any of the kids.
    720   *
    721   * Optimize out also the common case when just one new node is appended and
    722   * it doesn't match us.
    723   */
    724  if (mState == State::Dirty ||
    725      !nsContentUtils::IsInSameAnonymousTree(mRootNode, container) ||
    726      !MayContainRelevantNodes(container) ||
    727      (!aFirstNewContent->HasChildren() &&
    728       !aFirstNewContent->GetNextSibling() && !MatchSelf(aFirstNewContent))) {
    729    MaybeMarkDirty();
    730    return;
    731  }
    732 
    733  /*
    734   * We want to handle the case of ContentAppended by sometimes
    735   * appending the content to our list, not just setting state to
    736   * State::Dirty, since most of our ContentAppended notifications
    737   * should come during pageload and be at the end of the document.
    738   * Do a bit of work to see whether we could just append to what we
    739   * already have.
    740   */
    741 
    742  uint32_t ourCount = mElements.Length();
    743  const bool appendingToList = [&] {
    744    if (ourCount == 0) {
    745      return true;
    746    }
    747    if (mRootNode == container) {
    748      return true;
    749    }
    750    return nsContentUtils::PositionIsBefore(mElements.LastElement(),
    751                                            aFirstNewContent);
    752  }();
    753 
    754  if (!appendingToList) {
    755    // The new stuff is somewhere in the middle of our list; check
    756    // whether we need to invalidate
    757    for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
    758      if (MatchSelf(cur)) {
    759        // Uh-oh.  We're gonna have to add elements into the middle
    760        // of our list. That's not worth the effort.
    761        SetDirty();
    762        break;
    763      }
    764    }
    765 
    766    ASSERT_IN_SYNC;
    767    return;
    768  }
    769 
    770  /*
    771   * At this point we know we could append.  If we're not up to
    772   * date, however, that would be a bad idea -- it could miss some
    773   * content that we never picked up due to being lazy.  Further, we
    774   * may never get asked for this content... so don't grab it yet.
    775   */
    776  if (mState == State::Lazy) {
    777    return;
    778  }
    779 
    780  /*
    781   * We're up to date.  That means someone's actively using us; we
    782   * may as well grab this content....
    783   */
    784  if (mDeep) {
    785    for (nsIContent* cur = aFirstNewContent; cur;
    786         cur = cur->GetNextNode(container)) {
    787      if (cur->IsElement() && Match(cur->AsElement())) {
    788        mElements.AppendElement(cur);
    789        InvalidateNamedItemsCacheForInsertion(*cur->AsElement());
    790      }
    791    }
    792  } else {
    793    for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
    794      if (cur->IsElement() && Match(cur->AsElement())) {
    795        mElements.AppendElement(cur);
    796        InvalidateNamedItemsCacheForInsertion(*cur->AsElement());
    797      }
    798    }
    799  }
    800 
    801  ASSERT_IN_SYNC;
    802 }
    803 
    804 void nsContentList::ContentInserted(nsIContent* aChild,
    805                                    const ContentInsertInfo&) {
    806  // Note that aChild->GetParentNode() can be null here if we are inserting into
    807  // the document itself; any attempted optimizations to this method should deal
    808  // with that.
    809  if (mState != State::Dirty &&
    810      MayContainRelevantNodes(aChild->GetParentNode()) &&
    811      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
    812      MatchSelf(aChild)) {
    813    SetDirty();
    814  }
    815 
    816  ASSERT_IN_SYNC;
    817 }
    818 
    819 void nsContentList::ContentWillBeRemoved(nsIContent* aChild,
    820                                         const ContentRemoveInfo&) {
    821  if (mState != State::Dirty &&
    822      MayContainRelevantNodes(aChild->GetParentNode()) &&
    823      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
    824      MatchSelf(aChild)) {
    825    SetDirty();
    826  }
    827 
    828  ASSERT_IN_SYNC;
    829 }
    830 
    831 bool nsContentList::Match(Element* aElement) {
    832  if (mFunc) {
    833    return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData);
    834  }
    835 
    836  if (!mXMLMatchAtom) return false;
    837 
    838  NodeInfo* ni = aElement->NodeInfo();
    839 
    840  bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
    841  bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
    842  bool toReturn = mMatchAll;
    843  if (!unknown && !wildcard) toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
    844 
    845  if (toReturn) return toReturn;
    846 
    847  bool matchHTML =
    848      mIsHTMLDocument && aElement->GetNameSpaceID() == kNameSpaceID_XHTML;
    849 
    850  if (unknown) {
    851    return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom)
    852                     : ni->QualifiedNameEquals(mXMLMatchAtom);
    853  }
    854 
    855  if (wildcard) {
    856    return matchHTML ? ni->Equals(mHTMLMatchAtom) : ni->Equals(mXMLMatchAtom);
    857  }
    858 
    859  return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId)
    860                   : ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
    861 }
    862 
    863 bool nsContentList::MatchSelf(nsIContent* aContent) {
    864  MOZ_ASSERT(aContent, "Can't match null stuff, you know");
    865  MOZ_ASSERT(mDeep || aContent->GetParentNode() == mRootNode,
    866             "MatchSelf called on a node that we can't possibly match");
    867 
    868  if (!aContent->IsElement()) {
    869    return false;
    870  }
    871 
    872  if (Match(aContent->AsElement())) return true;
    873 
    874  if (!mDeep) return false;
    875 
    876  for (nsIContent* cur = aContent->GetFirstChild(); cur;
    877       cur = cur->GetNextNode(aContent)) {
    878    if (cur->IsElement() && Match(cur->AsElement())) {
    879      return true;
    880    }
    881  }
    882 
    883  return false;
    884 }
    885 
    886 void nsContentList::PopulateSelf(uint32_t aNeededLength,
    887                                 uint32_t aExpectedElementsIfDirty) {
    888  if (!mRootNode) {
    889    return;
    890  }
    891 
    892  ASSERT_IN_SYNC;
    893 
    894  uint32_t count = mElements.Length();
    895  NS_ASSERTION(mState != State::Dirty || count == aExpectedElementsIfDirty,
    896               "Reset() not called when setting state to State::Dirty?");
    897 
    898  if (count >= aNeededLength)  // We're all set
    899    return;
    900 
    901  uint32_t elementsToAppend = aNeededLength - count;
    902 #ifdef DEBUG
    903  uint32_t invariant = elementsToAppend + mElements.Length();
    904 #endif
    905 
    906  if (mDeep) {
    907    // If we already have nodes start searching at the last one, otherwise
    908    // start searching at the root.
    909    nsINode* cur = count ? mElements[count - 1].get() : mRootNode;
    910    do {
    911      cur = cur->GetNextNode(mRootNode);
    912      if (!cur) {
    913        break;
    914      }
    915      if (cur->IsElement() && Match(cur->AsElement())) {
    916        // Append AsElement() to get nsIContent instead of nsINode
    917        mElements.AppendElement(cur->AsElement());
    918        --elementsToAppend;
    919      }
    920    } while (elementsToAppend);
    921  } else {
    922    nsIContent* cur = count ? mElements[count - 1]->GetNextSibling()
    923                            : mRootNode->GetFirstChild();
    924    for (; cur && elementsToAppend; cur = cur->GetNextSibling()) {
    925      if (cur->IsElement() && Match(cur->AsElement())) {
    926        mElements.AppendElement(cur);
    927        --elementsToAppend;
    928      }
    929    }
    930  }
    931 
    932  NS_ASSERTION(elementsToAppend + mElements.Length() == invariant,
    933               "Something is awry!");
    934 
    935  if (elementsToAppend != 0) {
    936    mState = State::UpToDate;
    937  } else {
    938    mState = State::Lazy;
    939  }
    940 
    941  SetEnabledCallbacks(nsIMutationObserver::kAll);
    942 
    943  ASSERT_IN_SYNC;
    944 }
    945 
    946 void nsContentList::RemoveFromHashtable() {
    947  if (mFunc) {
    948    // nsCacheableFuncStringContentList can be in a hash table without being
    949    // in gContentListHashTable, but it will have been removed from the hash
    950    // table in its dtor before it runs the nsContentList dtor.
    951    MOZ_RELEASE_ASSERT(!mInHashtable);
    952 
    953    // This can't be in gContentListHashTable.
    954    return;
    955  }
    956 
    957  nsDependentAtomString str(mXMLMatchAtom);
    958  nsContentListKey key(mRootNode, mMatchNameSpaceId, str, mIsHTMLDocument);
    959  sRecentlyUsedContentLists.Remove(key);
    960 
    961  if (gContentListHashTable) {
    962    gContentListHashTable->RemoveEntry(&key);
    963 
    964    if (gContentListHashTable->Count() == 0) {
    965      gContentListHashTable = nullptr;
    966    }
    967  }
    968 
    969  MOZ_RELEASE_ASSERT(!mInHashtable);
    970 }
    971 
    972 void nsContentList::BringSelfUpToDate(bool aDoFlush) {
    973  if (mFlushesNeeded && mRootNode && aDoFlush) {
    974    // XXX sXBL/XBL2 issue
    975    if (Document* doc = mRootNode->GetUncomposedDoc()) {
    976      // Flush pending content changes Bug 4891.
    977      doc->FlushPendingNotifications(FlushType::ContentAndNotify);
    978    }
    979  }
    980 
    981  if (mState != State::UpToDate) {
    982    PopulateSelf(uint32_t(-1));
    983  }
    984 
    985  mMissedUpdates = 0;
    986 
    987  ASSERT_IN_SYNC;
    988  NS_ASSERTION(!mRootNode || mState == State::UpToDate,
    989               "PopulateSelf dod not bring content list up to date!");
    990 }
    991 
    992 nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList() {
    993  RemoveFromFuncStringHashtable();
    994 }
    995 
    996 void nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable() {
    997  if (!gFuncStringContentListHashTable) {
    998    MOZ_RELEASE_ASSERT(!mInHashtable);
    999    return;
   1000  }
   1001 
   1002  nsFuncStringCacheKey key(mRootNode, mFunc, mString);
   1003  gFuncStringContentListHashTable->RemoveEntry(&key);
   1004 
   1005  if (gFuncStringContentListHashTable->Count() == 0) {
   1006    gFuncStringContentListHashTable = nullptr;
   1007  }
   1008 
   1009  MOZ_RELEASE_ASSERT(!mInHashtable);
   1010 }
   1011 
   1012 #ifdef DEBUG_CONTENT_LIST
   1013 void nsContentList::AssertInSync() {
   1014  if (mState == State::Dirty) {
   1015    return;
   1016  }
   1017 
   1018  if (!mRootNode) {
   1019    NS_ASSERTION(mElements.Length() == 0 && mState == State::Dirty,
   1020                 "Empty iterator isn't quite empty?");
   1021    return;
   1022  }
   1023 
   1024  // XXX This code will need to change if nsContentLists can ever match
   1025  // elements that are outside of the document element.
   1026  nsIContent* root = mRootNode->IsDocument()
   1027                         ? mRootNode->AsDocument()->GetRootElement()
   1028                         : mRootNode->AsContent();
   1029 
   1030  PreContentIterator preOrderIter;
   1031  if (mDeep) {
   1032    preOrderIter.Init(root);
   1033    preOrderIter.First();
   1034  }
   1035 
   1036  uint32_t cnt = 0, index = 0;
   1037  while (true) {
   1038    if (cnt == mElements.Length() && mState == State::Lazy) {
   1039      break;
   1040    }
   1041 
   1042    nsIContent* cur =
   1043        mDeep ? preOrderIter.GetCurrentNode() : mRootNode->GetChildAt(index++);
   1044    if (!cur) {
   1045      break;
   1046    }
   1047 
   1048    if (cur->IsElement() && Match(cur->AsElement())) {
   1049      NS_ASSERTION(cnt < mElements.Length() && mElements[cnt] == cur,
   1050                   "Elements is out of sync");
   1051      ++cnt;
   1052    }
   1053 
   1054    if (mDeep) {
   1055      preOrderIter.Next();
   1056    }
   1057  }
   1058 
   1059  NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
   1060 }
   1061 #endif
   1062 
   1063 //-----------------------------------------------------
   1064 // nsCachableElementsByNameNodeList
   1065 
   1066 JSObject* nsCachableElementsByNameNodeList::WrapObject(
   1067    JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
   1068  return NodeList_Binding::Wrap(cx, this, aGivenProto);
   1069 }
   1070 
   1071 void nsCachableElementsByNameNodeList::AttributeChanged(
   1072    Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute,
   1073    AttrModType aModType, const nsAttrValue* aOldValue) {
   1074  // No need to rebuild the list if the changed attribute is not the name
   1075  // attribute.
   1076  if (aAttribute != nsGkAtoms::name) {
   1077    InvalidateNamedItemsCacheForAttributeChange(aNameSpaceID, aAttribute);
   1078    return;
   1079  }
   1080 
   1081  nsCacheableFuncStringContentList::AttributeChanged(
   1082      aElement, aNameSpaceID, aAttribute, aModType, aOldValue);
   1083 }
   1084 
   1085 //-----------------------------------------------------
   1086 // nsCacheableFuncStringHTMLCollection
   1087 
   1088 JSObject* nsCacheableFuncStringHTMLCollection::WrapObject(
   1089    JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
   1090  return HTMLCollection_Binding::Wrap(cx, this, aGivenProto);
   1091 }
   1092 
   1093 //-----------------------------------------------------
   1094 // nsLabelsNodeList
   1095 
   1096 JSObject* nsLabelsNodeList::WrapObject(JSContext* cx,
   1097                                       JS::Handle<JSObject*> aGivenProto) {
   1098  return NodeList_Binding::Wrap(cx, this, aGivenProto);
   1099 }
   1100 
   1101 void nsLabelsNodeList::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
   1102                                        nsAtom* aAttribute, AttrModType,
   1103                                        const nsAttrValue* aOldValue) {
   1104  MOZ_ASSERT(aElement, "Must have a content node to work with");
   1105  if (mState == State::Dirty ||
   1106      !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
   1107    return;
   1108  }
   1109 
   1110  InvalidateNamedItemsCacheForAttributeChange(aNameSpaceID, aAttribute);
   1111 
   1112  // We need to handle input type changes to or from "hidden".
   1113  if (aElement->IsHTMLElement(nsGkAtoms::input) &&
   1114      aAttribute == nsGkAtoms::type && aNameSpaceID == kNameSpaceID_None) {
   1115    SetDirty();
   1116    return;
   1117  }
   1118 }
   1119 
   1120 void nsLabelsNodeList::ContentAppended(nsIContent* aFirstNewContent,
   1121                                       const ContentAppendInfo&) {
   1122  nsIContent* container = aFirstNewContent->GetParent();
   1123  // If a labelable element is moved to outside or inside of
   1124  // nested associated labels, we're gonna have to modify
   1125  // the content list.
   1126  if (mState != State::Dirty &&
   1127      nsContentUtils::IsInSameAnonymousTree(mRootNode, container)) {
   1128    SetDirty();
   1129    return;
   1130  }
   1131 }
   1132 
   1133 void nsLabelsNodeList::ContentInserted(nsIContent* aChild,
   1134                                       const ContentInsertInfo&) {
   1135  // If a labelable element is moved to outside or inside of
   1136  // nested associated labels, we're gonna have to modify
   1137  // the content list.
   1138  if (mState != State::Dirty &&
   1139      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
   1140    SetDirty();
   1141    return;
   1142  }
   1143 }
   1144 
   1145 void nsLabelsNodeList::ContentWillBeRemoved(nsIContent* aChild,
   1146                                            const ContentRemoveInfo&) {
   1147  // If a labelable element is removed, we're gonna have to clean
   1148  // the content list.
   1149  if (mState != State::Dirty &&
   1150      nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild)) {
   1151    SetDirty();
   1152    return;
   1153  }
   1154 }
   1155 
   1156 void nsLabelsNodeList::MaybeResetRoot(nsINode* aRootNode) {
   1157  MOZ_ASSERT(aRootNode, "Must have root");
   1158  if (mRootNode == aRootNode) {
   1159    return;
   1160  }
   1161 
   1162  MOZ_ASSERT(mIsLiveList, "nsLabelsNodeList is always a live list");
   1163  if (mRootNode) {
   1164    mRootNode->RemoveMutationObserver(this);
   1165  }
   1166  mRootNode = aRootNode;
   1167  mRootNode->AddMutationObserver(this);
   1168  SetDirty();
   1169 }
   1170 
   1171 void nsLabelsNodeList::PopulateSelf(uint32_t aNeededLength,
   1172                                    uint32_t aExpectedElementsIfDirty) {
   1173  if (!mRootNode) {
   1174    return;
   1175  }
   1176 
   1177  // Start searching at the root.
   1178  nsINode* cur = mRootNode;
   1179  if (mElements.IsEmpty() && cur->IsElement() && Match(cur->AsElement())) {
   1180    mElements.AppendElement(cur->AsElement());
   1181    ++aExpectedElementsIfDirty;
   1182  }
   1183 
   1184  nsContentList::PopulateSelf(aNeededLength, aExpectedElementsIfDirty);
   1185 }