Files
Mozilla/mozilla/content/base/src/nsContentList.h
bzbarsky%mit.edu 0f3150f7ad Make sure content lists don't look at "anonymous nodes" (nodes that are
anonymous descendants of the root of the list).  Bug 240851, r=sicking, sr=jst


git-svn-id: svn://10.0.0.236/trunk@155704 18797224-902f-48f8-a5cc-f745e15eee43
2004-04-29 21:07:44 +00:00

364 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsContentList_h___
#define nsContentList_h___
#include "nsISupports.h"
#include "nsVoidArray.h"
#include "nsString.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIDOMNodeList.h"
#include "nsStubDocumentObserver.h"
#include "nsIContentList.h"
#include "nsIAtom.h"
#include "nsINameSpaceManager.h"
// This is a callback function type that can be used to implement an
// arbitrary matching algorithm. aContent is the content that may
// match the list, while aNamespaceID, aAtom, and aData are whatever
// was passed to the list's constructor.
typedef PRBool (*nsContentListMatchFunc)(nsIContent* aContent,
PRInt32 aNamespaceID,
nsIAtom* aAtom,
const nsAString& aData);
class nsIDocument;
class nsIDOMHTMLFormElement;
class nsBaseContentList : public nsIDOMNodeList
{
public:
nsBaseContentList();
virtual ~nsBaseContentList();
NS_DECL_ISUPPORTS
// nsIDOMNodeList
NS_DECL_NSIDOMNODELIST
virtual void AppendElement(nsIContent *aContent);
virtual void RemoveElement(nsIContent *aContent);
virtual PRInt32 IndexOf(nsIContent *aContent, PRBool aDoFlush);
virtual void Reset();
static void Shutdown();
protected:
nsAutoVoidArray mElements;
};
// This class is used only by form element code and this is a static
// list of elements. NOTE! This list holds strong references to
// the elements in the list.
class nsFormContentList : public nsBaseContentList
{
public:
nsFormContentList(nsIDOMHTMLFormElement *aForm,
nsBaseContentList& aContentList);
virtual ~nsFormContentList();
virtual void AppendElement(nsIContent *aContent);
virtual void RemoveElement(nsIContent *aContent);
virtual void Reset();
};
/**
* Class that's used as the key to hash nsContentList implementations
* for fast retrieval
*/
class nsContentListKey
{
public:
nsContentListKey(nsIDocument *aDocument,
nsIAtom* aMatchAtom,
PRInt32 aMatchNameSpaceId,
nsIContent* aRootContent)
: mMatchAtom(aMatchAtom),
mMatchNameSpaceId(aMatchNameSpaceId),
mDocument(aDocument),
mRootContent(aRootContent)
{
}
nsContentListKey(const nsContentListKey& aContentListKey)
: mMatchAtom(aContentListKey.mMatchAtom),
mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId),
mDocument(aContentListKey.mDocument),
mRootContent(aContentListKey.mRootContent)
{
}
PRBool Equals(const nsContentListKey& aContentListKey) const
{
return
mMatchAtom == aContentListKey.mMatchAtom &&
mMatchNameSpaceId == aContentListKey.mMatchNameSpaceId &&
mDocument == aContentListKey.mDocument &&
mRootContent == aContentListKey.mRootContent;
}
inline PRUint32 GetHash(void) const
{
return
NS_PTR_TO_INT32(mMatchAtom.get()) ^
(NS_PTR_TO_INT32(mRootContent) << 8) ^
(NS_PTR_TO_INT32(mDocument) << 16) ^
(mMatchNameSpaceId << 24);
}
protected:
nsCOMPtr<nsIAtom> mMatchAtom;
PRInt32 mMatchNameSpaceId;
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
};
/**
* Class that implements a live NodeList that matches nodes in the
* tree based on some criterion
*/
class nsContentList : public nsBaseContentList,
protected nsContentListKey,
public nsIDOMHTMLCollection,
public nsStubDocumentObserver,
public nsIContentList
{
public:
NS_DECL_ISUPPORTS_INHERITED
nsContentList(nsIDocument *aDocument,
nsIAtom* aMatchAtom,
PRInt32 aMatchNameSpaceId,
nsIContent* aRootContent = nsnull,
PRBool aDeep = PR_TRUE);
nsContentList(nsIDocument *aDocument,
nsContentListMatchFunc aFunc,
const nsAString& aData,
nsIContent* aRootContent = nsnull,
PRBool aDeep = PR_TRUE,
nsIAtom* aMatchAtom = nsnull,
PRInt32 aMatchNameSpaceId = kNameSpaceID_None);
virtual ~nsContentList();
// nsIDOMHTMLCollection
NS_DECL_NSIDOMHTMLCOLLECTION
/// nsIContentList
virtual nsISupports *GetParentObject();
virtual PRUint32 Length(PRBool aDoFlush);
virtual nsIContent *Item(PRUint32 aIndex, PRBool aDoFlush);
virtual nsIContent *NamedItem(const nsAString& aName, PRBool aDoFlush);
virtual PRInt32 IndexOf(nsIContent *aContent, PRBool aDoFlush);
virtual void RootDestroyed();
// nsIDocumentObserver
virtual void AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRInt32 aModType);
virtual void ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
PRInt32 aNewIndexInContainer);
virtual void ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, PRInt32 aIndexInContainer);
virtual void ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
nsIContent* aChild, PRInt32 aIndexInContainer);
virtual void DocumentWillBeDestroyed(nsIDocument *aDocument);
// Other public methods
nsContentListKey* GetKey() {
return NS_STATIC_CAST(nsContentListKey*, this);
}
protected:
void Init(nsIDocument *aDocument);
/**
* 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);
/**
* 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();
/**
* @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
* (though if mDeep is false, only aContainer == mRootContent
* counts), PR_FALSE otherwise
*/
PRBool MayContainRelevantNodes(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);
/**
* 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.
*/
void CheckDocumentExistence();
/**
* Remove ourselves from the hashtable that caches commonly accessed
* content lists. Generally done on destruction.
*/
void RemoveFromHashtable();
/**
* If state is not LIST_UP_TO_DATE, fully populate ourselves with
* all the nodes we can find.
*/
inline void BringSelfUpToDate(PRBool aDoFlush);
/**
* A function to check whether aContent is anonymous from our point
* of view. If it is, we don't care about it, since we should never
* contain it or any of its kids.
*/
PRBool IsContentAnonymous(nsIContent* aContent);
/**
* Function to use to determine whether a piece of content matches
* our criterion
*/
nsContentListMatchFunc mFunc;
/**
* Closure data to pass to mFunc when we call it
*/
const nsAFlatString* mData;
/**
* 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;
/**
* Whether to actually descend the tree. If this is false, we won't
* consider grandkids of mRootContent.
*/
PRPackedBool mDeep;
};
/**
* 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
nsresult
NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom,
PRInt32 aMatchNameSpaceId, nsIContent* aRootContent,
nsIContentList** aInstancePtrResult);
#endif // nsContentList_h___