1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsContentList.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1095 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * nsBaseContentList is a basic list of content nodes; nsContentList 1.12 + * is a commonly used NodeList implementation (used for 1.13 + * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc). 1.14 + */ 1.15 + 1.16 +#include "nsContentList.h" 1.17 +#include "nsIContent.h" 1.18 +#include "nsIDOMNode.h" 1.19 +#include "nsIDocument.h" 1.20 +#include "mozilla/dom/Element.h" 1.21 +#include "nsWrapperCacheInlines.h" 1.22 +#include "nsContentUtils.h" 1.23 +#include "nsCCUncollectableMarker.h" 1.24 +#include "nsGkAtoms.h" 1.25 +#include "mozilla/dom/HTMLCollectionBinding.h" 1.26 +#include "mozilla/dom/NodeListBinding.h" 1.27 +#include "mozilla/Likely.h" 1.28 +#include "nsGenericHTMLElement.h" 1.29 +#include "jsfriendapi.h" 1.30 +#include <algorithm> 1.31 + 1.32 +// Form related includes 1.33 +#include "nsIDOMHTMLFormElement.h" 1.34 + 1.35 +#include "pldhash.h" 1.36 + 1.37 +#ifdef DEBUG_CONTENT_LIST 1.38 +#include "nsIContentIterator.h" 1.39 +#define ASSERT_IN_SYNC AssertInSync() 1.40 +#else 1.41 +#define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO 1.42 +#endif 1.43 + 1.44 +using namespace mozilla; 1.45 +using namespace mozilla::dom; 1.46 + 1.47 +nsBaseContentList::~nsBaseContentList() 1.48 +{ 1.49 +} 1.50 + 1.51 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList) 1.52 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList) 1.53 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements) 1.54 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.55 + tmp->RemoveFromCaches(); 1.56 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.57 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList) 1.58 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements) 1.59 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.60 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.61 +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsBaseContentList) 1.62 + 1.63 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList) 1.64 + if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) { 1.65 + for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) { 1.66 + nsIContent* c = tmp->mElements[i]; 1.67 + if (c->IsPurple()) { 1.68 + c->RemovePurple(); 1.69 + } 1.70 + Element::MarkNodeChildren(c); 1.71 + } 1.72 + return true; 1.73 + } 1.74 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1.75 + 1.76 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList) 1.77 + return nsCCUncollectableMarker::sGeneration && tmp->IsBlack(); 1.78 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1.79 + 1.80 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList) 1.81 + return nsCCUncollectableMarker::sGeneration && tmp->IsBlack(); 1.82 +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1.83 + 1.84 +#define NS_CONTENT_LIST_INTERFACES(_class) \ 1.85 + NS_INTERFACE_TABLE_ENTRY(_class, nsINodeList) \ 1.86 + NS_INTERFACE_TABLE_ENTRY(_class, nsIDOMNodeList) 1.87 + 1.88 +// QueryInterface implementation for nsBaseContentList 1.89 +NS_INTERFACE_TABLE_HEAD(nsBaseContentList) 1.90 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.91 + NS_INTERFACE_TABLE(nsBaseContentList, nsINodeList, nsIDOMNodeList) 1.92 + NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList) 1.93 +NS_INTERFACE_MAP_END 1.94 + 1.95 + 1.96 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList) 1.97 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBaseContentList) 1.98 + 1.99 + 1.100 +NS_IMETHODIMP 1.101 +nsBaseContentList::GetLength(uint32_t* aLength) 1.102 +{ 1.103 + *aLength = mElements.Length(); 1.104 + 1.105 + return NS_OK; 1.106 +} 1.107 + 1.108 +NS_IMETHODIMP 1.109 +nsBaseContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn) 1.110 +{ 1.111 + nsISupports *tmp = Item(aIndex); 1.112 + 1.113 + if (!tmp) { 1.114 + *aReturn = nullptr; 1.115 + 1.116 + return NS_OK; 1.117 + } 1.118 + 1.119 + return CallQueryInterface(tmp, aReturn); 1.120 +} 1.121 + 1.122 +nsIContent* 1.123 +nsBaseContentList::Item(uint32_t aIndex) 1.124 +{ 1.125 + return mElements.SafeElementAt(aIndex); 1.126 +} 1.127 + 1.128 + 1.129 +int32_t 1.130 +nsBaseContentList::IndexOf(nsIContent *aContent, bool aDoFlush) 1.131 +{ 1.132 + return mElements.IndexOf(aContent); 1.133 +} 1.134 + 1.135 +int32_t 1.136 +nsBaseContentList::IndexOf(nsIContent* aContent) 1.137 +{ 1.138 + return IndexOf(aContent, true); 1.139 +} 1.140 + 1.141 +NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList, nsBaseContentList, 1.142 + mRoot) 1.143 + 1.144 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsSimpleContentList) 1.145 +NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList) 1.146 + 1.147 + 1.148 +NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList) 1.149 +NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList) 1.150 + 1.151 +JSObject* 1.152 +nsSimpleContentList::WrapObject(JSContext *cx) 1.153 +{ 1.154 + return NodeListBinding::Wrap(cx, this); 1.155 +} 1.156 + 1.157 +// Hashtable for storing nsContentLists 1.158 +static PLDHashTable gContentListHashTable; 1.159 + 1.160 +#define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31 1.161 +static nsContentList* 1.162 + sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {}; 1.163 + 1.164 +static MOZ_ALWAYS_INLINE uint32_t 1.165 +RecentlyUsedCacheIndex(const nsContentListKey& aKey) 1.166 +{ 1.167 + return aKey.GetHash() % RECENTLY_USED_CONTENT_LIST_CACHE_SIZE; 1.168 +} 1.169 + 1.170 +struct ContentListHashEntry : public PLDHashEntryHdr 1.171 +{ 1.172 + nsContentList* mContentList; 1.173 +}; 1.174 + 1.175 +static PLDHashNumber 1.176 +ContentListHashtableHashKey(PLDHashTable *table, const void *key) 1.177 +{ 1.178 + const nsContentListKey* list = static_cast<const nsContentListKey *>(key); 1.179 + return list->GetHash(); 1.180 +} 1.181 + 1.182 +static bool 1.183 +ContentListHashtableMatchEntry(PLDHashTable *table, 1.184 + const PLDHashEntryHdr *entry, 1.185 + const void *key) 1.186 +{ 1.187 + const ContentListHashEntry *e = 1.188 + static_cast<const ContentListHashEntry *>(entry); 1.189 + const nsContentList* list = e->mContentList; 1.190 + const nsContentListKey* ourKey = static_cast<const nsContentListKey *>(key); 1.191 + 1.192 + return list->MatchesKey(*ourKey); 1.193 +} 1.194 + 1.195 +already_AddRefed<nsContentList> 1.196 +NS_GetContentList(nsINode* aRootNode, 1.197 + int32_t aMatchNameSpaceId, 1.198 + const nsAString& aTagname) 1.199 +{ 1.200 + NS_ASSERTION(aRootNode, "content list has to have a root"); 1.201 + 1.202 + nsRefPtr<nsContentList> list; 1.203 + nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname); 1.204 + uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(hashKey); 1.205 + nsContentList* cachedList = sRecentlyUsedContentLists[recentlyUsedCacheIndex]; 1.206 + if (cachedList && cachedList->MatchesKey(hashKey)) { 1.207 + list = cachedList; 1.208 + return list.forget(); 1.209 + } 1.210 + 1.211 + static const PLDHashTableOps hash_table_ops = 1.212 + { 1.213 + PL_DHashAllocTable, 1.214 + PL_DHashFreeTable, 1.215 + ContentListHashtableHashKey, 1.216 + ContentListHashtableMatchEntry, 1.217 + PL_DHashMoveEntryStub, 1.218 + PL_DHashClearEntryStub, 1.219 + PL_DHashFinalizeStub 1.220 + }; 1.221 + 1.222 + // Initialize the hashtable if needed. 1.223 + if (!gContentListHashTable.ops) { 1.224 + PL_DHashTableInit(&gContentListHashTable, 1.225 + &hash_table_ops, nullptr, 1.226 + sizeof(ContentListHashEntry), 1.227 + 16); 1.228 + } 1.229 + 1.230 + ContentListHashEntry *entry = nullptr; 1.231 + // First we look in our hashtable. Then we create a content list if needed 1.232 + if (gContentListHashTable.ops) { 1.233 + 1.234 + // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases 1.235 + // when the entry is already in the hashtable. 1.236 + entry = static_cast<ContentListHashEntry *> 1.237 + (PL_DHashTableOperate(&gContentListHashTable, 1.238 + &hashKey, 1.239 + PL_DHASH_ADD)); 1.240 + if (entry) 1.241 + list = entry->mContentList; 1.242 + } 1.243 + 1.244 + if (!list) { 1.245 + // We need to create a ContentList and add it to our new entry, if 1.246 + // we have an entry 1.247 + nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname); 1.248 + nsCOMPtr<nsIAtom> htmlAtom; 1.249 + if (aMatchNameSpaceId == kNameSpaceID_Unknown) { 1.250 + nsAutoString lowercaseName; 1.251 + nsContentUtils::ASCIIToLower(aTagname, lowercaseName); 1.252 + htmlAtom = do_GetAtom(lowercaseName); 1.253 + } else { 1.254 + htmlAtom = xmlAtom; 1.255 + } 1.256 + list = new nsContentList(aRootNode, aMatchNameSpaceId, 1.257 + htmlAtom, xmlAtom); 1.258 + if (entry) { 1.259 + entry->mContentList = list; 1.260 + } 1.261 + } 1.262 + 1.263 + sRecentlyUsedContentLists[recentlyUsedCacheIndex] = list; 1.264 + return list.forget(); 1.265 +} 1.266 + 1.267 +#ifdef DEBUG 1.268 +const nsCacheableFuncStringContentList::ContentListType 1.269 + nsCacheableFuncStringNodeList::sType = nsCacheableFuncStringContentList::eNodeList; 1.270 +const nsCacheableFuncStringContentList::ContentListType 1.271 + nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection; 1.272 +#endif 1.273 + 1.274 +JSObject* 1.275 +nsCacheableFuncStringNodeList::WrapObject(JSContext *cx) 1.276 +{ 1.277 + return NodeListBinding::Wrap(cx, this); 1.278 +} 1.279 + 1.280 + 1.281 +JSObject* 1.282 +nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx) 1.283 +{ 1.284 + return HTMLCollectionBinding::Wrap(cx, this); 1.285 +} 1.286 + 1.287 +// Hashtable for storing nsCacheableFuncStringContentList 1.288 +static PLDHashTable gFuncStringContentListHashTable; 1.289 + 1.290 +struct FuncStringContentListHashEntry : public PLDHashEntryHdr 1.291 +{ 1.292 + nsCacheableFuncStringContentList* mContentList; 1.293 +}; 1.294 + 1.295 +static PLDHashNumber 1.296 +FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key) 1.297 +{ 1.298 + const nsFuncStringCacheKey* funcStringKey = 1.299 + static_cast<const nsFuncStringCacheKey *>(key); 1.300 + return funcStringKey->GetHash(); 1.301 +} 1.302 + 1.303 +static bool 1.304 +FuncStringContentListHashtableMatchEntry(PLDHashTable *table, 1.305 + const PLDHashEntryHdr *entry, 1.306 + const void *key) 1.307 +{ 1.308 + const FuncStringContentListHashEntry *e = 1.309 + static_cast<const FuncStringContentListHashEntry *>(entry); 1.310 + const nsFuncStringCacheKey* ourKey = 1.311 + static_cast<const nsFuncStringCacheKey *>(key); 1.312 + 1.313 + return e->mContentList->Equals(ourKey); 1.314 +} 1.315 + 1.316 +template<class ListType> 1.317 +already_AddRefed<nsContentList> 1.318 +GetFuncStringContentList(nsINode* aRootNode, 1.319 + nsContentListMatchFunc aFunc, 1.320 + nsContentListDestroyFunc aDestroyFunc, 1.321 + nsFuncStringContentListDataAllocator aDataAllocator, 1.322 + const nsAString& aString) 1.323 +{ 1.324 + NS_ASSERTION(aRootNode, "content list has to have a root"); 1.325 + 1.326 + nsRefPtr<nsCacheableFuncStringContentList> list; 1.327 + 1.328 + static const PLDHashTableOps hash_table_ops = 1.329 + { 1.330 + PL_DHashAllocTable, 1.331 + PL_DHashFreeTable, 1.332 + FuncStringContentListHashtableHashKey, 1.333 + FuncStringContentListHashtableMatchEntry, 1.334 + PL_DHashMoveEntryStub, 1.335 + PL_DHashClearEntryStub, 1.336 + PL_DHashFinalizeStub 1.337 + }; 1.338 + 1.339 + // Initialize the hashtable if needed. 1.340 + if (!gFuncStringContentListHashTable.ops) { 1.341 + PL_DHashTableInit(&gFuncStringContentListHashTable, 1.342 + &hash_table_ops, nullptr, 1.343 + sizeof(FuncStringContentListHashEntry), 1.344 + 16); 1.345 + } 1.346 + 1.347 + FuncStringContentListHashEntry *entry = nullptr; 1.348 + // First we look in our hashtable. Then we create a content list if needed 1.349 + if (gFuncStringContentListHashTable.ops) { 1.350 + nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString); 1.351 + 1.352 + // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases 1.353 + // when the entry is already in the hashtable. 1.354 + entry = static_cast<FuncStringContentListHashEntry *> 1.355 + (PL_DHashTableOperate(&gFuncStringContentListHashTable, 1.356 + &hashKey, 1.357 + PL_DHASH_ADD)); 1.358 + if (entry) { 1.359 + list = entry->mContentList; 1.360 +#ifdef DEBUG 1.361 + MOZ_ASSERT_IF(list, list->mType == ListType::sType); 1.362 +#endif 1.363 + } 1.364 + } 1.365 + 1.366 + if (!list) { 1.367 + // We need to create a ContentList and add it to our new entry, if 1.368 + // we have an entry 1.369 + list = new ListType(aRootNode, aFunc, aDestroyFunc, aDataAllocator, 1.370 + aString); 1.371 + if (entry) { 1.372 + entry->mContentList = list; 1.373 + } 1.374 + } 1.375 + 1.376 + // Don't cache these lists globally 1.377 + 1.378 + return list.forget(); 1.379 +} 1.380 + 1.381 +already_AddRefed<nsContentList> 1.382 +NS_GetFuncStringNodeList(nsINode* aRootNode, 1.383 + nsContentListMatchFunc aFunc, 1.384 + nsContentListDestroyFunc aDestroyFunc, 1.385 + nsFuncStringContentListDataAllocator aDataAllocator, 1.386 + const nsAString& aString) 1.387 +{ 1.388 + return GetFuncStringContentList<nsCacheableFuncStringNodeList>(aRootNode, 1.389 + aFunc, 1.390 + aDestroyFunc, 1.391 + aDataAllocator, 1.392 + aString); 1.393 +} 1.394 + 1.395 +already_AddRefed<nsContentList> 1.396 +NS_GetFuncStringHTMLCollection(nsINode* aRootNode, 1.397 + nsContentListMatchFunc aFunc, 1.398 + nsContentListDestroyFunc aDestroyFunc, 1.399 + nsFuncStringContentListDataAllocator aDataAllocator, 1.400 + const nsAString& aString) 1.401 +{ 1.402 + return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(aRootNode, 1.403 + aFunc, 1.404 + aDestroyFunc, 1.405 + aDataAllocator, 1.406 + aString); 1.407 +} 1.408 + 1.409 +// nsContentList implementation 1.410 + 1.411 +nsContentList::nsContentList(nsINode* aRootNode, 1.412 + int32_t aMatchNameSpaceId, 1.413 + nsIAtom* aHTMLMatchAtom, 1.414 + nsIAtom* aXMLMatchAtom, 1.415 + bool aDeep) 1.416 + : nsBaseContentList(), 1.417 + mRootNode(aRootNode), 1.418 + mMatchNameSpaceId(aMatchNameSpaceId), 1.419 + mHTMLMatchAtom(aHTMLMatchAtom), 1.420 + mXMLMatchAtom(aXMLMatchAtom), 1.421 + mFunc(nullptr), 1.422 + mDestroyFunc(nullptr), 1.423 + mData(nullptr), 1.424 + mState(LIST_DIRTY), 1.425 + mDeep(aDeep), 1.426 + mFuncMayDependOnAttr(false) 1.427 +{ 1.428 + NS_ASSERTION(mRootNode, "Must have root"); 1.429 + if (nsGkAtoms::_asterix == mHTMLMatchAtom) { 1.430 + NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterix, "HTML atom and XML atom are not both asterix?"); 1.431 + mMatchAll = true; 1.432 + } 1.433 + else { 1.434 + mMatchAll = false; 1.435 + } 1.436 + mRootNode->AddMutationObserver(this); 1.437 + 1.438 + // We only need to flush if we're in an non-HTML document, since the 1.439 + // HTML5 parser doesn't need flushing. Further, if we're not in a 1.440 + // document at all right now (in the GetCurrentDoc() sense), we're 1.441 + // not parser-created and don't need to be flushing stuff under us 1.442 + // to get our kids right. 1.443 + nsIDocument* doc = mRootNode->GetCurrentDoc(); 1.444 + mFlushesNeeded = doc && !doc->IsHTML(); 1.445 +} 1.446 + 1.447 +nsContentList::nsContentList(nsINode* aRootNode, 1.448 + nsContentListMatchFunc aFunc, 1.449 + nsContentListDestroyFunc aDestroyFunc, 1.450 + void* aData, 1.451 + bool aDeep, 1.452 + nsIAtom* aMatchAtom, 1.453 + int32_t aMatchNameSpaceId, 1.454 + bool aFuncMayDependOnAttr) 1.455 + : nsBaseContentList(), 1.456 + mRootNode(aRootNode), 1.457 + mMatchNameSpaceId(aMatchNameSpaceId), 1.458 + mHTMLMatchAtom(aMatchAtom), 1.459 + mXMLMatchAtom(aMatchAtom), 1.460 + mFunc(aFunc), 1.461 + mDestroyFunc(aDestroyFunc), 1.462 + mData(aData), 1.463 + mState(LIST_DIRTY), 1.464 + mMatchAll(false), 1.465 + mDeep(aDeep), 1.466 + mFuncMayDependOnAttr(aFuncMayDependOnAttr) 1.467 +{ 1.468 + NS_ASSERTION(mRootNode, "Must have root"); 1.469 + mRootNode->AddMutationObserver(this); 1.470 + 1.471 + // We only need to flush if we're in an non-HTML document, since the 1.472 + // HTML5 parser doesn't need flushing. Further, if we're not in a 1.473 + // document at all right now (in the GetCurrentDoc() sense), we're 1.474 + // not parser-created and don't need to be flushing stuff under us 1.475 + // to get our kids right. 1.476 + nsIDocument* doc = mRootNode->GetCurrentDoc(); 1.477 + mFlushesNeeded = doc && !doc->IsHTML(); 1.478 +} 1.479 + 1.480 +nsContentList::~nsContentList() 1.481 +{ 1.482 + RemoveFromHashtable(); 1.483 + if (mRootNode) { 1.484 + mRootNode->RemoveMutationObserver(this); 1.485 + } 1.486 + 1.487 + if (mDestroyFunc) { 1.488 + // Clean up mData 1.489 + (*mDestroyFunc)(mData); 1.490 + } 1.491 +} 1.492 + 1.493 +JSObject* 1.494 +nsContentList::WrapObject(JSContext *cx) 1.495 +{ 1.496 + return HTMLCollectionBinding::Wrap(cx, this); 1.497 +} 1.498 + 1.499 +NS_IMPL_ISUPPORTS_INHERITED(nsContentList, nsBaseContentList, 1.500 + nsIHTMLCollection, nsIDOMHTMLCollection, 1.501 + nsIMutationObserver) 1.502 + 1.503 +uint32_t 1.504 +nsContentList::Length(bool aDoFlush) 1.505 +{ 1.506 + BringSelfUpToDate(aDoFlush); 1.507 + 1.508 + return mElements.Length(); 1.509 +} 1.510 + 1.511 +nsIContent * 1.512 +nsContentList::Item(uint32_t aIndex, bool aDoFlush) 1.513 +{ 1.514 + if (mRootNode && aDoFlush && mFlushesNeeded) { 1.515 + // XXX sXBL/XBL2 issue 1.516 + nsIDocument* doc = mRootNode->GetCurrentDoc(); 1.517 + if (doc) { 1.518 + // Flush pending content changes Bug 4891. 1.519 + doc->FlushPendingNotifications(Flush_ContentAndNotify); 1.520 + } 1.521 + } 1.522 + 1.523 + if (mState != LIST_UP_TO_DATE) 1.524 + PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1); 1.525 + 1.526 + ASSERT_IN_SYNC; 1.527 + NS_ASSERTION(!mRootNode || mState != LIST_DIRTY, 1.528 + "PopulateSelf left the list in a dirty (useless) state!"); 1.529 + 1.530 + return mElements.SafeElementAt(aIndex); 1.531 +} 1.532 + 1.533 +Element* 1.534 +nsContentList::NamedItem(const nsAString& aName, bool aDoFlush) 1.535 +{ 1.536 + BringSelfUpToDate(aDoFlush); 1.537 + 1.538 + uint32_t i, count = mElements.Length(); 1.539 + 1.540 + // Typically IDs and names are atomized 1.541 + nsCOMPtr<nsIAtom> name = do_GetAtom(aName); 1.542 + NS_ENSURE_TRUE(name, nullptr); 1.543 + 1.544 + for (i = 0; i < count; i++) { 1.545 + nsIContent *content = mElements[i]; 1.546 + // XXX Should this pass eIgnoreCase? 1.547 + if (content && 1.548 + (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, 1.549 + name, eCaseMatters) || 1.550 + content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, 1.551 + name, eCaseMatters))) { 1.552 + return content->AsElement(); 1.553 + } 1.554 + } 1.555 + 1.556 + return nullptr; 1.557 +} 1.558 + 1.559 +void 1.560 +nsContentList::GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames) 1.561 +{ 1.562 + if (!(aFlags & JSITER_HIDDEN)) { 1.563 + return; 1.564 + } 1.565 + 1.566 + BringSelfUpToDate(true); 1.567 + 1.568 + nsAutoTArray<nsIAtom*, 8> atoms; 1.569 + for (uint32_t i = 0; i < mElements.Length(); ++i) { 1.570 + nsIContent *content = mElements.ElementAt(i); 1.571 + nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(content); 1.572 + if (el) { 1.573 + // XXXbz should we be checking for particular tags here? How 1.574 + // stable is this part of the spec? 1.575 + // Note: nsINode::HasName means the name is exposed on the document, 1.576 + // which is false for options, so we don't check it here. 1.577 + const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name); 1.578 + if (val && val->Type() == nsAttrValue::eAtom) { 1.579 + nsIAtom* name = val->GetAtomValue(); 1.580 + if (!atoms.Contains(name)) { 1.581 + atoms.AppendElement(name); 1.582 + } 1.583 + } 1.584 + } 1.585 + if (content->HasID()) { 1.586 + nsIAtom* id = content->GetID(); 1.587 + if (!atoms.Contains(id)) { 1.588 + atoms.AppendElement(id); 1.589 + } 1.590 + } 1.591 + } 1.592 + 1.593 + aNames.SetCapacity(atoms.Length()); 1.594 + for (uint32_t i = 0; i < atoms.Length(); ++i) { 1.595 + aNames.AppendElement(nsDependentAtomString(atoms[i])); 1.596 + } 1.597 +} 1.598 + 1.599 +int32_t 1.600 +nsContentList::IndexOf(nsIContent *aContent, bool aDoFlush) 1.601 +{ 1.602 + BringSelfUpToDate(aDoFlush); 1.603 + 1.604 + return mElements.IndexOf(aContent); 1.605 +} 1.606 + 1.607 +int32_t 1.608 +nsContentList::IndexOf(nsIContent* aContent) 1.609 +{ 1.610 + return IndexOf(aContent, true); 1.611 +} 1.612 + 1.613 +void 1.614 +nsContentList::NodeWillBeDestroyed(const nsINode* aNode) 1.615 +{ 1.616 + // We shouldn't do anything useful from now on 1.617 + 1.618 + RemoveFromCaches(); 1.619 + mRootNode = nullptr; 1.620 + 1.621 + // We will get no more updates, so we can never know we're up to 1.622 + // date 1.623 + SetDirty(); 1.624 +} 1.625 + 1.626 +NS_IMETHODIMP 1.627 +nsContentList::GetLength(uint32_t* aLength) 1.628 +{ 1.629 + *aLength = Length(true); 1.630 + 1.631 + return NS_OK; 1.632 +} 1.633 + 1.634 +NS_IMETHODIMP 1.635 +nsContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn) 1.636 +{ 1.637 + nsINode* node = Item(aIndex); 1.638 + 1.639 + if (node) { 1.640 + return CallQueryInterface(node, aReturn); 1.641 + } 1.642 + 1.643 + *aReturn = nullptr; 1.644 + 1.645 + return NS_OK; 1.646 +} 1.647 + 1.648 +NS_IMETHODIMP 1.649 +nsContentList::NamedItem(const nsAString& aName, nsIDOMNode** aReturn) 1.650 +{ 1.651 + nsIContent *content = NamedItem(aName, true); 1.652 + 1.653 + if (content) { 1.654 + return CallQueryInterface(content, aReturn); 1.655 + } 1.656 + 1.657 + *aReturn = nullptr; 1.658 + 1.659 + return NS_OK; 1.660 +} 1.661 + 1.662 +Element* 1.663 +nsContentList::GetElementAt(uint32_t aIndex) 1.664 +{ 1.665 + return static_cast<Element*>(Item(aIndex, true)); 1.666 +} 1.667 + 1.668 +nsIContent* 1.669 +nsContentList::Item(uint32_t aIndex) 1.670 +{ 1.671 + return GetElementAt(aIndex); 1.672 +} 1.673 + 1.674 +void 1.675 +nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement, 1.676 + int32_t aNameSpaceID, nsIAtom* aAttribute, 1.677 + int32_t aModType) 1.678 +{ 1.679 + NS_PRECONDITION(aElement, "Must have a content node to work with"); 1.680 + 1.681 + if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY || 1.682 + !MayContainRelevantNodes(aElement->GetParentNode()) || 1.683 + !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) { 1.684 + // Either we're already dirty or this notification doesn't affect 1.685 + // whether we might match aElement. 1.686 + return; 1.687 + } 1.688 + 1.689 + if (Match(aElement)) { 1.690 + if (mElements.IndexOf(aElement) == mElements.NoIndex) { 1.691 + // We match aElement now, and it's not in our list already. Just dirty 1.692 + // ourselves; this is simpler than trying to figure out where to insert 1.693 + // aElement. 1.694 + SetDirty(); 1.695 + } 1.696 + } else { 1.697 + // We no longer match aElement. Remove it from our list. If it's 1.698 + // already not there, this is a no-op (though a potentially 1.699 + // expensive one). Either way, no change of mState is required 1.700 + // here. 1.701 + mElements.RemoveElement(aElement); 1.702 + } 1.703 +} 1.704 + 1.705 +void 1.706 +nsContentList::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer, 1.707 + nsIContent* aFirstNewContent, 1.708 + int32_t aNewIndexInContainer) 1.709 +{ 1.710 + NS_PRECONDITION(aContainer, "Can't get at the new content if no container!"); 1.711 + 1.712 + /* 1.713 + * If the state is LIST_DIRTY then we have no useful information in our list 1.714 + * and we want to put off doing work as much as possible. 1.715 + * 1.716 + * Also, if aContainer is anonymous from our point of view, we know that we 1.717 + * can't possibly be matching any of the kids. 1.718 + * 1.719 + * Optimize out also the common case when just one new node is appended and 1.720 + * it doesn't match us. 1.721 + */ 1.722 + if (mState == LIST_DIRTY || 1.723 + !nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer) || 1.724 + !MayContainRelevantNodes(aContainer) || 1.725 + (!aFirstNewContent->HasChildren() && 1.726 + !aFirstNewContent->GetNextSibling() && 1.727 + !MatchSelf(aFirstNewContent))) { 1.728 + return; 1.729 + } 1.730 + 1.731 + /* 1.732 + * We want to handle the case of ContentAppended by sometimes 1.733 + * appending the content to our list, not just setting state to 1.734 + * LIST_DIRTY, since most of our ContentAppended notifications 1.735 + * should come during pageload and be at the end of the document. 1.736 + * Do a bit of work to see whether we could just append to what we 1.737 + * already have. 1.738 + */ 1.739 + 1.740 + int32_t count = aContainer->GetChildCount(); 1.741 + 1.742 + if (count > 0) { 1.743 + uint32_t ourCount = mElements.Length(); 1.744 + bool appendToList = false; 1.745 + if (ourCount == 0) { 1.746 + appendToList = true; 1.747 + } else { 1.748 + nsIContent* ourLastContent = mElements[ourCount - 1]; 1.749 + /* 1.750 + * We want to append instead of invalidating if the first thing 1.751 + * that got appended comes after ourLastContent. 1.752 + */ 1.753 + if (nsContentUtils::PositionIsBefore(ourLastContent, aFirstNewContent)) { 1.754 + appendToList = true; 1.755 + } 1.756 + } 1.757 + 1.758 + 1.759 + if (!appendToList) { 1.760 + // The new stuff is somewhere in the middle of our list; check 1.761 + // whether we need to invalidate 1.762 + for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { 1.763 + if (MatchSelf(cur)) { 1.764 + // Uh-oh. We're gonna have to add elements into the middle 1.765 + // of our list. That's not worth the effort. 1.766 + SetDirty(); 1.767 + break; 1.768 + } 1.769 + } 1.770 + 1.771 + ASSERT_IN_SYNC; 1.772 + return; 1.773 + } 1.774 + 1.775 + /* 1.776 + * At this point we know we could append. If we're not up to 1.777 + * date, however, that would be a bad idea -- it could miss some 1.778 + * content that we never picked up due to being lazy. Further, we 1.779 + * may never get asked for this content... so don't grab it yet. 1.780 + */ 1.781 + if (mState == LIST_LAZY) // be lazy 1.782 + return; 1.783 + 1.784 + /* 1.785 + * We're up to date. That means someone's actively using us; we 1.786 + * may as well grab this content.... 1.787 + */ 1.788 + if (mDeep) { 1.789 + for (nsIContent* cur = aFirstNewContent; 1.790 + cur; 1.791 + cur = cur->GetNextNode(aContainer)) { 1.792 + if (cur->IsElement() && Match(cur->AsElement())) { 1.793 + mElements.AppendElement(cur); 1.794 + } 1.795 + } 1.796 + } else { 1.797 + for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) { 1.798 + if (cur->IsElement() && Match(cur->AsElement())) { 1.799 + mElements.AppendElement(cur); 1.800 + } 1.801 + } 1.802 + } 1.803 + 1.804 + ASSERT_IN_SYNC; 1.805 + } 1.806 +} 1.807 + 1.808 +void 1.809 +nsContentList::ContentInserted(nsIDocument *aDocument, 1.810 + nsIContent* aContainer, 1.811 + nsIContent* aChild, 1.812 + int32_t aIndexInContainer) 1.813 +{ 1.814 + // Note that aContainer can be null here if we are inserting into 1.815 + // the document itself; any attempted optimizations to this method 1.816 + // should deal with that. 1.817 + if (mState != LIST_DIRTY && 1.818 + MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) && 1.819 + nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) && 1.820 + MatchSelf(aChild)) { 1.821 + SetDirty(); 1.822 + } 1.823 + 1.824 + ASSERT_IN_SYNC; 1.825 +} 1.826 + 1.827 +void 1.828 +nsContentList::ContentRemoved(nsIDocument *aDocument, 1.829 + nsIContent* aContainer, 1.830 + nsIContent* aChild, 1.831 + int32_t aIndexInContainer, 1.832 + nsIContent* aPreviousSibling) 1.833 +{ 1.834 + // Note that aContainer can be null here if we are removing from 1.835 + // the document itself; any attempted optimizations to this method 1.836 + // should deal with that. 1.837 + if (mState != LIST_DIRTY && 1.838 + MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) && 1.839 + nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) && 1.840 + MatchSelf(aChild)) { 1.841 + SetDirty(); 1.842 + } 1.843 + 1.844 + ASSERT_IN_SYNC; 1.845 +} 1.846 + 1.847 +bool 1.848 +nsContentList::Match(Element *aElement) 1.849 +{ 1.850 + if (mFunc) { 1.851 + return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData); 1.852 + } 1.853 + 1.854 + if (!mXMLMatchAtom) 1.855 + return false; 1.856 + 1.857 + nsINodeInfo *ni = aElement->NodeInfo(); 1.858 + 1.859 + bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown; 1.860 + bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard; 1.861 + bool toReturn = mMatchAll; 1.862 + if (!unknown && !wildcard) 1.863 + toReturn &= ni->NamespaceEquals(mMatchNameSpaceId); 1.864 + 1.865 + if (toReturn) 1.866 + return toReturn; 1.867 + 1.868 + bool matchHTML = aElement->GetNameSpaceID() == kNameSpaceID_XHTML && 1.869 + aElement->OwnerDoc()->IsHTML(); 1.870 + 1.871 + if (unknown) { 1.872 + return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) : 1.873 + ni->QualifiedNameEquals(mXMLMatchAtom); 1.874 + } 1.875 + 1.876 + if (wildcard) { 1.877 + return matchHTML ? ni->Equals(mHTMLMatchAtom) : 1.878 + ni->Equals(mXMLMatchAtom); 1.879 + } 1.880 + 1.881 + return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId) : 1.882 + ni->Equals(mXMLMatchAtom, mMatchNameSpaceId); 1.883 +} 1.884 + 1.885 +bool 1.886 +nsContentList::MatchSelf(nsIContent *aContent) 1.887 +{ 1.888 + NS_PRECONDITION(aContent, "Can't match null stuff, you know"); 1.889 + NS_PRECONDITION(mDeep || aContent->GetParentNode() == mRootNode, 1.890 + "MatchSelf called on a node that we can't possibly match"); 1.891 + 1.892 + if (!aContent->IsElement()) { 1.893 + return false; 1.894 + } 1.895 + 1.896 + if (Match(aContent->AsElement())) 1.897 + return true; 1.898 + 1.899 + if (!mDeep) 1.900 + return false; 1.901 + 1.902 + for (nsIContent* cur = aContent->GetFirstChild(); 1.903 + cur; 1.904 + cur = cur->GetNextNode(aContent)) { 1.905 + if (cur->IsElement() && Match(cur->AsElement())) { 1.906 + return true; 1.907 + } 1.908 + } 1.909 + 1.910 + return false; 1.911 +} 1.912 + 1.913 +void 1.914 +nsContentList::PopulateSelf(uint32_t aNeededLength) 1.915 +{ 1.916 + if (!mRootNode) { 1.917 + return; 1.918 + } 1.919 + 1.920 + ASSERT_IN_SYNC; 1.921 + 1.922 + uint32_t count = mElements.Length(); 1.923 + NS_ASSERTION(mState != LIST_DIRTY || count == 0, 1.924 + "Reset() not called when setting state to LIST_DIRTY?"); 1.925 + 1.926 + if (count >= aNeededLength) // We're all set 1.927 + return; 1.928 + 1.929 + uint32_t elementsToAppend = aNeededLength - count; 1.930 +#ifdef DEBUG 1.931 + uint32_t invariant = elementsToAppend + mElements.Length(); 1.932 +#endif 1.933 + 1.934 + if (mDeep) { 1.935 + // If we already have nodes start searching at the last one, otherwise 1.936 + // start searching at the root. 1.937 + nsINode* cur = count ? mElements[count - 1] : mRootNode; 1.938 + do { 1.939 + cur = cur->GetNextNode(mRootNode); 1.940 + if (!cur) { 1.941 + break; 1.942 + } 1.943 + if (cur->IsElement() && Match(cur->AsElement())) { 1.944 + // Append AsElement() to get nsIContent instead of nsINode 1.945 + mElements.AppendElement(cur->AsElement()); 1.946 + --elementsToAppend; 1.947 + } 1.948 + } while (elementsToAppend); 1.949 + } else { 1.950 + nsIContent* cur = 1.951 + count ? mElements[count-1]->GetNextSibling() : mRootNode->GetFirstChild(); 1.952 + for ( ; cur && elementsToAppend; cur = cur->GetNextSibling()) { 1.953 + if (cur->IsElement() && Match(cur->AsElement())) { 1.954 + mElements.AppendElement(cur); 1.955 + --elementsToAppend; 1.956 + } 1.957 + } 1.958 + } 1.959 + 1.960 + NS_ASSERTION(elementsToAppend + mElements.Length() == invariant, 1.961 + "Something is awry!"); 1.962 + 1.963 + if (elementsToAppend != 0) 1.964 + mState = LIST_UP_TO_DATE; 1.965 + else 1.966 + mState = LIST_LAZY; 1.967 + 1.968 + ASSERT_IN_SYNC; 1.969 +} 1.970 + 1.971 +void 1.972 +nsContentList::RemoveFromHashtable() 1.973 +{ 1.974 + if (mFunc) { 1.975 + // This can't be in the table anyway 1.976 + return; 1.977 + } 1.978 + 1.979 + nsDependentAtomString str(mXMLMatchAtom); 1.980 + nsContentListKey key(mRootNode, mMatchNameSpaceId, str); 1.981 + uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key); 1.982 + if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) { 1.983 + sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr; 1.984 + } 1.985 + 1.986 + if (!gContentListHashTable.ops) 1.987 + return; 1.988 + 1.989 + PL_DHashTableOperate(&gContentListHashTable, 1.990 + &key, 1.991 + PL_DHASH_REMOVE); 1.992 + 1.993 + if (gContentListHashTable.entryCount == 0) { 1.994 + PL_DHashTableFinish(&gContentListHashTable); 1.995 + gContentListHashTable.ops = nullptr; 1.996 + } 1.997 +} 1.998 + 1.999 +void 1.1000 +nsContentList::BringSelfUpToDate(bool aDoFlush) 1.1001 +{ 1.1002 + if (mRootNode && aDoFlush && mFlushesNeeded) { 1.1003 + // XXX sXBL/XBL2 issue 1.1004 + nsIDocument* doc = mRootNode->GetCurrentDoc(); 1.1005 + if (doc) { 1.1006 + // Flush pending content changes Bug 4891. 1.1007 + doc->FlushPendingNotifications(Flush_ContentAndNotify); 1.1008 + } 1.1009 + } 1.1010 + 1.1011 + if (mState != LIST_UP_TO_DATE) 1.1012 + PopulateSelf(uint32_t(-1)); 1.1013 + 1.1014 + ASSERT_IN_SYNC; 1.1015 + NS_ASSERTION(!mRootNode || mState == LIST_UP_TO_DATE, 1.1016 + "PopulateSelf dod not bring content list up to date!"); 1.1017 +} 1.1018 + 1.1019 +nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList() 1.1020 +{ 1.1021 + RemoveFromFuncStringHashtable(); 1.1022 +} 1.1023 + 1.1024 +void 1.1025 +nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable() 1.1026 +{ 1.1027 + if (!gFuncStringContentListHashTable.ops) { 1.1028 + return; 1.1029 + } 1.1030 + 1.1031 + nsFuncStringCacheKey key(mRootNode, mFunc, mString); 1.1032 + PL_DHashTableOperate(&gFuncStringContentListHashTable, 1.1033 + &key, 1.1034 + PL_DHASH_REMOVE); 1.1035 + 1.1036 + if (gFuncStringContentListHashTable.entryCount == 0) { 1.1037 + PL_DHashTableFinish(&gFuncStringContentListHashTable); 1.1038 + gFuncStringContentListHashTable.ops = nullptr; 1.1039 + } 1.1040 +} 1.1041 + 1.1042 +#ifdef DEBUG_CONTENT_LIST 1.1043 +void 1.1044 +nsContentList::AssertInSync() 1.1045 +{ 1.1046 + if (mState == LIST_DIRTY) { 1.1047 + return; 1.1048 + } 1.1049 + 1.1050 + if (!mRootNode) { 1.1051 + NS_ASSERTION(mElements.Length() == 0 && mState == LIST_DIRTY, 1.1052 + "Empty iterator isn't quite empty?"); 1.1053 + return; 1.1054 + } 1.1055 + 1.1056 + // XXX This code will need to change if nsContentLists can ever match 1.1057 + // elements that are outside of the document element. 1.1058 + nsIContent *root; 1.1059 + if (mRootNode->IsNodeOfType(nsINode::eDOCUMENT)) { 1.1060 + root = static_cast<nsIDocument*>(mRootNode)->GetRootElement(); 1.1061 + } 1.1062 + else { 1.1063 + root = static_cast<nsIContent*>(mRootNode); 1.1064 + } 1.1065 + 1.1066 + nsCOMPtr<nsIContentIterator> iter; 1.1067 + if (mDeep) { 1.1068 + iter = NS_NewPreContentIterator(); 1.1069 + iter->Init(root); 1.1070 + iter->First(); 1.1071 + } 1.1072 + 1.1073 + uint32_t cnt = 0, index = 0; 1.1074 + while (true) { 1.1075 + if (cnt == mElements.Length() && mState == LIST_LAZY) { 1.1076 + break; 1.1077 + } 1.1078 + 1.1079 + nsIContent *cur = mDeep ? iter->GetCurrentNode() : 1.1080 + mRootNode->GetChildAt(index++); 1.1081 + if (!cur) { 1.1082 + break; 1.1083 + } 1.1084 + 1.1085 + if (cur->IsElement() && Match(cur->AsElement())) { 1.1086 + NS_ASSERTION(cnt < mElements.Length() && mElements[cnt] == cur, 1.1087 + "Elements is out of sync"); 1.1088 + ++cnt; 1.1089 + } 1.1090 + 1.1091 + if (mDeep) { 1.1092 + iter->Next(); 1.1093 + } 1.1094 + } 1.1095 + 1.1096 + NS_ASSERTION(cnt == mElements.Length(), "Too few elements"); 1.1097 +} 1.1098 +#endif