1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsContentList.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,569 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * nsBaseContentList is a basic list of content nodes; nsContentList 1.11 + * is a commonly used NodeList implementation (used for 1.12 + * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc). 1.13 + */ 1.14 + 1.15 +#ifndef nsContentList_h___ 1.16 +#define nsContentList_h___ 1.17 + 1.18 +#include "mozilla/Attributes.h" 1.19 +#include "nsContentListDeclarations.h" 1.20 +#include "nsISupports.h" 1.21 +#include "nsTArray.h" 1.22 +#include "nsString.h" 1.23 +#include "nsIHTMLCollection.h" 1.24 +#include "nsIDOMNodeList.h" 1.25 +#include "nsINodeList.h" 1.26 +#include "nsStubMutationObserver.h" 1.27 +#include "nsIAtom.h" 1.28 +#include "nsCycleCollectionParticipant.h" 1.29 +#include "nsNameSpaceManager.h" 1.30 +#include "nsWrapperCache.h" 1.31 +#include "nsHashKeys.h" 1.32 +#include "mozilla/HashFunctions.h" 1.33 + 1.34 +namespace mozilla { 1.35 +namespace dom { 1.36 +class Element; 1.37 +} 1.38 +} 1.39 + 1.40 + 1.41 +class nsBaseContentList : public nsINodeList 1.42 +{ 1.43 +public: 1.44 + nsBaseContentList() 1.45 + { 1.46 + SetIsDOMBinding(); 1.47 + } 1.48 + virtual ~nsBaseContentList(); 1.49 + 1.50 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.51 + 1.52 + // nsIDOMNodeList 1.53 + NS_DECL_NSIDOMNODELIST 1.54 + 1.55 + // nsINodeList 1.56 + virtual int32_t IndexOf(nsIContent* aContent) MOZ_OVERRIDE; 1.57 + virtual nsIContent* Item(uint32_t aIndex) MOZ_OVERRIDE; 1.58 + 1.59 + uint32_t Length() const { 1.60 + return mElements.Length(); 1.61 + } 1.62 + 1.63 + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsBaseContentList) 1.64 + 1.65 + void AppendElement(nsIContent *aContent) 1.66 + { 1.67 + mElements.AppendElement(aContent); 1.68 + } 1.69 + void MaybeAppendElement(nsIContent* aContent) 1.70 + { 1.71 + if (aContent) 1.72 + AppendElement(aContent); 1.73 + } 1.74 + 1.75 + /** 1.76 + * Insert the element at a given index, shifting the objects at 1.77 + * the given index and later to make space. 1.78 + * @param aContent Element to insert, must not be null 1.79 + * @param aIndex Index to insert the element at. 1.80 + */ 1.81 + void InsertElementAt(nsIContent* aContent, int32_t aIndex) 1.82 + { 1.83 + NS_ASSERTION(aContent, "Element to insert must not be null"); 1.84 + mElements.InsertElementAt(aIndex, aContent); 1.85 + } 1.86 + 1.87 + void RemoveElement(nsIContent *aContent) 1.88 + { 1.89 + mElements.RemoveElement(aContent); 1.90 + } 1.91 + 1.92 + void Reset() { 1.93 + mElements.Clear(); 1.94 + } 1.95 + 1.96 + virtual int32_t IndexOf(nsIContent *aContent, bool aDoFlush); 1.97 + 1.98 + virtual JSObject* WrapObject(JSContext *cx) 1.99 + MOZ_OVERRIDE = 0; 1.100 + 1.101 + void SetCapacity(uint32_t aCapacity) 1.102 + { 1.103 + mElements.SetCapacity(aCapacity); 1.104 + } 1.105 +protected: 1.106 + /** 1.107 + * To be called from non-destructor locations (e.g. unlink) that want to 1.108 + * remove from caches. Cacheable subclasses should override. 1.109 + */ 1.110 + virtual void RemoveFromCaches() 1.111 + { 1.112 + } 1.113 + 1.114 + nsTArray< nsCOMPtr<nsIContent> > mElements; 1.115 +}; 1.116 + 1.117 + 1.118 +class nsSimpleContentList : public nsBaseContentList 1.119 +{ 1.120 +public: 1.121 + nsSimpleContentList(nsINode *aRoot) : nsBaseContentList(), 1.122 + mRoot(aRoot) 1.123 + { 1.124 + } 1.125 + 1.126 + NS_DECL_ISUPPORTS_INHERITED 1.127 + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsSimpleContentList, 1.128 + nsBaseContentList) 1.129 + 1.130 + virtual nsINode* GetParentObject() MOZ_OVERRIDE 1.131 + { 1.132 + return mRoot; 1.133 + } 1.134 + virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; 1.135 + 1.136 +private: 1.137 + // This has to be a strong reference, the root might go away before the list. 1.138 + nsCOMPtr<nsINode> mRoot; 1.139 +}; 1.140 + 1.141 +/** 1.142 + * Class that's used as the key to hash nsContentList implementations 1.143 + * for fast retrieval 1.144 + */ 1.145 +struct nsContentListKey 1.146 +{ 1.147 + nsContentListKey(nsINode* aRootNode, 1.148 + int32_t aMatchNameSpaceId, 1.149 + const nsAString& aTagname) 1.150 + : mRootNode(aRootNode), 1.151 + mMatchNameSpaceId(aMatchNameSpaceId), 1.152 + mTagname(aTagname), 1.153 + mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode, 1.154 + mMatchNameSpaceId)) 1.155 + { 1.156 + } 1.157 + 1.158 + nsContentListKey(const nsContentListKey& aContentListKey) 1.159 + : mRootNode(aContentListKey.mRootNode), 1.160 + mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId), 1.161 + mTagname(aContentListKey.mTagname), 1.162 + mHash(aContentListKey.mHash) 1.163 + { 1.164 + } 1.165 + 1.166 + inline uint32_t GetHash(void) const 1.167 + { 1.168 + return mHash; 1.169 + } 1.170 + 1.171 + nsINode* const mRootNode; // Weak ref 1.172 + const int32_t mMatchNameSpaceId; 1.173 + const nsAString& mTagname; 1.174 + const uint32_t mHash; 1.175 +}; 1.176 + 1.177 +/** 1.178 + * LIST_UP_TO_DATE means that the list is up to date and need not do 1.179 + * any walking to be able to answer any questions anyone may have. 1.180 + */ 1.181 +#define LIST_UP_TO_DATE 0 1.182 +/** 1.183 + * LIST_DIRTY means that the list contains no useful information and 1.184 + * if anyone asks it anything it will have to populate itself before 1.185 + * answering. 1.186 + */ 1.187 +#define LIST_DIRTY 1 1.188 +/** 1.189 + * LIST_LAZY means that the list has populated itself to a certain 1.190 + * extent and that that part of the list is still valid. Requests for 1.191 + * things outside that part of the list will require walking the tree 1.192 + * some more. When a list is in this state, the last thing in 1.193 + * mElements is the last node in the tree that the list looked at. 1.194 + */ 1.195 +#define LIST_LAZY 2 1.196 + 1.197 +/** 1.198 + * Class that implements a live NodeList that matches Elements in the 1.199 + * tree based on some criterion. 1.200 + */ 1.201 +class nsContentList : public nsBaseContentList, 1.202 + public nsIHTMLCollection, 1.203 + public nsStubMutationObserver 1.204 +{ 1.205 +public: 1.206 + NS_DECL_ISUPPORTS_INHERITED 1.207 + 1.208 + /** 1.209 + * @param aRootNode The node under which to limit our search. 1.210 + * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId. 1.211 + * The special value "*" always matches whatever aMatchAtom 1.212 + * is matched against. 1.213 + * @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the 1.214 + * tagName to match. 1.215 + * If kNameSpaceID_Wildcard, then aMatchAtom is the 1.216 + * localName to match. 1.217 + * Otherwise we match nodes whose namespace is 1.218 + * aMatchNameSpaceId and localName matches 1.219 + * aMatchAtom. 1.220 + * @param aDeep If false, then look only at children of the root, nothing 1.221 + * deeper. If true, then look at the whole subtree rooted at 1.222 + * our root. 1.223 + */ 1.224 + nsContentList(nsINode* aRootNode, 1.225 + int32_t aMatchNameSpaceId, 1.226 + nsIAtom* aHTMLMatchAtom, 1.227 + nsIAtom* aXMLMatchAtom, 1.228 + bool aDeep = true); 1.229 + 1.230 + /** 1.231 + * @param aRootNode The node under which to limit our search. 1.232 + * @param aFunc the function to be called to determine whether we match. 1.233 + * This function MUST NOT ever cause mutation of the DOM. 1.234 + * The nsContentList implementation guarantees that everything 1.235 + * passed to the function will be IsElement(). 1.236 + * @param aDestroyFunc the function that will be called to destroy aData 1.237 + * @param aData closure data that will need to be passed back to aFunc 1.238 + * @param aDeep If false, then look only at children of the root, nothing 1.239 + * deeper. If true, then look at the whole subtree rooted at 1.240 + * our root. 1.241 + * @param aMatchAtom an atom to be passed back to aFunc 1.242 + * @param aMatchNameSpaceId a namespace id to be passed back to aFunc 1.243 + * @param aFuncMayDependOnAttr a boolean that indicates whether this list is 1.244 + * sensitive to attribute changes. 1.245 + */ 1.246 + nsContentList(nsINode* aRootNode, 1.247 + nsContentListMatchFunc aFunc, 1.248 + nsContentListDestroyFunc aDestroyFunc, 1.249 + void* aData, 1.250 + bool aDeep = true, 1.251 + nsIAtom* aMatchAtom = nullptr, 1.252 + int32_t aMatchNameSpaceId = kNameSpaceID_None, 1.253 + bool aFuncMayDependOnAttr = true); 1.254 + virtual ~nsContentList(); 1.255 + 1.256 + // nsWrapperCache 1.257 + using nsWrapperCache::GetWrapperPreserveColor; 1.258 + virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE; 1.259 +protected: 1.260 + virtual JSObject* GetWrapperPreserveColorInternal() MOZ_OVERRIDE 1.261 + { 1.262 + return nsWrapperCache::GetWrapperPreserveColor(); 1.263 + } 1.264 +public: 1.265 + 1.266 + // nsIDOMHTMLCollection 1.267 + NS_DECL_NSIDOMHTMLCOLLECTION 1.268 + 1.269 + // nsBaseContentList overrides 1.270 + virtual int32_t IndexOf(nsIContent *aContent, bool aDoFlush) MOZ_OVERRIDE; 1.271 + virtual int32_t IndexOf(nsIContent* aContent) MOZ_OVERRIDE; 1.272 + virtual nsINode* GetParentObject() MOZ_OVERRIDE 1.273 + { 1.274 + return mRootNode; 1.275 + } 1.276 + 1.277 + virtual nsIContent* Item(uint32_t aIndex) MOZ_OVERRIDE; 1.278 + virtual mozilla::dom::Element* GetElementAt(uint32_t index) MOZ_OVERRIDE; 1.279 + virtual mozilla::dom::Element* 1.280 + GetFirstNamedElement(const nsAString& aName, bool& aFound) MOZ_OVERRIDE 1.281 + { 1.282 + mozilla::dom::Element* item = NamedItem(aName, true); 1.283 + aFound = !!item; 1.284 + return item; 1.285 + } 1.286 + virtual void GetSupportedNames(unsigned aFlags, 1.287 + nsTArray<nsString>& aNames) MOZ_OVERRIDE; 1.288 + 1.289 + // nsContentList public methods 1.290 + NS_HIDDEN_(uint32_t) Length(bool aDoFlush); 1.291 + NS_HIDDEN_(nsIContent*) Item(uint32_t aIndex, bool aDoFlush); 1.292 + NS_HIDDEN_(mozilla::dom::Element*) 1.293 + NamedItem(const nsAString& aName, bool aDoFlush); 1.294 + 1.295 + // nsIMutationObserver 1.296 + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 1.297 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 1.298 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 1.299 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 1.300 + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED 1.301 + 1.302 + static nsContentList* FromSupports(nsISupports* aSupports) 1.303 + { 1.304 + nsINodeList* list = static_cast<nsINodeList*>(aSupports); 1.305 +#ifdef DEBUG 1.306 + { 1.307 + nsCOMPtr<nsINodeList> list_qi = do_QueryInterface(aSupports); 1.308 + 1.309 + // If this assertion fires the QI implementation for the object in 1.310 + // question doesn't use the nsINodeList pointer as the nsISupports 1.311 + // pointer. That must be fixed, or we'll crash... 1.312 + NS_ASSERTION(list_qi == list, "Uh, fix QI!"); 1.313 + } 1.314 +#endif 1.315 + return static_cast<nsContentList*>(list); 1.316 + } 1.317 + 1.318 + bool MatchesKey(const nsContentListKey& aKey) const 1.319 + { 1.320 + // The root node is most commonly the same: the document. And the 1.321 + // most common namespace id is kNameSpaceID_Unknown. So check the 1.322 + // string first. 1.323 + NS_PRECONDITION(mXMLMatchAtom, 1.324 + "How did we get here with a null match atom on our list?"); 1.325 + return 1.326 + mXMLMatchAtom->Equals(aKey.mTagname) && 1.327 + mRootNode == aKey.mRootNode && 1.328 + mMatchNameSpaceId == aKey.mMatchNameSpaceId; 1.329 + } 1.330 + 1.331 + /** 1.332 + * Sets the state to LIST_DIRTY and clears mElements array. 1.333 + * @note This is the only acceptable way to set state to LIST_DIRTY. 1.334 + */ 1.335 + void SetDirty() 1.336 + { 1.337 + mState = LIST_DIRTY; 1.338 + Reset(); 1.339 + } 1.340 + 1.341 +protected: 1.342 + /** 1.343 + * Returns whether the element matches our criterion 1.344 + * 1.345 + * @param aElement the element to attempt to match 1.346 + * @return whether we match 1.347 + */ 1.348 + bool Match(mozilla::dom::Element *aElement); 1.349 + /** 1.350 + * See if anything in the subtree rooted at aContent, including 1.351 + * aContent itself, matches our criterion. 1.352 + * 1.353 + * @param aContent the root of the subtree to match against 1.354 + * @return whether we match something in the tree rooted at aContent 1.355 + */ 1.356 + bool MatchSelf(nsIContent *aContent); 1.357 + 1.358 + /** 1.359 + * Populate our list. Stop once we have at least aNeededLength 1.360 + * elements. At the end of PopulateSelf running, either the last 1.361 + * node we examined is the last node in our array or we have 1.362 + * traversed the whole document (or both). 1.363 + * 1.364 + * @param aNeededLength the length the list should have when we are 1.365 + * done (unless it exhausts the document) 1.366 + */ 1.367 + void PopulateSelf(uint32_t aNeededLength); 1.368 + 1.369 + /** 1.370 + * @param aContainer a content node which must be a descendant of 1.371 + * mRootNode 1.372 + * @return true if children or descendants of aContainer could match our 1.373 + * criterion. 1.374 + * false otherwise. 1.375 + */ 1.376 + bool MayContainRelevantNodes(nsINode* aContainer) 1.377 + { 1.378 + return mDeep || aContainer == mRootNode; 1.379 + } 1.380 + 1.381 + /** 1.382 + * Remove ourselves from the hashtable that caches commonly accessed 1.383 + * content lists. Generally done on destruction. 1.384 + */ 1.385 + void RemoveFromHashtable(); 1.386 + /** 1.387 + * If state is not LIST_UP_TO_DATE, fully populate ourselves with 1.388 + * all the nodes we can find. 1.389 + */ 1.390 + inline void BringSelfUpToDate(bool aDoFlush); 1.391 + 1.392 + /** 1.393 + * To be called from non-destructor locations that want to remove from caches. 1.394 + * Needed because if subclasses want to have cache behavior they can't just 1.395 + * override RemoveFromHashtable(), since we call that in our destructor. 1.396 + */ 1.397 + virtual void RemoveFromCaches() MOZ_OVERRIDE 1.398 + { 1.399 + RemoveFromHashtable(); 1.400 + } 1.401 + 1.402 + nsINode* mRootNode; // Weak ref 1.403 + int32_t mMatchNameSpaceId; 1.404 + nsCOMPtr<nsIAtom> mHTMLMatchAtom; 1.405 + nsCOMPtr<nsIAtom> mXMLMatchAtom; 1.406 + 1.407 + /** 1.408 + * Function to use to determine whether a piece of content matches 1.409 + * our criterion 1.410 + */ 1.411 + nsContentListMatchFunc mFunc; 1.412 + /** 1.413 + * Cleanup closure data with this. 1.414 + */ 1.415 + nsContentListDestroyFunc mDestroyFunc; 1.416 + /** 1.417 + * Closure data to pass to mFunc when we call it 1.418 + */ 1.419 + void* mData; 1.420 + /** 1.421 + * The current state of the list (possible values are: 1.422 + * LIST_UP_TO_DATE, LIST_LAZY, LIST_DIRTY 1.423 + */ 1.424 + uint8_t mState; 1.425 + 1.426 + // The booleans have to use uint8_t to pack with mState, because MSVC won't 1.427 + // pack different typedefs together. Once we no longer have to worry about 1.428 + // flushes in XML documents, we can go back to using bool for the 1.429 + // booleans. 1.430 + 1.431 + /** 1.432 + * True if we are looking for elements named "*" 1.433 + */ 1.434 + uint8_t mMatchAll : 1; 1.435 + /** 1.436 + * Whether to actually descend the tree. If this is false, we won't 1.437 + * consider grandkids of mRootNode. 1.438 + */ 1.439 + uint8_t mDeep : 1; 1.440 + /** 1.441 + * Whether the return value of mFunc could depend on the values of 1.442 + * attributes. 1.443 + */ 1.444 + uint8_t mFuncMayDependOnAttr : 1; 1.445 + /** 1.446 + * Whether we actually need to flush to get our state correct. 1.447 + */ 1.448 + uint8_t mFlushesNeeded : 1; 1.449 + 1.450 +#ifdef DEBUG_CONTENT_LIST 1.451 + void AssertInSync(); 1.452 +#endif 1.453 +}; 1.454 + 1.455 +/** 1.456 + * A class of cacheable content list; cached on the combination of aRootNode + aFunc + aDataString 1.457 + */ 1.458 +class nsCacheableFuncStringContentList; 1.459 + 1.460 +class MOZ_STACK_CLASS nsFuncStringCacheKey { 1.461 +public: 1.462 + nsFuncStringCacheKey(nsINode* aRootNode, 1.463 + nsContentListMatchFunc aFunc, 1.464 + const nsAString& aString) : 1.465 + mRootNode(aRootNode), 1.466 + mFunc(aFunc), 1.467 + mString(aString) 1.468 + {} 1.469 + 1.470 + uint32_t GetHash(void) const 1.471 + { 1.472 + uint32_t hash = mozilla::HashString(mString); 1.473 + return mozilla::AddToHash(hash, mRootNode, mFunc); 1.474 + } 1.475 + 1.476 +private: 1.477 + friend class nsCacheableFuncStringContentList; 1.478 + 1.479 + nsINode* const mRootNode; 1.480 + const nsContentListMatchFunc mFunc; 1.481 + const nsAString& mString; 1.482 +}; 1.483 + 1.484 +// aDestroyFunc is allowed to be null 1.485 +// aDataAllocator must always return a non-null pointer 1.486 +class nsCacheableFuncStringContentList : public nsContentList { 1.487 +public: 1.488 + virtual ~nsCacheableFuncStringContentList(); 1.489 + 1.490 + bool Equals(const nsFuncStringCacheKey* aKey) { 1.491 + return mRootNode == aKey->mRootNode && mFunc == aKey->mFunc && 1.492 + mString == aKey->mString; 1.493 + } 1.494 + 1.495 +#ifdef DEBUG 1.496 + enum ContentListType { 1.497 + eNodeList, 1.498 + eHTMLCollection 1.499 + }; 1.500 + ContentListType mType; 1.501 +#endif 1.502 + 1.503 +protected: 1.504 + nsCacheableFuncStringContentList(nsINode* aRootNode, 1.505 + nsContentListMatchFunc aFunc, 1.506 + nsContentListDestroyFunc aDestroyFunc, 1.507 + nsFuncStringContentListDataAllocator aDataAllocator, 1.508 + const nsAString& aString) : 1.509 + nsContentList(aRootNode, aFunc, aDestroyFunc, nullptr), 1.510 + mString(aString) 1.511 + { 1.512 + mData = (*aDataAllocator)(aRootNode, &mString); 1.513 + MOZ_ASSERT(mData); 1.514 + } 1.515 + 1.516 + virtual void RemoveFromCaches() MOZ_OVERRIDE { 1.517 + RemoveFromFuncStringHashtable(); 1.518 + } 1.519 + void RemoveFromFuncStringHashtable(); 1.520 + 1.521 + nsString mString; 1.522 +}; 1.523 + 1.524 +class nsCacheableFuncStringNodeList 1.525 + : public nsCacheableFuncStringContentList 1.526 +{ 1.527 +public: 1.528 + nsCacheableFuncStringNodeList(nsINode* aRootNode, 1.529 + nsContentListMatchFunc aFunc, 1.530 + nsContentListDestroyFunc aDestroyFunc, 1.531 + nsFuncStringContentListDataAllocator aDataAllocator, 1.532 + const nsAString& aString) 1.533 + : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc, 1.534 + aDataAllocator, aString) 1.535 + { 1.536 +#ifdef DEBUG 1.537 + mType = eNodeList; 1.538 +#endif 1.539 + } 1.540 + 1.541 + virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; 1.542 + 1.543 +#ifdef DEBUG 1.544 + static const ContentListType sType; 1.545 +#endif 1.546 +}; 1.547 + 1.548 +class nsCacheableFuncStringHTMLCollection 1.549 + : public nsCacheableFuncStringContentList 1.550 +{ 1.551 +public: 1.552 + nsCacheableFuncStringHTMLCollection(nsINode* aRootNode, 1.553 + nsContentListMatchFunc aFunc, 1.554 + nsContentListDestroyFunc aDestroyFunc, 1.555 + nsFuncStringContentListDataAllocator aDataAllocator, 1.556 + const nsAString& aString) 1.557 + : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc, 1.558 + aDataAllocator, aString) 1.559 + { 1.560 +#ifdef DEBUG 1.561 + mType = eHTMLCollection; 1.562 +#endif 1.563 + } 1.564 + 1.565 + virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; 1.566 + 1.567 +#ifdef DEBUG 1.568 + static const ContentListType sType; 1.569 +#endif 1.570 +}; 1.571 + 1.572 +#endif // nsContentList_h___