tor-browser

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

nsContentList.h (22665B)


      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 #ifndef nsContentList_h___
     14 #define nsContentList_h___
     15 
     16 #include "mozilla/Attributes.h"
     17 #include "mozilla/HashFunctions.h"
     18 #include "mozilla/MemoryReporting.h"
     19 #include "mozilla/dom/NameSpaceConstants.h"
     20 #include "nsAtomHashKeys.h"
     21 #include "nsContentListDeclarations.h"
     22 #include "nsCycleCollectionParticipant.h"
     23 #include "nsHashKeys.h"
     24 #include "nsIHTMLCollection.h"
     25 #include "nsINodeList.h"
     26 #include "nsISupports.h"
     27 #include "nsNameSpaceManager.h"
     28 #include "nsString.h"
     29 #include "nsStubMutationObserver.h"
     30 #include "nsTArray.h"
     31 #include "nsWrapperCache.h"
     32 
     33 // XXX Avoid including this here by moving function bodies to the cpp file.
     34 #include "nsIContent.h"
     35 
     36 namespace mozilla::dom {
     37 class Element;
     38 }  // namespace mozilla::dom
     39 
     40 class nsBaseContentList : public nsINodeList {
     41 protected:
     42  using Element = mozilla::dom::Element;
     43 
     44 public:
     45  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     46 
     47  // nsINodeList
     48  int32_t IndexOf(nsIContent* aContent) override;
     49  nsIContent* Item(uint32_t aIndex) override;
     50 
     51  uint32_t Length() override { return mElements.Length(); }
     52 
     53  NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS(nsBaseContentList)
     54 
     55  void AppendElement(nsIContent* aContent) {
     56    MOZ_ASSERT(aContent);
     57    mElements.AppendElement(aContent);
     58  }
     59  void MaybeAppendElement(nsIContent* aContent) {
     60    if (aContent) {
     61      AppendElement(aContent);
     62    }
     63  }
     64 
     65  /**
     66   * Insert the element at a given index, shifting the objects at
     67   * the given index and later to make space.
     68   * @param aContent Element to insert, must not be null
     69   * @param aIndex Index to insert the element at.
     70   */
     71  void InsertElementAt(nsIContent* aContent, int32_t aIndex) {
     72    NS_ASSERTION(aContent, "Element to insert must not be null");
     73    mElements.InsertElementAt(aIndex, aContent);
     74  }
     75 
     76  void RemoveElement(nsIContent* aContent) {
     77    mElements.RemoveElement(aContent);
     78  }
     79 
     80  void Reset() { mElements.Clear(); }
     81 
     82  virtual int32_t IndexOf(nsIContent* aContent, bool aDoFlush);
     83 
     84  JSObject* WrapObject(JSContext* cx,
     85                       JS::Handle<JSObject*> aGivenProto) override = 0;
     86 
     87  void SetCapacity(uint32_t aCapacity) { mElements.SetCapacity(aCapacity); }
     88 
     89  virtual void LastRelease() {}
     90 
     91  // Memory reporting.  For now, subclasses of nsBaseContentList don't really
     92  // need to report any members that are not part of the object itself, so we
     93  // don't need to make this virtual.
     94  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
     95 
     96 protected:
     97  virtual ~nsBaseContentList();
     98 
     99  /**
    100   * To be called from non-destructor locations (e.g. unlink) that want to
    101   * remove from caches.  Cacheable subclasses should override.
    102   */
    103  virtual void RemoveFromCaches() {}
    104 
    105  AutoTArray<nsCOMPtr<nsIContent>, 10> mElements;
    106 };
    107 
    108 class nsSimpleContentList : public nsBaseContentList {
    109 public:
    110  explicit nsSimpleContentList(nsINode* aRoot)
    111      : nsBaseContentList(), mRoot(aRoot) {}
    112 
    113  NS_DECL_ISUPPORTS_INHERITED
    114  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsSimpleContentList,
    115                                           nsBaseContentList)
    116 
    117  nsINode* GetParentObject() override { return mRoot; }
    118  JSObject* WrapObject(JSContext* cx,
    119                       JS::Handle<JSObject*> aGivenProto) override;
    120 
    121 protected:
    122  virtual ~nsSimpleContentList() = default;
    123 
    124 private:
    125  // This has to be a strong reference, the root might go away before the list.
    126  nsCOMPtr<nsINode> mRoot;
    127 };
    128 
    129 // Used for returning lists that will always be empty, such as the applets list
    130 // in HTML Documents
    131 class nsEmptyContentList final : public nsBaseContentList,
    132                                 public nsIHTMLCollection {
    133 public:
    134  explicit nsEmptyContentList(nsINode* aRoot)
    135      : nsBaseContentList(), mRoot(aRoot) {}
    136 
    137  NS_DECL_ISUPPORTS_INHERITED
    138  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsEmptyContentList,
    139                                           nsBaseContentList)
    140 
    141  nsINode* GetParentObject() override { return mRoot; }
    142 
    143  JSObject* WrapObject(JSContext* cx,
    144                       JS::Handle<JSObject*> aGivenProto) override;
    145 
    146  JSObject* GetWrapperPreserveColorInternal() override {
    147    return nsWrapperCache::GetWrapperPreserveColor();
    148  }
    149  void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override {
    150    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
    151  }
    152 
    153  uint32_t Length() final { return 0; }
    154  nsIContent* Item(uint32_t aIndex) override;
    155  Element* GetElementAt(uint32_t index) override;
    156  Element* GetFirstNamedElement(const nsAString& aName, bool& aFound) override;
    157  void GetSupportedNames(nsTArray<nsString>& aNames) override;
    158 
    159 protected:
    160  virtual ~nsEmptyContentList() = default;
    161 
    162 private:
    163  // This has to be a strong reference, the root might go away before the list.
    164  nsCOMPtr<nsINode> mRoot;
    165 };
    166 
    167 /**
    168 * Class that's used as the key to hash nsContentList implementations
    169 * for fast retrieval
    170 */
    171 struct nsContentListKey {
    172  // We have to take an aIsHTMLDocument arg for two reasons:
    173  // 1) We don't want to include Document.h in this header.
    174  // 2) We need to do that to make nsContentList::RemoveFromHashtable
    175  //    work, because by the time it's called the document of the
    176  //    list's root node might have changed.
    177  nsContentListKey(nsINode* aRootNode, int32_t aMatchNameSpaceId,
    178                   const nsAString& aTagname, bool aIsHTMLDocument)
    179      : mRootNode(aRootNode),
    180        mMatchNameSpaceId(aMatchNameSpaceId),
    181        mTagname(aTagname),
    182        mIsHTMLDocument(aIsHTMLDocument),
    183        mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode,
    184                                 mMatchNameSpaceId, mIsHTMLDocument)) {}
    185 
    186  nsContentListKey(const nsContentListKey& aContentListKey) = default;
    187 
    188  inline uint32_t GetHash(void) const { return mHash; }
    189 
    190  nsINode* const mRootNode;  // Weak ref
    191  const int32_t mMatchNameSpaceId;
    192  const nsAString& mTagname;
    193  bool mIsHTMLDocument;
    194  const uint32_t mHash;
    195 };
    196 
    197 /**
    198 * Class that implements a possibly live NodeList that matches Elements
    199 * in the tree based on some criterion.
    200 */
    201 class nsContentList : public nsBaseContentList,
    202                      public nsIHTMLCollection,
    203                      public nsStubMutationObserver {
    204 protected:
    205  enum class State : uint8_t {
    206    // The list is up to date and need not do any walking to be able to answer
    207    // any questions anyone may have.
    208    UpToDate = 0,
    209    // The list contains no useful information and if anyone asks it anything it
    210    // will have to populate itself before answering.
    211    Dirty,
    212    // The list has populated itself to a certain extent and that that part of
    213    // the list is still valid.  Requests for things outside that part of the
    214    // list will require walking the tree some more.  When a list is in this
    215    // state, the last thing in mElements is the last node in the tree that the
    216    // list looked at.
    217    Lazy,
    218  };
    219 
    220 public:
    221  NS_DECL_ISUPPORTS_INHERITED
    222 
    223  /**
    224   * @param aRootNode The node under which to limit our search.
    225   * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId.
    226   *                   The special value "*" always matches whatever aMatchAtom
    227   *                   is matched against.
    228   * @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
    229   *                          tagName to match.
    230   *                          If kNameSpaceID_Wildcard, then aMatchAtom is the
    231   *                          localName to match.
    232   *                          Otherwise we match nodes whose namespace is
    233   *                          aMatchNameSpaceId and localName matches
    234   *                          aMatchAtom.
    235   * @param aDeep If false, then look only at children of the root, nothing
    236   *              deeper.  If true, then look at the whole subtree rooted at
    237   *              our root.
    238   * @param aLiveList Whether the created list should be a live list observing
    239   *                  mutations to the DOM tree.
    240   */
    241  nsContentList(nsINode* aRootNode, int32_t aMatchNameSpaceId,
    242                nsAtom* aHTMLMatchAtom, nsAtom* aXMLMatchAtom,
    243                bool aDeep = true, bool aLiveList = true);
    244 
    245  /**
    246   * @param aRootNode The node under which to limit our search.
    247   * @param aFunc the function to be called to determine whether we match.
    248   *              This function MUST NOT ever cause mutation of the DOM.
    249   *              The nsContentList implementation guarantees that everything
    250   *              passed to the function will be IsElement().
    251   * @param aDestroyFunc the function that will be called to destroy aData
    252   * @param aData closure data that will need to be passed back to aFunc
    253   * @param aDeep If false, then look only at children of the root, nothing
    254   *              deeper.  If true, then look at the whole subtree rooted at
    255   *              our root.
    256   * @param aMatchAtom an atom to be passed back to aFunc
    257   * @param aMatchNameSpaceId a namespace id to be passed back to aFunc
    258   * @param aFuncMayDependOnAttr a boolean that indicates whether this list is
    259   *                             sensitive to attribute changes.
    260   * @param aLiveList Whether the created list should be a live list observing
    261   *                  mutations to the DOM tree.
    262   */
    263  nsContentList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
    264                nsContentListDestroyFunc aDestroyFunc, void* aData,
    265                bool aDeep = true, nsAtom* aMatchAtom = nullptr,
    266                int32_t aMatchNameSpaceId = kNameSpaceID_None,
    267                bool aFuncMayDependOnAttr = true, bool aLiveList = true);
    268 
    269  // nsWrapperCache
    270  using nsWrapperCache::GetWrapperPreserveColor;
    271  using nsWrapperCache::PreserveWrapper;
    272  JSObject* WrapObject(JSContext* aCx,
    273                       JS::Handle<JSObject*> aGivenProto) override;
    274 
    275 protected:
    276  virtual ~nsContentList();
    277 
    278  JSObject* GetWrapperPreserveColorInternal() override {
    279    return nsWrapperCache::GetWrapperPreserveColor();
    280  }
    281  void PreserveWrapperInternal(nsISupports* aScriptObjectHolder) override {
    282    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
    283  }
    284 
    285 public:
    286  // nsBaseContentList overrides
    287  int32_t IndexOf(nsIContent* aContent, bool aDoFlush) override;
    288  int32_t IndexOf(nsIContent* aContent) override;
    289  nsINode* GetParentObject() override { return mRootNode; }
    290 
    291  uint32_t Length() final { return Length(true); }
    292  nsIContent* Item(uint32_t aIndex) final;
    293  Element* GetElementAt(uint32_t index) override;
    294  Element* GetFirstNamedElement(const nsAString& aName, bool& aFound) override {
    295    Element* item = NamedItem(aName, true);
    296    aFound = !!item;
    297    return item;
    298  }
    299  void GetSupportedNames(nsTArray<nsString>& aNames) override;
    300 
    301  // nsContentList public methods
    302  uint32_t Length(bool aDoFlush);
    303  nsIContent* Item(uint32_t aIndex, bool aDoFlush);
    304  Element* NamedItem(const nsAString& aName, bool aDoFlush);
    305 
    306  // nsIMutationObserver
    307  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    308  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    309  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    310  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    311  NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
    312 
    313  static nsContentList* FromSupports(nsISupports* aSupports) {
    314    nsINodeList* list = static_cast<nsINodeList*>(aSupports);
    315 #ifdef DEBUG
    316    {
    317      nsCOMPtr<nsINodeList> list_qi = do_QueryInterface(aSupports);
    318 
    319      // If this assertion fires the QI implementation for the object in
    320      // question doesn't use the nsINodeList pointer as the nsISupports
    321      // pointer. That must be fixed, or we'll crash...
    322      NS_ASSERTION(list_qi == list, "Uh, fix QI!");
    323    }
    324 #endif
    325    return static_cast<nsContentList*>(list);
    326  }
    327 
    328  bool MatchesKey(const nsContentListKey& aKey) const {
    329    // The root node is most commonly the same: the document.  And the
    330    // most common namespace id is kNameSpaceID_Unknown.  So check the
    331    // string first.  Cases in which whether our root's ownerDocument
    332    // is HTML changes are extremely rare, so check those last.
    333    MOZ_ASSERT(mXMLMatchAtom,
    334               "How did we get here with a null match atom on our list?");
    335    return mXMLMatchAtom->Equals(aKey.mTagname) &&
    336           mRootNode == aKey.mRootNode &&
    337           mMatchNameSpaceId == aKey.mMatchNameSpaceId &&
    338           mIsHTMLDocument == aKey.mIsHTMLDocument;
    339  }
    340 
    341  /**
    342   * Sets the state to LIST_DIRTY and clears mElements array.
    343   * @note This is the only acceptable way to set state to LIST_DIRTY.
    344   */
    345  void SetDirty() {
    346    mState = State::Dirty;
    347    InvalidateNamedItemsCache();
    348    Reset();
    349    SetEnabledCallbacks(nsIMutationObserver::kNodeWillBeDestroyed);
    350  }
    351 
    352  void LastRelease() override;
    353 
    354  class HashEntry;
    355 
    356 protected:
    357  // A cache from name to the first named item in mElements. Only possibly
    358  // non-null when mState is State::UpToDate. Elements are kept alive by our
    359  // mElements array.
    360  using NamedItemsCache = nsTHashMap<nsAtomHashKey, Element*>;
    361 
    362  void InvalidateNamedItemsCache() {
    363    mNamedItemsCache = nullptr;
    364    mNamedItemsCacheValid = false;
    365  }
    366 
    367  inline void InsertElementInNamedItemsCache(nsIContent&);
    368  inline void InvalidateNamedItemsCacheForAttributeChange(int32_t aNameSpaceID,
    369                                                          nsAtom* aAttribute);
    370  inline void InvalidateNamedItemsCacheForInsertion(Element&);
    371  inline void InvalidateNamedItemsCacheForDeletion(Element&);
    372 
    373  void EnsureNamedItemsCacheValid(bool aDoFlush);
    374 
    375  /**
    376   * Returns whether the element matches our criterion
    377   *
    378   * @param  aElement the element to attempt to match
    379   * @return whether we match
    380   */
    381  bool Match(Element* aElement);
    382  /**
    383   * See if anything in the subtree rooted at aContent, including
    384   * aContent itself, matches our criterion.
    385   *
    386   * @param  aContent the root of the subtree to match against
    387   * @return whether we match something in the tree rooted at aContent
    388   */
    389  bool MatchSelf(nsIContent* aContent);
    390 
    391  /**
    392   * Populate our list.  Stop once we have at least aNeededLength
    393   * elements.  At the end of PopulateSelf running, either the last
    394   * node we examined is the last node in our array or we have
    395   * traversed the whole document (or both).
    396   *
    397   * @param aNeededLength the length the list should have when we are
    398   *        done (unless it exhausts the document)
    399   * @param aExpectedElementsIfDirty is for debugging only to
    400   *        assert that mElements has expected number of entries.
    401   */
    402  virtual void PopulateSelf(uint32_t aNeededLength,
    403                            uint32_t aExpectedElementsIfDirty = 0);
    404 
    405  /**
    406   * @param  aContainer a content node which must be a descendant of
    407   *         mRootNode
    408   * @return true if children or descendants of aContainer could match our
    409   *                 criterion.
    410   *         false otherwise.
    411   */
    412  bool MayContainRelevantNodes(nsINode* aContainer) {
    413    return mDeep || aContainer == mRootNode;
    414  }
    415 
    416  /**
    417   * Remove ourselves from the hashtable that caches commonly accessed
    418   * content lists.  Generally done on destruction.
    419   */
    420  void RemoveFromHashtable();
    421  /**
    422   * If state is not LIST_UP_TO_DATE, fully populate ourselves with
    423   * all the nodes we can find.
    424   */
    425  inline void BringSelfUpToDate(bool aDoFlush);
    426 
    427  /**
    428   * To be called from non-destructor locations that want to remove from caches.
    429   * Needed because if subclasses want to have cache behavior they can't just
    430   * override RemoveFromHashtable(), since we call that in our destructor.
    431   */
    432  void RemoveFromCaches() override { RemoveFromHashtable(); }
    433 
    434  void MaybeMarkDirty() {
    435    if (mState != State::Dirty && ++mMissedUpdates > 128) {
    436      mMissedUpdates = 0;
    437      SetDirty();
    438    }
    439  }
    440 
    441  nsINode* mRootNode;  // Weak ref
    442  int32_t mMatchNameSpaceId;
    443  RefPtr<nsAtom> mHTMLMatchAtom;
    444  RefPtr<nsAtom> mXMLMatchAtom;
    445 
    446  /**
    447   * Function to use to determine whether a piece of content matches
    448   * our criterion
    449   */
    450  nsContentListMatchFunc mFunc = nullptr;
    451  /**
    452   * Cleanup closure data with this.
    453   */
    454  nsContentListDestroyFunc mDestroyFunc = nullptr;
    455  /**
    456   * Closure data to pass to mFunc when we call it
    457   */
    458  void* mData = nullptr;
    459 
    460  mozilla::UniquePtr<NamedItemsCache> mNamedItemsCache;
    461 
    462  uint8_t mMissedUpdates = 0;
    463 
    464  // The current state of the list.
    465  State mState;
    466 
    467  /**
    468   * True if we are looking for elements named "*"
    469   */
    470  bool mMatchAll : 1;
    471  /**
    472   * Whether to actually descend the tree.  If this is false, we won't
    473   * consider grandkids of mRootNode.
    474   */
    475  bool mDeep : 1;
    476  /**
    477   * Whether the return value of mFunc could depend on the values of
    478   * attributes.
    479   */
    480  bool mFuncMayDependOnAttr : 1;
    481  /**
    482   * Whether we actually need to flush to get our state correct.
    483   */
    484  bool mFlushesNeeded : 1;
    485  /**
    486   * Whether the ownerDocument of our root node at list creation time was an
    487   * HTML document.  Only needed when we're doing a namespace/atom match, not
    488   * when doing function matching, always false otherwise.
    489   */
    490  bool mIsHTMLDocument : 1;
    491  /**
    492   * True mNamedItemsCache is valid. Note mNamedItemsCache might still be null
    493   * if there's no named items at all.
    494   */
    495  bool mNamedItemsCacheValid : 1;
    496  /**
    497   * Whether the list observes mutations to the DOM tree.
    498   */
    499  const bool mIsLiveList : 1;
    500  /*
    501   * True if this content list is cached in a hash table.
    502   * For nsContentList (but not its subclasses), the hash table is
    503   * gContentListHashTable.
    504   * For nsCacheableFuncStringContentList, the hash table is
    505   * gFuncStringContentListHashTable.
    506   * Other subclasses of nsContentList can't be in hash tables.
    507   */
    508  bool mInHashtable : 1;
    509 
    510 #ifdef DEBUG_CONTENT_LIST
    511  void AssertInSync();
    512 #endif
    513 };
    514 
    515 /**
    516 * A class of cacheable content list; cached on the combination of aRootNode +
    517 * aFunc + aDataString
    518 */
    519 class nsCacheableFuncStringContentList;
    520 
    521 class MOZ_STACK_CLASS nsFuncStringCacheKey {
    522 public:
    523  nsFuncStringCacheKey(nsINode* aRootNode, nsContentListMatchFunc aFunc,
    524                       const nsAString& aString)
    525      : mRootNode(aRootNode), mFunc(aFunc), mString(aString) {}
    526 
    527  uint32_t GetHash(void) const {
    528    uint32_t hash = mozilla::HashString(mString);
    529    return mozilla::AddToHash(hash, mRootNode, mFunc);
    530  }
    531 
    532 private:
    533  friend class nsCacheableFuncStringContentList;
    534 
    535  nsINode* const mRootNode;
    536  const nsContentListMatchFunc mFunc;
    537  const nsAString& mString;
    538 };
    539 
    540 // aDestroyFunc is allowed to be null
    541 // aDataAllocator must always return a non-null pointer
    542 class nsCacheableFuncStringContentList : public nsContentList {
    543 public:
    544  virtual ~nsCacheableFuncStringContentList();
    545 
    546  bool Equals(const nsFuncStringCacheKey* aKey) {
    547    return mRootNode == aKey->mRootNode && mFunc == aKey->mFunc &&
    548           mString == aKey->mString;
    549  }
    550 
    551  enum ContentListType { eNodeList, eHTMLCollection };
    552 #ifdef DEBUG
    553  ContentListType mType;
    554 #endif
    555 
    556  class HashEntry;
    557 
    558 protected:
    559  nsCacheableFuncStringContentList(
    560      nsINode* aRootNode, nsContentListMatchFunc aFunc,
    561      nsContentListDestroyFunc aDestroyFunc,
    562      nsFuncStringContentListDataAllocator aDataAllocator,
    563      const nsAString& aString, mozilla::DebugOnly<ContentListType> aType)
    564      : nsContentList(aRootNode, aFunc, aDestroyFunc, nullptr),
    565 #ifdef DEBUG
    566        mType(aType),
    567 #endif
    568        mString(aString) {
    569    mData = (*aDataAllocator)(aRootNode, &mString);
    570    MOZ_ASSERT(mData);
    571  }
    572 
    573  void RemoveFromCaches() override { RemoveFromFuncStringHashtable(); }
    574  void RemoveFromFuncStringHashtable();
    575 
    576  nsString mString;
    577 };
    578 
    579 class nsCachableElementsByNameNodeList
    580    : public nsCacheableFuncStringContentList {
    581 public:
    582  nsCachableElementsByNameNodeList(
    583      nsINode* aRootNode, nsContentListMatchFunc aFunc,
    584      nsContentListDestroyFunc aDestroyFunc,
    585      nsFuncStringContentListDataAllocator aDataAllocator,
    586      const nsAString& aString)
    587      : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
    588                                         aDataAllocator, aString, eNodeList) {}
    589 
    590  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    591 
    592  JSObject* WrapObject(JSContext* cx,
    593                       JS::Handle<JSObject*> aGivenProto) override;
    594 
    595 #ifdef DEBUG
    596  static const ContentListType sType;
    597 #endif
    598 };
    599 
    600 class nsCacheableFuncStringHTMLCollection
    601    : public nsCacheableFuncStringContentList {
    602 public:
    603  nsCacheableFuncStringHTMLCollection(
    604      nsINode* aRootNode, nsContentListMatchFunc aFunc,
    605      nsContentListDestroyFunc aDestroyFunc,
    606      nsFuncStringContentListDataAllocator aDataAllocator,
    607      const nsAString& aString)
    608      : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
    609                                         aDataAllocator, aString,
    610                                         eHTMLCollection) {}
    611 
    612  JSObject* WrapObject(JSContext* cx,
    613                       JS::Handle<JSObject*> aGivenProto) override;
    614 
    615 #ifdef DEBUG
    616  static const ContentListType sType;
    617 #endif
    618 };
    619 
    620 class nsLabelsNodeList final : public nsContentList {
    621 public:
    622  nsLabelsNodeList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
    623                   nsContentListDestroyFunc aDestroyFunc, void* aData)
    624      : nsContentList(aRootNode, aFunc, aDestroyFunc, aData) {}
    625 
    626  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    627  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    628  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    629  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    630 
    631  JSObject* WrapObject(JSContext* cx,
    632                       JS::Handle<JSObject*> aGivenProto) override;
    633 
    634  /**
    635   * Reset root, mutation observer, and clear content list
    636   * if the root has been changed.
    637   *
    638   * @param aRootNode The node under which to limit our search.
    639   */
    640  void MaybeResetRoot(nsINode* aRootNode);
    641 
    642 private:
    643  /**
    644   * Start searching at the last one if we already have nodes, otherwise
    645   * start searching at the root.
    646   *
    647   * @param aNeededLength The list of length should have when we are
    648   *                      done (unless it exhausts the document).
    649   * @param aExpectedElementsIfDirty is for debugging only to
    650   *        assert that mElements has expected number of entries.
    651   */
    652  void PopulateSelf(uint32_t aNeededLength,
    653                    uint32_t aExpectedElementsIfDirty = 0) override;
    654 };
    655 #endif  // nsContentList_h___