1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * nsBaseContentList is a basic list of content nodes; nsContentList
9 * is a commonly used NodeList implementation (used for
10 * getElementsByTagName, some properties on nsIDOMHTMLDocument, etc).
13 #include "nsContentList.h"
14 #include "nsIContent.h"
15 #include "nsIDOMNode.h"
16 #include "nsIDocument.h"
17 #include "mozilla/dom/Element.h"
18 #include "nsWrapperCacheInlines.h"
19 #include "nsContentUtils.h"
20 #include "nsCCUncollectableMarker.h"
21 #include "nsGkAtoms.h"
22 #include "mozilla/dom/HTMLCollectionBinding.h"
23 #include "mozilla/dom/NodeListBinding.h"
24 #include "mozilla/Likely.h"
25 #include "nsGenericHTMLElement.h"
26 #include "jsfriendapi.h"
28 #include "mozilla/dom/NodeInfoInlines.h"
30 // Form related includes
31 #include "nsIDOMHTMLFormElement.h"
35 #ifdef DEBUG_CONTENT_LIST
36 #include "nsIContentIterator.h"
37 #define ASSERT_IN_SYNC AssertInSync()
39 #define ASSERT_IN_SYNC PR_BEGIN_MACRO PR_END_MACRO
42 using namespace mozilla
;
43 using namespace mozilla::dom
;
45 nsBaseContentList::~nsBaseContentList()
49 NS_IMPL_CYCLE_COLLECTION_CLASS(nsBaseContentList
)
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBaseContentList
)
51 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements
)
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
53 tmp
->RemoveFromCaches();
54 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBaseContentList
)
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements
)
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
59 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsBaseContentList
)
61 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsBaseContentList
)
62 if (nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack()) {
63 for (uint32_t i
= 0; i
< tmp
->mElements
.Length(); ++i
) {
64 nsIContent
* c
= tmp
->mElements
[i
];
68 Element::MarkNodeChildren(c
);
72 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
74 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsBaseContentList
)
75 return nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack();
76 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
78 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsBaseContentList
)
79 return nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack();
80 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
82 #define NS_CONTENT_LIST_INTERFACES(_class) \
83 NS_INTERFACE_TABLE_ENTRY(_class, nsINodeList) \
84 NS_INTERFACE_TABLE_ENTRY(_class, nsIDOMNodeList)
86 // QueryInterface implementation for nsBaseContentList
87 NS_INTERFACE_TABLE_HEAD(nsBaseContentList
)
88 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
89 NS_INTERFACE_TABLE(nsBaseContentList
, nsINodeList
, nsIDOMNodeList
)
90 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsBaseContentList
)
94 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBaseContentList
)
95 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBaseContentList
)
99 nsBaseContentList::GetLength(uint32_t* aLength
)
101 *aLength
= mElements
.Length();
107 nsBaseContentList::Item(uint32_t aIndex
, nsIDOMNode
** aReturn
)
109 nsISupports
*tmp
= Item(aIndex
);
117 return CallQueryInterface(tmp
, aReturn
);
121 nsBaseContentList::Item(uint32_t aIndex
)
123 return mElements
.SafeElementAt(aIndex
);
128 nsBaseContentList::IndexOf(nsIContent
*aContent
, bool aDoFlush
)
130 return mElements
.IndexOf(aContent
);
134 nsBaseContentList::IndexOf(nsIContent
* aContent
)
136 return IndexOf(aContent
, true);
139 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsSimpleContentList
, nsBaseContentList
,
142 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsSimpleContentList
)
143 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList
)
146 NS_IMPL_ADDREF_INHERITED(nsSimpleContentList
, nsBaseContentList
)
147 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList
, nsBaseContentList
)
150 nsSimpleContentList::WrapObject(JSContext
*cx
)
152 return NodeListBinding::Wrap(cx
, this);
155 // Hashtable for storing nsContentLists
156 static PLDHashTable gContentListHashTable
;
158 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
159 static nsContentList
*
160 sRecentlyUsedContentLists
[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE
] = {};
162 static MOZ_ALWAYS_INLINE
uint32_t
163 RecentlyUsedCacheIndex(const nsContentListKey
& aKey
)
165 return aKey
.GetHash() % RECENTLY_USED_CONTENT_LIST_CACHE_SIZE
;
168 struct ContentListHashEntry
: public PLDHashEntryHdr
170 nsContentList
* mContentList
;
174 ContentListHashtableHashKey(PLDHashTable
*table
, const void *key
)
176 const nsContentListKey
* list
= static_cast<const nsContentListKey
*>(key
);
177 return list
->GetHash();
181 ContentListHashtableMatchEntry(PLDHashTable
*table
,
182 const PLDHashEntryHdr
*entry
,
185 const ContentListHashEntry
*e
=
186 static_cast<const ContentListHashEntry
*>(entry
);
187 const nsContentList
* list
= e
->mContentList
;
188 const nsContentListKey
* ourKey
= static_cast<const nsContentListKey
*>(key
);
190 return list
->MatchesKey(*ourKey
);
193 already_AddRefed
<nsContentList
>
194 NS_GetContentList(nsINode
* aRootNode
,
195 int32_t aMatchNameSpaceId
,
196 const nsAString
& aTagname
)
198 NS_ASSERTION(aRootNode
, "content list has to have a root");
200 nsRefPtr
<nsContentList
> list
;
201 nsContentListKey
hashKey(aRootNode
, aMatchNameSpaceId
, aTagname
);
202 uint32_t recentlyUsedCacheIndex
= RecentlyUsedCacheIndex(hashKey
);
203 nsContentList
* cachedList
= sRecentlyUsedContentLists
[recentlyUsedCacheIndex
];
204 if (cachedList
&& cachedList
->MatchesKey(hashKey
)) {
206 return list
.forget();
209 static const PLDHashTableOps hash_table_ops
=
213 ContentListHashtableHashKey
,
214 ContentListHashtableMatchEntry
,
215 PL_DHashMoveEntryStub
,
216 PL_DHashClearEntryStub
,
220 // Initialize the hashtable if needed.
221 if (!gContentListHashTable
.ops
) {
222 PL_DHashTableInit(&gContentListHashTable
, &hash_table_ops
, nullptr,
223 sizeof(ContentListHashEntry
));
226 ContentListHashEntry
*entry
= nullptr;
227 // First we look in our hashtable. Then we create a content list if needed
228 if (gContentListHashTable
.ops
) {
230 // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
231 // when the entry is already in the hashtable.
232 entry
= static_cast<ContentListHashEntry
*>
233 (PL_DHashTableAdd(&gContentListHashTable
, &hashKey
));
235 list
= entry
->mContentList
;
239 // We need to create a ContentList and add it to our new entry, if
241 nsCOMPtr
<nsIAtom
> xmlAtom
= do_GetAtom(aTagname
);
242 nsCOMPtr
<nsIAtom
> htmlAtom
;
243 if (aMatchNameSpaceId
== kNameSpaceID_Unknown
) {
244 nsAutoString lowercaseName
;
245 nsContentUtils::ASCIIToLower(aTagname
, lowercaseName
);
246 htmlAtom
= do_GetAtom(lowercaseName
);
250 list
= new nsContentList(aRootNode
, aMatchNameSpaceId
,
253 entry
->mContentList
= list
;
257 sRecentlyUsedContentLists
[recentlyUsedCacheIndex
] = list
;
258 return list
.forget();
262 const nsCacheableFuncStringContentList::ContentListType
263 nsCacheableFuncStringNodeList::sType
= nsCacheableFuncStringContentList::eNodeList
;
264 const nsCacheableFuncStringContentList::ContentListType
265 nsCacheableFuncStringHTMLCollection::sType
= nsCacheableFuncStringContentList::eHTMLCollection
;
269 nsCacheableFuncStringNodeList::WrapObject(JSContext
*cx
)
271 return NodeListBinding::Wrap(cx
, this);
276 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext
*cx
)
278 return HTMLCollectionBinding::Wrap(cx
, this);
281 // Hashtable for storing nsCacheableFuncStringContentList
282 static PLDHashTable gFuncStringContentListHashTable
;
284 struct FuncStringContentListHashEntry
: public PLDHashEntryHdr
286 nsCacheableFuncStringContentList
* mContentList
;
290 FuncStringContentListHashtableHashKey(PLDHashTable
*table
, const void *key
)
292 const nsFuncStringCacheKey
* funcStringKey
=
293 static_cast<const nsFuncStringCacheKey
*>(key
);
294 return funcStringKey
->GetHash();
298 FuncStringContentListHashtableMatchEntry(PLDHashTable
*table
,
299 const PLDHashEntryHdr
*entry
,
302 const FuncStringContentListHashEntry
*e
=
303 static_cast<const FuncStringContentListHashEntry
*>(entry
);
304 const nsFuncStringCacheKey
* ourKey
=
305 static_cast<const nsFuncStringCacheKey
*>(key
);
307 return e
->mContentList
->Equals(ourKey
);
310 template<class ListType
>
311 already_AddRefed
<nsContentList
>
312 GetFuncStringContentList(nsINode
* aRootNode
,
313 nsContentListMatchFunc aFunc
,
314 nsContentListDestroyFunc aDestroyFunc
,
315 nsFuncStringContentListDataAllocator aDataAllocator
,
316 const nsAString
& aString
)
318 NS_ASSERTION(aRootNode
, "content list has to have a root");
320 nsRefPtr
<nsCacheableFuncStringContentList
> list
;
322 static const PLDHashTableOps hash_table_ops
=
326 FuncStringContentListHashtableHashKey
,
327 FuncStringContentListHashtableMatchEntry
,
328 PL_DHashMoveEntryStub
,
329 PL_DHashClearEntryStub
,
333 // Initialize the hashtable if needed.
334 if (!gFuncStringContentListHashTable
.ops
) {
335 PL_DHashTableInit(&gFuncStringContentListHashTable
, &hash_table_ops
,
336 nullptr, sizeof(FuncStringContentListHashEntry
));
339 FuncStringContentListHashEntry
*entry
= nullptr;
340 // First we look in our hashtable. Then we create a content list if needed
341 if (gFuncStringContentListHashTable
.ops
) {
342 nsFuncStringCacheKey
hashKey(aRootNode
, aFunc
, aString
);
344 // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
345 // when the entry is already in the hashtable.
346 entry
= static_cast<FuncStringContentListHashEntry
*>
347 (PL_DHashTableAdd(&gFuncStringContentListHashTable
,
350 list
= entry
->mContentList
;
352 MOZ_ASSERT_IF(list
, list
->mType
== ListType::sType
);
358 // We need to create a ContentList and add it to our new entry, if
360 list
= new ListType(aRootNode
, aFunc
, aDestroyFunc
, aDataAllocator
,
363 entry
->mContentList
= list
;
367 // Don't cache these lists globally
369 return list
.forget();
372 already_AddRefed
<nsContentList
>
373 NS_GetFuncStringNodeList(nsINode
* aRootNode
,
374 nsContentListMatchFunc aFunc
,
375 nsContentListDestroyFunc aDestroyFunc
,
376 nsFuncStringContentListDataAllocator aDataAllocator
,
377 const nsAString
& aString
)
379 return GetFuncStringContentList
<nsCacheableFuncStringNodeList
>(aRootNode
,
386 already_AddRefed
<nsContentList
>
387 NS_GetFuncStringHTMLCollection(nsINode
* aRootNode
,
388 nsContentListMatchFunc aFunc
,
389 nsContentListDestroyFunc aDestroyFunc
,
390 nsFuncStringContentListDataAllocator aDataAllocator
,
391 const nsAString
& aString
)
393 return GetFuncStringContentList
<nsCacheableFuncStringHTMLCollection
>(aRootNode
,
400 // nsContentList implementation
402 nsContentList::nsContentList(nsINode
* aRootNode
,
403 int32_t aMatchNameSpaceId
,
404 nsIAtom
* aHTMLMatchAtom
,
405 nsIAtom
* aXMLMatchAtom
,
407 : nsBaseContentList(),
408 mRootNode(aRootNode
),
409 mMatchNameSpaceId(aMatchNameSpaceId
),
410 mHTMLMatchAtom(aHTMLMatchAtom
),
411 mXMLMatchAtom(aXMLMatchAtom
),
413 mDestroyFunc(nullptr),
417 mFuncMayDependOnAttr(false)
419 NS_ASSERTION(mRootNode
, "Must have root");
420 if (nsGkAtoms::_asterix
== mHTMLMatchAtom
) {
421 NS_ASSERTION(mXMLMatchAtom
== nsGkAtoms::_asterix
, "HTML atom and XML atom are not both asterix?");
427 mRootNode
->AddMutationObserver(this);
429 // We only need to flush if we're in an non-HTML document, since the
430 // HTML5 parser doesn't need flushing. Further, if we're not in a
431 // document at all right now (in the GetUncomposedDoc() sense), we're
432 // not parser-created and don't need to be flushing stuff under us
433 // to get our kids right.
434 nsIDocument
* doc
= mRootNode
->GetUncomposedDoc();
435 mFlushesNeeded
= doc
&& !doc
->IsHTML();
438 nsContentList::nsContentList(nsINode
* aRootNode
,
439 nsContentListMatchFunc aFunc
,
440 nsContentListDestroyFunc aDestroyFunc
,
444 int32_t aMatchNameSpaceId
,
445 bool aFuncMayDependOnAttr
)
446 : nsBaseContentList(),
447 mRootNode(aRootNode
),
448 mMatchNameSpaceId(aMatchNameSpaceId
),
449 mHTMLMatchAtom(aMatchAtom
),
450 mXMLMatchAtom(aMatchAtom
),
452 mDestroyFunc(aDestroyFunc
),
457 mFuncMayDependOnAttr(aFuncMayDependOnAttr
)
459 NS_ASSERTION(mRootNode
, "Must have root");
460 mRootNode
->AddMutationObserver(this);
462 // We only need to flush if we're in an non-HTML document, since the
463 // HTML5 parser doesn't need flushing. Further, if we're not in a
464 // document at all right now (in the GetUncomposedDoc() sense), we're
465 // not parser-created and don't need to be flushing stuff under us
466 // to get our kids right.
467 nsIDocument
* doc
= mRootNode
->GetUncomposedDoc();
468 mFlushesNeeded
= doc
&& !doc
->IsHTML();
471 nsContentList::~nsContentList()
473 RemoveFromHashtable();
475 mRootNode
->RemoveMutationObserver(this);
480 (*mDestroyFunc
)(mData
);
485 nsContentList::WrapObject(JSContext
*cx
)
487 return HTMLCollectionBinding::Wrap(cx
, this);
490 NS_IMPL_ISUPPORTS_INHERITED(nsContentList
, nsBaseContentList
,
491 nsIHTMLCollection
, nsIDOMHTMLCollection
,
495 nsContentList::Length(bool aDoFlush
)
497 BringSelfUpToDate(aDoFlush
);
499 return mElements
.Length();
503 nsContentList::Item(uint32_t aIndex
, bool aDoFlush
)
505 if (mRootNode
&& aDoFlush
&& mFlushesNeeded
) {
506 // XXX sXBL/XBL2 issue
507 nsIDocument
* doc
= mRootNode
->GetUncomposedDoc();
509 // Flush pending content changes Bug 4891.
510 doc
->FlushPendingNotifications(Flush_ContentAndNotify
);
514 if (mState
!= LIST_UP_TO_DATE
)
515 PopulateSelf(std::min(aIndex
, UINT32_MAX
- 1) + 1);
518 NS_ASSERTION(!mRootNode
|| mState
!= LIST_DIRTY
,
519 "PopulateSelf left the list in a dirty (useless) state!");
521 return mElements
.SafeElementAt(aIndex
);
525 nsContentList::NamedItem(const nsAString
& aName
, bool aDoFlush
)
527 if (aName
.IsEmpty()) {
531 BringSelfUpToDate(aDoFlush
);
533 uint32_t i
, count
= mElements
.Length();
535 // Typically IDs and names are atomized
536 nsCOMPtr
<nsIAtom
> name
= do_GetAtom(aName
);
537 NS_ENSURE_TRUE(name
, nullptr);
539 for (i
= 0; i
< count
; i
++) {
540 nsIContent
*content
= mElements
[i
];
541 // XXX Should this pass eIgnoreCase?
543 (content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
544 name
, eCaseMatters
) ||
545 content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::id
,
546 name
, eCaseMatters
))) {
547 return content
->AsElement();
555 nsContentList::GetSupportedNames(unsigned aFlags
, nsTArray
<nsString
>& aNames
)
557 if (!(aFlags
& JSITER_HIDDEN
)) {
561 BringSelfUpToDate(true);
563 nsAutoTArray
<nsIAtom
*, 8> atoms
;
564 for (uint32_t i
= 0; i
< mElements
.Length(); ++i
) {
565 nsIContent
*content
= mElements
.ElementAt(i
);
566 if (content
->HasID()) {
567 nsIAtom
* id
= content
->GetID();
568 MOZ_ASSERT(id
!= nsGkAtoms::_empty
,
569 "Empty ids don't get atomized");
570 if (!atoms
.Contains(id
)) {
571 atoms
.AppendElement(id
);
575 nsGenericHTMLElement
* el
= nsGenericHTMLElement::FromContent(content
);
577 // XXXbz should we be checking for particular tags here? How
578 // stable is this part of the spec?
579 // Note: nsINode::HasName means the name is exposed on the document,
580 // which is false for options, so we don't check it here.
581 const nsAttrValue
* val
= el
->GetParsedAttr(nsGkAtoms::name
);
582 if (val
&& val
->Type() == nsAttrValue::eAtom
) {
583 nsIAtom
* name
= val
->GetAtomValue();
584 MOZ_ASSERT(name
!= nsGkAtoms::_empty
,
585 "Empty names don't get atomized");
586 if (!atoms
.Contains(name
)) {
587 atoms
.AppendElement(name
);
593 aNames
.SetCapacity(atoms
.Length());
594 for (uint32_t i
= 0; i
< atoms
.Length(); ++i
) {
595 aNames
.AppendElement(nsDependentAtomString(atoms
[i
]));
600 nsContentList::IndexOf(nsIContent
*aContent
, bool aDoFlush
)
602 BringSelfUpToDate(aDoFlush
);
604 return mElements
.IndexOf(aContent
);
608 nsContentList::IndexOf(nsIContent
* aContent
)
610 return IndexOf(aContent
, true);
614 nsContentList::NodeWillBeDestroyed(const nsINode
* aNode
)
616 // We shouldn't do anything useful from now on
621 // We will get no more updates, so we can never know we're up to
627 nsContentList::GetLength(uint32_t* aLength
)
629 *aLength
= Length(true);
635 nsContentList::Item(uint32_t aIndex
, nsIDOMNode
** aReturn
)
637 nsINode
* node
= Item(aIndex
);
640 return CallQueryInterface(node
, aReturn
);
649 nsContentList::NamedItem(const nsAString
& aName
, nsIDOMNode
** aReturn
)
651 nsIContent
*content
= NamedItem(aName
, true);
654 return CallQueryInterface(content
, aReturn
);
663 nsContentList::GetElementAt(uint32_t aIndex
)
665 return static_cast<Element
*>(Item(aIndex
, true));
669 nsContentList::Item(uint32_t aIndex
)
671 return GetElementAt(aIndex
);
675 nsContentList::AttributeChanged(nsIDocument
*aDocument
, Element
* aElement
,
676 int32_t aNameSpaceID
, nsIAtom
* aAttribute
,
679 NS_PRECONDITION(aElement
, "Must have a content node to work with");
681 if (!mFunc
|| !mFuncMayDependOnAttr
|| mState
== LIST_DIRTY
||
682 !MayContainRelevantNodes(aElement
->GetParentNode()) ||
683 !nsContentUtils::IsInSameAnonymousTree(mRootNode
, aElement
)) {
684 // Either we're already dirty or this notification doesn't affect
685 // whether we might match aElement.
689 if (Match(aElement
)) {
690 if (mElements
.IndexOf(aElement
) == mElements
.NoIndex
) {
691 // We match aElement now, and it's not in our list already. Just dirty
692 // ourselves; this is simpler than trying to figure out where to insert
697 // We no longer match aElement. Remove it from our list. If it's
698 // already not there, this is a no-op (though a potentially
699 // expensive one). Either way, no change of mState is required
701 mElements
.RemoveElement(aElement
);
706 nsContentList::ContentAppended(nsIDocument
* aDocument
, nsIContent
* aContainer
,
707 nsIContent
* aFirstNewContent
,
708 int32_t aNewIndexInContainer
)
710 NS_PRECONDITION(aContainer
, "Can't get at the new content if no container!");
713 * If the state is LIST_DIRTY then we have no useful information in our list
714 * and we want to put off doing work as much as possible.
716 * Also, if aContainer is anonymous from our point of view, we know that we
717 * can't possibly be matching any of the kids.
719 * Optimize out also the common case when just one new node is appended and
720 * it doesn't match us.
722 if (mState
== LIST_DIRTY
||
723 !nsContentUtils::IsInSameAnonymousTree(mRootNode
, aContainer
) ||
724 !MayContainRelevantNodes(aContainer
) ||
725 (!aFirstNewContent
->HasChildren() &&
726 !aFirstNewContent
->GetNextSibling() &&
727 !MatchSelf(aFirstNewContent
))) {
732 * We want to handle the case of ContentAppended by sometimes
733 * appending the content to our list, not just setting state to
734 * LIST_DIRTY, since most of our ContentAppended notifications
735 * should come during pageload and be at the end of the document.
736 * Do a bit of work to see whether we could just append to what we
740 int32_t count
= aContainer
->GetChildCount();
743 uint32_t ourCount
= mElements
.Length();
744 bool appendToList
= false;
748 nsIContent
* ourLastContent
= mElements
[ourCount
- 1];
750 * We want to append instead of invalidating if the first thing
751 * that got appended comes after ourLastContent.
753 if (nsContentUtils::PositionIsBefore(ourLastContent
, aFirstNewContent
)) {
760 // The new stuff is somewhere in the middle of our list; check
761 // whether we need to invalidate
762 for (nsIContent
* cur
= aFirstNewContent
; cur
; cur
= cur
->GetNextSibling()) {
763 if (MatchSelf(cur
)) {
764 // Uh-oh. We're gonna have to add elements into the middle
765 // of our list. That's not worth the effort.
776 * At this point we know we could append. If we're not up to
777 * date, however, that would be a bad idea -- it could miss some
778 * content that we never picked up due to being lazy. Further, we
779 * may never get asked for this content... so don't grab it yet.
781 if (mState
== LIST_LAZY
) // be lazy
785 * We're up to date. That means someone's actively using us; we
786 * may as well grab this content....
789 for (nsIContent
* cur
= aFirstNewContent
;
791 cur
= cur
->GetNextNode(aContainer
)) {
792 if (cur
->IsElement() && Match(cur
->AsElement())) {
793 mElements
.AppendElement(cur
);
797 for (nsIContent
* cur
= aFirstNewContent
; cur
; cur
= cur
->GetNextSibling()) {
798 if (cur
->IsElement() && Match(cur
->AsElement())) {
799 mElements
.AppendElement(cur
);
809 nsContentList::ContentInserted(nsIDocument
*aDocument
,
810 nsIContent
* aContainer
,
812 int32_t aIndexInContainer
)
814 // Note that aContainer can be null here if we are inserting into
815 // the document itself; any attempted optimizations to this method
816 // should deal with that.
817 if (mState
!= LIST_DIRTY
&&
818 MayContainRelevantNodes(NODE_FROM(aContainer
, aDocument
)) &&
819 nsContentUtils::IsInSameAnonymousTree(mRootNode
, aChild
) &&
828 nsContentList::ContentRemoved(nsIDocument
*aDocument
,
829 nsIContent
* aContainer
,
831 int32_t aIndexInContainer
,
832 nsIContent
* aPreviousSibling
)
834 // Note that aContainer can be null here if we are removing from
835 // the document itself; any attempted optimizations to this method
836 // should deal with that.
837 if (mState
!= LIST_DIRTY
&&
838 MayContainRelevantNodes(NODE_FROM(aContainer
, aDocument
)) &&
839 nsContentUtils::IsInSameAnonymousTree(mRootNode
, aChild
) &&
848 nsContentList::Match(Element
*aElement
)
851 return (*mFunc
)(aElement
, mMatchNameSpaceId
, mXMLMatchAtom
, mData
);
857 mozilla::dom::NodeInfo
*ni
= aElement
->NodeInfo();
859 bool unknown
= mMatchNameSpaceId
== kNameSpaceID_Unknown
;
860 bool wildcard
= mMatchNameSpaceId
== kNameSpaceID_Wildcard
;
861 bool toReturn
= mMatchAll
;
862 if (!unknown
&& !wildcard
)
863 toReturn
&= ni
->NamespaceEquals(mMatchNameSpaceId
);
868 bool matchHTML
= aElement
->GetNameSpaceID() == kNameSpaceID_XHTML
&&
869 aElement
->OwnerDoc()->IsHTML();
872 return matchHTML
? ni
->QualifiedNameEquals(mHTMLMatchAtom
) :
873 ni
->QualifiedNameEquals(mXMLMatchAtom
);
877 return matchHTML
? ni
->Equals(mHTMLMatchAtom
) :
878 ni
->Equals(mXMLMatchAtom
);
881 return matchHTML
? ni
->Equals(mHTMLMatchAtom
, mMatchNameSpaceId
) :
882 ni
->Equals(mXMLMatchAtom
, mMatchNameSpaceId
);
886 nsContentList::MatchSelf(nsIContent
*aContent
)
888 NS_PRECONDITION(aContent
, "Can't match null stuff, you know");
889 NS_PRECONDITION(mDeep
|| aContent
->GetParentNode() == mRootNode
,
890 "MatchSelf called on a node that we can't possibly match");
892 if (!aContent
->IsElement()) {
896 if (Match(aContent
->AsElement()))
902 for (nsIContent
* cur
= aContent
->GetFirstChild();
904 cur
= cur
->GetNextNode(aContent
)) {
905 if (cur
->IsElement() && Match(cur
->AsElement())) {
914 nsContentList::PopulateSelf(uint32_t aNeededLength
)
922 uint32_t count
= mElements
.Length();
923 NS_ASSERTION(mState
!= LIST_DIRTY
|| count
== 0,
924 "Reset() not called when setting state to LIST_DIRTY?");
926 if (count
>= aNeededLength
) // We're all set
929 uint32_t elementsToAppend
= aNeededLength
- count
;
931 uint32_t invariant
= elementsToAppend
+ mElements
.Length();
935 // If we already have nodes start searching at the last one, otherwise
936 // start searching at the root.
937 nsINode
* cur
= count
? mElements
[count
- 1] : mRootNode
;
939 cur
= cur
->GetNextNode(mRootNode
);
943 if (cur
->IsElement() && Match(cur
->AsElement())) {
944 // Append AsElement() to get nsIContent instead of nsINode
945 mElements
.AppendElement(cur
->AsElement());
948 } while (elementsToAppend
);
951 count
? mElements
[count
-1]->GetNextSibling() : mRootNode
->GetFirstChild();
952 for ( ; cur
&& elementsToAppend
; cur
= cur
->GetNextSibling()) {
953 if (cur
->IsElement() && Match(cur
->AsElement())) {
954 mElements
.AppendElement(cur
);
960 NS_ASSERTION(elementsToAppend
+ mElements
.Length() == invariant
,
961 "Something is awry!");
963 if (elementsToAppend
!= 0)
964 mState
= LIST_UP_TO_DATE
;
972 nsContentList::RemoveFromHashtable()
975 // This can't be in the table anyway
979 nsDependentAtomString
str(mXMLMatchAtom
);
980 nsContentListKey
key(mRootNode
, mMatchNameSpaceId
, str
);
981 uint32_t recentlyUsedCacheIndex
= RecentlyUsedCacheIndex(key
);
982 if (sRecentlyUsedContentLists
[recentlyUsedCacheIndex
] == this) {
983 sRecentlyUsedContentLists
[recentlyUsedCacheIndex
] = nullptr;
986 if (!gContentListHashTable
.ops
)
989 PL_DHashTableRemove(&gContentListHashTable
, &key
);
991 if (gContentListHashTable
.EntryCount() == 0) {
992 PL_DHashTableFinish(&gContentListHashTable
);
993 gContentListHashTable
.ops
= nullptr;
998 nsContentList::BringSelfUpToDate(bool aDoFlush
)
1000 if (mRootNode
&& aDoFlush
&& mFlushesNeeded
) {
1001 // XXX sXBL/XBL2 issue
1002 nsIDocument
* doc
= mRootNode
->GetUncomposedDoc();
1004 // Flush pending content changes Bug 4891.
1005 doc
->FlushPendingNotifications(Flush_ContentAndNotify
);
1009 if (mState
!= LIST_UP_TO_DATE
)
1010 PopulateSelf(uint32_t(-1));
1013 NS_ASSERTION(!mRootNode
|| mState
== LIST_UP_TO_DATE
,
1014 "PopulateSelf dod not bring content list up to date!");
1017 nsCacheableFuncStringContentList::~nsCacheableFuncStringContentList()
1019 RemoveFromFuncStringHashtable();
1023 nsCacheableFuncStringContentList::RemoveFromFuncStringHashtable()
1025 if (!gFuncStringContentListHashTable
.ops
) {
1029 nsFuncStringCacheKey
key(mRootNode
, mFunc
, mString
);
1030 PL_DHashTableRemove(&gFuncStringContentListHashTable
, &key
);
1032 if (gFuncStringContentListHashTable
.EntryCount() == 0) {
1033 PL_DHashTableFinish(&gFuncStringContentListHashTable
);
1034 gFuncStringContentListHashTable
.ops
= nullptr;
1038 #ifdef DEBUG_CONTENT_LIST
1040 nsContentList::AssertInSync()
1042 if (mState
== LIST_DIRTY
) {
1047 NS_ASSERTION(mElements
.Length() == 0 && mState
== LIST_DIRTY
,
1048 "Empty iterator isn't quite empty?");
1052 // XXX This code will need to change if nsContentLists can ever match
1053 // elements that are outside of the document element.
1055 if (mRootNode
->IsNodeOfType(nsINode::eDOCUMENT
)) {
1056 root
= static_cast<nsIDocument
*>(mRootNode
)->GetRootElement();
1059 root
= static_cast<nsIContent
*>(mRootNode
);
1062 nsCOMPtr
<nsIContentIterator
> iter
;
1064 iter
= NS_NewPreContentIterator();
1069 uint32_t cnt
= 0, index
= 0;
1071 if (cnt
== mElements
.Length() && mState
== LIST_LAZY
) {
1075 nsIContent
*cur
= mDeep
? iter
->GetCurrentNode() :
1076 mRootNode
->GetChildAt(index
++);
1081 if (cur
->IsElement() && Match(cur
->AsElement())) {
1082 NS_ASSERTION(cnt
< mElements
.Length() && mElements
[cnt
] == cur
,
1083 "Elements is out of sync");
1092 NS_ASSERTION(cnt
== mElements
.Length(), "Too few elements");