content/base/src/nsContentList.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

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.)

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=78: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * nsBaseContentList is a basic list of content nodes; nsContentList
michael@0 9 * is a commonly used NodeList implementation (used for
michael@0 10 * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc).
michael@0 11 */
michael@0 12
michael@0 13 #include "nsContentList.h"
michael@0 14 #include "nsIContent.h"
michael@0 15 #include "nsIDOMNode.h"
michael@0 16 #include "nsIDocument.h"
michael@0 17 #include "mozilla/dom/Element.h"
michael@0 18 #include "nsWrapperCacheInlines.h"
michael@0 19 #include "nsContentUtils.h"
michael@0 20 #include "nsCCUncollectableMarker.h"
michael@0 21 #include "nsGkAtoms.h"
michael@0 22 #include "mozilla/dom/HTMLCollectionBinding.h"
michael@0 23 #include "mozilla/dom/NodeListBinding.h"
michael@0 24 #include "mozilla/Likely.h"
michael@0 25 #include "nsGenericHTMLElement.h"
michael@0 26 #include "jsfriendapi.h"
michael@0 27 #include <algorithm>
michael@0 28
michael@0 29 // Form related includes
michael@0 30 #include "nsIDOMHTMLFormElement.h"
michael@0 31
michael@0 32 #include "pldhash.h"
michael@0 33
michael@0 34 #ifdef DEBUG_CONTENT_LIST
michael@0 35 #include "nsIContentIterator.h"
michael@0 36 #define ASSERT_IN_SYNC AssertInSync()
michael@0 37 #else
michael@0 38 #define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO
michael@0 39 #endif
michael@0 40
michael@0 41 using namespace mozilla;
michael@0 42 using namespace mozilla::dom;
michael@0 43
michael@0 44 nsBaseContentList::~nsBaseContentList()
michael@0 45 {
michael@0 46 }
michael@0 47
michael@0 48 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList)
michael@0 49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList)
michael@0 50 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
michael@0 51 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
michael@0 52 tmp->RemoveFromCaches();
michael@0 53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList)
michael@0 55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
michael@0 56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 58 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsBaseContentList)
michael@0 59
michael@0 60 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList)
michael@0 61 if (nsCCUncollectableMarker::sGeneration && tmp->IsBlack()) {
michael@0 62 for (uint32_t i = 0; i < tmp->mElements.Length(); ++i) {
michael@0 63 nsIContent* c = tmp->mElements[i];
michael@0 64 if (c->IsPurple()) {
michael@0 65 c->RemovePurple();
michael@0 66 }
michael@0 67 Element::MarkNodeChildren(c);
michael@0 68 }
michael@0 69 return true;
michael@0 70 }
michael@0 71 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
michael@0 72
michael@0 73 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList)
michael@0 74 return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
michael@0 75 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
michael@0 76
michael@0 77 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList)
michael@0 78 return nsCCUncollectableMarker::sGeneration && tmp->IsBlack();
michael@0 79 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
michael@0 80
michael@0 81 #define NS_CONTENT_LIST_INTERFACES(_class) \
michael@0 82 NS_INTERFACE_TABLE_ENTRY(_class, nsINodeList) \
michael@0 83 NS_INTERFACE_TABLE_ENTRY(_class, nsIDOMNodeList)
michael@0 84
michael@0 85 // QueryInterface implementation for nsBaseContentList
michael@0 86 NS_INTERFACE_TABLE_HEAD(nsBaseContentList)
michael@0 87 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 88 NS_INTERFACE_TABLE(nsBaseContentList, nsINodeList, nsIDOMNodeList)
michael@0 89 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList)
michael@0 90 NS_INTERFACE_MAP_END
michael@0 91
michael@0 92
michael@0 93 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList)
michael@0 94 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBaseContentList)
michael@0 95
michael@0 96
michael@0 97 NS_IMETHODIMP
michael@0 98 nsBaseContentList::GetLength(uint32_t* aLength)
michael@0 99 {
michael@0 100 *aLength = mElements.Length();
michael@0 101
michael@0 102 return NS_OK;
michael@0 103 }
michael@0 104
michael@0 105 NS_IMETHODIMP
michael@0 106 nsBaseContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
michael@0 107 {
michael@0 108 nsISupports *tmp = Item(aIndex);
michael@0 109
michael@0 110 if (!tmp) {
michael@0 111 *aReturn = nullptr;
michael@0 112
michael@0 113 return NS_OK;
michael@0 114 }
michael@0 115
michael@0 116 return CallQueryInterface(tmp, aReturn);
michael@0 117 }
michael@0 118
michael@0 119 nsIContent*
michael@0 120 nsBaseContentList::Item(uint32_t aIndex)
michael@0 121 {
michael@0 122 return mElements.SafeElementAt(aIndex);
michael@0 123 }
michael@0 124
michael@0 125
michael@0 126 int32_t
michael@0 127 nsBaseContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
michael@0 128 {
michael@0 129 return mElements.IndexOf(aContent);
michael@0 130 }
michael@0 131
michael@0 132 int32_t
michael@0 133 nsBaseContentList::IndexOf(nsIContent* aContent)
michael@0 134 {
michael@0 135 return IndexOf(aContent, true);
michael@0 136 }
michael@0 137
michael@0 138 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList, nsBaseContentList,
michael@0 139 mRoot)
michael@0 140
michael@0 141 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsSimpleContentList)
michael@0 142 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
michael@0 143
michael@0 144
michael@0 145 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList, nsBaseContentList)
michael@0 146 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
michael@0 147
michael@0 148 JSObject*
michael@0 149 nsSimpleContentList::WrapObject(JSContext *cx)
michael@0 150 {
michael@0 151 return NodeListBinding::Wrap(cx, this);
michael@0 152 }
michael@0 153
michael@0 154 // Hashtable for storing nsContentLists
michael@0 155 static PLDHashTable gContentListHashTable;
michael@0 156
michael@0 157 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
michael@0 158 static nsContentList*
michael@0 159 sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {};
michael@0 160
michael@0 161 static MOZ_ALWAYS_INLINE uint32_t
michael@0 162 RecentlyUsedCacheIndex(const nsContentListKey& aKey)
michael@0 163 {
michael@0 164 return aKey.GetHash() % RECENTLY_USED_CONTENT_LIST_CACHE_SIZE;
michael@0 165 }
michael@0 166
michael@0 167 struct ContentListHashEntry : public PLDHashEntryHdr
michael@0 168 {
michael@0 169 nsContentList* mContentList;
michael@0 170 };
michael@0 171
michael@0 172 static PLDHashNumber
michael@0 173 ContentListHashtableHashKey(PLDHashTable *table, const void *key)
michael@0 174 {
michael@0 175 const nsContentListKey* list = static_cast<const nsContentListKey *>(key);
michael@0 176 return list->GetHash();
michael@0 177 }
michael@0 178
michael@0 179 static bool
michael@0 180 ContentListHashtableMatchEntry(PLDHashTable *table,
michael@0 181 const PLDHashEntryHdr *entry,
michael@0 182 const void *key)
michael@0 183 {
michael@0 184 const ContentListHashEntry *e =
michael@0 185 static_cast<const ContentListHashEntry *>(entry);
michael@0 186 const nsContentList* list = e->mContentList;
michael@0 187 const nsContentListKey* ourKey = static_cast<const nsContentListKey *>(key);
michael@0 188
michael@0 189 return list->MatchesKey(*ourKey);
michael@0 190 }
michael@0 191
michael@0 192 already_AddRefed<nsContentList>
michael@0 193 NS_GetContentList(nsINode* aRootNode,
michael@0 194 int32_t aMatchNameSpaceId,
michael@0 195 const nsAString& aTagname)
michael@0 196 {
michael@0 197 NS_ASSERTION(aRootNode, "content list has to have a root");
michael@0 198
michael@0 199 nsRefPtr<nsContentList> list;
michael@0 200 nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname);
michael@0 201 uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(hashKey);
michael@0 202 nsContentList* cachedList = sRecentlyUsedContentLists[recentlyUsedCacheIndex];
michael@0 203 if (cachedList && cachedList->MatchesKey(hashKey)) {
michael@0 204 list = cachedList;
michael@0 205 return list.forget();
michael@0 206 }
michael@0 207
michael@0 208 static const PLDHashTableOps hash_table_ops =
michael@0 209 {
michael@0 210 PL_DHashAllocTable,
michael@0 211 PL_DHashFreeTable,
michael@0 212 ContentListHashtableHashKey,
michael@0 213 ContentListHashtableMatchEntry,
michael@0 214 PL_DHashMoveEntryStub,
michael@0 215 PL_DHashClearEntryStub,
michael@0 216 PL_DHashFinalizeStub
michael@0 217 };
michael@0 218
michael@0 219 // Initialize the hashtable if needed.
michael@0 220 if (!gContentListHashTable.ops) {
michael@0 221 PL_DHashTableInit(&gContentListHashTable,
michael@0 222 &hash_table_ops, nullptr,
michael@0 223 sizeof(ContentListHashEntry),
michael@0 224 16);
michael@0 225 }
michael@0 226
michael@0 227 ContentListHashEntry *entry = nullptr;
michael@0 228 // First we look in our hashtable. Then we create a content list if needed
michael@0 229 if (gContentListHashTable.ops) {
michael@0 230
michael@0 231 // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
michael@0 232 // when the entry is already in the hashtable.
michael@0 233 entry = static_cast<ContentListHashEntry *>
michael@0 234 (PL_DHashTableOperate(&gContentListHashTable,
michael@0 235 &hashKey,
michael@0 236 PL_DHASH_ADD));
michael@0 237 if (entry)
michael@0 238 list = entry->mContentList;
michael@0 239 }
michael@0 240
michael@0 241 if (!list) {
michael@0 242 // We need to create a ContentList and add it to our new entry, if
michael@0 243 // we have an entry
michael@0 244 nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
michael@0 245 nsCOMPtr<nsIAtom> htmlAtom;
michael@0 246 if (aMatchNameSpaceId == kNameSpaceID_Unknown) {
michael@0 247 nsAutoString lowercaseName;
michael@0 248 nsContentUtils::ASCIIToLower(aTagname, lowercaseName);
michael@0 249 htmlAtom = do_GetAtom(lowercaseName);
michael@0 250 } else {
michael@0 251 htmlAtom = xmlAtom;
michael@0 252 }
michael@0 253 list = new nsContentList(aRootNode, aMatchNameSpaceId,
michael@0 254 htmlAtom, xmlAtom);
michael@0 255 if (entry) {
michael@0 256 entry->mContentList = list;
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260 sRecentlyUsedContentLists[recentlyUsedCacheIndex] = list;
michael@0 261 return list.forget();
michael@0 262 }
michael@0 263
michael@0 264 #ifdef DEBUG
michael@0 265 const nsCacheableFuncStringContentList::ContentListType
michael@0 266 nsCacheableFuncStringNodeList::sType = nsCacheableFuncStringContentList::eNodeList;
michael@0 267 const nsCacheableFuncStringContentList::ContentListType
michael@0 268 nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
michael@0 269 #endif
michael@0 270
michael@0 271 JSObject*
michael@0 272 nsCacheableFuncStringNodeList::WrapObject(JSContext *cx)
michael@0 273 {
michael@0 274 return NodeListBinding::Wrap(cx, this);
michael@0 275 }
michael@0 276
michael@0 277
michael@0 278 JSObject*
michael@0 279 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx)
michael@0 280 {
michael@0 281 return HTMLCollectionBinding::Wrap(cx, this);
michael@0 282 }
michael@0 283
michael@0 284 // Hashtable for storing nsCacheableFuncStringContentList
michael@0 285 static PLDHashTable gFuncStringContentListHashTable;
michael@0 286
michael@0 287 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
michael@0 288 {
michael@0 289 nsCacheableFuncStringContentList* mContentList;
michael@0 290 };
michael@0 291
michael@0 292 static PLDHashNumber
michael@0 293 FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
michael@0 294 {
michael@0 295 const nsFuncStringCacheKey* funcStringKey =
michael@0 296 static_cast<const nsFuncStringCacheKey *>(key);
michael@0 297 return funcStringKey->GetHash();
michael@0 298 }
michael@0 299
michael@0 300 static bool
michael@0 301 FuncStringContentListHashtableMatchEntry(PLDHashTable *table,
michael@0 302 const PLDHashEntryHdr *entry,
michael@0 303 const void *key)
michael@0 304 {
michael@0 305 const FuncStringContentListHashEntry *e =
michael@0 306 static_cast<const FuncStringContentListHashEntry *>(entry);
michael@0 307 const nsFuncStringCacheKey* ourKey =
michael@0 308 static_cast<const nsFuncStringCacheKey *>(key);
michael@0 309
michael@0 310 return e->mContentList->Equals(ourKey);
michael@0 311 }
michael@0 312
michael@0 313 template<class ListType>
michael@0 314 already_AddRefed<nsContentList>
michael@0 315 GetFuncStringContentList(nsINode* aRootNode,
michael@0 316 nsContentListMatchFunc aFunc,
michael@0 317 nsContentListDestroyFunc aDestroyFunc,
michael@0 318 nsFuncStringContentListDataAllocator aDataAllocator,
michael@0 319 const nsAString& aString)
michael@0 320 {
michael@0 321 NS_ASSERTION(aRootNode, "content list has to have a root");
michael@0 322
michael@0 323 nsRefPtr<nsCacheableFuncStringContentList> list;
michael@0 324
michael@0 325 static const PLDHashTableOps hash_table_ops =
michael@0 326 {
michael@0 327 PL_DHashAllocTable,
michael@0 328 PL_DHashFreeTable,
michael@0 329 FuncStringContentListHashtableHashKey,
michael@0 330 FuncStringContentListHashtableMatchEntry,
michael@0 331 PL_DHashMoveEntryStub,
michael@0 332 PL_DHashClearEntryStub,
michael@0 333 PL_DHashFinalizeStub
michael@0 334 };
michael@0 335
michael@0 336 // Initialize the hashtable if needed.
michael@0 337 if (!gFuncStringContentListHashTable.ops) {
michael@0 338 PL_DHashTableInit(&gFuncStringContentListHashTable,
michael@0 339 &hash_table_ops, nullptr,
michael@0 340 sizeof(FuncStringContentListHashEntry),
michael@0 341 16);
michael@0 342 }
michael@0 343
michael@0 344 FuncStringContentListHashEntry *entry = nullptr;
michael@0 345 // First we look in our hashtable. Then we create a content list if needed
michael@0 346 if (gFuncStringContentListHashTable.ops) {
michael@0 347 nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
michael@0 348
michael@0 349 // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
michael@0 350 // when the entry is already in the hashtable.
michael@0 351 entry = static_cast<FuncStringContentListHashEntry *>
michael@0 352 (PL_DHashTableOperate(&gFuncStringContentListHashTable,
michael@0 353 &hashKey,
michael@0 354 PL_DHASH_ADD));
michael@0 355 if (entry) {
michael@0 356 list = entry->mContentList;
michael@0 357 #ifdef DEBUG
michael@0 358 MOZ_ASSERT_IF(list, list->mType == ListType::sType);
michael@0 359 #endif
michael@0 360 }
michael@0 361 }
michael@0 362
michael@0 363 if (!list) {
michael@0 364 // We need to create a ContentList and add it to our new entry, if
michael@0 365 // we have an entry
michael@0 366 list = new ListType(aRootNode, aFunc, aDestroyFunc, aDataAllocator,
michael@0 367 aString);
michael@0 368 if (entry) {
michael@0 369 entry->mContentList = list;
michael@0 370 }
michael@0 371 }
michael@0 372
michael@0 373 // Don't cache these lists globally
michael@0 374
michael@0 375 return list.forget();
michael@0 376 }
michael@0 377
michael@0 378 already_AddRefed<nsContentList>
michael@0 379 NS_GetFuncStringNodeList(nsINode* aRootNode,
michael@0 380 nsContentListMatchFunc aFunc,
michael@0 381 nsContentListDestroyFunc aDestroyFunc,
michael@0 382 nsFuncStringContentListDataAllocator aDataAllocator,
michael@0 383 const nsAString& aString)
michael@0 384 {
michael@0 385 return GetFuncStringContentList<nsCacheableFuncStringNodeList>(aRootNode,
michael@0 386 aFunc,
michael@0 387 aDestroyFunc,
michael@0 388 aDataAllocator,
michael@0 389 aString);
michael@0 390 }
michael@0 391
michael@0 392 already_AddRefed<nsContentList>
michael@0 393 NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
michael@0 394 nsContentListMatchFunc aFunc,
michael@0 395 nsContentListDestroyFunc aDestroyFunc,
michael@0 396 nsFuncStringContentListDataAllocator aDataAllocator,
michael@0 397 const nsAString& aString)
michael@0 398 {
michael@0 399 return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(aRootNode,
michael@0 400 aFunc,
michael@0 401 aDestroyFunc,
michael@0 402 aDataAllocator,
michael@0 403 aString);
michael@0 404 }
michael@0 405
michael@0 406 // nsContentList implementation
michael@0 407
michael@0 408 nsContentList::nsContentList(nsINode* aRootNode,
michael@0 409 int32_t aMatchNameSpaceId,
michael@0 410 nsIAtom* aHTMLMatchAtom,
michael@0 411 nsIAtom* aXMLMatchAtom,
michael@0 412 bool aDeep)
michael@0 413 : nsBaseContentList(),
michael@0 414 mRootNode(aRootNode),
michael@0 415 mMatchNameSpaceId(aMatchNameSpaceId),
michael@0 416 mHTMLMatchAtom(aHTMLMatchAtom),
michael@0 417 mXMLMatchAtom(aXMLMatchAtom),
michael@0 418 mFunc(nullptr),
michael@0 419 mDestroyFunc(nullptr),
michael@0 420 mData(nullptr),
michael@0 421 mState(LIST_DIRTY),
michael@0 422 mDeep(aDeep),
michael@0 423 mFuncMayDependOnAttr(false)
michael@0 424 {
michael@0 425 NS_ASSERTION(mRootNode, "Must have root");
michael@0 426 if (nsGkAtoms::_asterix == mHTMLMatchAtom) {
michael@0 427 NS_ASSERTION(mXMLMatchAtom == nsGkAtoms::_asterix, "HTML atom and XML atom are not both asterix?");
michael@0 428 mMatchAll = true;
michael@0 429 }
michael@0 430 else {
michael@0 431 mMatchAll = false;
michael@0 432 }
michael@0 433 mRootNode->AddMutationObserver(this);
michael@0 434
michael@0 435 // We only need to flush if we're in an non-HTML document, since the
michael@0 436 // HTML5 parser doesn't need flushing. Further, if we're not in a
michael@0 437 // document at all right now (in the GetCurrentDoc() sense), we're
michael@0 438 // not parser-created and don't need to be flushing stuff under us
michael@0 439 // to get our kids right.
michael@0 440 nsIDocument* doc = mRootNode->GetCurrentDoc();
michael@0 441 mFlushesNeeded = doc && !doc->IsHTML();
michael@0 442 }
michael@0 443
michael@0 444 nsContentList::nsContentList(nsINode* aRootNode,
michael@0 445 nsContentListMatchFunc aFunc,
michael@0 446 nsContentListDestroyFunc aDestroyFunc,
michael@0 447 void* aData,
michael@0 448 bool aDeep,
michael@0 449 nsIAtom* aMatchAtom,
michael@0 450 int32_t aMatchNameSpaceId,
michael@0 451 bool aFuncMayDependOnAttr)
michael@0 452 : nsBaseContentList(),
michael@0 453 mRootNode(aRootNode),
michael@0 454 mMatchNameSpaceId(aMatchNameSpaceId),
michael@0 455 mHTMLMatchAtom(aMatchAtom),
michael@0 456 mXMLMatchAtom(aMatchAtom),
michael@0 457 mFunc(aFunc),
michael@0 458 mDestroyFunc(aDestroyFunc),
michael@0 459 mData(aData),
michael@0 460 mState(LIST_DIRTY),
michael@0 461 mMatchAll(false),
michael@0 462 mDeep(aDeep),
michael@0 463 mFuncMayDependOnAttr(aFuncMayDependOnAttr)
michael@0 464 {
michael@0 465 NS_ASSERTION(mRootNode, "Must have root");
michael@0 466 mRootNode->AddMutationObserver(this);
michael@0 467
michael@0 468 // We only need to flush if we're in an non-HTML document, since the
michael@0 469 // HTML5 parser doesn't need flushing. Further, if we're not in a
michael@0 470 // document at all right now (in the GetCurrentDoc() sense), we're
michael@0 471 // not parser-created and don't need to be flushing stuff under us
michael@0 472 // to get our kids right.
michael@0 473 nsIDocument* doc = mRootNode->GetCurrentDoc();
michael@0 474 mFlushesNeeded = doc && !doc->IsHTML();
michael@0 475 }
michael@0 476
michael@0 477 nsContentList::~nsContentList()
michael@0 478 {
michael@0 479 RemoveFromHashtable();
michael@0 480 if (mRootNode) {
michael@0 481 mRootNode->RemoveMutationObserver(this);
michael@0 482 }
michael@0 483
michael@0 484 if (mDestroyFunc) {
michael@0 485 // Clean up mData
michael@0 486 (*mDestroyFunc)(mData);
michael@0 487 }
michael@0 488 }
michael@0 489
michael@0 490 JSObject*
michael@0 491 nsContentList::WrapObject(JSContext *cx)
michael@0 492 {
michael@0 493 return HTMLCollectionBinding::Wrap(cx, this);
michael@0 494 }
michael@0 495
michael@0 496 NS_IMPL_ISUPPORTS_INHERITED(nsContentList, nsBaseContentList,
michael@0 497 nsIHTMLCollection, nsIDOMHTMLCollection,
michael@0 498 nsIMutationObserver)
michael@0 499
michael@0 500 uint32_t
michael@0 501 nsContentList::Length(bool aDoFlush)
michael@0 502 {
michael@0 503 BringSelfUpToDate(aDoFlush);
michael@0 504
michael@0 505 return mElements.Length();
michael@0 506 }
michael@0 507
michael@0 508 nsIContent *
michael@0 509 nsContentList::Item(uint32_t aIndex, bool aDoFlush)
michael@0 510 {
michael@0 511 if (mRootNode && aDoFlush && mFlushesNeeded) {
michael@0 512 // XXX sXBL/XBL2 issue
michael@0 513 nsIDocument* doc = mRootNode->GetCurrentDoc();
michael@0 514 if (doc) {
michael@0 515 // Flush pending content changes Bug 4891.
michael@0 516 doc->FlushPendingNotifications(Flush_ContentAndNotify);
michael@0 517 }
michael@0 518 }
michael@0 519
michael@0 520 if (mState != LIST_UP_TO_DATE)
michael@0 521 PopulateSelf(std::min(aIndex, UINT32_MAX - 1) + 1);
michael@0 522
michael@0 523 ASSERT_IN_SYNC;
michael@0 524 NS_ASSERTION(!mRootNode || mState != LIST_DIRTY,
michael@0 525 "PopulateSelf left the list in a dirty (useless) state!");
michael@0 526
michael@0 527 return mElements.SafeElementAt(aIndex);
michael@0 528 }
michael@0 529
michael@0 530 Element*
michael@0 531 nsContentList::NamedItem(const nsAString& aName, bool aDoFlush)
michael@0 532 {
michael@0 533 BringSelfUpToDate(aDoFlush);
michael@0 534
michael@0 535 uint32_t i, count = mElements.Length();
michael@0 536
michael@0 537 // Typically IDs and names are atomized
michael@0 538 nsCOMPtr<nsIAtom> name = do_GetAtom(aName);
michael@0 539 NS_ENSURE_TRUE(name, nullptr);
michael@0 540
michael@0 541 for (i = 0; i < count; i++) {
michael@0 542 nsIContent *content = mElements[i];
michael@0 543 // XXX Should this pass eIgnoreCase?
michael@0 544 if (content &&
michael@0 545 (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
michael@0 546 name, eCaseMatters) ||
michael@0 547 content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
michael@0 548 name, eCaseMatters))) {
michael@0 549 return content->AsElement();
michael@0 550 }
michael@0 551 }
michael@0 552
michael@0 553 return nullptr;
michael@0 554 }
michael@0 555
michael@0 556 void
michael@0 557 nsContentList::GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames)
michael@0 558 {
michael@0 559 if (!(aFlags & JSITER_HIDDEN)) {
michael@0 560 return;
michael@0 561 }
michael@0 562
michael@0 563 BringSelfUpToDate(true);
michael@0 564
michael@0 565 nsAutoTArray<nsIAtom*, 8> atoms;
michael@0 566 for (uint32_t i = 0; i < mElements.Length(); ++i) {
michael@0 567 nsIContent *content = mElements.ElementAt(i);
michael@0 568 nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(content);
michael@0 569 if (el) {
michael@0 570 // XXXbz should we be checking for particular tags here? How
michael@0 571 // stable is this part of the spec?
michael@0 572 // Note: nsINode::HasName means the name is exposed on the document,
michael@0 573 // which is false for options, so we don't check it here.
michael@0 574 const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
michael@0 575 if (val && val->Type() == nsAttrValue::eAtom) {
michael@0 576 nsIAtom* name = val->GetAtomValue();
michael@0 577 if (!atoms.Contains(name)) {
michael@0 578 atoms.AppendElement(name);
michael@0 579 }
michael@0 580 }
michael@0 581 }
michael@0 582 if (content->HasID()) {
michael@0 583 nsIAtom* id = content->GetID();
michael@0 584 if (!atoms.Contains(id)) {
michael@0 585 atoms.AppendElement(id);
michael@0 586 }
michael@0 587 }
michael@0 588 }
michael@0 589
michael@0 590 aNames.SetCapacity(atoms.Length());
michael@0 591 for (uint32_t i = 0; i < atoms.Length(); ++i) {
michael@0 592 aNames.AppendElement(nsDependentAtomString(atoms[i]));
michael@0 593 }
michael@0 594 }
michael@0 595
michael@0 596 int32_t
michael@0 597 nsContentList::IndexOf(nsIContent *aContent, bool aDoFlush)
michael@0 598 {
michael@0 599 BringSelfUpToDate(aDoFlush);
michael@0 600
michael@0 601 return mElements.IndexOf(aContent);
michael@0 602 }
michael@0 603
michael@0 604 int32_t
michael@0 605 nsContentList::IndexOf(nsIContent* aContent)
michael@0 606 {
michael@0 607 return IndexOf(aContent, true);
michael@0 608 }
michael@0 609
michael@0 610 void
michael@0 611 nsContentList::NodeWillBeDestroyed(const nsINode* aNode)
michael@0 612 {
michael@0 613 // We shouldn't do anything useful from now on
michael@0 614
michael@0 615 RemoveFromCaches();
michael@0 616 mRootNode = nullptr;
michael@0 617
michael@0 618 // We will get no more updates, so we can never know we're up to
michael@0 619 // date
michael@0 620 SetDirty();
michael@0 621 }
michael@0 622
michael@0 623 NS_IMETHODIMP
michael@0 624 nsContentList::GetLength(uint32_t* aLength)
michael@0 625 {
michael@0 626 *aLength = Length(true);
michael@0 627
michael@0 628 return NS_OK;
michael@0 629 }
michael@0 630
michael@0 631 NS_IMETHODIMP
michael@0 632 nsContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
michael@0 633 {
michael@0 634 nsINode* node = Item(aIndex);
michael@0 635
michael@0 636 if (node) {
michael@0 637 return CallQueryInterface(node, aReturn);
michael@0 638 }
michael@0 639
michael@0 640 *aReturn = nullptr;
michael@0 641
michael@0 642 return NS_OK;
michael@0 643 }
michael@0 644
michael@0 645 NS_IMETHODIMP
michael@0 646 nsContentList::NamedItem(const nsAString& aName, nsIDOMNode** aReturn)
michael@0 647 {
michael@0 648 nsIContent *content = NamedItem(aName, true);
michael@0 649
michael@0 650 if (content) {
michael@0 651 return CallQueryInterface(content, aReturn);
michael@0 652 }
michael@0 653
michael@0 654 *aReturn = nullptr;
michael@0 655
michael@0 656 return NS_OK;
michael@0 657 }
michael@0 658
michael@0 659 Element*
michael@0 660 nsContentList::GetElementAt(uint32_t aIndex)
michael@0 661 {
michael@0 662 return static_cast<Element*>(Item(aIndex, true));
michael@0 663 }
michael@0 664
michael@0 665 nsIContent*
michael@0 666 nsContentList::Item(uint32_t aIndex)
michael@0 667 {
michael@0 668 return GetElementAt(aIndex);
michael@0 669 }
michael@0 670
michael@0 671 void
michael@0 672 nsContentList::AttributeChanged(nsIDocument *aDocument, Element* aElement,
michael@0 673 int32_t aNameSpaceID, nsIAtom* aAttribute,
michael@0 674 int32_t aModType)
michael@0 675 {
michael@0 676 NS_PRECONDITION(aElement, "Must have a content node to work with");
michael@0 677
michael@0 678 if (!mFunc || !mFuncMayDependOnAttr || mState == LIST_DIRTY ||
michael@0 679 !MayContainRelevantNodes(aElement->GetParentNode()) ||
michael@0 680 !nsContentUtils::IsInSameAnonymousTree(mRootNode, aElement)) {
michael@0 681 // Either we're already dirty or this notification doesn't affect
michael@0 682 // whether we might match aElement.
michael@0 683 return;
michael@0 684 }
michael@0 685
michael@0 686 if (Match(aElement)) {
michael@0 687 if (mElements.IndexOf(aElement) == mElements.NoIndex) {
michael@0 688 // We match aElement now, and it's not in our list already. Just dirty
michael@0 689 // ourselves; this is simpler than trying to figure out where to insert
michael@0 690 // aElement.
michael@0 691 SetDirty();
michael@0 692 }
michael@0 693 } else {
michael@0 694 // We no longer match aElement. Remove it from our list. If it's
michael@0 695 // already not there, this is a no-op (though a potentially
michael@0 696 // expensive one). Either way, no change of mState is required
michael@0 697 // here.
michael@0 698 mElements.RemoveElement(aElement);
michael@0 699 }
michael@0 700 }
michael@0 701
michael@0 702 void
michael@0 703 nsContentList::ContentAppended(nsIDocument* aDocument, nsIContent* aContainer,
michael@0 704 nsIContent* aFirstNewContent,
michael@0 705 int32_t aNewIndexInContainer)
michael@0 706 {
michael@0 707 NS_PRECONDITION(aContainer, "Can't get at the new content if no container!");
michael@0 708
michael@0 709 /*
michael@0 710 * If the state is LIST_DIRTY then we have no useful information in our list
michael@0 711 * and we want to put off doing work as much as possible.
michael@0 712 *
michael@0 713 * Also, if aContainer is anonymous from our point of view, we know that we
michael@0 714 * can't possibly be matching any of the kids.
michael@0 715 *
michael@0 716 * Optimize out also the common case when just one new node is appended and
michael@0 717 * it doesn't match us.
michael@0 718 */
michael@0 719 if (mState == LIST_DIRTY ||
michael@0 720 !nsContentUtils::IsInSameAnonymousTree(mRootNode, aContainer) ||
michael@0 721 !MayContainRelevantNodes(aContainer) ||
michael@0 722 (!aFirstNewContent->HasChildren() &&
michael@0 723 !aFirstNewContent->GetNextSibling() &&
michael@0 724 !MatchSelf(aFirstNewContent))) {
michael@0 725 return;
michael@0 726 }
michael@0 727
michael@0 728 /*
michael@0 729 * We want to handle the case of ContentAppended by sometimes
michael@0 730 * appending the content to our list, not just setting state to
michael@0 731 * LIST_DIRTY, since most of our ContentAppended notifications
michael@0 732 * should come during pageload and be at the end of the document.
michael@0 733 * Do a bit of work to see whether we could just append to what we
michael@0 734 * already have.
michael@0 735 */
michael@0 736
michael@0 737 int32_t count = aContainer->GetChildCount();
michael@0 738
michael@0 739 if (count > 0) {
michael@0 740 uint32_t ourCount = mElements.Length();
michael@0 741 bool appendToList = false;
michael@0 742 if (ourCount == 0) {
michael@0 743 appendToList = true;
michael@0 744 } else {
michael@0 745 nsIContent* ourLastContent = mElements[ourCount - 1];
michael@0 746 /*
michael@0 747 * We want to append instead of invalidating if the first thing
michael@0 748 * that got appended comes after ourLastContent.
michael@0 749 */
michael@0 750 if (nsContentUtils::PositionIsBefore(ourLastContent, aFirstNewContent)) {
michael@0 751 appendToList = true;
michael@0 752 }
michael@0 753 }
michael@0 754
michael@0 755
michael@0 756 if (!appendToList) {
michael@0 757 // The new stuff is somewhere in the middle of our list; check
michael@0 758 // whether we need to invalidate
michael@0 759 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
michael@0 760 if (MatchSelf(cur)) {
michael@0 761 // Uh-oh. We're gonna have to add elements into the middle
michael@0 762 // of our list. That's not worth the effort.
michael@0 763 SetDirty();
michael@0 764 break;
michael@0 765 }
michael@0 766 }
michael@0 767
michael@0 768 ASSERT_IN_SYNC;
michael@0 769 return;
michael@0 770 }
michael@0 771
michael@0 772 /*
michael@0 773 * At this point we know we could append. If we're not up to
michael@0 774 * date, however, that would be a bad idea -- it could miss some
michael@0 775 * content that we never picked up due to being lazy. Further, we
michael@0 776 * may never get asked for this content... so don't grab it yet.
michael@0 777 */
michael@0 778 if (mState == LIST_LAZY) // be lazy
michael@0 779 return;
michael@0 780
michael@0 781 /*
michael@0 782 * We're up to date. That means someone's actively using us; we
michael@0 783 * may as well grab this content....
michael@0 784 */
michael@0 785 if (mDeep) {
michael@0 786 for (nsIContent* cur = aFirstNewContent;
michael@0 787 cur;
michael@0 788 cur = cur->GetNextNode(aContainer)) {
michael@0 789 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 790 mElements.AppendElement(cur);
michael@0 791 }
michael@0 792 }
michael@0 793 } else {
michael@0 794 for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
michael@0 795 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 796 mElements.AppendElement(cur);
michael@0 797 }
michael@0 798 }
michael@0 799 }
michael@0 800
michael@0 801 ASSERT_IN_SYNC;
michael@0 802 }
michael@0 803 }
michael@0 804
michael@0 805 void
michael@0 806 nsContentList::ContentInserted(nsIDocument *aDocument,
michael@0 807 nsIContent* aContainer,
michael@0 808 nsIContent* aChild,
michael@0 809 int32_t aIndexInContainer)
michael@0 810 {
michael@0 811 // Note that aContainer can be null here if we are inserting into
michael@0 812 // the document itself; any attempted optimizations to this method
michael@0 813 // should deal with that.
michael@0 814 if (mState != LIST_DIRTY &&
michael@0 815 MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
michael@0 816 nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
michael@0 817 MatchSelf(aChild)) {
michael@0 818 SetDirty();
michael@0 819 }
michael@0 820
michael@0 821 ASSERT_IN_SYNC;
michael@0 822 }
michael@0 823
michael@0 824 void
michael@0 825 nsContentList::ContentRemoved(nsIDocument *aDocument,
michael@0 826 nsIContent* aContainer,
michael@0 827 nsIContent* aChild,
michael@0 828 int32_t aIndexInContainer,
michael@0 829 nsIContent* aPreviousSibling)
michael@0 830 {
michael@0 831 // Note that aContainer can be null here if we are removing from
michael@0 832 // the document itself; any attempted optimizations to this method
michael@0 833 // should deal with that.
michael@0 834 if (mState != LIST_DIRTY &&
michael@0 835 MayContainRelevantNodes(NODE_FROM(aContainer, aDocument)) &&
michael@0 836 nsContentUtils::IsInSameAnonymousTree(mRootNode, aChild) &&
michael@0 837 MatchSelf(aChild)) {
michael@0 838 SetDirty();
michael@0 839 }
michael@0 840
michael@0 841 ASSERT_IN_SYNC;
michael@0 842 }
michael@0 843
michael@0 844 bool
michael@0 845 nsContentList::Match(Element *aElement)
michael@0 846 {
michael@0 847 if (mFunc) {
michael@0 848 return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData);
michael@0 849 }
michael@0 850
michael@0 851 if (!mXMLMatchAtom)
michael@0 852 return false;
michael@0 853
michael@0 854 nsINodeInfo *ni = aElement->NodeInfo();
michael@0 855
michael@0 856 bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
michael@0 857 bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
michael@0 858 bool toReturn = mMatchAll;
michael@0 859 if (!unknown && !wildcard)
michael@0 860 toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
michael@0 861
michael@0 862 if (toReturn)
michael@0 863 return toReturn;
michael@0 864
michael@0 865 bool matchHTML = aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
michael@0 866 aElement->OwnerDoc()->IsHTML();
michael@0 867
michael@0 868 if (unknown) {
michael@0 869 return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) :
michael@0 870 ni->QualifiedNameEquals(mXMLMatchAtom);
michael@0 871 }
michael@0 872
michael@0 873 if (wildcard) {
michael@0 874 return matchHTML ? ni->Equals(mHTMLMatchAtom) :
michael@0 875 ni->Equals(mXMLMatchAtom);
michael@0 876 }
michael@0 877
michael@0 878 return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId) :
michael@0 879 ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
michael@0 880 }
michael@0 881
michael@0 882 bool
michael@0 883 nsContentList::MatchSelf(nsIContent *aContent)
michael@0 884 {
michael@0 885 NS_PRECONDITION(aContent, "Can't match null stuff, you know");
michael@0 886 NS_PRECONDITION(mDeep || aContent->GetParentNode() == mRootNode,
michael@0 887 "MatchSelf called on a node that we can't possibly match");
michael@0 888
michael@0 889 if (!aContent->IsElement()) {
michael@0 890 return false;
michael@0 891 }
michael@0 892
michael@0 893 if (Match(aContent->AsElement()))
michael@0 894 return true;
michael@0 895
michael@0 896 if (!mDeep)
michael@0 897 return false;
michael@0 898
michael@0 899 for (nsIContent* cur = aContent->GetFirstChild();
michael@0 900 cur;
michael@0 901 cur = cur->GetNextNode(aContent)) {
michael@0 902 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 903 return true;
michael@0 904 }
michael@0 905 }
michael@0 906
michael@0 907 return false;
michael@0 908 }
michael@0 909
michael@0 910 void
michael@0 911 nsContentList::PopulateSelf(uint32_t aNeededLength)
michael@0 912 {
michael@0 913 if (!mRootNode) {
michael@0 914 return;
michael@0 915 }
michael@0 916
michael@0 917 ASSERT_IN_SYNC;
michael@0 918
michael@0 919 uint32_t count = mElements.Length();
michael@0 920 NS_ASSERTION(mState != LIST_DIRTY || count == 0,
michael@0 921 "Reset() not called when setting state to LIST_DIRTY?");
michael@0 922
michael@0 923 if (count >= aNeededLength) // We're all set
michael@0 924 return;
michael@0 925
michael@0 926 uint32_t elementsToAppend = aNeededLength - count;
michael@0 927 #ifdef DEBUG
michael@0 928 uint32_t invariant = elementsToAppend + mElements.Length();
michael@0 929 #endif
michael@0 930
michael@0 931 if (mDeep) {
michael@0 932 // If we already have nodes start searching at the last one, otherwise
michael@0 933 // start searching at the root.
michael@0 934 nsINode* cur = count ? mElements[count - 1] : mRootNode;
michael@0 935 do {
michael@0 936 cur = cur->GetNextNode(mRootNode);
michael@0 937 if (!cur) {
michael@0 938 break;
michael@0 939 }
michael@0 940 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 941 // Append AsElement() to get nsIContent instead of nsINode
michael@0 942 mElements.AppendElement(cur->AsElement());
michael@0 943 --elementsToAppend;
michael@0 944 }
michael@0 945 } while (elementsToAppend);
michael@0 946 } else {
michael@0 947 nsIContent* cur =
michael@0 948 count ? mElements[count-1]->GetNextSibling() : mRootNode->GetFirstChild();
michael@0 949 for ( ; cur && elementsToAppend; cur = cur->GetNextSibling()) {
michael@0 950 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 951 mElements.AppendElement(cur);
michael@0 952 --elementsToAppend;
michael@0 953 }
michael@0 954 }
michael@0 955 }
michael@0 956
michael@0 957 NS_ASSERTION(elementsToAppend + mElements.Length() == invariant,
michael@0 958 "Something is awry!");
michael@0 959
michael@0 960 if (elementsToAppend != 0)
michael@0 961 mState = LIST_UP_TO_DATE;
michael@0 962 else
michael@0 963 mState = LIST_LAZY;
michael@0 964
michael@0 965 ASSERT_IN_SYNC;
michael@0 966 }
michael@0 967
michael@0 968 void
michael@0 969 nsContentList::RemoveFromHashtable()
michael@0 970 {
michael@0 971 if (mFunc) {
michael@0 972 // This can't be in the table anyway
michael@0 973 return;
michael@0 974 }
michael@0 975
michael@0 976 nsDependentAtomString str(mXMLMatchAtom);
michael@0 977 nsContentListKey key(mRootNode, mMatchNameSpaceId, str);
michael@0 978 uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
michael@0 979 if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
michael@0 980 sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
michael@0 981 }
michael@0 982
michael@0 983 if (!gContentListHashTable.ops)
michael@0 984 return;
michael@0 985
michael@0 986 PL_DHashTableOperate(&gContentListHashTable,
michael@0 987 &key,
michael@0 988 PL_DHASH_REMOVE);
michael@0 989
michael@0 990 if (gContentListHashTable.entryCount == 0) {
michael@0 991 PL_DHashTableFinish(&gContentListHashTable);
michael@0 992 gContentListHashTable.ops = nullptr;
michael@0 993 }
michael@0 994 }
michael@0 995
michael@0 996 void
michael@0 997 nsContentList::BringSelfUpToDate(bool aDoFlush)
michael@0 998 {
michael@0 999 if (mRootNode && aDoFlush && mFlushesNeeded) {
michael@0 1000 // XXX sXBL/XBL2 issue
michael@0 1001 nsIDocument* doc = mRootNode->GetCurrentDoc();
michael@0 1002 if (doc) {
michael@0 1003 // Flush pending content changes Bug 4891.
michael@0 1004 doc->FlushPendingNotifications(Flush_ContentAndNotify);
michael@0 1005 }
michael@0 1006 }
michael@0 1007
michael@0 1008 if (mState != LIST_UP_TO_DATE)
michael@0 1009 PopulateSelf(uint32_t(-1));
michael@0 1010
michael@0 1011 ASSERT_IN_SYNC;
michael@0 1012 NS_ASSERTION(!mRootNode || mState == LIST_UP_TO_DATE,
michael@0 1013 "PopulateSelf dod not bring content list up to date!");
michael@0 1014 }
michael@0 1015
michael@0 1016 nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
michael@0 1017 {
michael@0 1018 RemoveFromFuncStringHashtable();
michael@0 1019 }
michael@0 1020
michael@0 1021 void
michael@0 1022 nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
michael@0 1023 {
michael@0 1024 if (!gFuncStringContentListHashTable.ops) {
michael@0 1025 return;
michael@0 1026 }
michael@0 1027
michael@0 1028 nsFuncStringCacheKey key(mRootNode, mFunc, mString);
michael@0 1029 PL_DHashTableOperate(&gFuncStringContentListHashTable,
michael@0 1030 &key,
michael@0 1031 PL_DHASH_REMOVE);
michael@0 1032
michael@0 1033 if (gFuncStringContentListHashTable.entryCount == 0) {
michael@0 1034 PL_DHashTableFinish(&gFuncStringContentListHashTable);
michael@0 1035 gFuncStringContentListHashTable.ops = nullptr;
michael@0 1036 }
michael@0 1037 }
michael@0 1038
michael@0 1039 #ifdef DEBUG_CONTENT_LIST
michael@0 1040 void
michael@0 1041 nsContentList::AssertInSync()
michael@0 1042 {
michael@0 1043 if (mState == LIST_DIRTY) {
michael@0 1044 return;
michael@0 1045 }
michael@0 1046
michael@0 1047 if (!mRootNode) {
michael@0 1048 NS_ASSERTION(mElements.Length() == 0 && mState == LIST_DIRTY,
michael@0 1049 "Empty iterator isn't quite empty?");
michael@0 1050 return;
michael@0 1051 }
michael@0 1052
michael@0 1053 // XXX This code will need to change if nsContentLists can ever match
michael@0 1054 // elements that are outside of the document element.
michael@0 1055 nsIContent *root;
michael@0 1056 if (mRootNode->IsNodeOfType(nsINode::eDOCUMENT)) {
michael@0 1057 root = static_cast<nsIDocument*>(mRootNode)->GetRootElement();
michael@0 1058 }
michael@0 1059 else {
michael@0 1060 root = static_cast<nsIContent*>(mRootNode);
michael@0 1061 }
michael@0 1062
michael@0 1063 nsCOMPtr<nsIContentIterator> iter;
michael@0 1064 if (mDeep) {
michael@0 1065 iter = NS_NewPreContentIterator();
michael@0 1066 iter->Init(root);
michael@0 1067 iter->First();
michael@0 1068 }
michael@0 1069
michael@0 1070 uint32_t cnt = 0, index = 0;
michael@0 1071 while (true) {
michael@0 1072 if (cnt == mElements.Length() && mState == LIST_LAZY) {
michael@0 1073 break;
michael@0 1074 }
michael@0 1075
michael@0 1076 nsIContent *cur = mDeep ? iter->GetCurrentNode() :
michael@0 1077 mRootNode->GetChildAt(index++);
michael@0 1078 if (!cur) {
michael@0 1079 break;
michael@0 1080 }
michael@0 1081
michael@0 1082 if (cur->IsElement() && Match(cur->AsElement())) {
michael@0 1083 NS_ASSERTION(cnt < mElements.Length() && mElements[cnt] == cur,
michael@0 1084 "Elements is out of sync");
michael@0 1085 ++cnt;
michael@0 1086 }
michael@0 1087
michael@0 1088 if (mDeep) {
michael@0 1089 iter->Next();
michael@0 1090 }
michael@0 1091 }
michael@0 1092
michael@0 1093 NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
michael@0 1094 }
michael@0 1095 #endif

mercurial