зеркало из https://github.com/mozilla/pjs.git
Make content lists lazy. Bug 104603, r=jkeiser, sr=jst
This commit is contained in:
Родитель
f3834c4493
Коммит
b477bcd31e
|
@ -69,6 +69,22 @@ public:
|
||||||
static PRBool InSameDoc(nsIDOMNode *aNode,
|
static PRBool InSameDoc(nsIDOMNode *aNode,
|
||||||
nsIDOMNode *aOther);
|
nsIDOMNode *aOther);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not ever pass null pointers to this method. If one of your
|
||||||
|
* nsIContents is null, you have to decide for yourself what
|
||||||
|
* "IsDescendantOf" really means.
|
||||||
|
*
|
||||||
|
* @param aPossibleDescendant node to test for being a descendant of
|
||||||
|
* aPossibleAncestor
|
||||||
|
* @param aPossibleAncestor node to test for being an ancestor of
|
||||||
|
* aPossibleDescendant
|
||||||
|
* @return PR_TRUE if aPossibleDescendant is a descendant of
|
||||||
|
* aPossibleAncestor (or is aPossibleAncestor). PR_FALSE
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
static PRBool ContentIsDescendantOf(nsIContent* aPossibleDescendant,
|
||||||
|
nsIContent* aPossibleAncestor);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This method fills the |aArray| with all ancestor nodes of |aNode|
|
* This method fills the |aArray| with all ancestor nodes of |aNode|
|
||||||
* including |aNode| at the zero index.
|
* including |aNode| at the zero index.
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "nsContentList.h"
|
#include "nsContentList.h"
|
||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
#include "nsIDOMNode.h"
|
#include "nsIDOMNode.h"
|
||||||
|
#include "nsIDOM3Node.h"
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
#include "nsINameSpaceManager.h"
|
#include "nsINameSpaceManager.h"
|
||||||
#include "nsGenericElement.h"
|
#include "nsGenericElement.h"
|
||||||
|
@ -381,29 +382,6 @@ NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom,
|
||||||
|
|
||||||
// nsContentList implementation
|
// nsContentList implementation
|
||||||
|
|
||||||
nsContentList::nsContentList(const nsContentList& aContentList)
|
|
||||||
: nsBaseContentList(), nsContentListKey(aContentList)
|
|
||||||
{
|
|
||||||
mFunc = aContentList.mFunc;
|
|
||||||
|
|
||||||
if (aContentList.mData) {
|
|
||||||
mData = new nsString(*aContentList.mData);
|
|
||||||
} else {
|
|
||||||
mData = nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
mMatchAll = aContentList.mMatchAll;
|
|
||||||
mElements = aContentList.mElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsContentList::nsContentList(nsIDocument *aDocument)
|
|
||||||
: nsBaseContentList(), nsContentListKey(aDocument, nsnull, kNameSpaceID_Unknown, nsnull)
|
|
||||||
{
|
|
||||||
mFunc = nsnull;
|
|
||||||
mData = nsnull;
|
|
||||||
mMatchAll = PR_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsContentList::nsContentList(nsIDocument *aDocument,
|
nsContentList::nsContentList(nsIDocument *aDocument,
|
||||||
nsIAtom* aMatchAtom,
|
nsIAtom* aMatchAtom,
|
||||||
PRInt32 aMatchNameSpaceId,
|
PRInt32 aMatchNameSpaceId,
|
||||||
|
@ -418,6 +396,7 @@ nsContentList::nsContentList(nsIDocument *aDocument,
|
||||||
}
|
}
|
||||||
mFunc = nsnull;
|
mFunc = nsnull;
|
||||||
mData = nsnull;
|
mData = nsnull;
|
||||||
|
mState = LIST_DIRTY;
|
||||||
Init(aDocument);
|
Init(aDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,6 +417,7 @@ nsContentList::nsContentList(nsIDocument *aDocument,
|
||||||
mMatchAtom = nsnull;
|
mMatchAtom = nsnull;
|
||||||
mRootContent = aRootContent;
|
mRootContent = aRootContent;
|
||||||
mMatchAll = PR_FALSE;
|
mMatchAll = PR_FALSE;
|
||||||
|
mState = LIST_DIRTY;
|
||||||
Init(aDocument);
|
Init(aDocument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,7 +432,6 @@ void nsContentList::Init(nsIDocument *aDocument)
|
||||||
if (mDocument) {
|
if (mDocument) {
|
||||||
mDocument->AddObserver(this);
|
mDocument->AddObserver(this);
|
||||||
}
|
}
|
||||||
PopulateSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsContentList::~nsContentList()
|
nsContentList::~nsContentList()
|
||||||
|
@ -483,9 +462,7 @@ nsContentList::GetLength(PRUint32* aLength, PRBool aDoFlush)
|
||||||
{
|
{
|
||||||
nsresult result = CheckDocumentExistence();
|
nsresult result = CheckDocumentExistence();
|
||||||
if (NS_SUCCEEDED(result)) {
|
if (NS_SUCCEEDED(result)) {
|
||||||
if (mDocument && aDoFlush) {
|
BringSelfUpToDate(aDoFlush);
|
||||||
mDocument->FlushPendingNotifications(PR_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
*aLength = mElements.Count();
|
*aLength = mElements.Count();
|
||||||
}
|
}
|
||||||
|
@ -503,7 +480,13 @@ nsContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn, PRBool aDoFlush)
|
||||||
mDocument->FlushPendingNotifications(PR_FALSE);
|
mDocument->FlushPendingNotifications(PR_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsISupports *element = NS_STATIC_CAST(nsISupports *,
|
if (mState != LIST_UP_TO_DATE)
|
||||||
|
PopulateSelf(aIndex+1);
|
||||||
|
|
||||||
|
NS_ASSERTION(!mDocument || mState != LIST_DIRTY,
|
||||||
|
"PopulateSelf left the list in a dirty (useless) state!");
|
||||||
|
|
||||||
|
nsIContent *element = NS_STATIC_CAST(nsIContent *,
|
||||||
mElements.SafeElementAt(aIndex));
|
mElements.SafeElementAt(aIndex));
|
||||||
|
|
||||||
if (element) {
|
if (element) {
|
||||||
|
@ -523,9 +506,7 @@ nsContentList::NamedItem(const nsAString& aName, nsIDOMNode** aReturn, PRBool aD
|
||||||
nsresult result = CheckDocumentExistence();
|
nsresult result = CheckDocumentExistence();
|
||||||
|
|
||||||
if (NS_SUCCEEDED(result)) {
|
if (NS_SUCCEEDED(result)) {
|
||||||
if (mDocument && aDoFlush) {
|
BringSelfUpToDate(aDoFlush);
|
||||||
mDocument->FlushPendingNotifications(PR_FALSE); // Flush pending content changes Bug 4891
|
|
||||||
}
|
|
||||||
|
|
||||||
PRInt32 i, count = mElements.Count();
|
PRInt32 i, count = mElements.Count();
|
||||||
|
|
||||||
|
@ -556,9 +537,7 @@ nsContentList::IndexOf(nsIContent *aContent, PRInt32& aIndex, PRBool aDoFlush)
|
||||||
{
|
{
|
||||||
nsresult result = CheckDocumentExistence();
|
nsresult result = CheckDocumentExistence();
|
||||||
if (NS_SUCCEEDED(result)) {
|
if (NS_SUCCEEDED(result)) {
|
||||||
if (mDocument && aDoFlush) {
|
BringSelfUpToDate(aDoFlush);
|
||||||
mDocument->FlushPendingNotifications(PR_FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
aIndex = mElements.IndexOf(aContent);
|
aIndex = mElements.IndexOf(aContent);
|
||||||
}
|
}
|
||||||
|
@ -588,22 +567,93 @@ NS_IMETHODIMP
|
||||||
nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
|
nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
|
||||||
PRInt32 aNewIndexInContainer)
|
PRInt32 aNewIndexInContainer)
|
||||||
{
|
{
|
||||||
PRInt32 i, count;
|
/*
|
||||||
|
* If the state is LIST_DIRTY then we have no useful information in
|
||||||
|
* our list and we want to put off doing work as much as possible.
|
||||||
|
*/
|
||||||
|
if (mState == LIST_DIRTY)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
PRInt32 count;
|
||||||
aContainer->ChildCount(count);
|
aContainer->ChildCount(count);
|
||||||
|
|
||||||
if ((count > 0) && IsDescendantOfRoot(aContainer)) {
|
/*
|
||||||
PRBool repopulate = PR_FALSE;
|
* We want to handle the case of ContentAppended by sometimes
|
||||||
|
* appending the content to our list, not just setting state to
|
||||||
|
* LIST_DIRTY, since most of our ContentAppended notifications
|
||||||
|
* should come during pageload and be at the end of the document.
|
||||||
|
* Do a bit of work to see whether we could just append to what we
|
||||||
|
* already have.
|
||||||
|
*/
|
||||||
|
|
||||||
for (i = aNewIndexInContainer; i <= count-1; i++) {
|
if ((count > 0) && IsDescendantOfRoot(aContainer)) {
|
||||||
|
PRInt32 ourCount = mElements.Count();
|
||||||
|
PRBool appendToList = PR_FALSE;
|
||||||
|
if (ourCount == 0) {
|
||||||
|
appendToList = PR_TRUE;
|
||||||
|
} else {
|
||||||
|
nsIContent* ourLastContent =
|
||||||
|
NS_STATIC_CAST(nsIContent*, mElements.ElementAt(ourCount - 1));
|
||||||
|
/*
|
||||||
|
* We want to append instead of invalidating in two cases:
|
||||||
|
* 1) aContainer is an ancestor of ourLastContent (this case
|
||||||
|
covers aContainer == ourLastContent)
|
||||||
|
* 2) aContainer comes after ourLastContent in document order
|
||||||
|
*/
|
||||||
|
if (nsContentUtils::ContentIsDescendantOf(ourLastContent, aContainer)) {
|
||||||
|
appendToList = PR_TRUE;
|
||||||
|
} else {
|
||||||
|
nsCOMPtr<nsIDOM3Node> ourLastDOM3Node(do_QueryInterface(ourLastContent));
|
||||||
|
nsCOMPtr<nsIDOMNode> newNodeContainer(do_QueryInterface(aContainer));
|
||||||
|
if (ourLastDOM3Node && newNodeContainer) {
|
||||||
|
PRUint16 comparisonFlags;
|
||||||
|
nsresult rv = ourLastDOM3Node->CompareTreePosition(newNodeContainer,
|
||||||
|
&comparisonFlags);
|
||||||
|
if (NS_SUCCEEDED(rv) &&
|
||||||
|
(comparisonFlags & nsIDOMNode::TREE_POSITION_FOLLOWING)) {
|
||||||
|
appendToList = PR_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32 i;
|
||||||
|
|
||||||
|
if (!appendToList) {
|
||||||
|
// The new stuff is somewhere in the middle of our list; check
|
||||||
|
// whether we need to invalidate
|
||||||
nsCOMPtr<nsIContent> content;
|
nsCOMPtr<nsIContent> content;
|
||||||
|
for (i = aNewIndexInContainer; i <= count-1; ++i) {
|
||||||
aContainer->ChildAt(i, *getter_AddRefs(content));
|
aContainer->ChildAt(i, *getter_AddRefs(content));
|
||||||
if (mMatchAll || MatchSelf(content)) {
|
if (MatchSelf(content)) {
|
||||||
repopulate = PR_TRUE;
|
// Uh-oh. We're gonna have to add elements into the middle
|
||||||
|
// of our list. That's not worth the effort.
|
||||||
|
mState = LIST_DIRTY;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (repopulate) {
|
|
||||||
PopulateSelf();
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At this point we know we could append. If we're not up to
|
||||||
|
* date, however, that would be a bad idea -- it could miss some
|
||||||
|
* content that we never picked up due to being lazy. Further, we
|
||||||
|
* may never get asked for this content... so don't grab it yet.
|
||||||
|
*/
|
||||||
|
if (mState == LIST_LAZY) // be lazy
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We're up to date. That means someone's actively using us; we
|
||||||
|
* may as well grab this content....
|
||||||
|
*/
|
||||||
|
nsCOMPtr<nsIContent> content;
|
||||||
|
for (i = aNewIndexInContainer; i <= count-1; ++i) {
|
||||||
|
aContainer->ChildAt(i, *getter_AddRefs(content));
|
||||||
|
PRUint32 limit = PRUint32(-1);
|
||||||
|
PopulateWith(content, PR_TRUE, limit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,11 +666,11 @@ nsContentList::ContentInserted(nsIDocument *aDocument,
|
||||||
nsIContent* aChild,
|
nsIContent* aChild,
|
||||||
PRInt32 aIndexInContainer)
|
PRInt32 aIndexInContainer)
|
||||||
{
|
{
|
||||||
if (IsDescendantOfRoot(aContainer)) {
|
if (mState == LIST_DIRTY)
|
||||||
if (mMatchAll || MatchSelf(aChild)) {
|
return NS_OK;
|
||||||
PopulateSelf();
|
|
||||||
}
|
if (IsDescendantOfRoot(aContainer) && MatchSelf(aChild))
|
||||||
}
|
mState = LIST_DIRTY;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -632,9 +682,12 @@ nsContentList::ContentReplaced(nsIDocument *aDocument,
|
||||||
nsIContent* aNewChild,
|
nsIContent* aNewChild,
|
||||||
PRInt32 aIndexInContainer)
|
PRInt32 aIndexInContainer)
|
||||||
{
|
{
|
||||||
|
if (mState == LIST_DIRTY)
|
||||||
|
return NS_OK;
|
||||||
|
|
||||||
if (IsDescendantOfRoot(aContainer)) {
|
if (IsDescendantOfRoot(aContainer)) {
|
||||||
if (mMatchAll || MatchSelf(aOldChild) || MatchSelf(aNewChild)) {
|
if (MatchSelf(aOldChild) || MatchSelf(aNewChild)) {
|
||||||
PopulateSelf();
|
mState = LIST_DIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ContainsRoot(aOldChild)) {
|
else if (ContainsRoot(aOldChild)) {
|
||||||
|
@ -650,8 +703,10 @@ nsContentList::ContentRemoved(nsIDocument *aDocument,
|
||||||
nsIContent* aChild,
|
nsIContent* aChild,
|
||||||
PRInt32 aIndexInContainer)
|
PRInt32 aIndexInContainer)
|
||||||
{
|
{
|
||||||
if (IsDescendantOfRoot(aContainer) && MatchSelf(aChild)) {
|
if (IsDescendantOfRoot(aContainer)) {
|
||||||
PopulateSelf();
|
if (MatchSelf(aChild)) {
|
||||||
|
mState = LIST_DIRTY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (ContainsRoot(aChild)) {
|
else if (ContainsRoot(aChild)) {
|
||||||
DisconnectFromDocument();
|
DisconnectFromDocument();
|
||||||
|
@ -663,66 +718,50 @@ nsContentList::ContentRemoved(nsIDocument *aDocument,
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsContentList::DocumentWillBeDestroyed(nsIDocument *aDocument)
|
nsContentList::DocumentWillBeDestroyed(nsIDocument *aDocument)
|
||||||
{
|
{
|
||||||
if (mDocument) {
|
DisconnectFromDocument();
|
||||||
// Our key will change... Best remove ourselves before that happens.
|
|
||||||
RemoveFromHashtable();
|
|
||||||
aDocument->RemoveObserver(this);
|
|
||||||
mDocument = nsnull;
|
|
||||||
}
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRBool
|
||||||
// Returns whether the content element matches the
|
nsContentList::Match(nsIContent *aContent)
|
||||||
// criterion
|
|
||||||
nsresult
|
|
||||||
nsContentList::Match(nsIContent *aContent, PRBool *aMatch)
|
|
||||||
{
|
{
|
||||||
*aMatch = PR_FALSE;
|
if (!aContent)
|
||||||
|
return PR_FALSE;
|
||||||
if (!aContent) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mMatchAtom) {
|
if (mMatchAtom) {
|
||||||
nsCOMPtr<nsINodeInfo> ni;
|
nsCOMPtr<nsINodeInfo> ni;
|
||||||
aContent->GetNodeInfo(*getter_AddRefs(ni));
|
aContent->GetNodeInfo(*getter_AddRefs(ni));
|
||||||
|
|
||||||
if (!ni)
|
if (!ni)
|
||||||
return NS_OK;
|
return PR_FALSE;
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
return NS_OK;
|
return PR_FALSE;
|
||||||
|
|
||||||
PRUint16 type;
|
PRUint16 type;
|
||||||
node->GetNodeType(&type);
|
node->GetNodeType(&type);
|
||||||
|
|
||||||
if (type != nsIDOMNode::ELEMENT_NODE)
|
if (type != nsIDOMNode::ELEMENT_NODE)
|
||||||
return NS_OK;
|
return PR_FALSE;
|
||||||
|
|
||||||
if (mMatchNameSpaceId == kNameSpaceID_Unknown) {
|
if (mMatchNameSpaceId == kNameSpaceID_Unknown) {
|
||||||
if (mMatchAll || ni->Equals(mMatchAtom)) {
|
return (mMatchAll || ni->Equals(mMatchAtom));
|
||||||
*aMatch = PR_TRUE;
|
|
||||||
}
|
|
||||||
} else if ((mMatchAll && ni->NamespaceEquals(mMatchNameSpaceId)) ||
|
|
||||||
ni->Equals(mMatchAtom, mMatchNameSpaceId)) {
|
|
||||||
*aMatch = PR_TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ((mMatchAll && ni->NamespaceEquals(mMatchNameSpaceId)) ||
|
||||||
|
ni->Equals(mMatchAtom, mMatchNameSpaceId));
|
||||||
}
|
}
|
||||||
else if (mFunc) {
|
else if (mFunc) {
|
||||||
*aMatch = (*mFunc)(aContent, mData);
|
return (*mFunc)(aContent, mData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were created outside the context of a document and we
|
|
||||||
// have root content, then check if our content has been added
|
|
||||||
// to a document yet. If so, we'll become an observer of the document.
|
|
||||||
nsresult
|
nsresult
|
||||||
nsContentList::CheckDocumentExistence()
|
nsContentList::CheckDocumentExistence()
|
||||||
{
|
{
|
||||||
|
@ -731,25 +770,21 @@ nsContentList::CheckDocumentExistence()
|
||||||
result = mRootContent->GetDocument(mDocument);
|
result = mRootContent->GetDocument(mDocument);
|
||||||
if (mDocument) {
|
if (mDocument) {
|
||||||
mDocument->AddObserver(this);
|
mDocument->AddObserver(this);
|
||||||
PopulateSelf();
|
mState = LIST_DIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match recursively. See if anything in the subtree
|
|
||||||
// matches the criterion.
|
|
||||||
PRBool
|
PRBool
|
||||||
nsContentList::MatchSelf(nsIContent *aContent)
|
nsContentList::MatchSelf(nsIContent *aContent)
|
||||||
{
|
{
|
||||||
PRBool match;
|
|
||||||
PRInt32 i, count;
|
|
||||||
|
|
||||||
Match(aContent, &match);
|
if (Match(aContent))
|
||||||
if (match) {
|
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
|
||||||
|
PRInt32 i, count = -1;
|
||||||
|
|
||||||
aContent->ChildCount(count);
|
aContent->ChildCount(count);
|
||||||
nsCOMPtr<nsIContent> child;
|
nsCOMPtr<nsIContent> child;
|
||||||
|
@ -763,103 +798,142 @@ nsContentList::MatchSelf(nsIContent *aContent)
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all elements in this subtree that match to our list.
|
|
||||||
void
|
void
|
||||||
nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot)
|
nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot,
|
||||||
|
PRUint32 & aElementsToAppend)
|
||||||
{
|
{
|
||||||
PRBool match;
|
|
||||||
PRInt32 i, count;
|
|
||||||
|
|
||||||
if (aIncludeRoot) {
|
if (aIncludeRoot) {
|
||||||
Match(aContent, &match);
|
if (Match(aContent)) {
|
||||||
if (match) {
|
|
||||||
mElements.AppendElement(aContent);
|
mElements.AppendElement(aContent);
|
||||||
|
--aElementsToAppend;
|
||||||
|
if (aElementsToAppend == 0)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRInt32 i, count;
|
||||||
aContent->ChildCount(count);
|
aContent->ChildCount(count);
|
||||||
nsCOMPtr<nsIContent> child;
|
nsCOMPtr<nsIContent> child;
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
aContent->ChildAt(i, *getter_AddRefs(child));
|
aContent->ChildAt(i, *getter_AddRefs(child));
|
||||||
PopulateWith(child, PR_TRUE);
|
PopulateWith(child, PR_TRUE, aElementsToAppend);
|
||||||
|
if (aElementsToAppend == 0)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear out our old list and build up a new one
|
|
||||||
void
|
void
|
||||||
nsContentList::PopulateSelf()
|
nsContentList::PopulateWithStartingAfter(nsIContent *aStartRoot,
|
||||||
|
nsIContent *aStartChild,
|
||||||
|
PRUint32 & aElementsToAppend)
|
||||||
{
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
PRUint32 invariant = aElementsToAppend + mElements.Count();
|
||||||
|
#endif
|
||||||
|
PRInt32 i = 0;
|
||||||
|
if (aStartChild) {
|
||||||
|
aStartRoot->IndexOf(aStartChild, i);
|
||||||
|
NS_ASSERTION(i >= 0, "The start child must be a child of the start root!");
|
||||||
|
++i; // move to one past
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt32 childCount;
|
||||||
|
aStartRoot->ChildCount(childCount);
|
||||||
|
nsCOMPtr<nsIContent> child;
|
||||||
|
for ( ; i < childCount; ++i) {
|
||||||
|
aStartRoot->ChildAt(i, *getter_AddRefs(child));
|
||||||
|
PopulateWith(child, PR_TRUE, aElementsToAppend);
|
||||||
|
NS_ASSERTION(aElementsToAppend + mElements.Count() == invariant,
|
||||||
|
"Something is awry in PopulateWith!");
|
||||||
|
if (aElementsToAppend == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIContent> parent;
|
||||||
|
aStartRoot->GetParent(*getter_AddRefs(parent));
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
PopulateWithStartingAfter(parent, aStartRoot, aElementsToAppend);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsContentList::PopulateSelf(PRUint32 aNeededLength)
|
||||||
|
{
|
||||||
|
if (mState == LIST_DIRTY) {
|
||||||
Reset();
|
Reset();
|
||||||
if (mRootContent) {
|
}
|
||||||
PopulateWith(mRootContent, PR_FALSE);
|
PRUint32 count = mElements.Count();
|
||||||
|
|
||||||
|
if (count >= aNeededLength) // We're all set
|
||||||
|
return;
|
||||||
|
|
||||||
|
PRUint32 elementsToAppend = aNeededLength - count;
|
||||||
|
#ifdef DEBUG
|
||||||
|
PRUint32 invariant = elementsToAppend + mElements.Count();
|
||||||
|
#endif
|
||||||
|
if (count != 0) {
|
||||||
|
PopulateWithStartingAfter(NS_STATIC_CAST(nsIContent*,
|
||||||
|
mElements.ElementAt(count - 1)),
|
||||||
|
nsnull,
|
||||||
|
elementsToAppend);
|
||||||
|
NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
|
||||||
|
"Something is awry in PopulateWithStartingAfter!");
|
||||||
|
} else if (mRootContent) {
|
||||||
|
PopulateWith(mRootContent, PR_FALSE, elementsToAppend);
|
||||||
|
NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
|
||||||
|
"Something is awry in PopulateWith!");
|
||||||
}
|
}
|
||||||
else if (mDocument) {
|
else if (mDocument) {
|
||||||
nsCOMPtr<nsIContent> root;
|
nsCOMPtr<nsIContent> root;
|
||||||
mDocument->GetRootContent(getter_AddRefs(root));
|
mDocument->GetRootContent(getter_AddRefs(root));
|
||||||
if (root) {
|
if (root) {
|
||||||
PopulateWith(root, PR_TRUE);
|
PopulateWith(root, PR_TRUE, elementsToAppend);
|
||||||
}
|
NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
|
||||||
|
"Something is awry in PopulateWith!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDocument) {
|
||||||
|
if (elementsToAppend != 0)
|
||||||
|
mState = LIST_UP_TO_DATE;
|
||||||
|
else
|
||||||
|
mState = LIST_LAZY;
|
||||||
|
} else {
|
||||||
|
// No document means we have to stay on our toes since we don't
|
||||||
|
// get content notifications.
|
||||||
|
mState = LIST_DIRTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the specified element a descendant of the root? If there
|
|
||||||
// is no root, then yes. Otherwise keep tracing up the tree from
|
|
||||||
// the element till we find our root, or until we reach the
|
|
||||||
// document root.
|
|
||||||
PRBool
|
PRBool
|
||||||
nsContentList::IsDescendantOfRoot(nsIContent* aContainer)
|
nsContentList::IsDescendantOfRoot(nsIContent* aContainer)
|
||||||
{
|
{
|
||||||
if (!mRootContent) {
|
if (!mRootContent) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
nsCOMPtr<nsIDocument> doc;
|
||||||
|
aContainer->GetDocument(*getter_AddRefs(doc));
|
||||||
|
NS_ASSERTION(doc == mDocument, "We should not get in here if aContainer is appended to some _other_ document!");
|
||||||
|
#endif
|
||||||
return PR_TRUE;
|
return PR_TRUE;
|
||||||
}
|
}
|
||||||
else if (mRootContent == aContainer) {
|
|
||||||
return PR_TRUE;
|
if (!aContainer) {
|
||||||
}
|
|
||||||
else if (!aContainer) {
|
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
nsCOMPtr<nsIContent> parent;
|
|
||||||
PRBool ret;
|
|
||||||
|
|
||||||
aContainer->GetParent(*getter_AddRefs(parent));
|
return nsContentUtils::ContentIsDescendantOf(aContainer, mRootContent);
|
||||||
ret = IsDescendantOfRoot(parent);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does this subtree contain the root?
|
|
||||||
PRBool
|
PRBool
|
||||||
nsContentList::ContainsRoot(nsIContent* aContent)
|
nsContentList::ContainsRoot(nsIContent* aContent)
|
||||||
{
|
{
|
||||||
if (!mRootContent) {
|
if (!mRootContent || !aContent) {
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
else if (mRootContent == aContent) {
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PRInt32 i, count;
|
|
||||||
|
|
||||||
aContent->ChildCount(count);
|
return nsContentUtils::ContentIsDescendantOf(mRootContent, aContent);
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
nsCOMPtr<nsIContent> child;
|
|
||||||
|
|
||||||
aContent->ChildAt(i, *getter_AddRefs(child));
|
|
||||||
|
|
||||||
if (ContainsRoot(child)) {
|
|
||||||
return PR_TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PR_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our root content has been disconnected from the
|
|
||||||
// document, so stop observing. The list then becomes
|
|
||||||
// a snapshot rather than a dynamic list.
|
|
||||||
void
|
void
|
||||||
nsContentList::DisconnectFromDocument()
|
nsContentList::DisconnectFromDocument()
|
||||||
{
|
{
|
||||||
|
@ -869,6 +943,10 @@ nsContentList::DisconnectFromDocument()
|
||||||
mDocument->RemoveObserver(this);
|
mDocument->RemoveObserver(this);
|
||||||
mDocument = nsnull;
|
mDocument = nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We will get no more updates, so we can never know we're up to
|
||||||
|
// date
|
||||||
|
mState = LIST_DIRTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -887,3 +965,16 @@ nsContentList::RemoveFromHashtable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsContentList::BringSelfUpToDate(PRBool aDoFlush)
|
||||||
|
{
|
||||||
|
if (mDocument && aDoFlush) {
|
||||||
|
mDocument->FlushPendingNotifications(PR_FALSE); // Flush pending content changes Bug 4891
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState != LIST_UP_TO_DATE)
|
||||||
|
PopulateSelf(PRUint32(-1));
|
||||||
|
|
||||||
|
NS_ASSERTION(!mDocument || mState == LIST_UP_TO_DATE,
|
||||||
|
"PopulateSelf dod not bring content list up to date!");
|
||||||
|
}
|
||||||
|
|
|
@ -90,6 +90,10 @@ public:
|
||||||
NS_IMETHOD Reset();
|
NS_IMETHOD Reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that's used as the key to hash nsContentList implementations
|
||||||
|
* for fast retrieval
|
||||||
|
*/
|
||||||
class nsContentListKey
|
class nsContentListKey
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -133,9 +137,15 @@ protected:
|
||||||
nsCOMPtr<nsIAtom> mMatchAtom;
|
nsCOMPtr<nsIAtom> mMatchAtom;
|
||||||
PRInt32 mMatchNameSpaceId;
|
PRInt32 mMatchNameSpaceId;
|
||||||
nsIDocument* mDocument; // Weak ref
|
nsIDocument* mDocument; // Weak ref
|
||||||
|
// XXX What if the mRootContent is detached from the doc and _then_
|
||||||
|
// goes away (so we never get notified)?
|
||||||
nsIContent* mRootContent; // Weak ref
|
nsIContent* mRootContent; // Weak ref
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that implements a live NodeList that matches nodes in the
|
||||||
|
* tree based on some criterion
|
||||||
|
*/
|
||||||
class nsContentList : public nsBaseContentList,
|
class nsContentList : public nsBaseContentList,
|
||||||
protected nsContentListKey,
|
protected nsContentListKey,
|
||||||
public nsIDOMHTMLCollection,
|
public nsIDOMHTMLCollection,
|
||||||
|
@ -145,8 +155,6 @@ class nsContentList : public nsBaseContentList,
|
||||||
public:
|
public:
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
|
||||||
nsContentList(const nsContentList& aContentList);
|
|
||||||
nsContentList(nsIDocument *aDocument);
|
|
||||||
nsContentList(nsIDocument *aDocument,
|
nsContentList(nsIDocument *aDocument,
|
||||||
nsIAtom* aMatchAtom,
|
nsIAtom* aMatchAtom,
|
||||||
PRInt32 aMatchNameSpaceId,
|
PRInt32 aMatchNameSpaceId,
|
||||||
|
@ -234,22 +242,135 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
nsresult Match(nsIContent *aContent, PRBool *aMatch);
|
|
||||||
void Init(nsIDocument *aDocument);
|
void Init(nsIDocument *aDocument);
|
||||||
void PopulateWith(nsIContent *aContent, PRBool aIncludeRoot);
|
/**
|
||||||
|
* Returns whether the content element matches our criterion
|
||||||
|
*
|
||||||
|
* @param aContent the content to attempt to match
|
||||||
|
* @return whether we match
|
||||||
|
*/
|
||||||
|
PRBool Match(nsIContent *aContent);
|
||||||
|
/**
|
||||||
|
* Match recursively. See if anything in the subtree rooted at
|
||||||
|
* aContent matches our criterion.
|
||||||
|
*
|
||||||
|
* @param aContent the root of the subtree to match against
|
||||||
|
* @return whether we match something in the tree rooted at aContent
|
||||||
|
*/
|
||||||
PRBool MatchSelf(nsIContent *aContent);
|
PRBool MatchSelf(nsIContent *aContent);
|
||||||
void PopulateSelf();
|
|
||||||
|
/**
|
||||||
|
* Add elements in the subtree rooted in aContent that match our
|
||||||
|
* criterion to our list until we've picked up aElementsToAppend
|
||||||
|
* elements. This function enforces the invariant that
|
||||||
|
* |aElementsToAppend + mElements.Count()| is a constant.
|
||||||
|
*
|
||||||
|
* @param aContent the root of the subtree we want to traverse
|
||||||
|
* @param aIncludeRoot whether to include the root in the traversal
|
||||||
|
* @param aElementsToAppend how many elements to append to the list
|
||||||
|
* before stopping
|
||||||
|
*/
|
||||||
|
void PopulateWith(nsIContent *aContent, PRBool aIncludeRoot,
|
||||||
|
PRUint32 & aElementsToAppend);
|
||||||
|
/**
|
||||||
|
* Populate our list starting at the child of aStartRoot that comes
|
||||||
|
* after aStartChild (if such exists) and continuing in document
|
||||||
|
* order. Stop once we've picked up aElementsToAppend elements.
|
||||||
|
* This function enforces the invariant that |aElementsToAppend +
|
||||||
|
* mElements.Count()| is a constant.
|
||||||
|
*
|
||||||
|
* @param aStartRoot the node with whose children we want to start traversal
|
||||||
|
* @param aStartChild the child after which we want to start
|
||||||
|
* @param aElementsToAppend how many elements to append to the list
|
||||||
|
* before stopping
|
||||||
|
*/
|
||||||
|
void PopulateWithStartingAfter(nsIContent *aStartRoot,
|
||||||
|
nsIContent *aStartChild,
|
||||||
|
PRUint32 & aElementsToAppend);
|
||||||
|
/**
|
||||||
|
* Populate our list. Stop once we have at least aNeededLength
|
||||||
|
* elements. At the end of PopulateSelf running, either the last
|
||||||
|
* node we examined is the last node in our array or we have
|
||||||
|
* traversed the whole document (or both).
|
||||||
|
*
|
||||||
|
* @param aNeededLength the length the list should have when we are
|
||||||
|
* done (unless it exhausts the document)
|
||||||
|
*/
|
||||||
|
void PopulateSelf(PRUint32 aNeededLength);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Our root content has been disconnected from the document, so stop
|
||||||
|
* observing. From this point on, if someone asks us something we
|
||||||
|
* walk the tree rooted at mRootContent starting at the beginning
|
||||||
|
* and going as far as we need to to answer the question.
|
||||||
|
*/
|
||||||
void DisconnectFromDocument();
|
void DisconnectFromDocument();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param aContainer a content node which could be a descendant of
|
||||||
|
* mRootContent
|
||||||
|
* @return PR_TRUE if mRootContent is null, PR_FALSE if aContainer
|
||||||
|
* is null, PR_TRUE if aContainer is a descendant of mRootContent,
|
||||||
|
* PR_FALSE otherwise
|
||||||
|
*/
|
||||||
PRBool IsDescendantOfRoot(nsIContent* aContainer);
|
PRBool IsDescendantOfRoot(nsIContent* aContainer);
|
||||||
|
/**
|
||||||
|
* Does this subtree contain our mRootContent?
|
||||||
|
*
|
||||||
|
* @param aContainer the root of the subtree
|
||||||
|
* @return PR_FALSE if mRootContent is null, otherwise whether
|
||||||
|
* mRootContent is a descendant of aContainer
|
||||||
|
*/
|
||||||
PRBool ContainsRoot(nsIContent* aContent);
|
PRBool ContainsRoot(nsIContent* aContent);
|
||||||
|
/**
|
||||||
|
* If we have no document and we have a root content, then check if
|
||||||
|
* our content has been added to a document. If so, we'll become an
|
||||||
|
* observer of the document.
|
||||||
|
*/
|
||||||
nsresult CheckDocumentExistence();
|
nsresult CheckDocumentExistence();
|
||||||
void RemoveFromHashtable();
|
void RemoveFromHashtable();
|
||||||
|
inline void BringSelfUpToDate(PRBool aDoFlush);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to use to determine whether a piece of content matches
|
||||||
|
* our criterion
|
||||||
|
*/
|
||||||
nsContentListMatchFunc mFunc;
|
nsContentListMatchFunc mFunc;
|
||||||
|
/**
|
||||||
|
* Closure data to pass to mFunc when we call it
|
||||||
|
*/
|
||||||
nsString* mData;
|
nsString* mData;
|
||||||
PRBool mMatchAll;
|
/**
|
||||||
|
* True if we are looking for elements named "*"
|
||||||
|
*/
|
||||||
|
PRPackedBool mMatchAll;
|
||||||
|
/**
|
||||||
|
* The current state of the list (possible values are:
|
||||||
|
* LIST_UP_TO_DATE, LIST_LAZY, LIST_DIRTY
|
||||||
|
*/
|
||||||
|
PRUint8 mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LIST_UP_TO_DATE means that the list is up to date and need not do
|
||||||
|
* any walking to be able to answer any questions anyone may have.
|
||||||
|
*/
|
||||||
|
#define LIST_UP_TO_DATE 0
|
||||||
|
/**
|
||||||
|
* LIST_DIRTY means that the list contains no useful information and
|
||||||
|
* if anyone asks it anything it will have to populate itself before
|
||||||
|
* answering.
|
||||||
|
*/
|
||||||
|
#define LIST_DIRTY 1
|
||||||
|
/**
|
||||||
|
* LIST_LAZY means that the list has populated itself to a certain
|
||||||
|
* extent and that that part of the list is still valid. Requests for
|
||||||
|
* things outside that part of the list will require walking the tree
|
||||||
|
* some more. When a list is in this state, the last thing in
|
||||||
|
* mElements is the last node in the tree that the list looked at.
|
||||||
|
*/
|
||||||
|
#define LIST_LAZY 2
|
||||||
|
|
||||||
extern nsresult
|
extern nsresult
|
||||||
NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom,
|
NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom,
|
||||||
PRInt32 aMatchNameSpaceId, nsIContent* aRootContent,
|
PRInt32 aMatchNameSpaceId, nsIContent* aRootContent,
|
||||||
|
|
|
@ -584,6 +584,26 @@ nsContentUtils::InSameDoc(nsIDOMNode* aNode, nsIDOMNode* aOther)
|
||||||
return PR_FALSE;
|
return PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
PRBool
|
||||||
|
nsContentUtils::ContentIsDescendantOf(nsIContent* aPossibleDescendant,
|
||||||
|
nsIContent* aPossibleAncestor)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(aPossibleDescendant, "The possible descendant is null!");
|
||||||
|
NS_PRECONDITION(aPossibleAncestor, "The possible ancestor is null!");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIContent> parent;
|
||||||
|
do {
|
||||||
|
if (aPossibleDescendant == aPossibleAncestor)
|
||||||
|
return PR_TRUE;
|
||||||
|
aPossibleDescendant->GetParent(*getter_AddRefs(parent));
|
||||||
|
aPossibleDescendant = parent;
|
||||||
|
} while (aPossibleDescendant);
|
||||||
|
|
||||||
|
return PR_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
nsresult
|
||||||
nsContentUtils::GetAncestors(nsIDOMNode* aNode,
|
nsContentUtils::GetAncestors(nsIDOMNode* aNode,
|
||||||
|
|
|
@ -1600,7 +1600,7 @@ nsFormControlList::AddElementToTable(nsIFormControl* aChild,
|
||||||
|
|
||||||
// Found an element, create a list, add the element to the list and put
|
// Found an element, create a list, add the element to the list and put
|
||||||
// the list in the hash
|
// the list in the hash
|
||||||
nsContentList *list = new nsContentList(nsnull);
|
nsBaseContentList *list = new nsBaseContentList();
|
||||||
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
list->AppendElement(content);
|
list->AppendElement(content);
|
||||||
|
@ -1620,11 +1620,11 @@ nsFormControlList::AddElementToTable(nsIFormControl* aChild,
|
||||||
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
// Upcast, uggly, but it works!
|
// Upcast, uggly, but it works!
|
||||||
nsContentList *list = NS_STATIC_CAST(nsContentList *,
|
nsBaseContentList *list = NS_STATIC_CAST(nsBaseContentList *,
|
||||||
(nsIDOMNodeList *)nodeList.get());
|
(nsIDOMNodeList *)nodeList.get());
|
||||||
|
|
||||||
PRInt32 oldIndex = -1;
|
PRInt32 oldIndex = -1;
|
||||||
list->IndexOf(newChild, oldIndex, PR_TRUE);
|
list->IndexOf(newChild, oldIndex);
|
||||||
|
|
||||||
// Add the new child only if it's not in our list already
|
// Add the new child only if it's not in our list already
|
||||||
if (oldIndex < 0) {
|
if (oldIndex < 0) {
|
||||||
|
@ -1686,7 +1686,7 @@ nsFormControlList::RemoveElementFromTable(nsIFormControl* aChild,
|
||||||
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
// Upcast, uggly, but it works!
|
// Upcast, uggly, but it works!
|
||||||
nsContentList *list = NS_STATIC_CAST(nsContentList *,
|
nsBaseContentList *list = NS_STATIC_CAST(nsBaseContentList *,
|
||||||
(nsIDOMNodeList *)nodeList.get());
|
(nsIDOMNodeList *)nodeList.get());
|
||||||
|
|
||||||
list->RemoveElement(content);
|
list->RemoveElement(content);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче