Bug 1665252 - remove allowpaymentrequest attribute from HTMLIFrameElement r=dom-worke...
[gecko.git] / dom / base / nsContentList.h
blob7a0d5f23240e0245731c47024e7ca03edcf7fd1a
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/. */
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).
13 #ifndef nsContentList_h___
14 #define nsContentList_h___
16 #include "mozilla/Attributes.h"
17 #include "nsContentListDeclarations.h"
18 #include "nsISupports.h"
19 #include "nsTArray.h"
20 #include "nsString.h"
21 #include "nsIHTMLCollection.h"
22 #include "nsINodeList.h"
23 #include "nsStubMutationObserver.h"
24 #include "nsAtom.h"
25 #include "nsCycleCollectionParticipant.h"
26 #include "nsNameSpaceManager.h"
27 #include "nsWrapperCache.h"
28 #include "nsHashKeys.h"
29 #include "mozilla/HashFunctions.h"
30 #include "mozilla/MemoryReporting.h"
31 #include "mozilla/dom/NameSpaceConstants.h"
33 namespace mozilla {
34 namespace dom {
35 class Element;
36 } // namespace dom
37 } // namespace mozilla
39 class nsBaseContentList : public nsINodeList {
40 public:
41 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
43 // nsINodeList
44 virtual int32_t IndexOf(nsIContent* aContent) override;
45 virtual nsIContent* Item(uint32_t aIndex) override;
47 uint32_t Length() override { return mElements.Length(); }
49 NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsBaseContentList)
51 void AppendElement(nsIContent* aContent) {
52 mElements.AppendElement(aContent);
54 void MaybeAppendElement(nsIContent* aContent) {
55 if (aContent) AppendElement(aContent);
58 /**
59 * Insert the element at a given index, shifting the objects at
60 * the given index and later to make space.
61 * @param aContent Element to insert, must not be null
62 * @param aIndex Index to insert the element at.
64 void InsertElementAt(nsIContent* aContent, int32_t aIndex) {
65 NS_ASSERTION(aContent, "Element to insert must not be null");
66 mElements.InsertElementAt(aIndex, aContent);
69 void RemoveElement(nsIContent* aContent) {
70 mElements.RemoveElement(aContent);
73 void Reset() { mElements.Clear(); }
75 virtual int32_t IndexOf(nsIContent* aContent, bool aDoFlush);
77 virtual JSObject* WrapObject(JSContext* cx,
78 JS::Handle<JSObject*> aGivenProto) override = 0;
80 void SetCapacity(uint32_t aCapacity) { mElements.SetCapacity(aCapacity); }
82 virtual void LastRelease() {}
84 // Memory reporting. For now, subclasses of nsBaseContentList don't really
85 // need to report any members that are not part of the object itself, so we
86 // don't need to make this virtual.
87 size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
89 protected:
90 virtual ~nsBaseContentList();
92 /**
93 * To be called from non-destructor locations (e.g. unlink) that want to
94 * remove from caches. Cacheable subclasses should override.
96 virtual void RemoveFromCaches() {}
98 AutoTArray<nsCOMPtr<nsIContent>, 10> mElements;
101 class nsSimpleContentList : public nsBaseContentList {
102 public:
103 explicit nsSimpleContentList(nsINode* aRoot)
104 : nsBaseContentList(), mRoot(aRoot) {}
106 NS_DECL_ISUPPORTS_INHERITED
107 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsSimpleContentList,
108 nsBaseContentList)
110 virtual nsINode* GetParentObject() override { return mRoot; }
111 virtual JSObject* WrapObject(JSContext* cx,
112 JS::Handle<JSObject*> aGivenProto) override;
114 protected:
115 virtual ~nsSimpleContentList() = default;
117 private:
118 // This has to be a strong reference, the root might go away before the list.
119 nsCOMPtr<nsINode> mRoot;
122 // Used for returning lists that will always be empty, such as the applets list
123 // in HTML Documents
124 class nsEmptyContentList final : public nsBaseContentList,
125 public nsIHTMLCollection {
126 public:
127 explicit nsEmptyContentList(nsINode* aRoot)
128 : nsBaseContentList(), mRoot(aRoot) {}
130 NS_DECL_ISUPPORTS_INHERITED
131 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsEmptyContentList,
132 nsBaseContentList)
134 virtual nsINode* GetParentObject() override { return mRoot; }
136 virtual JSObject* WrapObject(JSContext* cx,
137 JS::Handle<JSObject*> aGivenProto) override;
139 virtual JSObject* GetWrapperPreserveColorInternal() override {
140 return nsWrapperCache::GetWrapperPreserveColor();
142 virtual void PreserveWrapperInternal(
143 nsISupports* aScriptObjectHolder) override {
144 nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
147 uint32_t Length() final { return 0; }
148 virtual nsIContent* Item(uint32_t aIndex) override;
149 virtual mozilla::dom::Element* GetElementAt(uint32_t index) override;
150 virtual mozilla::dom::Element* GetFirstNamedElement(const nsAString& aName,
151 bool& aFound) override;
152 virtual void GetSupportedNames(nsTArray<nsString>& aNames) override;
154 protected:
155 virtual ~nsEmptyContentList() = default;
157 private:
158 // This has to be a strong reference, the root might go away before the list.
159 nsCOMPtr<nsINode> mRoot;
163 * Class that's used as the key to hash nsContentList implementations
164 * for fast retrieval
166 struct nsContentListKey {
167 // We have to take an aIsHTMLDocument arg for two reasons:
168 // 1) We don't want to include Document.h in this header.
169 // 2) We need to do that to make nsContentList::RemoveFromHashtable
170 // work, because by the time it's called the document of the
171 // list's root node might have changed.
172 nsContentListKey(nsINode* aRootNode, int32_t aMatchNameSpaceId,
173 const nsAString& aTagname, bool aIsHTMLDocument)
174 : mRootNode(aRootNode),
175 mMatchNameSpaceId(aMatchNameSpaceId),
176 mTagname(aTagname),
177 mIsHTMLDocument(aIsHTMLDocument),
178 mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode,
179 mMatchNameSpaceId, mIsHTMLDocument)) {}
181 nsContentListKey(const nsContentListKey& aContentListKey) = default;
183 inline uint32_t GetHash(void) const { return mHash; }
185 nsINode* const mRootNode; // Weak ref
186 const int32_t mMatchNameSpaceId;
187 const nsAString& mTagname;
188 bool mIsHTMLDocument;
189 const uint32_t mHash;
193 * LIST_UP_TO_DATE means that the list is up to date and need not do
194 * any walking to be able to answer any questions anyone may have.
196 #define LIST_UP_TO_DATE 0
198 * LIST_DIRTY means that the list contains no useful information and
199 * if anyone asks it anything it will have to populate itself before
200 * answering.
202 #define LIST_DIRTY 1
204 * LIST_LAZY means that the list has populated itself to a certain
205 * extent and that that part of the list is still valid. Requests for
206 * things outside that part of the list will require walking the tree
207 * some more. When a list is in this state, the last thing in
208 * mElements is the last node in the tree that the list looked at.
210 #define LIST_LAZY 2
213 * Class that implements a possibly live NodeList that matches Elements
214 * in the tree based on some criterion.
216 class nsContentList : public nsBaseContentList,
217 public nsIHTMLCollection,
218 public nsStubMutationObserver {
219 public:
220 NS_DECL_ISUPPORTS_INHERITED
223 * @param aRootNode The node under which to limit our search.
224 * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId.
225 * The special value "*" always matches whatever aMatchAtom
226 * is matched against.
227 * @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
228 * tagName to match.
229 * If kNameSpaceID_Wildcard, then aMatchAtom is the
230 * localName to match.
231 * Otherwise we match nodes whose namespace is
232 * aMatchNameSpaceId and localName matches
233 * aMatchAtom.
234 * @param aDeep If false, then look only at children of the root, nothing
235 * deeper. If true, then look at the whole subtree rooted at
236 * our root.
237 * @param aLiveList Whether the created list should be a live list observing
238 * mutations to the DOM tree.
240 nsContentList(nsINode* aRootNode, int32_t aMatchNameSpaceId,
241 nsAtom* aHTMLMatchAtom, nsAtom* aXMLMatchAtom,
242 bool aDeep = true, bool aLiveList = true);
245 * @param aRootNode The node under which to limit our search.
246 * @param aFunc the function to be called to determine whether we match.
247 * This function MUST NOT ever cause mutation of the DOM.
248 * The nsContentList implementation guarantees that everything
249 * passed to the function will be IsElement().
250 * @param aDestroyFunc the function that will be called to destroy aData
251 * @param aData closure data that will need to be passed back to aFunc
252 * @param aDeep If false, then look only at children of the root, nothing
253 * deeper. If true, then look at the whole subtree rooted at
254 * our root.
255 * @param aMatchAtom an atom to be passed back to aFunc
256 * @param aMatchNameSpaceId a namespace id to be passed back to aFunc
257 * @param aFuncMayDependOnAttr a boolean that indicates whether this list is
258 * sensitive to attribute changes.
259 * @param aLiveList Whether the created list should be a live list observing
260 * mutations to the DOM tree.
262 nsContentList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
263 nsContentListDestroyFunc aDestroyFunc, void* aData,
264 bool aDeep = true, nsAtom* aMatchAtom = nullptr,
265 int32_t aMatchNameSpaceId = kNameSpaceID_None,
266 bool aFuncMayDependOnAttr = true, bool aLiveList = true);
268 // nsWrapperCache
269 using nsWrapperCache::GetWrapperPreserveColor;
270 using nsWrapperCache::PreserveWrapper;
271 virtual JSObject* WrapObject(JSContext* aCx,
272 JS::Handle<JSObject*> aGivenProto) override;
274 protected:
275 virtual ~nsContentList();
277 virtual JSObject* GetWrapperPreserveColorInternal() override {
278 return nsWrapperCache::GetWrapperPreserveColor();
280 virtual void PreserveWrapperInternal(
281 nsISupports* aScriptObjectHolder) override {
282 nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
285 public:
286 // nsBaseContentList overrides
287 virtual int32_t IndexOf(nsIContent* aContent, bool aDoFlush) override;
288 virtual int32_t IndexOf(nsIContent* aContent) override;
289 virtual nsINode* GetParentObject() override { return mRootNode; }
291 uint32_t Length() final { return Length(true); }
292 nsIContent* Item(uint32_t aIndex) final;
293 virtual mozilla::dom::Element* GetElementAt(uint32_t index) override;
294 virtual mozilla::dom::Element* GetFirstNamedElement(const nsAString& aName,
295 bool& aFound) override {
296 mozilla::dom::Element* item = NamedItem(aName, true);
297 aFound = !!item;
298 return item;
300 virtual void GetSupportedNames(nsTArray<nsString>& aNames) override;
302 // nsContentList public methods
303 uint32_t Length(bool aDoFlush);
304 nsIContent* Item(uint32_t aIndex, bool aDoFlush);
305 mozilla::dom::Element* NamedItem(const nsAString& aName, bool aDoFlush);
307 // nsIMutationObserver
308 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
309 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
310 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
311 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
312 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
314 static nsContentList* FromSupports(nsISupports* aSupports) {
315 nsINodeList* list = static_cast<nsINodeList*>(aSupports);
316 #ifdef DEBUG
318 nsCOMPtr<nsINodeList> list_qi = do_QueryInterface(aSupports);
320 // If this assertion fires the QI implementation for the object in
321 // question doesn't use the nsINodeList pointer as the nsISupports
322 // pointer. That must be fixed, or we'll crash...
323 NS_ASSERTION(list_qi == list, "Uh, fix QI!");
325 #endif
326 return static_cast<nsContentList*>(list);
329 bool MatchesKey(const nsContentListKey& aKey) const {
330 // The root node is most commonly the same: the document. And the
331 // most common namespace id is kNameSpaceID_Unknown. So check the
332 // string first. Cases in which whether our root's ownerDocument
333 // is HTML changes are extremely rare, so check those last.
334 MOZ_ASSERT(mXMLMatchAtom,
335 "How did we get here with a null match atom on our list?");
336 return mXMLMatchAtom->Equals(aKey.mTagname) &&
337 mRootNode == aKey.mRootNode &&
338 mMatchNameSpaceId == aKey.mMatchNameSpaceId &&
339 mIsHTMLDocument == aKey.mIsHTMLDocument;
343 * Sets the state to LIST_DIRTY and clears mElements array.
344 * @note This is the only acceptable way to set state to LIST_DIRTY.
346 void SetDirty() {
347 mState = LIST_DIRTY;
348 Reset();
351 virtual void LastRelease() override;
353 protected:
355 * Returns whether the element matches our criterion
357 * @param aElement the element to attempt to match
358 * @return whether we match
360 bool Match(mozilla::dom::Element* aElement);
362 * See if anything in the subtree rooted at aContent, including
363 * aContent itself, matches our criterion.
365 * @param aContent the root of the subtree to match against
366 * @return whether we match something in the tree rooted at aContent
368 bool MatchSelf(nsIContent* aContent);
371 * Populate our list. Stop once we have at least aNeededLength
372 * elements. At the end of PopulateSelf running, either the last
373 * node we examined is the last node in our array or we have
374 * traversed the whole document (or both).
376 * @param aNeededLength the length the list should have when we are
377 * done (unless it exhausts the document)
378 * @param aExpectedElementsIfDirty is for debugging only to
379 * assert that mElements has expected number of entries.
381 virtual void PopulateSelf(uint32_t aNeededLength,
382 uint32_t aExpectedElementsIfDirty = 0);
385 * @param aContainer a content node which must be a descendant of
386 * mRootNode
387 * @return true if children or descendants of aContainer could match our
388 * criterion.
389 * false otherwise.
391 bool MayContainRelevantNodes(nsINode* aContainer) {
392 return mDeep || aContainer == mRootNode;
396 * Remove ourselves from the hashtable that caches commonly accessed
397 * content lists. Generally done on destruction.
399 void RemoveFromHashtable();
401 * If state is not LIST_UP_TO_DATE, fully populate ourselves with
402 * all the nodes we can find.
404 inline void BringSelfUpToDate(bool aDoFlush);
407 * To be called from non-destructor locations that want to remove from caches.
408 * Needed because if subclasses want to have cache behavior they can't just
409 * override RemoveFromHashtable(), since we call that in our destructor.
411 virtual void RemoveFromCaches() override { RemoveFromHashtable(); }
413 nsINode* mRootNode; // Weak ref
414 int32_t mMatchNameSpaceId;
415 RefPtr<nsAtom> mHTMLMatchAtom;
416 RefPtr<nsAtom> mXMLMatchAtom;
419 * Function to use to determine whether a piece of content matches
420 * our criterion
422 nsContentListMatchFunc mFunc;
424 * Cleanup closure data with this.
426 nsContentListDestroyFunc mDestroyFunc;
428 * Closure data to pass to mFunc when we call it
430 void* mData;
432 * The current state of the list (possible values are:
433 * LIST_UP_TO_DATE, LIST_LAZY, LIST_DIRTY
435 uint8_t mState;
437 // The booleans have to use uint8_t to pack with mState, because MSVC won't
438 // pack different typedefs together. Once we no longer have to worry about
439 // flushes in XML documents, we can go back to using bool for the
440 // booleans.
443 * True if we are looking for elements named "*"
445 uint8_t mMatchAll : 1;
447 * Whether to actually descend the tree. If this is false, we won't
448 * consider grandkids of mRootNode.
450 uint8_t mDeep : 1;
452 * Whether the return value of mFunc could depend on the values of
453 * attributes.
455 uint8_t mFuncMayDependOnAttr : 1;
457 * Whether we actually need to flush to get our state correct.
459 uint8_t mFlushesNeeded : 1;
461 * Whether the ownerDocument of our root node at list creation time was an
462 * HTML document. Only needed when we're doing a namespace/atom match, not
463 * when doing function matching, always false otherwise.
465 uint8_t mIsHTMLDocument : 1;
467 * Whether the list observes mutations to the DOM tree.
469 const uint8_t mIsLiveList : 1;
471 #ifdef DEBUG_CONTENT_LIST
472 void AssertInSync();
473 #endif
477 * A class of cacheable content list; cached on the combination of aRootNode +
478 * aFunc + aDataString
480 class nsCacheableFuncStringContentList;
482 class MOZ_STACK_CLASS nsFuncStringCacheKey {
483 public:
484 nsFuncStringCacheKey(nsINode* aRootNode, nsContentListMatchFunc aFunc,
485 const nsAString& aString)
486 : mRootNode(aRootNode), mFunc(aFunc), mString(aString) {}
488 uint32_t GetHash(void) const {
489 uint32_t hash = mozilla::HashString(mString);
490 return mozilla::AddToHash(hash, mRootNode, mFunc);
493 private:
494 friend class nsCacheableFuncStringContentList;
496 nsINode* const mRootNode;
497 const nsContentListMatchFunc mFunc;
498 const nsAString& mString;
501 // aDestroyFunc is allowed to be null
502 // aDataAllocator must always return a non-null pointer
503 class nsCacheableFuncStringContentList : public nsContentList {
504 public:
505 virtual ~nsCacheableFuncStringContentList();
507 bool Equals(const nsFuncStringCacheKey* aKey) {
508 return mRootNode == aKey->mRootNode && mFunc == aKey->mFunc &&
509 mString == aKey->mString;
512 enum ContentListType { eNodeList, eHTMLCollection };
513 #ifdef DEBUG
514 ContentListType mType;
515 #endif
517 protected:
518 nsCacheableFuncStringContentList(
519 nsINode* aRootNode, nsContentListMatchFunc aFunc,
520 nsContentListDestroyFunc aDestroyFunc,
521 nsFuncStringContentListDataAllocator aDataAllocator,
522 const nsAString& aString, mozilla::DebugOnly<ContentListType> aType)
523 : nsContentList(aRootNode, aFunc, aDestroyFunc, nullptr),
524 #ifdef DEBUG
525 mType(aType),
526 #endif
527 mString(aString) {
528 mData = (*aDataAllocator)(aRootNode, &mString);
529 MOZ_ASSERT(mData);
532 virtual void RemoveFromCaches() override { RemoveFromFuncStringHashtable(); }
533 void RemoveFromFuncStringHashtable();
535 nsString mString;
538 class nsCachableElementsByNameNodeList
539 : public nsCacheableFuncStringContentList {
540 public:
541 nsCachableElementsByNameNodeList(
542 nsINode* aRootNode, nsContentListMatchFunc aFunc,
543 nsContentListDestroyFunc aDestroyFunc,
544 nsFuncStringContentListDataAllocator aDataAllocator,
545 const nsAString& aString)
546 : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
547 aDataAllocator, aString, eNodeList) {}
549 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
551 virtual JSObject* WrapObject(JSContext* cx,
552 JS::Handle<JSObject*> aGivenProto) override;
554 #ifdef DEBUG
555 static const ContentListType sType;
556 #endif
559 class nsCacheableFuncStringHTMLCollection
560 : public nsCacheableFuncStringContentList {
561 public:
562 nsCacheableFuncStringHTMLCollection(
563 nsINode* aRootNode, nsContentListMatchFunc aFunc,
564 nsContentListDestroyFunc aDestroyFunc,
565 nsFuncStringContentListDataAllocator aDataAllocator,
566 const nsAString& aString)
567 : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
568 aDataAllocator, aString,
569 eHTMLCollection) {}
571 virtual JSObject* WrapObject(JSContext* cx,
572 JS::Handle<JSObject*> aGivenProto) override;
574 #ifdef DEBUG
575 static const ContentListType sType;
576 #endif
579 class nsLabelsNodeList final : public nsContentList {
580 public:
581 nsLabelsNodeList(nsINode* aRootNode, nsContentListMatchFunc aFunc,
582 nsContentListDestroyFunc aDestroyFunc, void* aData)
583 : nsContentList(aRootNode, aFunc, aDestroyFunc, aData) {}
585 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
586 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
587 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
588 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
590 virtual JSObject* WrapObject(JSContext* cx,
591 JS::Handle<JSObject*> aGivenProto) override;
594 * Reset root, mutation observer, and clear content list
595 * if the root has been changed.
597 * @param aRootNode The node under which to limit our search.
599 void MaybeResetRoot(nsINode* aRootNode);
601 private:
603 * Start searching at the last one if we already have nodes, otherwise
604 * start searching at the root.
606 * @param aNeededLength The list of length should have when we are
607 * done (unless it exhausts the document).
608 * @param aExpectedElementsIfDirty is for debugging only to
609 * assert that mElements has expected number of entries.
611 void PopulateSelf(uint32_t aNeededLength,
612 uint32_t aExpectedElementsIfDirty = 0) override;
614 #endif // nsContentList_h___