Back to index

lightning-sunbird  0.9+nobinonly
nsBindingManager.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 Communicator client 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  *   Original Author: David W. Hyatt (hyatt@netscape.com)
00024  *   Alec Flett <alecf@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsCOMPtr.h"
00041 #include "nsIXBLService.h"
00042 #include "nsIInputStream.h"
00043 #include "nsDoubleHashtable.h"
00044 #include "nsIURI.h"
00045 #include "nsIURL.h"
00046 #include "nsIChannel.h"
00047 #include "nsXPIDLString.h"
00048 #include "nsIParser.h"
00049 #include "nsParserCIID.h"
00050 #include "nsNetUtil.h"
00051 #include "plstr.h"
00052 #include "nsIContent.h"
00053 #include "nsIDOMElement.h"
00054 #include "nsIDocument.h"
00055 #include "nsContentUtils.h"
00056 #include "nsIPresShell.h"
00057 #include "nsIXMLContentSink.h"
00058 #include "nsContentCID.h"
00059 #include "nsXMLDocument.h"
00060 #include "nsHTMLAtoms.h"
00061 #include "nsSupportsArray.h"
00062 #include "nsITextContent.h"
00063 #include "nsIStreamListener.h"
00064 
00065 #include "nsXBLBinding.h"
00066 #include "nsXBLPrototypeBinding.h"
00067 #include "nsIXBLDocumentInfo.h"
00068 #include "nsXBLInsertionPoint.h"
00069 
00070 #include "nsIStyleSheet.h"
00071 #include "nsHTMLStyleSheet.h"
00072 #include "nsIHTMLCSSStyleSheet.h"
00073 
00074 #include "nsIStyleRuleProcessor.h"
00075 #include "nsIWeakReference.h"
00076 
00077 #include "jsapi.h"
00078 #include "nsIXPConnect.h"
00079 #include "nsDOMCID.h"
00080 #include "nsIDOMScriptObjectFactory.h"
00081 #include "nsIPrincipal.h"
00082 #include "nsIScriptGlobalObject.h"
00083 #include "nsIScriptObjectPrincipal.h"
00084 #include "nsIConsoleService.h"
00085 #include "nsIScriptError.h"
00086 
00087 #include "nsIScriptContext.h"
00088 #include "nsBindingManager.h"
00089 
00090 // ==================================================================
00091 // = nsAnonymousContentList 
00092 // ==================================================================
00093 
00094 class nsAnonymousContentList : public nsGenericDOMNodeList
00095 {
00096 public:
00097   nsAnonymousContentList(nsVoidArray* aElements);
00098   virtual ~nsAnonymousContentList();
00099 
00100   // nsIDOMNodeList interface
00101   NS_DECL_NSIDOMNODELIST
00102 
00103   PRInt32 GetInsertionPointCount() { return mElements->Count(); }
00104 
00105   nsXBLInsertionPoint* GetInsertionPointAt(PRInt32 i) { return NS_STATIC_CAST(nsXBLInsertionPoint*, mElements->ElementAt(i)); }
00106   void RemoveInsertionPointAt(PRInt32 i) {
00107     nsXBLInsertionPoint* insertionPoint =
00108       NS_STATIC_CAST(nsXBLInsertionPoint*, mElements->SafeElementAt(i));
00109     NS_IF_RELEASE(insertionPoint);
00110     mElements->RemoveElementAt(i);
00111   }
00112 
00113 private:
00114   nsVoidArray* mElements;
00115 };
00116 
00117 MOZ_DECL_CTOR_COUNTER(nsAnonymousContentList)
00118 
00119 nsAnonymousContentList::nsAnonymousContentList(nsVoidArray* aElements)
00120   : mElements(aElements)
00121 {
00122   MOZ_COUNT_CTOR(nsAnonymousContentList);
00123 
00124   // We don't reference count our Anonymous reference (to avoid circular
00125   // references). We'll be told when the Anonymous goes away.
00126 }
00127 
00128 PRBool PR_CALLBACK ReleaseInsertionPoint(void* aElement, void* aData)
00129 {
00130   nsXBLInsertionPoint* insertionPoint =
00131     NS_STATIC_CAST(nsXBLInsertionPoint*, aElement);
00132   NS_IF_RELEASE(insertionPoint);
00133   return PR_TRUE;
00134 }
00135 
00136 nsAnonymousContentList::~nsAnonymousContentList()
00137 {
00138   MOZ_COUNT_DTOR(nsAnonymousContentList);
00139   mElements->EnumerateForwards(ReleaseInsertionPoint, nsnull);
00140   delete mElements;
00141 }
00142 
00143 NS_IMETHODIMP
00144 nsAnonymousContentList::GetLength(PRUint32* aLength)
00145 {
00146   NS_ASSERTION(aLength != nsnull, "null ptr");
00147   if (! aLength)
00148       return NS_ERROR_NULL_POINTER;
00149 
00150   PRInt32 cnt = mElements->Count();
00151 
00152   *aLength = 0;
00153   for (PRInt32 i = 0; i < cnt; i++)
00154     *aLength += NS_STATIC_CAST(nsXBLInsertionPoint*, mElements->ElementAt(i))->ChildCount();
00155 
00156   return NS_OK;
00157 }
00158 
00159 NS_IMETHODIMP    
00160 nsAnonymousContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
00161 {
00162   PRInt32 cnt = mElements->Count();
00163   PRUint32 pointCount = 0;
00164 
00165   for (PRInt32 i = 0; i < cnt; i++) {
00166     aIndex -= pointCount;
00167     
00168     nsXBLInsertionPoint* point = NS_STATIC_CAST(nsXBLInsertionPoint*, mElements->ElementAt(i));
00169     pointCount = point->ChildCount();
00170 
00171     if (aIndex < pointCount) {
00172       nsCOMPtr<nsIContent> result = point->ChildAt(aIndex);
00173       if (result)
00174         return CallQueryInterface(result, aReturn);
00175       return NS_ERROR_FAILURE;
00176     }
00177   }
00178 
00179   return NS_ERROR_FAILURE;
00180 }
00181 
00182 //
00183 // Generic pldhash table stuff for mapping one nsISupports to another
00184 //
00185 // These values are never null - a null value implies that this
00186 // whole key should be removed (See SetOrRemoveObject)
00187 class ObjectEntry : public PLDHashEntryHdr
00188 {
00189 public:
00190 
00191   // note that these are allocated within the PLDHashTable, but we
00192   // want to keep track of them anyway
00193   ObjectEntry() { MOZ_COUNT_CTOR(ObjectEntry); }
00194   ~ObjectEntry() { MOZ_COUNT_DTOR(ObjectEntry); }
00195   
00196   nsISupports* GetValue() { return mValue; }
00197   nsISupports* GetKey() { return mKey; }
00198   void SetValue(nsISupports* aValue) { mValue = aValue; }
00199   void SetKey(nsISupports* aKey) { mKey = aKey; }
00200   
00201 private:
00202   nsCOMPtr<nsISupports> mKey;
00203   nsCOMPtr<nsISupports> mValue;
00204 };
00205 
00206 PR_STATIC_CALLBACK(void)
00207 ClearObjectEntry(PLDHashTable* table, PLDHashEntryHdr *entry)
00208 {
00209   ObjectEntry* objEntry = NS_STATIC_CAST(ObjectEntry*, entry);
00210   objEntry->~ObjectEntry();
00211 }
00212 
00213 PR_STATIC_CALLBACK(PRBool)
00214 InitObjectEntry(PLDHashTable* table, PLDHashEntryHdr* entry, const void* key)
00215 {
00216   new (entry) ObjectEntry;
00217   return PR_TRUE;
00218 }
00219   
00220 
00221 
00222 static PLDHashTableOps ObjectTableOps = {
00223   PL_DHashAllocTable,
00224   PL_DHashFreeTable,
00225   PL_DHashGetKeyStub,
00226   PL_DHashVoidPtrKeyStub,
00227   PL_DHashMatchEntryStub,
00228   PL_DHashMoveEntryStub,
00229   ClearObjectEntry,
00230   PL_DHashFinalizeStub,
00231   InitObjectEntry
00232 };
00233 
00234 // helper routine for adding a new entry
00235 static nsresult
00236 AddObjectEntry(PLDHashTable& table, nsISupports* aKey, nsISupports* aValue)
00237 {
00238   NS_ASSERTION(aKey, "key must be non-null");
00239   if (!aKey) return NS_ERROR_INVALID_ARG;
00240   
00241   ObjectEntry *entry =
00242     NS_STATIC_CAST(ObjectEntry*,
00243                    PL_DHashTableOperate(&table, aKey, PL_DHASH_ADD));
00244 
00245   if (!entry)
00246     return NS_ERROR_OUT_OF_MEMORY;
00247 
00248   // only add the key if the entry is new
00249   if (!entry->GetKey())
00250     entry->SetKey(aKey);
00251 
00252   // now attach the new entry - note that entry->mValue could possibly
00253   // have a value already, this will release that.
00254   entry->SetValue(aValue);
00255   
00256   return NS_OK;
00257 }
00258 
00259 // helper routine for looking up an existing entry. Note that the
00260 // return result is NOT addreffed
00261 static nsISupports*
00262 LookupObject(PLDHashTable& table, nsISupports* aKey)
00263 {
00264   ObjectEntry *entry =
00265     NS_STATIC_CAST(ObjectEntry*,
00266                    PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
00267 
00268   if (PL_DHASH_ENTRY_IS_BUSY(entry))
00269     return entry->GetValue();
00270 
00271   return nsnull;
00272 }
00273 
00274 inline void
00275 RemoveObjectEntry(PLDHashTable& table, nsISupports* aKey)
00276 {
00277   PL_DHashTableOperate(&table, aKey, PL_DHASH_REMOVE);
00278 }
00279 
00280 static nsresult
00281 SetOrRemoveObject(PLDHashTable& table, nsISupports* aKey, nsISupports* aValue)
00282 {
00283   if (aValue) {
00284     // lazily create the table, but only when adding elements
00285     if (!table.ops &&
00286         !PL_DHashTableInit(&table, &ObjectTableOps, nsnull,
00287                            sizeof(ObjectEntry), 16)) {
00288       table.ops = nsnull;
00289       return NS_ERROR_OUT_OF_MEMORY;
00290     }
00291     return AddObjectEntry(table, aKey, aValue);
00292   }
00293 
00294   // no value, so remove the key from the table
00295   if (table.ops)
00296     RemoveObjectEntry(table, aKey);
00297   return NS_OK;
00298 }
00299 
00300 // Implementation /////////////////////////////////////////////////////////////////
00301 
00302 // Static member variable initialization
00303 
00304 // Implement our nsISupports methods
00305 NS_IMPL_ISUPPORTS3(nsBindingManager, nsIBindingManager, nsIStyleRuleSupplier, nsIDocumentObserver)
00306 
00307 // Constructors/Destructors
00308 nsBindingManager::nsBindingManager(nsIDocument* aDocument)
00309   : mProcessingAttachedStack(PR_FALSE),
00310     mProcessOnEndUpdate(PR_FALSE),
00311     mProcessAttachedQueueEvent(nsnull),
00312     mDocument(aDocument)
00313 {
00314   mContentListTable.ops = nsnull;
00315   mAnonymousNodesTable.ops = nsnull;
00316   mInsertionParentTable.ops = nsnull;
00317   mWrapperTable.ops = nsnull;
00318 }
00319 
00320 static PRBool PR_CALLBACK
00321 ReleaseBindings(void *aElement, void *aData)
00322 {
00323   nsXBLBinding *binding = NS_STATIC_CAST(nsXBLBinding*, aElement);
00324   NS_IF_RELEASE(binding);
00325   return PR_TRUE;
00326 }
00327 
00328 nsBindingManager::~nsBindingManager(void)
00329 {
00330   if (mContentListTable.ops)
00331     PL_DHashTableFinish(&mContentListTable);
00332   if (mAnonymousNodesTable.ops)
00333     PL_DHashTableFinish(&mAnonymousNodesTable);
00334   if (mInsertionParentTable.ops)
00335     PL_DHashTableFinish(&mInsertionParentTable);
00336   if (mWrapperTable.ops)
00337     PL_DHashTableFinish(&mWrapperTable);
00338   mAttachedStack.EnumerateForwards(ReleaseBindings, nsnull);
00339 }
00340 
00341 nsXBLBinding*
00342 nsBindingManager::GetBinding(nsIContent* aContent)
00343 {
00344   if (mBindingTable.IsInitialized())
00345     return mBindingTable.GetWeak(aContent);
00346 
00347   return nsnull;
00348 }
00349 
00350 NS_IMETHODIMP
00351 nsBindingManager::SetBinding(nsIContent* aContent, nsXBLBinding* aBinding)
00352 {
00353   if (!mBindingTable.IsInitialized()) {
00354     if (!mBindingTable.Init())
00355       return NS_ERROR_OUT_OF_MEMORY;
00356   }
00357 
00358   // After this point, aBinding will be the most-derived binding for aContent.
00359   // If we already have a binding for aContent in our table, make sure to
00360   // remove it from the attached stack.  Otherwise we might end up firing its
00361   // constructor twice (if aBinding inherits from it) or firing its constructor
00362   // after aContent has been deleted (if aBinding is null and the content node
00363   // dies before we process mAttachedStack).
00364   nsXBLBinding* oldBinding = mBindingTable.GetWeak(aContent);
00365   if (oldBinding) {
00366     nsXBLBinding* oldBindingWithCtor =
00367       oldBinding->GetFirstBindingWithConstructor();
00368     if (oldBindingWithCtor && mAttachedStack.RemoveElement(oldBindingWithCtor)) {
00369       NS_RELEASE(oldBindingWithCtor);
00370     }
00371   }
00372   
00373   PRBool result = PR_TRUE;
00374 
00375   if (aBinding) {
00376     result = mBindingTable.Put(aContent, aBinding);
00377   } else {
00378     mBindingTable.Remove(aContent);
00379 
00380     // The death of the bindings means the death of the JS wrapper,
00381     // and the flushing of our explicit and anonymous insertion point
00382     // lists.
00383     SetWrappedJS(aContent, nsnull);
00384     SetContentListFor(aContent, nsnull);
00385     SetAnonymousNodesFor(aContent, nsnull);
00386   }
00387 
00388   return result ? NS_OK : NS_ERROR_FAILURE;
00389 }
00390 
00391 NS_IMETHODIMP
00392 nsBindingManager::GetInsertionParent(nsIContent* aContent, nsIContent** aResult) 
00393 { 
00394   if (mInsertionParentTable.ops) {
00395     *aResult = NS_STATIC_CAST(nsIContent*,
00396                               LookupObject(mInsertionParentTable, aContent));
00397     NS_IF_ADDREF(*aResult);
00398   }
00399   else {
00400     *aResult = nsnull;
00401   }
00402 
00403   return NS_OK;
00404 }
00405 
00406 NS_IMETHODIMP
00407 nsBindingManager::SetInsertionParent(nsIContent* aContent, nsIContent* aParent)
00408 {
00409   return SetOrRemoveObject(mInsertionParentTable, aContent, aParent);
00410 }
00411 
00412 NS_IMETHODIMP
00413 nsBindingManager::GetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS** aResult) 
00414 { 
00415   if (mWrapperTable.ops) {
00416     *aResult = NS_STATIC_CAST(nsIXPConnectWrappedJS*, LookupObject(mWrapperTable, aContent));
00417     NS_IF_ADDREF(*aResult);
00418   }
00419   else {
00420     *aResult = nsnull;
00421   }
00422 
00423   return NS_OK;
00424 }
00425 
00426 NS_IMETHODIMP
00427 nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
00428 {
00429   return SetOrRemoveObject(mWrapperTable, aContent, aWrappedJS);
00430 }
00431 
00432 NS_IMETHODIMP
00433 nsBindingManager::ChangeDocumentFor(nsIContent* aContent, nsIDocument* aOldDocument,
00434                                     nsIDocument* aNewDocument)
00435 {
00436   // XXXbz this code is pretty broken, since moving from one document
00437   // to another always passes through a null document!
00438   NS_PRECONDITION(aOldDocument != nsnull, "no old document");
00439   NS_PRECONDITION(!aNewDocument,
00440                   "Changing to a non-null new document not supported yet");
00441   if (! aOldDocument)
00442     return NS_ERROR_NULL_POINTER;
00443 
00444   // Hold a ref to the binding so it won't die when we remove it from our
00445   // table.
00446   nsRefPtr<nsXBLBinding> binding = nsBindingManager::GetBinding(aContent);
00447   if (binding) {
00448     binding->ChangeDocument(aOldDocument, aNewDocument);
00449     SetBinding(aContent, nsnull);
00450     if (aNewDocument)
00451       aNewDocument->BindingManager()->SetBinding(aContent, binding);
00452   }
00453 
00454   // Clear out insertion parents and content lists.
00455   SetInsertionParent(aContent, nsnull);
00456   SetContentListFor(aContent, nsnull);
00457   SetAnonymousNodesFor(aContent, nsnull);
00458 
00459   PRUint32 count = aOldDocument->GetNumberOfShells();
00460 
00461   for (PRUint32 i = 0; i < count; ++i) {
00462     nsIPresShell *shell = aOldDocument->GetShellAt(i);
00463     NS_ASSERTION(shell != nsnull, "Zoiks! nsIDocument::GetShellAt() broke");
00464 
00465     // now clear out the anonymous content for this node in the old presshell.
00466     // XXXbz this really doesn't belong here, somehow... either that, or we
00467     // need to better define what sort of bindings we're managing.
00468     shell->SetAnonymousContentFor(aContent, nsnull);
00469   }
00470 
00471   return NS_OK;
00472 }
00473 
00474 NS_IMETHODIMP
00475 nsBindingManager::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID,
00476                              nsIAtom** aResult)
00477 {
00478   nsXBLBinding *binding = nsBindingManager::GetBinding(aContent);
00479   
00480   if (binding) {
00481     *aResult = binding->GetBaseTag(aNameSpaceID);
00482 
00483     if (*aResult) {
00484       NS_ADDREF(*aResult);
00485       return NS_OK;
00486     }
00487   }
00488 
00489   *aNameSpaceID = aContent->GetNameSpaceID();
00490   NS_ADDREF(*aResult = aContent->Tag());
00491 
00492   return NS_OK;
00493 }
00494 
00495 NS_IMETHODIMP
00496 nsBindingManager::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
00497 { 
00498   // Locate the primary binding and get its node list of anonymous children.
00499   *aResult = nsnull;
00500   
00501   if (mContentListTable.ops) {
00502     *aResult = NS_STATIC_CAST(nsIDOMNodeList*,
00503                               LookupObject(mContentListTable, aContent));
00504     NS_IF_ADDREF(*aResult);
00505   }
00506   
00507   if (!*aResult) {
00508     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
00509     return node->GetChildNodes(aResult);
00510   }
00511 
00512   return NS_OK;
00513 }
00514 
00515 NS_IMETHODIMP
00516 nsBindingManager::SetContentListFor(nsIContent* aContent, nsVoidArray* aList)
00517 {
00518   nsIDOMNodeList* contentList = nsnull;
00519   if (aList) {
00520     contentList = new nsAnonymousContentList(aList);
00521     if (!contentList) {
00522       aList->EnumerateForwards(ReleaseInsertionPoint, nsnull);
00523       delete aList;
00524       return NS_ERROR_OUT_OF_MEMORY;
00525     }
00526   }
00527 
00528   return SetOrRemoveObject(mContentListTable, aContent, contentList);
00529 }
00530 
00531 NS_IMETHODIMP
00532 nsBindingManager::HasContentListFor(nsIContent* aContent, PRBool* aResult)
00533 {
00534   *aResult = PR_FALSE;
00535   if (mContentListTable.ops) {
00536     nsISupports* list = LookupObject(mContentListTable, aContent);
00537     *aResult = (list != nsnull);
00538   }
00539 
00540   return NS_OK;
00541 }
00542 
00543 nsresult
00544 nsBindingManager::GetAnonymousNodesInternal(nsIContent* aContent,
00545                                             nsIDOMNodeList** aResult,
00546                                             PRBool* aIsAnonymousContentList)
00547 { 
00548   // Locate the primary binding and get its node list of anonymous children.
00549   *aResult = nsnull;
00550   if (mAnonymousNodesTable.ops) {
00551     *aResult = NS_STATIC_CAST(nsIDOMNodeList*,
00552                               LookupObject(mAnonymousNodesTable, aContent));
00553     NS_IF_ADDREF(*aResult);
00554   }
00555 
00556   if (!*aResult) {
00557     *aIsAnonymousContentList = PR_FALSE;
00558     nsXBLBinding *binding = nsBindingManager::GetBinding(aContent);
00559     if (binding) {
00560       *aResult = binding->GetAnonymousNodes().get();
00561       return NS_OK;
00562     }
00563   } else
00564     *aIsAnonymousContentList = PR_TRUE;
00565 
00566   return NS_OK;
00567 }
00568 
00569 NS_IMETHODIMP
00570 nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
00571                                        nsIDOMNodeList** aResult)
00572 {
00573   PRBool dummy;
00574   return GetAnonymousNodesInternal(aContent, aResult, &dummy);
00575 }
00576 
00577 NS_IMETHODIMP
00578 nsBindingManager::SetAnonymousNodesFor(nsIContent* aContent, nsVoidArray* aList)
00579 {
00580   nsIDOMNodeList* contentList = nsnull;
00581   if (aList) {
00582     contentList = new nsAnonymousContentList(aList);
00583     if (!contentList) {
00584       aList->EnumerateForwards(ReleaseInsertionPoint, nsnull);
00585       delete aList;
00586       return NS_ERROR_OUT_OF_MEMORY;
00587     }
00588   
00589     // If there are any items in aList that are already in aContent's
00590     // AnonymousNodesList, we need to make sure they don't get deleted as
00591     // the lists are swapped.  So, get the current list and check.
00592     // FIXME: This is O(n*m) where n and m are the insertion point list
00593     //        lengths.  But, there usually aren't many insertion points.
00594 
00595     if (mAnonymousNodesTable.ops) {
00596       nsAnonymousContentList *oldList =
00597         NS_STATIC_CAST(nsAnonymousContentList*,
00598                        LookupObject(mAnonymousNodesTable, aContent));
00599       if (oldList) {
00600         PRInt32 i = 0;
00601         while (i < oldList->GetInsertionPointCount()) {
00602           nsXBLInsertionPoint *point = oldList->GetInsertionPointAt(i);
00603           if (aList->IndexOf(point) != -1) {
00604             // We don't want this point to be deleted, so remove it
00605             // from the old list.
00606             oldList->RemoveInsertionPointAt(i);
00607           } else {
00608             ++i;
00609           }
00610         }
00611       }
00612     }
00613   }
00614 
00615   return SetOrRemoveObject(mAnonymousNodesTable, aContent, contentList);
00616 }
00617 
00618 nsresult
00619 nsBindingManager::GetXBLChildNodesInternal(nsIContent* aContent,
00620                                            nsIDOMNodeList** aResult,
00621                                            PRBool* aIsAnonymousContentList)
00622 {
00623   *aResult = nsnull;
00624 
00625   PRUint32 length;
00626 
00627   // Retrieve the anonymous content that we should build.
00628   GetAnonymousNodesInternal(aContent, aResult, aIsAnonymousContentList);
00629   if (*aResult) {
00630     (*aResult)->GetLength(&length);
00631     if (length == 0)
00632       *aResult = nsnull;
00633   }
00634     
00635   // We may have an altered list of children from XBL insertion points.
00636   // If we don't have any anonymous kids, we next check to see if we have 
00637   // insertion points.
00638   if (! *aResult) {
00639     if (mContentListTable.ops) {
00640       *aResult = NS_STATIC_CAST(nsIDOMNodeList*,
00641                                 LookupObject(mContentListTable, aContent));
00642       NS_IF_ADDREF(*aResult);
00643       *aIsAnonymousContentList = PR_TRUE;
00644     }
00645   }
00646 
00647   return NS_OK;
00648 }
00649 
00650 NS_IMETHODIMP
00651 nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
00652 {
00653   PRBool dummy;
00654   return GetXBLChildNodesInternal(aContent, aResult, &dummy);
00655 }
00656 
00657 nsIContent*
00658 nsBindingManager::GetInsertionPoint(nsIContent* aParent, nsIContent* aChild,
00659                                     PRUint32* aIndex)
00660 {
00661   nsXBLBinding *binding = nsBindingManager::GetBinding(aParent);
00662   return binding ? binding->GetInsertionPoint(aChild, aIndex) : nsnull;
00663 }
00664 
00665 nsIContent*
00666 nsBindingManager::GetSingleInsertionPoint(nsIContent* aParent,
00667                                           PRUint32* aIndex,
00668                                           PRBool* aMultipleInsertionPoints)
00669 {
00670   nsXBLBinding *binding = nsBindingManager::GetBinding(aParent);
00671   if (binding)
00672     return binding->GetSingleInsertionPoint(aIndex, aMultipleInsertionPoints);
00673 
00674   *aMultipleInsertionPoints = PR_FALSE;
00675   return nsnull;
00676 }
00677 
00678 NS_IMETHODIMP
00679 nsBindingManager::AddLayeredBinding(nsIContent* aContent, nsIURI* aURL)
00680 {
00681   // First we need to load our binding.
00682   nsresult rv;
00683   nsCOMPtr<nsIXBLService> xblService = 
00684            do_GetService("@mozilla.org/xbl;1", &rv);
00685   if (!xblService)
00686     return rv;
00687 
00688   // Load the bindings.
00689   nsRefPtr<nsXBLBinding> binding;
00690   PRBool dummy;
00691   xblService->LoadBindings(aContent, aURL, PR_TRUE, getter_AddRefs(binding),
00692                            &dummy);
00693   if (binding) {
00694     AddToAttachedQueue(binding);
00695     ProcessAttachedQueue();
00696   }
00697 
00698   return NS_OK;
00699 }
00700 
00701 NS_IMETHODIMP
00702 nsBindingManager::RemoveLayeredBinding(nsIContent* aContent, nsIURI* aURL)
00703 {
00704   // Hold a ref to the binding so it won't die when we remove it from our table
00705   nsRefPtr<nsXBLBinding> binding = nsBindingManager::GetBinding(aContent);
00706   
00707   if (!binding) {
00708     return NS_OK;
00709   }
00710 
00711   // For now we can only handle removing a binding if it's the only one
00712   NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
00713 
00714   // Make sure that the binding has the URI that is requested to be removed
00715   nsIURI* bindingUri = binding->PrototypeBinding()->BindingURI();
00716   
00717   PRBool equalUri;
00718   nsresult rv = aURL->Equals(bindingUri, &equalUri);
00719   NS_ENSURE_SUCCESS(rv, rv);
00720   if (!equalUri) {
00721     return NS_OK;
00722   }
00723 
00724   // Make sure it isn't a style binding
00725   if (binding->IsStyleBinding()) {
00726     return NS_OK;
00727   }
00728 
00729   // Hold strong ref in case removing the binding tries to close the
00730   // window or something.
00731   // XXXbz should that be ownerdoc?  Wouldn't we need a ref to the
00732   // currentdoc too?  What's the one that should be passed to
00733   // ChangeDocument?
00734   nsCOMPtr<nsIDocument> doc = aContent->GetOwnerDoc();
00735   NS_ASSERTION(doc, "No owner document?");
00736   
00737   // Finally remove the binding...
00738   binding->UnhookEventHandlers();
00739   binding->ChangeDocument(doc, nsnull);
00740   SetBinding(aContent, nsnull);
00741   binding->MarkForDeath();
00742   
00743   // ...and recreate it's frames. We need to do this since the frames may have
00744   // been removed and style may have changed due to the removal of the
00745   // anonymous children.
00746   // XXXbz this should be using the current doc (if any), not the owner doc.
00747   nsIPresShell *presShell = doc->GetShellAt(0);
00748   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
00749 
00750   return presShell->RecreateFramesFor(aContent);;
00751 }
00752 
00753 NS_IMETHODIMP
00754 nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
00755                                       nsIURI* aURL,
00756                                       nsIDocument** aResult)
00757 {
00758   NS_PRECONDITION(aURL, "Must have a URI to load!");
00759   
00760   nsCAutoString otherScheme;
00761   aURL->GetScheme(otherScheme);
00762   
00763   nsCAutoString scheme;
00764   aBoundDoc->GetDocumentURI()->GetScheme(scheme);
00765 
00766   // First we need to load our binding.
00767   *aResult = nsnull;
00768   nsresult rv;
00769   nsCOMPtr<nsIXBLService> xblService = 
00770            do_GetService("@mozilla.org/xbl;1", &rv);
00771   if (!xblService)
00772     return rv;
00773 
00774   // Load the binding doc.
00775   nsCOMPtr<nsIXBLDocumentInfo> info;
00776   xblService->LoadBindingDocumentInfo(nsnull, aBoundDoc, aURL,
00777                                       PR_TRUE, getter_AddRefs(info));
00778   if (!info)
00779     return NS_ERROR_FAILURE;
00780 
00781   // XXXbz Why is this based on a scheme comparison?  Shouldn't this
00782   // be a real security check???
00783     if (!strcmp(scheme.get(), otherScheme.get()))
00784     info->GetDocument(aResult); // Addref happens here.
00785     
00786   return NS_OK;
00787 }
00788 
00789 NS_IMETHODIMP
00790 nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
00791 {
00792   if (!mAttachedStack.AppendElement(aBinding))
00793     return NS_ERROR_OUT_OF_MEMORY;
00794 
00795   NS_ADDREF(aBinding);
00796 
00797   // If we're in the middle of processing our queue already, don't
00798   // bother posting the event.
00799   if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
00800     nsCOMPtr<nsIEventQueueService> eventQueueService =
00801       do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
00802     nsCOMPtr<nsIEventQueue> eventQueue;
00803     if (eventQueueService) {
00804       eventQueueService->
00805         GetSpecialEventQueue(nsIEventQueueService::UI_THREAD_EVENT_QUEUE,
00806                              getter_AddRefs(eventQueue));
00807     }
00808     if (eventQueue) {
00809       ProcessAttachedQueueEvent* ev = new ProcessAttachedQueueEvent(this);
00810       if (ev && NS_FAILED(eventQueue->PostEvent(ev))) {
00811         PL_DestroyEvent(ev);
00812       } else {
00813         mProcessAttachedQueueEvent = ev;
00814       }
00815     }
00816   }
00817   
00818   return NS_OK;
00819 }
00820 
00821 NS_IMETHODIMP
00822 nsBindingManager::ClearAttachedQueue()
00823 {
00824   mAttachedStack.EnumerateForwards(ReleaseBindings, nsnull);
00825   mAttachedStack.Clear();
00826   return NS_OK;
00827 }
00828 
00829 void
00830 nsBindingManager::DoProcessAttachedQueue()
00831 {
00832   ProcessAttachedQueue();
00833 
00834   NS_ASSERTION(mAttachedStack.Count() == 0,
00835                "Shouldn't have pending bindings!");
00836   
00837   mProcessAttachedQueueEvent = nsnull;
00838 }
00839 
00840 NS_IMETHODIMP
00841 nsBindingManager::ProcessAttachedQueue()
00842 {
00843   if (mProcessingAttachedStack || mAttachedStack.Count() == 0)
00844     return NS_OK;
00845 
00846   mProcessingAttachedStack = PR_TRUE;
00847 
00848   PRInt32 lastItem;
00849   while ((lastItem = mAttachedStack.Count() - 1) >= 0) {
00850     nsXBLBinding *binding = NS_STATIC_CAST(nsXBLBinding*,
00851                                            mAttachedStack.FastElementAt(lastItem));
00852     mAttachedStack.RemoveElementAt(lastItem);
00853 
00854     NS_ASSERTION(binding, "null item in attached stack?");
00855     binding->ExecuteAttachedHandler();
00856     NS_RELEASE(binding);
00857   }
00858 
00859   mProcessingAttachedStack = PR_FALSE;
00860   NS_ASSERTION(mAttachedStack.Count() == 0, "How did we get here?");
00861   return NS_OK;
00862 }
00863 
00864 // Keep bindings and bound elements alive while executing detached handlers.
00865 struct BindingTableReadClosure
00866 {
00867   nsCOMArray<nsIContent> mBoundElements;
00868   nsVoidArray            mBindings;
00869 };
00870 
00871 PR_STATIC_CALLBACK(PLDHashOperator)
00872 AccumulateBindingsToDetach(nsISupports *aKey, nsXBLBinding *aBinding,
00873                            void* aClosure)
00874 {
00875   BindingTableReadClosure* closure =
00876     NS_STATIC_CAST(BindingTableReadClosure*, aClosure);
00877   if (aBinding && closure->mBindings.AppendElement(aBinding)) {
00878     if (!closure->mBoundElements.AppendObject(aBinding->GetBoundElement())) {
00879       closure->mBindings.RemoveElementAt(closure->mBindings.Count() - 1);
00880     } else {
00881       NS_ADDREF(aBinding);
00882     }
00883   }
00884 
00885   return PL_DHASH_NEXT;
00886 }
00887 
00888 NS_IMETHODIMP
00889 nsBindingManager::ExecuteDetachedHandlers()
00890 {
00891   // Walk our hashtable of bindings.
00892   if (mBindingTable.IsInitialized()) {
00893     BindingTableReadClosure closure;
00894     mBindingTable.EnumerateRead(AccumulateBindingsToDetach, &closure);
00895     PRUint32 i, count = closure.mBindings.Count();
00896     for (i = 0; i < count; ++i) {
00897       NS_STATIC_CAST(nsXBLBinding*, closure.mBindings[i])
00898         ->ExecuteDetachedHandler();
00899     }
00900     for (i = 0; i < count; ++i) {
00901       nsXBLBinding* b = NS_STATIC_CAST(nsXBLBinding*, closure.mBindings[i]);
00902       NS_RELEASE(b);
00903     }
00904   }
00905   return NS_OK;
00906 }
00907 
00908 NS_IMETHODIMP
00909 nsBindingManager::PutXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo)
00910 {
00911   NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
00912   
00913   NS_ENSURE_TRUE(mDocumentTable.IsInitialized() || mDocumentTable.Init(16),
00914                  NS_ERROR_OUT_OF_MEMORY);
00915 
00916   NS_ENSURE_TRUE(mDocumentTable.Put(aDocumentInfo->DocumentURI(),
00917                                     aDocumentInfo),
00918                  NS_ERROR_OUT_OF_MEMORY);
00919 
00920   return NS_OK;
00921 }
00922 
00923 NS_IMETHODIMP
00924 nsBindingManager::RemoveXBLDocumentInfo(nsIXBLDocumentInfo* aDocumentInfo)
00925 {
00926   if (!mDocumentTable.IsInitialized())
00927     return NS_OK;
00928 
00929   mDocumentTable.Remove(aDocumentInfo->DocumentURI());
00930   return NS_OK;
00931 }
00932 
00933 NS_IMETHODIMP
00934 nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL, nsIXBLDocumentInfo** aResult)
00935 {
00936   *aResult = nsnull;
00937   if (!mDocumentTable.IsInitialized())
00938     return NS_OK;
00939 
00940   mDocumentTable.Get(aURL, aResult);
00941   return NS_OK;
00942 }
00943 
00944 NS_IMETHODIMP
00945 nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
00946 {
00947   NS_PRECONDITION(aListener, "Must have a non-null listener!");
00948   
00949   NS_ENSURE_TRUE(mLoadingDocTable.IsInitialized() || mLoadingDocTable.Init(16),
00950                  NS_ERROR_OUT_OF_MEMORY);
00951   
00952   NS_ENSURE_TRUE(mLoadingDocTable.Put(aURL, aListener),
00953                  NS_ERROR_OUT_OF_MEMORY);
00954 
00955   return NS_OK;
00956 }
00957 
00958 NS_IMETHODIMP
00959 nsBindingManager::GetLoadingDocListener(nsIURI* aURL, nsIStreamListener** aResult)
00960 {
00961   *aResult = nsnull;
00962   if (!mLoadingDocTable.IsInitialized())
00963     return NS_OK;
00964 
00965   mLoadingDocTable.Get(aURL, aResult);
00966   return NS_OK;
00967 }
00968 
00969 NS_IMETHODIMP
00970 nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
00971 {
00972   if (!mLoadingDocTable.IsInitialized())
00973     return NS_OK;
00974 
00975   mLoadingDocTable.Remove(aURL);
00976 
00977   return NS_OK;
00978 }
00979 
00980 PR_STATIC_CALLBACK(PLDHashOperator)
00981 MarkForDeath(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
00982 {
00983   if (aBinding->MarkedForDeath())
00984     return PL_DHASH_NEXT; // Already marked for death.
00985 
00986   nsCAutoString path;
00987   aBinding->PrototypeBinding()->DocURI()->GetPath(path);
00988 
00989   if (!strncmp(path.get(), "/skin", 5))
00990     aBinding->MarkForDeath();
00991   
00992   return PL_DHASH_NEXT;
00993 }
00994 
00995 NS_IMETHODIMP
00996 nsBindingManager::FlushSkinBindings()
00997 {
00998   if (mBindingTable.IsInitialized())
00999     mBindingTable.EnumerateRead(MarkForDeath, nsnull);
01000   return NS_OK;
01001 }
01002 
01003 // Used below to protect from recurring in QI calls through XPConnect.
01004 struct AntiRecursionData {
01005   nsIContent* element; 
01006   REFNSIID iid; 
01007   AntiRecursionData* next;
01008 
01009   AntiRecursionData(nsIContent* aElement, 
01010                     REFNSIID aIID, 
01011                     AntiRecursionData* aNext)
01012     : element(aElement), iid(aIID), next(aNext) {}
01013 };
01014 
01015 NS_IMETHODIMP
01016 nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
01017                                            void** aResult)
01018 {
01019   *aResult = nsnull;
01020   nsXBLBinding *binding = nsBindingManager::GetBinding(aContent);
01021   if (binding) {
01022     // The binding should not be asked for nsISupports
01023     NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
01024     if (binding->ImplementsInterface(aIID)) {
01025       nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
01026       GetWrappedJS(aContent, getter_AddRefs(wrappedJS));
01027 
01028       if (wrappedJS) {
01029         // Protect from recurring in QI calls through XPConnect. 
01030         // This can happen when a second binding is being resolved.
01031         // At that point a wrappedJS exists, but it doesn't yet know about
01032         // the iid we are asking for. So, without this protection, 
01033         // AggregatedQueryInterface would end up recurring back into itself
01034         // through this code. 
01035         //
01036         // With this protection, when we detect the recursion we return 
01037         // NS_NOINTERFACE in the inner call. The outer call will then fall 
01038         // through (see below) and build a new chained wrappedJS for the iid.
01039         //
01040         // We're careful to not assume that only one direct nesting can occur
01041         // because there is a call into JS in the middle and we can't assume 
01042         // that this code won't be reached by some more complex nesting path.
01043         //
01044         // NOTE: We *assume* this is single threaded, so we can use a
01045         // static linked list to do the check.
01046 
01047         static AntiRecursionData* list = nsnull;
01048 
01049         for (AntiRecursionData* p = list; p; p = p->next) {
01050           if (p->element == aContent && p->iid.Equals(aIID)) {
01051             *aResult = nsnull;
01052             return NS_NOINTERFACE;
01053           }
01054         }
01055 
01056         AntiRecursionData item(aContent, aIID, list);
01057         list = &item;
01058 
01059         nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
01060         
01061         list = item.next;
01062         
01063         if (*aResult)
01064           return rv;
01065         
01066         // No result was found, so this must be another XBL interface.
01067         // Fall through to create a new wrapper.
01068       }
01069 
01070       // We have never made a wrapper for this implementation.
01071       // Create an XPC wrapper for the script object and hand it back.
01072 
01073       nsIDocument* doc = aContent->GetOwnerDoc();
01074       if (!doc)
01075         return NS_NOINTERFACE;
01076 
01077       nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
01078       if (!global)
01079         return NS_NOINTERFACE;
01080 
01081       nsIScriptContext *context = global->GetContext();
01082       if (!context)
01083         return NS_NOINTERFACE;
01084 
01085       JSContext* jscontext = (JSContext*)context->GetNativeContext();
01086       if (!jscontext)
01087         return NS_NOINTERFACE;
01088 
01089       nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
01090 
01091       nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
01092       xpConnect->GetWrappedNativeOfNativeObject(jscontext,
01093                                                 global->GetGlobalJSObject(),
01094                                                 aContent,
01095                                                 NS_GET_IID(nsISupports),
01096                                                 getter_AddRefs(wrapper));
01097       NS_ENSURE_TRUE(wrapper, NS_NOINTERFACE);
01098 
01099       JSObject* jsobj = nsnull;
01100 
01101       wrapper->GetJSObject(&jsobj);
01102       NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
01103 
01104       nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, jscontext,
01105                                                         jsobj, aIID, aResult);
01106       if (NS_FAILED(rv))
01107         return rv;
01108 
01109       // We successfully created a wrapper.  We will own this wrapper for as long as the binding remains
01110       // alive.  At the time the binding is cleared out of the bindingManager, we will remove the wrapper
01111       // from the bindingManager as well.
01112       nsISupports* supp = NS_STATIC_CAST(nsISupports*, *aResult);
01113       wrappedJS = do_QueryInterface(supp);
01114       SetWrappedJS(aContent, wrappedJS);
01115 
01116       return rv;
01117     }
01118   }
01119   
01120   *aResult = nsnull;
01121   return NS_NOINTERFACE;
01122 }
01123 
01124 NS_IMETHODIMP
01125 nsBindingManager::WalkRules(nsStyleSet* aStyleSet,
01126                             nsIStyleRuleProcessor::EnumFunc aFunc,
01127                             RuleProcessorData* aData,
01128                             PRBool* aCutOffInheritance)
01129 {
01130   *aCutOffInheritance = PR_FALSE;
01131   
01132   if (!aData->mContent)
01133     return NS_OK;
01134 
01135   // Walk the binding scope chain, starting with the binding attached to our
01136   // content, up till we run out of scopes or we get cut off.
01137   nsIContent *content = aData->mContent;
01138   
01139   do {
01140     nsXBLBinding *binding = nsBindingManager::GetBinding(content);
01141     if (binding) {
01142       aData->mScopedRoot = content;
01143       binding->WalkRules(aFunc, aData);
01144       // If we're not looking at our original content, allow the binding to cut
01145       // off style inheritance
01146       if (content != aData->mContent) {
01147         if (!binding->InheritsStyle()) {
01148           // Go no further; we're not inheriting style from anything above here
01149           break;
01150         }
01151       }
01152     }
01153 
01154     nsIContent* parent = GetEnclosingScope(content);
01155     if (parent == content)
01156       break; // The scrollbar case only is deliberately hacked to return itself
01157              // (see GetBindingParent in nsXULElement.cpp).  Actually, all
01158              // native anonymous content is thus hacked.  Cut off inheritance
01159              // here.
01160 
01161     content = parent;
01162   } while (content);
01163 
01164   // If "content" is non-null that means we cut off inheritance at some point
01165   // in the loop.
01166   *aCutOffInheritance = (content != nsnull);
01167 
01168   // Null out the scoped root that we set repeatedly
01169   aData->mScopedRoot = nsnull;
01170 
01171   return NS_OK;
01172 }
01173 
01174 NS_IMETHODIMP
01175 nsBindingManager::ShouldBuildChildFrames(nsIContent* aContent, PRBool* aResult)
01176 {
01177   *aResult = PR_TRUE;
01178 
01179   nsXBLBinding *binding = nsBindingManager::GetBinding(aContent);
01180 
01181   if (binding)
01182     *aResult = binding->ShouldBuildChildFrames();
01183 
01184   return NS_OK;
01185 }
01186 
01187 nsresult
01188 nsBindingManager::GetNestedInsertionPoint(nsIContent* aParent, nsIContent* aChild, nsIContent** aResult)
01189 {
01190   *aResult = nsnull;
01191 
01192   // Check to see if the content is anonymous.
01193   if (aChild->GetBindingParent() == aParent)
01194     return NS_OK; // It is anonymous. Don't use the insertion point, since that's only
01195                   // for the explicit kids.
01196 
01197   PRUint32 index;
01198   nsIContent *insertionElement = GetInsertionPoint(aParent, aChild, &index);
01199   if (insertionElement != aParent) {
01200     // See if we nest even further in.
01201     nsCOMPtr<nsIContent> nestedPoint;
01202     GetNestedInsertionPoint(insertionElement, aChild, getter_AddRefs(nestedPoint));
01203     if (nestedPoint)
01204       insertionElement = nestedPoint;
01205   }
01206 
01207   *aResult = insertionElement;
01208   NS_IF_ADDREF(*aResult);
01209   return NS_OK;
01210 }
01211 
01212 void
01213 nsBindingManager::ContentAppended(nsIDocument* aDocument,
01214                                   nsIContent* aContainer,
01215                                   PRInt32     aNewIndexInContainer)
01216 {
01217   // XXX This is hacked and not quite correct. See below.
01218   if (aNewIndexInContainer == -1 || 
01219       (!mContentListTable.ops && !mAnonymousNodesTable.ops))
01220     // It's anonymous.
01221     return;
01222 
01223   PRInt32 childCount = aContainer->GetChildCount();
01224 
01225   nsIContent *child = aContainer->GetChildAt(aNewIndexInContainer);
01226 
01227   nsCOMPtr<nsIContent> ins;
01228   GetNestedInsertionPoint(aContainer, child, getter_AddRefs(ins));
01229 
01230   if (ins) {
01231     nsCOMPtr<nsIDOMNodeList> nodeList;
01232     PRBool isAnonymousContentList;
01233     GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList),
01234                              &isAnonymousContentList);
01235 
01236     if (nodeList && isAnonymousContentList) {
01237       // Find a non-pseudo-insertion point and just jam ourselves in.
01238       // This is not 100% correct.  Hack city, baby.
01239       nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get()));
01240 
01241       PRInt32 count = contentList->GetInsertionPointCount();
01242       for (PRInt32 i = 0; i < count; i++) {
01243         nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
01244         PRInt32 index = point->GetInsertionIndex();
01245         if (index != -1) {
01246           // We're real. Jam all the kids in.
01247           // XXX Check the filters to find the correct points.
01248           for (PRInt32 j = aNewIndexInContainer; j < childCount; j++) {
01249             child = aContainer->GetChildAt(j);
01250             point->AddChild(child);
01251             SetInsertionParent(child, ins);
01252           }
01253           break;
01254         }
01255       }
01256     }
01257   }
01258 }
01259 
01260 void
01261 nsBindingManager::ContentInserted(nsIDocument* aDocument,
01262                                   nsIContent* aContainer,
01263                                   nsIContent* aChild,
01264                                   PRInt32 aIndexInContainer)
01265 {
01266 // XXX This is hacked just to make menus work again.
01267   if (aIndexInContainer == -1 || 
01268       (!mContentListTable.ops && !mAnonymousNodesTable.ops))
01269     // It's anonymous.
01270     return;
01271 
01272   nsCOMPtr<nsIContent> ins;
01273   GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(ins));
01274 
01275   if (ins) {
01276     nsCOMPtr<nsIDOMNodeList> nodeList;
01277     PRBool isAnonymousContentList;
01278     GetXBLChildNodesInternal(ins, getter_AddRefs(nodeList),
01279                              &isAnonymousContentList);
01280 
01281     if (nodeList && isAnonymousContentList) {
01282       // Find a non-pseudo-insertion point and just jam ourselves in.
01283       // This is not 100% correct.  Hack city, baby.
01284       nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList.get()));
01285 
01286       PRInt32 count = contentList->GetInsertionPointCount();
01287       for (PRInt32 i = 0; i < count; i++) {
01288         nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
01289         if (point->GetInsertionIndex() != -1) {
01290           // We're real. Jam the kid in.
01291           // XXX Check the filters to find the correct points.
01292 
01293           // Find the right insertion spot.  Can't just insert in the insertion
01294           // point at aIndexInContainer since the point may contain anonymous
01295           // content, not all of aContainer's kids, etc.  So find the last
01296           // child of aContainer that comes before aIndexInContainer and is in
01297           // the insertion point and insert right after it.
01298           PRInt32 pointSize = point->ChildCount();
01299           PRBool inserted = PR_FALSE;
01300           for (PRInt32 parentIndex = aIndexInContainer - 1;
01301                parentIndex >= 0 && !inserted; --parentIndex) {
01302             nsIContent* currentSibling = aContainer->GetChildAt(parentIndex);
01303             for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0;
01304                  --pointIndex) {
01305               nsCOMPtr<nsIContent> currContent = point->ChildAt(pointIndex);
01306               if (currContent == currentSibling) {
01307                 point->InsertChildAt(pointIndex + 1, aChild);
01308                 inserted = PR_TRUE;
01309                 break;
01310               }
01311             }
01312           }
01313           if (!inserted) {
01314             // None of our previous siblings are in here... just stick
01315             // ourselves in at the beginning of the insertion point.
01316             // XXXbz if we ever start doing the filter thing right, this may be
01317             // no good, since we may _still_ have anonymous kids in there and
01318             // may need to get the ordering with those right.
01319             point->InsertChildAt(0, aChild);
01320           }
01321           SetInsertionParent(aChild, ins);
01322           break;
01323         }
01324       }
01325     }
01326   }
01327 }
01328 
01329 void
01330 nsBindingManager::ContentRemoved(nsIDocument* aDocument,
01331                                  nsIContent* aContainer,
01332                                  nsIContent* aChild,
01333                                  PRInt32 aIndexInContainer)
01334 {
01335   if (aIndexInContainer == -1 || 
01336       (!mContentListTable.ops  && !mAnonymousNodesTable.ops))
01337     // It's anonymous.
01338     return;
01339 
01340   nsCOMPtr<nsIContent> point;
01341   GetNestedInsertionPoint(aContainer, aChild, getter_AddRefs(point));
01342 
01343   if (point) {
01344     nsCOMPtr<nsIDOMNodeList> nodeList;
01345     PRBool isAnonymousContentList;
01346     GetXBLChildNodesInternal(point, getter_AddRefs(nodeList),
01347                              &isAnonymousContentList);
01348 
01349     if (nodeList && isAnonymousContentList) {
01350       // Find a non-pseudo-insertion point and remove ourselves.
01351       nsAnonymousContentList* contentList = NS_STATIC_CAST(nsAnonymousContentList*, NS_STATIC_CAST(nsIDOMNodeList*, nodeList));
01352       PRInt32 count = contentList->GetInsertionPointCount();
01353       for (PRInt32 i =0; i < count; i++) {
01354         nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
01355         if (point->GetInsertionIndex() != -1) {
01356           point->RemoveChild(aChild);
01357         }
01358       }
01359     }
01360   }
01361 }
01362 
01363 void
01364 nsBindingManager::DocumentWillBeDestroyed(nsIDocument* aDocument)
01365 {
01366   // Make sure to not run any more XBL constructors
01367   mProcessingAttachedStack = PR_TRUE;
01368 
01369   mDocument = nsnull;
01370 }
01371 
01372 void
01373 nsBindingManager::BeginOutermostUpdate()
01374 {
01375   mProcessOnEndUpdate = (mAttachedStack.Count() == 0);
01376 }
01377 
01378 void
01379 nsBindingManager::EndOutermostUpdate()
01380 {
01381   if (mProcessOnEndUpdate) {
01382     mProcessOnEndUpdate = PR_FALSE;
01383     if (mAttachedStack.Count() != 0 && mDocument) {
01384       nsCOMPtr<nsIPresShell_MOZILLA_1_8_BRANCH2> shell =
01385         do_QueryInterface(mDocument->GetShellAt(0));
01386       if (shell) {
01387         shell->BlockFlushing();
01388       }
01389       ProcessAttachedQueue();
01390       if (shell) {
01391         shell->UnblockFlushing();
01392       }
01393     }
01394   }
01395 }
01396 
01397 static void PR_CALLBACK
01398 HandlePLEvent(nsBindingManager::ProcessAttachedQueueEvent* aEvent)
01399 {
01400   aEvent->HandleEvent();
01401 }
01402 
01403 static void PR_CALLBACK
01404 DestroyPLEvent(nsBindingManager::ProcessAttachedQueueEvent* aEvent)
01405 {
01406   delete aEvent;
01407 }
01408 
01409 nsBindingManager::ProcessAttachedQueueEvent::ProcessAttachedQueueEvent(nsBindingManager* aBindingManager)
01410   : mBindingManager(aBindingManager)
01411 {
01412   PL_InitEvent(this, aBindingManager,
01413                (PLHandleEventProc) ::HandlePLEvent,
01414                (PLDestroyEventProc) ::DestroyPLEvent);
01415   if (aBindingManager->mDocument) {
01416     aBindingManager->mDocument->BlockOnload();
01417   }
01418 }
01419 
01420 nsBindingManager::ProcessAttachedQueueEvent::~ProcessAttachedQueueEvent()
01421 {
01422   if (mBindingManager->mDocument) {
01423     nsCOMPtr<nsIDocument> doc = mBindingManager->mDocument;
01424     doc->UnblockOnload();
01425   }
01426 }