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