Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * nsBaseContentList is a basic list of content nodes; nsContentList
8 * is a commonly used NodeList implementation (used for
9 * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc).
10 */
12 #ifndef nsContentList_h___
13 #define nsContentList_h___
15 #include "mozilla/Attributes.h"
16 #include "nsContentListDeclarations.h"
17 #include "nsISupports.h"
18 #include "nsTArray.h"
19 #include "nsString.h"
20 #include "nsIHTMLCollection.h"
21 #include "nsIDOMNodeList.h"
22 #include "nsINodeList.h"
23 #include "nsStubMutationObserver.h"
24 #include "nsIAtom.h"
25 #include "nsCycleCollectionParticipant.h"
26 #include "nsNameSpaceManager.h"
27 #include "nsWrapperCache.h"
28 #include "nsHashKeys.h"
29 #include "mozilla/HashFunctions.h"
31 namespace mozilla {
32 namespace dom {
33 class Element;
34 }
35 }
38 class nsBaseContentList : public nsINodeList
39 {
40 public:
41 nsBaseContentList()
42 {
43 SetIsDOMBinding();
44 }
45 virtual ~nsBaseContentList();
47 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
49 // nsIDOMNodeList
50 NS_DECL_NSIDOMNODELIST
52 // nsINodeList
53 virtual int32_t IndexOf(nsIContent* aContent) MOZ_OVERRIDE;
54 virtual nsIContent* Item(uint32_t aIndex) MOZ_OVERRIDE;
56 uint32_t Length() const {
57 return mElements.Length();
58 }
60 NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsBaseContentList)
62 void AppendElement(nsIContent *aContent)
63 {
64 mElements.AppendElement(aContent);
65 }
66 void MaybeAppendElement(nsIContent* aContent)
67 {
68 if (aContent)
69 AppendElement(aContent);
70 }
72 /**
73 * Insert the element at a given index, shifting the objects at
74 * the given index and later to make space.
75 * @param aContent Element to insert, must not be null
76 * @param aIndex Index to insert the element at.
77 */
78 void InsertElementAt(nsIContent* aContent, int32_t aIndex)
79 {
80 NS_ASSERTION(aContent, "Element to insert must not be null");
81 mElements.InsertElementAt(aIndex, aContent);
82 }
84 void RemoveElement(nsIContent *aContent)
85 {
86 mElements.RemoveElement(aContent);
87 }
89 void Reset() {
90 mElements.Clear();
91 }
93 virtual int32_t IndexOf(nsIContent *aContent, bool aDoFlush);
95 virtual JSObject* WrapObject(JSContext *cx)
96 MOZ_OVERRIDE = 0;
98 void SetCapacity(uint32_t aCapacity)
99 {
100 mElements.SetCapacity(aCapacity);
101 }
102 protected:
103 /**
104 * To be called from non-destructor locations (e.g. unlink) that want to
105 * remove from caches. Cacheable subclasses should override.
106 */
107 virtual void RemoveFromCaches()
108 {
109 }
111 nsTArray< nsCOMPtr<nsIContent> > mElements;
112 };
115 class nsSimpleContentList : public nsBaseContentList
116 {
117 public:
118 nsSimpleContentList(nsINode *aRoot) : nsBaseContentList(),
119 mRoot(aRoot)
120 {
121 }
123 NS_DECL_ISUPPORTS_INHERITED
124 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsSimpleContentList,
125 nsBaseContentList)
127 virtual nsINode* GetParentObject() MOZ_OVERRIDE
128 {
129 return mRoot;
130 }
131 virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
133 private:
134 // This has to be a strong reference, the root might go away before the list.
135 nsCOMPtr<nsINode> mRoot;
136 };
138 /**
139 * Class that's used as the key to hash nsContentList implementations
140 * for fast retrieval
141 */
142 struct nsContentListKey
143 {
144 nsContentListKey(nsINode* aRootNode,
145 int32_t aMatchNameSpaceId,
146 const nsAString& aTagname)
147 : mRootNode(aRootNode),
148 mMatchNameSpaceId(aMatchNameSpaceId),
149 mTagname(aTagname),
150 mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode,
151 mMatchNameSpaceId))
152 {
153 }
155 nsContentListKey(const nsContentListKey& aContentListKey)
156 : mRootNode(aContentListKey.mRootNode),
157 mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId),
158 mTagname(aContentListKey.mTagname),
159 mHash(aContentListKey.mHash)
160 {
161 }
163 inline uint32_t GetHash(void) const
164 {
165 return mHash;
166 }
168 nsINode* const mRootNode; // Weak ref
169 const int32_t mMatchNameSpaceId;
170 const nsAString& mTagname;
171 const uint32_t mHash;
172 };
174 /**
175 * LIST_UP_TO_DATE means that the list is up to date and need not do
176 * any walking to be able to answer any questions anyone may have.
177 */
178 #define LIST_UP_TO_DATE 0
179 /**
180 * LIST_DIRTY means that the list contains no useful information and
181 * if anyone asks it anything it will have to populate itself before
182 * answering.
183 */
184 #define LIST_DIRTY 1
185 /**
186 * LIST_LAZY means that the list has populated itself to a certain
187 * extent and that that part of the list is still valid. Requests for
188 * things outside that part of the list will require walking the tree
189 * some more. When a list is in this state, the last thing in
190 * mElements is the last node in the tree that the list looked at.
191 */
192 #define LIST_LAZY 2
194 /**
195 * Class that implements a live NodeList that matches Elements in the
196 * tree based on some criterion.
197 */
198 class nsContentList : public nsBaseContentList,
199 public nsIHTMLCollection,
200 public nsStubMutationObserver
201 {
202 public:
203 NS_DECL_ISUPPORTS_INHERITED
205 /**
206 * @param aRootNode The node under which to limit our search.
207 * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId.
208 * The special value "*" always matches whatever aMatchAtom
209 * is matched against.
210 * @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
211 * tagName to match.
212 * If kNameSpaceID_Wildcard, then aMatchAtom is the
213 * localName to match.
214 * Otherwise we match nodes whose namespace is
215 * aMatchNameSpaceId and localName matches
216 * aMatchAtom.
217 * @param aDeep If false, then look only at children of the root, nothing
218 * deeper. If true, then look at the whole subtree rooted at
219 * our root.
220 */
221 nsContentList(nsINode* aRootNode,
222 int32_t aMatchNameSpaceId,
223 nsIAtom* aHTMLMatchAtom,
224 nsIAtom* aXMLMatchAtom,
225 bool aDeep = true);
227 /**
228 * @param aRootNode The node under which to limit our search.
229 * @param aFunc the function to be called to determine whether we match.
230 * This function MUST NOT ever cause mutation of the DOM.
231 * The nsContentList implementation guarantees that everything
232 * passed to the function will be IsElement().
233 * @param aDestroyFunc the function that will be called to destroy aData
234 * @param aData closure data that will need to be passed back to aFunc
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 aMatchAtom an atom to be passed back to aFunc
239 * @param aMatchNameSpaceId a namespace id to be passed back to aFunc
240 * @param aFuncMayDependOnAttr a boolean that indicates whether this list is
241 * sensitive to attribute changes.
242 */
243 nsContentList(nsINode* aRootNode,
244 nsContentListMatchFunc aFunc,
245 nsContentListDestroyFunc aDestroyFunc,
246 void* aData,
247 bool aDeep = true,
248 nsIAtom* aMatchAtom = nullptr,
249 int32_t aMatchNameSpaceId = kNameSpaceID_None,
250 bool aFuncMayDependOnAttr = true);
251 virtual ~nsContentList();
253 // nsWrapperCache
254 using nsWrapperCache::GetWrapperPreserveColor;
255 virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
256 protected:
257 virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE
258 {
259 return nsWrapperCache::GetWrapperPreserveColor();
260 }
261 public:
263 // nsIDOMHTMLCollection
264 NS_DECL_NSIDOMHTMLCOLLECTION
266 // nsBaseContentList overrides
267 virtual int32_t IndexOf(nsIContent *aContent, bool aDoFlush) MOZ_OVERRIDE;
268 virtual int32_t IndexOf(nsIContent* aContent) MOZ_OVERRIDE;
269 virtual nsINode* GetParentObject() MOZ_OVERRIDE
270 {
271 return mRootNode;
272 }
274 virtual nsIContent* Item(uint32_t aIndex) MOZ_OVERRIDE;
275 virtual mozilla::dom::Element* GetElementAt(uint32_t index) MOZ_OVERRIDE;
276 virtual mozilla::dom::Element*
277 GetFirstNamedElement(const nsAString& aName, bool& aFound) MOZ_OVERRIDE
278 {
279 mozilla::dom::Element* item = NamedItem(aName, true);
280 aFound = !!item;
281 return item;
282 }
283 virtual void GetSupportedNames(unsigned aFlags,
284 nsTArray<nsString>& aNames) MOZ_OVERRIDE;
286 // nsContentList public methods
287 NS_HIDDEN_(uint32_t) Length(bool aDoFlush);
288 NS_HIDDEN_(nsIContent*) Item(uint32_t aIndex, bool aDoFlush);
289 NS_HIDDEN_(mozilla::dom::Element*)
290 NamedItem(const nsAString& aName, bool aDoFlush);
292 // nsIMutationObserver
293 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
294 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
295 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
296 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
297 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
299 static nsContentList* FromSupports(nsISupports* aSupports)
300 {
301 nsINodeList* list = static_cast<nsINodeList*>(aSupports);
302 #ifdef DEBUG
303 {
304 nsCOMPtr<nsINodeList> list_qi = do_QueryInterface(aSupports);
306 // If this assertion fires the QI implementation for the object in
307 // question doesn't use the nsINodeList pointer as the nsISupports
308 // pointer. That must be fixed, or we'll crash...
309 NS_ASSERTION(list_qi == list, "Uh, fix QI!");
310 }
311 #endif
312 return static_cast<nsContentList*>(list);
313 }
315 bool MatchesKey(const nsContentListKey& aKey) const
316 {
317 // The root node is most commonly the same: the document. And the
318 // most common namespace id is kNameSpaceID_Unknown. So check the
319 // string first.
320 NS_PRECONDITION(mXMLMatchAtom,
321 "How did we get here with a null match atom on our list?");
322 return
323 mXMLMatchAtom->Equals(aKey.mTagname) &&
324 mRootNode == aKey.mRootNode &&
325 mMatchNameSpaceId == aKey.mMatchNameSpaceId;
326 }
328 /**
329 * Sets the state to LIST_DIRTY and clears mElements array.
330 * @note This is the only acceptable way to set state to LIST_DIRTY.
331 */
332 void SetDirty()
333 {
334 mState = LIST_DIRTY;
335 Reset();
336 }
338 protected:
339 /**
340 * Returns whether the element matches our criterion
341 *
342 * @param aElement the element to attempt to match
343 * @return whether we match
344 */
345 bool Match(mozilla::dom::Element *aElement);
346 /**
347 * See if anything in the subtree rooted at aContent, including
348 * aContent itself, matches our criterion.
349 *
350 * @param aContent the root of the subtree to match against
351 * @return whether we match something in the tree rooted at aContent
352 */
353 bool MatchSelf(nsIContent *aContent);
355 /**
356 * Populate our list. Stop once we have at least aNeededLength
357 * elements. At the end of PopulateSelf running, either the last
358 * node we examined is the last node in our array or we have
359 * traversed the whole document (or both).
360 *
361 * @param aNeededLength the length the list should have when we are
362 * done (unless it exhausts the document)
363 */
364 void PopulateSelf(uint32_t aNeededLength);
366 /**
367 * @param aContainer a content node which must be a descendant of
368 * mRootNode
369 * @return true if children or descendants of aContainer could match our
370 * criterion.
371 * false otherwise.
372 */
373 bool MayContainRelevantNodes(nsINode* aContainer)
374 {
375 return mDeep || aContainer == mRootNode;
376 }
378 /**
379 * Remove ourselves from the hashtable that caches commonly accessed
380 * content lists. Generally done on destruction.
381 */
382 void RemoveFromHashtable();
383 /**
384 * If state is not LIST_UP_TO_DATE, fully populate ourselves with
385 * all the nodes we can find.
386 */
387 inline void BringSelfUpToDate(bool aDoFlush);
389 /**
390 * To be called from non-destructor locations that want to remove from caches.
391 * Needed because if subclasses want to have cache behavior they can't just
392 * override RemoveFromHashtable(), since we call that in our destructor.
393 */
394 virtual void RemoveFromCaches() MOZ_OVERRIDE
395 {
396 RemoveFromHashtable();
397 }
399 nsINode* mRootNode; // Weak ref
400 int32_t mMatchNameSpaceId;
401 nsCOMPtr<nsIAtom> mHTMLMatchAtom;
402 nsCOMPtr<nsIAtom> mXMLMatchAtom;
404 /**
405 * Function to use to determine whether a piece of content matches
406 * our criterion
407 */
408 nsContentListMatchFunc mFunc;
409 /**
410 * Cleanup closure data with this.
411 */
412 nsContentListDestroyFunc mDestroyFunc;
413 /**
414 * Closure data to pass to mFunc when we call it
415 */
416 void* mData;
417 /**
418 * The current state of the list (possible values are:
419 * LIST_UP_TO_DATE, LIST_LAZY, LIST_DIRTY
420 */
421 uint8_t mState;
423 // The booleans have to use uint8_t to pack with mState, because MSVC won't
424 // pack different typedefs together. Once we no longer have to worry about
425 // flushes in XML documents, we can go back to using bool for the
426 // booleans.
428 /**
429 * True if we are looking for elements named "*"
430 */
431 uint8_t mMatchAll : 1;
432 /**
433 * Whether to actually descend the tree. If this is false, we won't
434 * consider grandkids of mRootNode.
435 */
436 uint8_t mDeep : 1;
437 /**
438 * Whether the return value of mFunc could depend on the values of
439 * attributes.
440 */
441 uint8_t mFuncMayDependOnAttr : 1;
442 /**
443 * Whether we actually need to flush to get our state correct.
444 */
445 uint8_t mFlushesNeeded : 1;
447 #ifdef DEBUG_CONTENT_LIST
448 void AssertInSync();
449 #endif
450 };
452 /**
453 * A class of cacheable content list; cached on the combination of aRootNode + aFunc + aDataString
454 */
455 class nsCacheableFuncStringContentList;
457 class MOZ_STACK_CLASS nsFuncStringCacheKey {
458 public:
459 nsFuncStringCacheKey(nsINode* aRootNode,
460 nsContentListMatchFunc aFunc,
461 const nsAString& aString) :
462 mRootNode(aRootNode),
463 mFunc(aFunc),
464 mString(aString)
465 {}
467 uint32_t GetHash(void) const
468 {
469 uint32_t hash = mozilla::HashString(mString);
470 return mozilla::AddToHash(hash, mRootNode, mFunc);
471 }
473 private:
474 friend class nsCacheableFuncStringContentList;
476 nsINode* const mRootNode;
477 const nsContentListMatchFunc mFunc;
478 const nsAString& mString;
479 };
481 // aDestroyFunc is allowed to be null
482 // aDataAllocator must always return a non-null pointer
483 class nsCacheableFuncStringContentList : public nsContentList {
484 public:
485 virtual ~nsCacheableFuncStringContentList();
487 bool Equals(const nsFuncStringCacheKey* aKey) {
488 return mRootNode == aKey->mRootNode && mFunc == aKey->mFunc &&
489 mString == aKey->mString;
490 }
492 #ifdef DEBUG
493 enum ContentListType {
494 eNodeList,
495 eHTMLCollection
496 };
497 ContentListType mType;
498 #endif
500 protected:
501 nsCacheableFuncStringContentList(nsINode* aRootNode,
502 nsContentListMatchFunc aFunc,
503 nsContentListDestroyFunc aDestroyFunc,
504 nsFuncStringContentListDataAllocator aDataAllocator,
505 const nsAString& aString) :
506 nsContentList(aRootNode, aFunc, aDestroyFunc, nullptr),
507 mString(aString)
508 {
509 mData = (*aDataAllocator)(aRootNode, &mString);
510 MOZ_ASSERT(mData);
511 }
513 virtual void RemoveFromCaches() MOZ_OVERRIDE {
514 RemoveFromFuncStringHashtable();
515 }
516 void RemoveFromFuncStringHashtable();
518 nsString mString;
519 };
521 class nsCacheableFuncStringNodeList
522 : public nsCacheableFuncStringContentList
523 {
524 public:
525 nsCacheableFuncStringNodeList(nsINode* aRootNode,
526 nsContentListMatchFunc aFunc,
527 nsContentListDestroyFunc aDestroyFunc,
528 nsFuncStringContentListDataAllocator aDataAllocator,
529 const nsAString& aString)
530 : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
531 aDataAllocator, aString)
532 {
533 #ifdef DEBUG
534 mType = eNodeList;
535 #endif
536 }
538 virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
540 #ifdef DEBUG
541 static const ContentListType sType;
542 #endif
543 };
545 class nsCacheableFuncStringHTMLCollection
546 : public nsCacheableFuncStringContentList
547 {
548 public:
549 nsCacheableFuncStringHTMLCollection(nsINode* aRootNode,
550 nsContentListMatchFunc aFunc,
551 nsContentListDestroyFunc aDestroyFunc,
552 nsFuncStringContentListDataAllocator aDataAllocator,
553 const nsAString& aString)
554 : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
555 aDataAllocator, aString)
556 {
557 #ifdef DEBUG
558 mType = eHTMLCollection;
559 #endif
560 }
562 virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
564 #ifdef DEBUG
565 static const ContentListType sType;
566 #endif
567 };
569 #endif // nsContentList_h___