Back to index

lightning-sunbird  0.9+nobinonly
nsContentList.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsContentList.h"
00039 #include "nsIContent.h"
00040 #include "nsIDOMNode.h"
00041 #include "nsIDOM3Node.h"
00042 #include "nsIDocument.h"
00043 #include "nsGenericElement.h"
00044 
00045 #include "nsContentUtils.h"
00046 
00047 #include "nsLayoutAtoms.h"
00048 #include "nsHTMLAtoms.h" // XXX until atoms get factored into nsLayoutAtoms
00049 
00050 // Form related includes
00051 #include "nsIDOMHTMLFormElement.h"
00052 
00053 #include "pldhash.h"
00054 
00055 
00056 static nsContentList *gCachedContentList;
00057 
00058 nsBaseContentList::nsBaseContentList()
00059 {
00060 }
00061 
00062 nsBaseContentList::~nsBaseContentList()
00063 {
00064 }
00065 
00066 
00067 // QueryInterface implementation for nsBaseContentList
00068 NS_INTERFACE_MAP_BEGIN(nsBaseContentList)
00069   NS_INTERFACE_MAP_ENTRY(nsIDOMNodeList)
00070   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNodeList)
00071   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(NodeList)
00072 NS_INTERFACE_MAP_END
00073 
00074 
00075 NS_IMPL_ADDREF(nsBaseContentList)
00076 NS_IMPL_RELEASE(nsBaseContentList)
00077 
00078 
00079 NS_IMETHODIMP
00080 nsBaseContentList::GetLength(PRUint32* aLength)
00081 {
00082   *aLength = mElements.Count();
00083 
00084   return NS_OK;
00085 }
00086 
00087 NS_IMETHODIMP
00088 nsBaseContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
00089 {
00090   nsISupports *tmp = mElements.SafeObjectAt(aIndex);
00091 
00092   if (!tmp) {
00093     *aReturn = nsnull;
00094 
00095     return NS_OK;
00096   }
00097 
00098   return CallQueryInterface(tmp, aReturn);
00099 }
00100 
00101 void
00102 nsBaseContentList::AppendElement(nsIContent *aContent)
00103 {
00104   mElements.AppendObject(aContent);
00105 }
00106 
00107 void
00108 nsBaseContentList::RemoveElement(nsIContent *aContent)
00109 {
00110   mElements.RemoveObject(aContent);
00111 }
00112 
00113 PRInt32
00114 nsBaseContentList::IndexOf(nsIContent *aContent, PRBool aDoFlush)
00115 {
00116   return mElements.IndexOf(aContent);
00117 }
00118 
00119 void
00120 nsBaseContentList::Reset()
00121 {
00122   mElements.Clear();
00123 }
00124 
00125 // static
00126 void
00127 nsBaseContentList::Shutdown()
00128 {
00129   NS_IF_RELEASE(gCachedContentList);
00130 }
00131 
00132 
00133 // nsFormContentList
00134 
00135 nsFormContentList::nsFormContentList(nsIDOMHTMLFormElement *aForm,
00136                                      nsBaseContentList& aContentList)
00137   : nsBaseContentList()
00138 {
00139 
00140   // move elements that belong to mForm into this content list
00141 
00142   PRUint32 i, length = 0;
00143   nsCOMPtr<nsIDOMNode> item;
00144 
00145   aContentList.GetLength(&length);
00146 
00147   for (i = 0; i < length; i++) {
00148     aContentList.Item(i, getter_AddRefs(item));
00149 
00150     nsCOMPtr<nsIContent> c(do_QueryInterface(item));
00151 
00152     if (c && nsContentUtils::BelongsInForm(aForm, c)) {
00153       AppendElement(c);
00154     }
00155   }
00156 }
00157 
00158 // Hashtable for storing nsContentLists
00159 static PLDHashTable gContentListHashTable;
00160 
00161 struct ContentListHashEntry : public PLDHashEntryHdr
00162 {
00163   nsContentList* mContentList;
00164 };
00165 
00166 PR_STATIC_CALLBACK(const void *)
00167 ContentListHashtableGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00168 {
00169   ContentListHashEntry *e = NS_STATIC_CAST(ContentListHashEntry *, entry);
00170   return e->mContentList->GetKey();
00171 }
00172 
00173 PR_STATIC_CALLBACK(PLDHashNumber)
00174 ContentListHashtableHashKey(PLDHashTable *table, const void *key)
00175 {
00176   const nsContentListKey* list = NS_STATIC_CAST(const nsContentListKey *, key);
00177   return list->GetHash();
00178 }
00179 
00180 PR_STATIC_CALLBACK(PRBool)
00181 ContentListHashtableMatchEntry(PLDHashTable *table,
00182                                const PLDHashEntryHdr *entry,
00183                                const void *key)
00184 {
00185   const ContentListHashEntry *e =
00186     NS_STATIC_CAST(const ContentListHashEntry *, entry);
00187   const nsContentListKey* list1 = e->mContentList->GetKey();
00188   const nsContentListKey* list2 = NS_STATIC_CAST(const nsContentListKey *, key);
00189 
00190   return list1->Equals(*list2);
00191 }
00192 
00193 already_AddRefed<nsContentList>
00194 NS_GetContentList(nsIDocument* aDocument, nsIAtom* aMatchAtom,
00195                   PRInt32 aMatchNameSpaceId, nsIContent* aRootContent)
00196 {
00197   nsContentList* list = nsnull;
00198 
00199   static PLDHashTableOps hash_table_ops =
00200   {
00201     PL_DHashAllocTable,
00202     PL_DHashFreeTable,
00203     ContentListHashtableGetKey,
00204     ContentListHashtableHashKey,
00205     ContentListHashtableMatchEntry,
00206     PL_DHashMoveEntryStub,
00207     PL_DHashClearEntryStub,
00208     PL_DHashFinalizeStub
00209   };
00210 
00211   // Initialize the hashtable if needed.
00212   if (!gContentListHashTable.ops) {
00213     PRBool success = PL_DHashTableInit(&gContentListHashTable,
00214                                        &hash_table_ops, nsnull,
00215                                        sizeof(ContentListHashEntry),
00216                                        16);
00217 
00218     if (!success) {
00219       gContentListHashTable.ops = nsnull;
00220     }
00221   }
00222   
00223   ContentListHashEntry *entry = nsnull;
00224   // First we look in our hashtable.  Then we create a content list if needed
00225   if (gContentListHashTable.ops) {
00226     nsContentListKey hashKey(aDocument, aMatchAtom,
00227                              aMatchNameSpaceId, aRootContent);
00228     
00229     // A PL_DHASH_ADD is equivalent to a PL_DHASH_LOOKUP for cases
00230     // when the entry is already in the hashtable.
00231     entry = NS_STATIC_CAST(ContentListHashEntry *,
00232                            PL_DHashTableOperate(&gContentListHashTable,
00233                                                 &hashKey,
00234                                                 PL_DHASH_ADD));
00235     if (entry)
00236       list = entry->mContentList;
00237   }
00238 
00239   if (!list) {
00240     // We need to create a ContentList and add it to our new entry, if
00241     // we have an entry
00242     list = new nsContentList(aDocument, aMatchAtom,
00243                              aMatchNameSpaceId, aRootContent);
00244     if (entry) {
00245       if (list)
00246         entry->mContentList = list;
00247       else
00248         PL_DHashTableRawRemove(&gContentListHashTable, entry);
00249     }
00250 
00251     NS_ENSURE_TRUE(list, nsnull);
00252   }
00253 
00254   NS_ADDREF(list);
00255 
00256   // Hold on to the last requested content list to avoid having it be
00257   // removed from the cache immediately when it's released. Avoid
00258   // bumping the refcount on the list if the requested list is the one
00259   // that's already cached.
00260 
00261   if (!aRootContent && gCachedContentList != list) {
00262     NS_IF_RELEASE(gCachedContentList);
00263 
00264     gCachedContentList = list;
00265     NS_ADDREF(gCachedContentList);
00266   }
00267 
00268   return list;
00269 }
00270 
00271 
00272 // nsContentList implementation
00273 
00274 nsContentList::nsContentList(nsIDocument *aDocument,
00275                              nsIAtom* aMatchAtom,
00276                              PRInt32 aMatchNameSpaceId,
00277                              nsIContent* aRootContent,
00278                              PRBool aDeep)
00279   : nsBaseContentList(),
00280     nsContentListKey(aDocument, aMatchAtom, aMatchNameSpaceId, aRootContent),
00281     mFunc(nsnull),
00282     mData(nsnull),
00283     mState(LIST_DIRTY),
00284     mDeep(aDeep)
00285 {
00286   NS_ASSERTION(mDeep || mRootContent, "Must have root content for non-deep list!");
00287   if (nsLayoutAtoms::wildcard == mMatchAtom) {
00288     mMatchAll = PR_TRUE;
00289   }
00290   else {
00291     mMatchAll = PR_FALSE;
00292   }
00293   Init(aDocument);
00294 }
00295 
00296 nsContentList::nsContentList(nsIDocument *aDocument,
00297                              nsContentListMatchFunc aFunc,
00298                              const nsAString& aData,
00299                              nsIContent* aRootContent,
00300                              PRBool aDeep,
00301                              nsIAtom* aMatchAtom,
00302                              PRInt32 aMatchNameSpaceId)
00303   : nsBaseContentList(),
00304     nsContentListKey(aDocument, aMatchAtom, aMatchNameSpaceId, aRootContent),
00305     mFunc(aFunc),
00306     mData(&EmptyString()),
00307     mMatchAll(PR_FALSE),
00308     mState(LIST_DIRTY),
00309     mDeep(aDeep)
00310 {
00311   NS_ASSERTION(mDeep || mRootContent, "Must have root content for non-deep list!");
00312   if (!aData.IsEmpty()) {
00313     mData = new nsString(aData);
00314     // If this fails, fail silently
00315   }
00316   Init(aDocument);
00317 }
00318 
00319 void nsContentList::Init(nsIDocument *aDocument)
00320 {
00321   // We don't reference count the reference to the document
00322   // If the document goes away first, we'll be informed and we
00323   // can drop our reference.
00324   // If we go away first, we'll get rid of ourselves from the
00325   // document's observer list.
00326   mDocument = aDocument;
00327   if (mDocument) {
00328     mDocument->AddObserver(this);
00329   }
00330 }
00331 
00332 nsContentList::~nsContentList()
00333 {
00334   RemoveFromHashtable();
00335   if (mDocument) {
00336     mDocument->RemoveObserver(this);
00337   }
00338   
00339   if (mData && mData != &EmptyString()) {
00340     // We actually allocated mData ourselves
00341     delete mData;
00342   }
00343 }
00344 
00345 
00346 // QueryInterface implementation for nsContentList
00347 NS_INTERFACE_MAP_BEGIN(nsContentList)
00348   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLCollection)
00349   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
00350   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(ContentList)
00351 NS_INTERFACE_MAP_END_INHERITING(nsBaseContentList)
00352 
00353 
00354 NS_IMPL_ADDREF_INHERITED(nsContentList, nsBaseContentList)
00355 NS_IMPL_RELEASE_INHERITED(nsContentList, nsBaseContentList)
00356 
00357 
00358 nsISupports *
00359 nsContentList::GetParentObject()
00360 {
00361   if (mRootContent) {
00362     return mRootContent;
00363   }
00364 
00365   return mDocument;
00366 }
00367   
00368 PRUint32
00369 nsContentList::Length(PRBool aDoFlush)
00370 {
00371   CheckDocumentExistence();
00372   BringSelfUpToDate(aDoFlush);
00373     
00374   PRInt32 count = mElements.Count();
00375 
00376   if (!mDocument) {
00377     SetDirty();
00378   }
00379 
00380   return count;
00381 }
00382 
00383 nsIContent *
00384 nsContentList::Item(PRUint32 aIndex, PRBool aDoFlush)
00385 {
00386   CheckDocumentExistence();
00387 
00388   if (mDocument && aDoFlush) {
00389     // Flush pending content changes Bug 4891.
00390     mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
00391   }
00392 
00393   if (mState != LIST_UP_TO_DATE)
00394     PopulateSelf(aIndex+1);
00395 
00396   NS_ASSERTION(!mDocument || mState != LIST_DIRTY,
00397                "PopulateSelf left the list in a dirty (useless) state!");
00398 
00399   nsIContent* content = mElements.SafeObjectAt(aIndex);
00400 
00401   if (!mDocument) {
00402     SetDirty();
00403   }
00404 
00405   return content;
00406 }
00407 
00408 nsIContent *
00409 nsContentList::NamedItem(const nsAString& aName, PRBool aDoFlush)
00410 {
00411   CheckDocumentExistence();
00412 
00413   BringSelfUpToDate(aDoFlush);
00414     
00415   PRInt32 i, count = mElements.Count();
00416 
00417   nsIContent* returnContent = nsnull;
00418   for (i = 0; i < count; i++) {
00419     nsIContent *content = mElements[i];
00420     if (content) {
00421       nsAutoString name;
00422       // XXX Should it be an EqualsIgnoreCase?
00423       if (((content->GetAttr(kNameSpaceID_None, nsHTMLAtoms::name,
00424                              name) == NS_CONTENT_ATTR_HAS_VALUE) &&
00425            aName.Equals(name)) ||
00426           ((content->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id,
00427                              name) == NS_CONTENT_ATTR_HAS_VALUE) &&
00428            aName.Equals(name))) {
00429         returnContent = content;
00430         break;
00431       }
00432     }
00433   }
00434 
00435   if (!mDocument) {
00436     SetDirty();
00437   }
00438   
00439   return returnContent;
00440 }
00441 
00442 PRInt32
00443 nsContentList::IndexOf(nsIContent *aContent, PRBool aDoFlush)
00444 {
00445   CheckDocumentExistence();
00446   BringSelfUpToDate(aDoFlush);
00447     
00448   PRInt32 index = mElements.IndexOf(aContent);
00449 
00450   if (!mDocument) {
00451     SetDirty();
00452   }
00453 
00454   return index;
00455 }
00456 
00457 void
00458 nsContentList::RootDestroyed()
00459 {
00460   // We shouldn't do anything useful from now on
00461   DisconnectFromDocument();  // This dirties us so we lose all state
00462   mRootContent = nsnull;
00463 }
00464 
00465 // static
00466 void
00467 nsContentList::OnDocumentDestroy(nsIDocument *aDocument)
00468 {
00469   // If our content list cache holds a list used for a document that's
00470   // now being destroyed, free the cache to prevent the list from
00471   // staying around until the next use of content lists ends up
00472   // replacing what's in the cache.
00473 
00474   if (gCachedContentList && gCachedContentList->mRootContent &&
00475       gCachedContentList->mRootContent->GetOwnerDoc() == aDocument) {
00476     NS_RELEASE(gCachedContentList);
00477   }
00478 }
00479 
00480 NS_IMETHODIMP
00481 nsContentList::GetLength(PRUint32* aLength)
00482 {
00483   *aLength = Length(PR_TRUE);
00484 
00485   return NS_OK;
00486 }
00487 
00488 NS_IMETHODIMP
00489 nsContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
00490 {
00491   nsIContent *content = Item(aIndex, PR_TRUE);
00492 
00493   if (content) {
00494     return CallQueryInterface(content, aReturn);
00495   }
00496 
00497   *aReturn = nsnull;
00498 
00499   return NS_OK;
00500 }
00501 
00502 NS_IMETHODIMP
00503 nsContentList::NamedItem(const nsAString& aName, nsIDOMNode** aReturn)
00504 {
00505   nsIContent *content = NamedItem(aName, PR_TRUE);
00506 
00507   if (content) {
00508     return CallQueryInterface(content, aReturn);
00509   }
00510 
00511   *aReturn = nsnull;
00512 
00513   return NS_OK;
00514 }
00515 
00516 void
00517 nsContentList::AttributeChanged(nsIDocument *aDocument, nsIContent* aContent,
00518                                 PRInt32 aNameSpaceID, nsIAtom* aAttribute,
00519                                 PRInt32 aModType)
00520 {
00521   NS_PRECONDITION(aContent, "Must have a content node to work with");
00522   
00523   if (!mFunc || mState == LIST_DIRTY || IsContentAnonymous(aContent)) {
00524     // Either we're already dirty or this notification doesn't affect
00525     // whether we might match aContent.
00526     return;
00527   }
00528   
00529   if (MayContainRelevantNodes(aContent->GetParent())) {
00530     if (Match(aContent)) {
00531       if (mElements.IndexOf(aContent) == -1) {
00532         // We match aContent now, and it's not in our list already.  Just dirty
00533         // ourselves; this is simpler than trying to figure out where to insert
00534         // aContent.
00535         SetDirty();
00536       }
00537     } else {
00538       // We no longer match aContent.  Remove it from our list.  If
00539       // it's already not there, this is a no-op (though a ptentially
00540       // expensive one).  Either way, no change of mState is required
00541       // here.
00542       mElements.RemoveObject(aContent);
00543     }
00544   }
00545 }
00546 
00547 void
00548 nsContentList::ContentAppended(nsIDocument *aDocument, nsIContent* aContainer,
00549                                PRInt32 aNewIndexInContainer)
00550 {
00551   NS_PRECONDITION(aContainer, "Can't get at the new content if no container!");
00552   
00553   /*
00554    * If the state is LIST_DIRTY then we have no useful information in our list
00555    * and we want to put off doing work as much as possible.  Also, if
00556    * aContainer is anonymous from our point of view, we know that we can't
00557    * possibly be matching any of the kids.
00558    */
00559   if (mState == LIST_DIRTY || IsContentAnonymous(aContainer)) 
00560     return;
00561 
00562   /*
00563    * We want to handle the case of ContentAppended by sometimes
00564    * appending the content to our list, not just setting state to
00565    * LIST_DIRTY, since most of our ContentAppended notifications
00566    * should come during pageload and be at the end of the document.
00567    * Do a bit of work to see whether we could just append to what we
00568    * already have.
00569    */
00570   
00571   PRInt32 count = aContainer->GetChildCount();
00572 
00573   if (count > 0 && MayContainRelevantNodes(aContainer)) {
00574     PRInt32 ourCount = mElements.Count();
00575     PRBool appendToList = PR_FALSE;
00576     if (ourCount == 0) {
00577       appendToList = PR_TRUE;
00578     } else {
00579       nsIContent* ourLastContent = mElements[ourCount - 1];
00580       /*
00581        * We want to append instead of invalidating if the first thing
00582        * that got appended comes after ourLastContent.
00583        */
00584       nsCOMPtr<nsIDOM3Node> ourLastDOM3Node(do_QueryInterface(ourLastContent));
00585       if (ourLastDOM3Node) {
00586         nsCOMPtr<nsIDOMNode> newNode =
00587           do_QueryInterface(aContainer->GetChildAt(aNewIndexInContainer));
00588         NS_ASSERTION(newNode, "Content being inserted is not a node.... why?");
00589 
00590         PRUint16 comparisonFlags;
00591         nsresult rv =
00592           ourLastDOM3Node->CompareDocumentPosition(newNode, &comparisonFlags);
00593         if (NS_SUCCEEDED(rv) && 
00594             (comparisonFlags & nsIDOM3Node::DOCUMENT_POSITION_FOLLOWING)) {
00595           appendToList = PR_TRUE;
00596         }
00597       }
00598     }
00599     
00600     PRInt32 i;
00601     
00602     if (!appendToList) {
00603       // The new stuff is somewhere in the middle of our list; check
00604       // whether we need to invalidate
00605       for (i = aNewIndexInContainer; i <= count-1; ++i) {
00606         if (MatchSelf(aContainer->GetChildAt(i))) {
00607           // Uh-oh.  We're gonna have to add elements into the middle
00608           // of our list. That's not worth the effort.
00609           SetDirty();
00610           break;
00611         }
00612       }
00613  
00614       return;
00615     }
00616 
00617     /*
00618      * At this point we know we could append.  If we're not up to
00619      * date, however, that would be a bad idea -- it could miss some
00620      * content that we never picked up due to being lazy.  Further, we
00621      * may never get asked for this content... so don't grab it yet.
00622      */
00623     if (mState == LIST_LAZY) // be lazy
00624       return;
00625 
00626     /*
00627      * We're up to date.  That means someone's actively using us; we
00628      * may as well grab this content....
00629      */
00630     for (i = aNewIndexInContainer; i <= count-1; ++i) {
00631       PRUint32 limit = PRUint32(-1);
00632       PopulateWith(aContainer->GetChildAt(i), PR_TRUE, limit);
00633     }
00634   }
00635 }
00636 
00637 void
00638 nsContentList::ContentInserted(nsIDocument *aDocument,
00639                                nsIContent* aContainer,
00640                                nsIContent* aChild,
00641                                PRInt32 aIndexInContainer)
00642 {
00643   // Note that aContainer can be null here if we are inserting into
00644   // the document itself; any attempted optimizations to this method
00645   // should deal with that.
00646   if (mState == LIST_DIRTY || IsContentAnonymous(aChild))
00647     return;
00648 
00649   if (MayContainRelevantNodes(aContainer) && MatchSelf(aChild))
00650     SetDirty();
00651 }
00652  
00653 void
00654 nsContentList::ContentRemoved(nsIDocument *aDocument,
00655                               nsIContent* aContainer,
00656                               nsIContent* aChild,
00657                               PRInt32 aIndexInContainer)
00658 {
00659   // Note that aContainer can be null here if we are removing from
00660   // the document itself; any attempted optimizations to this method
00661   // should deal with that.
00662   if (mState != LIST_DIRTY) {
00663     if (MayContainRelevantNodes(aContainer)) {
00664       if (!IsContentAnonymous(aChild) && MatchSelf(aChild)) {
00665         SetDirty();
00666       }
00667       return;
00668     }
00669   }
00670 
00671   // Even if aChild is anonymous from our point of view, it could
00672   // contain our root (eg say our root is an anonymous child of
00673   // aChild).
00674   if (ContainsRoot(aChild)) {
00675     DisconnectFromDocument();
00676   }
00677 }
00678 
00679 void
00680 nsContentList::DocumentWillBeDestroyed(nsIDocument *aDocument)
00681 {
00682   DisconnectFromDocument();
00683   Reset();
00684 }
00685 
00686 PRBool
00687 nsContentList::Match(nsIContent *aContent)
00688 {
00689   if (!aContent)
00690     return PR_FALSE;
00691 
00692   if (mFunc) {
00693     return (*mFunc)(aContent, mMatchNameSpaceId, mMatchAtom, *mData);
00694   }
00695 
00696   if (mMatchAtom) {
00697     if (!aContent->IsContentOfType(nsIContent::eELEMENT)) {
00698       return PR_FALSE;
00699     }
00700 
00701     nsINodeInfo *ni = aContent->GetNodeInfo();
00702     NS_ASSERTION(ni, "Element without nodeinfo!");
00703 
00704     if (mMatchNameSpaceId == kNameSpaceID_Unknown) {
00705       return (mMatchAll || ni->Equals(mMatchAtom));
00706     }
00707 
00708     return ((mMatchAll && ni->NamespaceEquals(mMatchNameSpaceId)) ||
00709             ni->Equals(mMatchAtom, mMatchNameSpaceId));
00710   }
00711 
00712   return PR_FALSE;
00713 }
00714 
00715 void
00716 nsContentList::CheckDocumentExistence()
00717 {
00718   if (!mDocument && mRootContent) {
00719     
00720     mDocument = mRootContent->GetDocument();
00721     if (mDocument) {
00722       mDocument->AddObserver(this);
00723       SetDirty();
00724     }
00725   }
00726 }
00727 
00728 PRBool 
00729 nsContentList::MatchSelf(nsIContent *aContent)
00730 {
00731   NS_PRECONDITION(aContent, "Can't match null stuff, you know");
00732   NS_PRECONDITION(mDeep || aContent->GetParent() == mRootContent,
00733                   "MatchSelf called on a node that we can't possibly match");
00734   
00735   if (Match(aContent))
00736     return PR_TRUE;
00737 
00738   if (!mDeep)
00739     return PR_FALSE;
00740 
00741   PRUint32 i, count = aContent->GetChildCount();
00742 
00743   for (i = 0; i < count; i++) {
00744     if (MatchSelf(aContent->GetChildAt(i))) {
00745       return PR_TRUE;
00746     }
00747   }
00748   
00749   return PR_FALSE;
00750 }
00751 
00752 void 
00753 nsContentList::PopulateWith(nsIContent *aContent, PRBool aIncludeRoot,
00754                             PRUint32 & aElementsToAppend)
00755 {
00756   NS_PRECONDITION(mDeep || aContent == mRootContent ||
00757                   aContent->GetParent() == mRootContent,
00758                   "PopulateWith called on nodes we can't possibly match");
00759   NS_PRECONDITION(mDeep || aIncludeRoot || aContent == mRootContent,
00760                   "Bogus root passed to PopulateWith in non-deep list");
00761   NS_PRECONDITION(!aIncludeRoot || aContent != mRootContent,
00762                   "We should never be trying to match mRootContent");
00763   
00764   if (aIncludeRoot) {
00765     if (Match(aContent)) {
00766       mElements.AppendObject(aContent);
00767       --aElementsToAppend;
00768       if (aElementsToAppend == 0)
00769         return;
00770     }
00771   }
00772 
00773   // Don't recurse down if we're not doing a deep match and we're
00774   // already looking at kids of the root.
00775   if (!mDeep && aIncludeRoot)
00776     return;
00777   
00778   PRUint32 i, count = aContent->GetChildCount();
00779 
00780   for (i = 0; i < count; i++) {
00781     PopulateWith(aContent->GetChildAt(i), PR_TRUE, aElementsToAppend);
00782     if (aElementsToAppend == 0)
00783       return;
00784   }
00785 }
00786 
00787 void 
00788 nsContentList::PopulateWithStartingAfter(nsIContent *aStartRoot,
00789                                          nsIContent *aStartChild,
00790                                          PRUint32 & aElementsToAppend)
00791 {
00792   NS_PRECONDITION(mDeep || aStartRoot == mRootContent ||
00793                   (aStartRoot->GetParent() == mRootContent &&
00794                    aStartChild == nsnull),
00795                   "Bogus aStartRoot or aStartChild");
00796 
00797   if (mDeep || aStartRoot == mRootContent) {
00798 #ifdef DEBUG
00799     PRUint32 invariant = aElementsToAppend + mElements.Count();
00800 #endif
00801     PRInt32 i = 0;
00802     if (aStartChild) {
00803       i = aStartRoot->IndexOf(aStartChild);
00804       NS_ASSERTION(i >= 0, "The start child must be a child of the start root!");
00805       ++i;  // move to one past
00806     }
00807 
00808     PRUint32 childCount = aStartRoot->GetChildCount();
00809     for ( ; ((PRUint32)i) < childCount; ++i) {
00810       PopulateWith(aStartRoot->GetChildAt(i), PR_TRUE, aElementsToAppend);
00811     
00812       NS_ASSERTION(aElementsToAppend + mElements.Count() == invariant,
00813                    "Something is awry in PopulateWith!");
00814       if (aElementsToAppend == 0)
00815         return;
00816     }
00817   }
00818 
00819   // We want to make sure we don't move up past our root node. So if
00820   // we're there, don't move to the parent.
00821   if (aStartRoot == mRootContent)
00822     return;
00823   
00824   nsIContent* parent = aStartRoot->GetParent();
00825   
00826   if (parent)
00827     PopulateWithStartingAfter(parent, aStartRoot, aElementsToAppend);
00828 }
00829 
00830 void 
00831 nsContentList::PopulateSelf(PRUint32 aNeededLength)
00832 {
00833   if (mState == LIST_DIRTY) {
00834     Reset();
00835   }
00836   PRUint32 count = mElements.Count();
00837   NS_ASSERTION(mState != LIST_DIRTY || count == 0,
00838                "Reset() not called when setting state to LIST_DIRTY?");
00839 
00840   if (count >= aNeededLength) // We're all set
00841     return;
00842 
00843   PRUint32 elementsToAppend = aNeededLength - count;
00844 #ifdef DEBUG
00845   PRUint32 invariant = elementsToAppend + mElements.Count();
00846 #endif
00847   if (count != 0) {
00848     PopulateWithStartingAfter(mElements[count - 1], nsnull, elementsToAppend);
00849     NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
00850                  "Something is awry in PopulateWithStartingAfter!");
00851   } else if (mRootContent) {
00852     PopulateWith(mRootContent, PR_FALSE, elementsToAppend);
00853     NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
00854                  "Something is awry in PopulateWith!");
00855   }
00856   else if (mDocument) {
00857     nsIContent *root = mDocument->GetRootContent();
00858     if (root) {
00859       PopulateWith(root, PR_TRUE, elementsToAppend);
00860       NS_ASSERTION(elementsToAppend + mElements.Count() == invariant,
00861                    "Something is awry in PopulateWith!");
00862     }
00863   }
00864 
00865   if (mDocument) {
00866     if (elementsToAppend != 0)
00867       mState = LIST_UP_TO_DATE;
00868     else
00869       mState = LIST_LAZY;
00870   }
00871   // Else no document, so we have to stay on our toes since we don't get
00872   // content notifications.  Caller will call SetDirty(); see documentation for
00873   // PopulateSelf() in the header.
00874 }
00875 
00876 PRBool
00877 nsContentList::MayContainRelevantNodes(nsIContent* aContainer) 
00878 {
00879   if (!mRootContent) {
00880 #ifdef DEBUG
00881     // aContainer can be null when ContentInserted/ContentRemoved are
00882     // called, but we still want to return PR_TRUE in such cases if
00883     // mRootContent is null.  We could pass the document into this
00884     // method instead of trying to get it from aContainer, but that
00885     // seems a little pointless just to run this debug-only integrity
00886     // check.
00887     if (aContainer) { 
00888       NS_ASSERTION(aContainer->GetDocument() == mDocument,
00889                    "We should not get in here if aContainer is in some _other_ document!");
00890     }
00891 #endif
00892     return PR_TRUE;
00893   }
00894 
00895   if (!aContainer) {
00896     return PR_FALSE;
00897   }
00898 
00899   if (!mDeep) {
00900     // We only care about cases when aContainer is our root content node.
00901     return aContainer == mRootContent;
00902   }
00903   
00904   return nsContentUtils::ContentIsDescendantOf(aContainer, mRootContent);
00905 }
00906 
00907 PRBool
00908 nsContentList::ContainsRoot(nsIContent* aContent)
00909 {
00910   if (!mRootContent || !aContent) {
00911     return PR_FALSE;
00912   }
00913 
00914   return nsContentUtils::ContentIsDescendantOf(mRootContent, aContent);
00915 }
00916 
00917 void 
00918 nsContentList::DisconnectFromDocument()
00919 {
00920   if (mDocument) {
00921     // Our key will change... Best remove ourselves before that happens.
00922     RemoveFromHashtable();
00923     mDocument->RemoveObserver(this);
00924     mDocument = nsnull;
00925   }
00926 
00927   // We will get no more updates, so we can never know we're up to
00928   // date
00929   SetDirty();
00930 }
00931 
00932 void
00933 nsContentList::RemoveFromHashtable()
00934 {
00935   if (mFunc) {
00936     // This can't be in the table anyway
00937     return;
00938   }
00939   
00940   if (!gContentListHashTable.ops)
00941     return;
00942 
00943   PL_DHashTableOperate(&gContentListHashTable,
00944                        GetKey(),
00945                        PL_DHASH_REMOVE);
00946 
00947   if (gContentListHashTable.entryCount == 0) {
00948     PL_DHashTableFinish(&gContentListHashTable);
00949     gContentListHashTable.ops = nsnull;
00950   }
00951 }
00952 
00953 void
00954 nsContentList::BringSelfUpToDate(PRBool aDoFlush)
00955 {
00956   if (mDocument && aDoFlush) {
00957     // Flush pending content changes Bug 4891.
00958     mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
00959   }
00960 
00961   if (mState != LIST_UP_TO_DATE)
00962     PopulateSelf(PRUint32(-1));
00963     
00964   NS_ASSERTION(!mDocument || mState == LIST_UP_TO_DATE,
00965                "PopulateSelf dod not bring content list up to date!");
00966 }
00967 
00968 PRBool
00969 nsContentList::IsContentAnonymous(nsIContent* aContent)
00970 {
00971   NS_PRECONDITION(aContent, "Must have a content node to work with");
00972   
00982   if (!mRootContent) {
00988     return aContent->GetBindingParent() != nsnull;
00989   }
00990 
01011   return mRootContent->GetBindingParent() != aContent->GetBindingParent();
01012 }