Back to index

lightning-sunbird  0.9+nobinonly
nsXBLPrototypeBinding.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  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsIAtom.h"
00041 #include "nsIXBLDocumentInfo.h"
00042 #include "nsIInputStream.h"
00043 #include "nsINameSpaceManager.h"
00044 #include "nsIURI.h"
00045 #include "nsIURL.h"
00046 #include "nsIDOMEventReceiver.h"
00047 #include "nsIChannel.h"
00048 #include "nsXPIDLString.h"
00049 #include "nsReadableUtils.h"
00050 #include "nsIParser.h"
00051 #include "nsParserCIID.h"
00052 #include "nsNetUtil.h"
00053 #include "plstr.h"
00054 #include "nsITextContent.h"
00055 #include "nsIDocument.h"
00056 #include "nsIXMLContentSink.h"
00057 #include "nsContentCID.h"
00058 #include "nsXMLDocument.h"
00059 #include "nsIDOMElement.h"
00060 #include "nsIDOMText.h"
00061 #include "nsSupportsArray.h"
00062 #include "nsXBLService.h"
00063 #include "nsXBLBinding.h"
00064 #include "nsXBLInsertionPoint.h"
00065 #include "nsXBLPrototypeBinding.h"
00066 #include "nsFixedSizeAllocator.h"
00067 #include "xptinfo.h"
00068 #include "nsIInterfaceInfoManager.h"
00069 #include "nsIPresShell.h"
00070 #include "nsIDocumentObserver.h"
00071 #include "nsHTMLAtoms.h"
00072 #include "nsXULAtoms.h"
00073 #include "nsXBLAtoms.h"
00074 #include "nsXBLProtoImpl.h"
00075 #include "nsCRT.h"
00076 #include "nsContentUtils.h"
00077 
00078 #include "nsIScriptContext.h"
00079 
00080 #include "nsICSSLoader.h"
00081 #include "nsIStyleRuleProcessor.h"
00082 
00083 // Helper Classes =====================================================================
00084 
00085 // nsXBLAttributeEntry and helpers.  This class is used to efficiently handle
00086 // attribute changes in anonymous content.
00087 
00088 class nsXBLAttributeEntry {
00089 public:
00090   nsIAtom* GetSrcAttribute() { return mSrcAttribute; }
00091   nsIAtom* GetDstAttribute() { return mDstAttribute; }
00092   PRInt32 GetDstNameSpace() { return mDstNameSpace; }
00093   
00094   nsIContent* GetElement() { return mElement; }
00095 
00096   nsXBLAttributeEntry* GetNext() { return mNext; }
00097   void SetNext(nsXBLAttributeEntry* aEntry) { mNext = aEntry; }
00098 
00099   static nsXBLAttributeEntry*
00100   Create(nsIAtom* aSrcAtom, nsIAtom* aDstAtom, PRInt32 aDstNameSpace, nsIContent* aContent) {
00101     void* place = nsXBLPrototypeBinding::kAttrPool->Alloc(sizeof(nsXBLAttributeEntry));
00102     return place ? ::new (place) nsXBLAttributeEntry(aSrcAtom, aDstAtom, aDstNameSpace, 
00103                                                      aContent) : nsnull;
00104   }
00105 
00106   static void
00107   Destroy(nsXBLAttributeEntry* aSelf) {
00108     aSelf->~nsXBLAttributeEntry();
00109     nsXBLPrototypeBinding::kAttrPool->Free(aSelf, sizeof(*aSelf));
00110   }
00111 
00112 protected:
00113   nsIContent* mElement;
00114 
00115   nsCOMPtr<nsIAtom> mSrcAttribute;
00116   nsCOMPtr<nsIAtom> mDstAttribute;
00117   PRInt32 mDstNameSpace;
00118   nsXBLAttributeEntry* mNext;
00119 
00120   nsXBLAttributeEntry(nsIAtom* aSrcAtom, nsIAtom* aDstAtom, PRInt32 aDstNameSpace,
00121                       nsIContent* aContent)
00122     : mElement(aContent),
00123       mSrcAttribute(aSrcAtom),
00124       mDstAttribute(aDstAtom),
00125       mDstNameSpace(aDstNameSpace),
00126       mNext(nsnull) { }
00127 
00128   ~nsXBLAttributeEntry() { delete mNext; }
00129 
00130 private:
00131   // Hide so that only Create() and Destroy() can be used to
00132   // allocate and deallocate from the heap
00133   static void* operator new(size_t) CPP_THROW_NEW { return 0; }
00134   static void operator delete(void*, size_t) {}
00135 };
00136 
00137 // nsXBLInsertionPointEntry and helpers.  This class stores all the necessary
00138 // info to figure out the position of an insertion point.
00139 // The same insertion point may be in the insertion point table for multiple
00140 // keys, so we refcount the entries.
00141 
00142 class nsXBLInsertionPointEntry {
00143 public:
00144   ~nsXBLInsertionPointEntry() {
00145     if (mDefaultContent) {
00146       // mDefaultContent is a sort of anonymous content within the XBL
00147       // document, and we own and manage it.  Unhook it here, since we're going
00148       // away.
00149       mDefaultContent->UnbindFromTree();
00150     }      
00151   }
00152   
00153   nsIContent* GetInsertionParent() { return mInsertionParent; }
00154   PRUint32 GetInsertionIndex() { return mInsertionIndex; }
00155   void SetInsertionIndex(PRUint32 aIndex) { mInsertionIndex = aIndex; }
00156 
00157   nsIContent* GetDefaultContent() { return mDefaultContent; }
00158   void SetDefaultContent(nsIContent* aChildren) { mDefaultContent = aChildren; }
00159 
00160 
00161   static nsXBLInsertionPointEntry*
00162   Create(nsIContent* aParent) {
00163     void* place = nsXBLPrototypeBinding::kInsPool->Alloc(sizeof(nsXBLInsertionPointEntry));
00164     return place ? ::new (place) nsXBLInsertionPointEntry(aParent) : nsnull;
00165   }
00166 
00167   static void
00168   Destroy(nsXBLInsertionPointEntry* aSelf) {
00169     aSelf->~nsXBLInsertionPointEntry();
00170     nsXBLPrototypeBinding::kInsPool->Free(aSelf, sizeof(*aSelf));
00171   }
00172 
00173   nsrefcnt AddRef() {
00174     ++mRefCnt;
00175     NS_LOG_ADDREF(this, mRefCnt, "nsXBLInsertionPointEntry", sizeof(nsXBLInsertionPointEntry));
00176     return mRefCnt;
00177   }
00178 
00179   nsrefcnt Release() {
00180     --mRefCnt;
00181     NS_LOG_RELEASE(this, mRefCnt, "nsXBLInsertionPointEntry");
00182     if (mRefCnt == 0) {
00183       Destroy(this);
00184       return 0;
00185     }
00186     return mRefCnt;
00187   }
00188 
00189 protected:
00190   nsCOMPtr<nsIContent> mInsertionParent;
00191   nsCOMPtr<nsIContent> mDefaultContent;
00192   PRUint32 mInsertionIndex;
00193   nsrefcnt mRefCnt;
00194 
00195   nsXBLInsertionPointEntry(nsIContent* aParent)
00196     : mInsertionParent(aParent),
00197       mInsertionIndex(0),
00198       mRefCnt(0) { }
00199 
00200 private:
00201   // Hide so that only Create() and Destroy() can be used to
00202   // allocate and deallocate from the heap
00203   static void* operator new(size_t) CPP_THROW_NEW { return 0; }
00204   static void operator delete(void*, size_t) {}
00205 };
00206 
00207 // =============================================================================
00208 
00209 // Static initialization
00210 PRUint32 nsXBLPrototypeBinding::gRefCnt = 0;
00211 
00212 nsFixedSizeAllocator* nsXBLPrototypeBinding::kAttrPool;
00213 nsFixedSizeAllocator* nsXBLPrototypeBinding::kInsPool;
00214 
00215 static const PRInt32 kNumElements = 128;
00216 
00217 static const size_t kAttrBucketSizes[] = {
00218   sizeof(nsXBLAttributeEntry)
00219 };
00220 
00221 static const PRInt32 kAttrNumBuckets = sizeof(kAttrBucketSizes)/sizeof(size_t);
00222 static const PRInt32 kAttrInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLAttributeEntry))) * kNumElements;
00223 
00224 static const size_t kInsBucketSizes[] = {
00225   sizeof(nsXBLInsertionPointEntry)
00226 };
00227 
00228 static const PRInt32 kInsNumBuckets = sizeof(kInsBucketSizes)/sizeof(size_t);
00229 static const PRInt32 kInsInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLInsertionPointEntry))) * kNumElements;
00230 
00231 // Implementation /////////////////////////////////////////////////////////////////
00232 
00233 MOZ_DECL_CTOR_COUNTER(nsXBLPrototypeBinding)
00234 
00235 // Constructors/Destructors
00236 nsXBLPrototypeBinding::nsXBLPrototypeBinding()
00237 : mImplementation(nsnull),
00238   mBaseBinding(nsnull),
00239   mInheritStyle(PR_TRUE), 
00240   mHasBaseProto(PR_TRUE),
00241   mKeyHandlersRegistered(PR_FALSE),
00242   mResources(nsnull),
00243   mAttributeTable(nsnull),
00244   mInsertionPointTable(nsnull),
00245   mInterfaceTable(nsnull)
00246 {
00247   MOZ_COUNT_CTOR(nsXBLPrototypeBinding);
00248   gRefCnt++;
00249 
00250   if (gRefCnt == 1) {
00251     kAttrPool = new nsFixedSizeAllocator();
00252     if (kAttrPool) {
00253       kAttrPool->Init("XBL Attribute Entries", kAttrBucketSizes, kAttrNumBuckets, kAttrInitialSize);
00254     }
00255     kInsPool = new nsFixedSizeAllocator();
00256     if (kInsPool) {
00257       kInsPool->Init("XBL Insertion Point Entries", kInsBucketSizes, kInsNumBuckets, kInsInitialSize);
00258     }
00259   }
00260 }
00261 
00262 nsresult
00263 nsXBLPrototypeBinding::Init(const nsACString& aID,
00264                             nsIXBLDocumentInfo* aInfo,
00265                             nsIContent* aElement)
00266 {
00267   if (!kAttrPool || !kInsPool) {
00268     return NS_ERROR_OUT_OF_MEMORY;
00269   }
00270 
00271   nsCOMPtr<nsIURI> uri;
00272   nsresult rv = NS_NewURI(getter_AddRefs(uri),
00273                           NS_LITERAL_CSTRING("#") + aID,
00274                           nsnull,
00275                           aInfo->DocumentURI());
00276   NS_ENSURE_SUCCESS(rv, rv);
00277 
00278   mBindingURI = do_QueryInterface(uri, &rv);
00279   NS_ENSURE_SUCCESS(rv, rv);
00280   
00281   mXBLDocInfoWeak = aInfo;
00282 
00283   SetBindingElement(aElement);
00284   return NS_OK;
00285 }
00286 
00287 void
00288 nsXBLPrototypeBinding::Initialize()
00289 {
00290   nsIContent* content = GetImmediateChild(nsXBLAtoms::content);
00291   if (content) {
00292     // Make sure to construct the attribute table first, since constructing the
00293     // insertion point table removes some of the subtrees, which makes them
00294     // unreachable by walking our DOM.
00295     ConstructAttributeTable(content);
00296     ConstructInsertionTable(content);
00297   }
00298 }
00299 
00300 nsXBLPrototypeBinding::~nsXBLPrototypeBinding(void)
00301 {
00302   delete mResources;
00303   delete mAttributeTable;
00304   delete mInsertionPointTable;
00305   delete mInterfaceTable;
00306   delete mImplementation;
00307   gRefCnt--;
00308   if (gRefCnt == 0) {
00309     delete kAttrPool;
00310     delete kInsPool;
00311   }
00312   MOZ_COUNT_DTOR(nsXBLPrototypeBinding);
00313 }
00314 
00315 void
00316 nsXBLPrototypeBinding::SetBasePrototype(nsXBLPrototypeBinding* aBinding)
00317 {
00318   if (mBaseBinding == aBinding)
00319     return;
00320 
00321   if (mBaseBinding) {
00322     NS_ERROR("Base XBL prototype binding is already defined!");
00323     return;
00324   }
00325 
00326   mBaseBinding = aBinding;
00327 }
00328 
00329 already_AddRefed<nsIContent>
00330 nsXBLPrototypeBinding::GetBindingElement()
00331 {
00332   nsIContent* result = mBinding;
00333   NS_IF_ADDREF(result);
00334   return result;
00335 }
00336 
00337 void
00338 nsXBLPrototypeBinding::SetBindingElement(nsIContent* aElement)
00339 {
00340   mBinding = aElement;
00341   nsAutoString inheritStyle;
00342   mBinding->GetAttr(kNameSpaceID_None, nsXBLAtoms::inheritstyle, inheritStyle);
00343   if (inheritStyle.EqualsLiteral("false"))
00344     mInheritStyle = PR_FALSE;
00345 }
00346 
00347 nsresult
00348 nsXBLPrototypeBinding::GetAllowScripts(PRBool* aResult)
00349 {
00350   return mXBLDocInfoWeak->GetScriptAccess(aResult);
00351 }
00352 
00353 PRBool
00354 nsXBLPrototypeBinding::LoadResources()
00355 {
00356   if (mResources) {
00357     PRBool result;
00358     mResources->LoadResources(&result);
00359     return result;
00360   }
00361 
00362   return PR_TRUE;
00363 }
00364 
00365 nsresult
00366 nsXBLPrototypeBinding::AddResource(nsIAtom* aResourceType, const nsAString& aSrc)
00367 {
00368   if (!mResources) {
00369     mResources = new nsXBLPrototypeResources(this);
00370     if (!mResources)
00371       return NS_ERROR_OUT_OF_MEMORY;
00372   }
00373 
00374   mResources->AddResource(aResourceType, aSrc);
00375   return NS_OK;
00376 }
00377 
00378 nsresult
00379 nsXBLPrototypeBinding::FlushSkinSheets()
00380 {
00381   if (mResources)
00382     return mResources->FlushSkinSheets();
00383   return NS_OK;
00384 }
00385 
00386 nsresult
00387 nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement)
00388 {
00389   if (mImplementation && mImplementation->CompiledMembers() &&
00390       mImplementation->mConstructor)
00391     return mImplementation->mConstructor->Execute(aBoundElement);
00392   return NS_OK;
00393 }
00394 
00395 nsresult
00396 nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement)
00397 {
00398   if (mImplementation && mImplementation->CompiledMembers() &&
00399       mImplementation->mDestructor)
00400     return mImplementation->mDestructor->Execute(aBoundElement);
00401   return NS_OK;
00402 }
00403 
00404 nsXBLProtoImplAnonymousMethod*
00405 nsXBLPrototypeBinding::GetConstructor()
00406 {
00407   if (mImplementation)
00408     return mImplementation->mConstructor;
00409 
00410   return nsnull;
00411 }
00412 
00413 nsXBLProtoImplAnonymousMethod*
00414 nsXBLPrototypeBinding::GetDestructor()
00415 {
00416   if (mImplementation)
00417     return mImplementation->mDestructor;
00418 
00419   return nsnull;
00420 }
00421 
00422 nsresult
00423 nsXBLPrototypeBinding::SetConstructor(nsXBLProtoImplAnonymousMethod* aMethod)
00424 {
00425   if (!mImplementation)
00426     return NS_ERROR_FAILURE;
00427   mImplementation->mConstructor = aMethod;
00428   return NS_OK;
00429 }
00430 
00431 nsresult
00432 nsXBLPrototypeBinding::SetDestructor(nsXBLProtoImplAnonymousMethod* aMethod)
00433 {
00434   if (!mImplementation)
00435     return NS_ERROR_FAILURE;
00436   mImplementation->mDestructor = aMethod;
00437   return NS_OK;
00438 }
00439 
00440 nsresult
00441 nsXBLPrototypeBinding::InstallImplementation(nsIContent* aBoundElement)
00442 {
00443   if (mImplementation)
00444     return mImplementation->InstallImplementation(this, aBoundElement);
00445   return NS_OK;
00446 }
00447 
00448 void
00449 nsXBLPrototypeBinding::AttributeChanged(nsIAtom* aAttribute,
00450                                         PRInt32 aNameSpaceID,
00451                                         PRBool aRemoveFlag, 
00452                                         nsIContent* aChangedElement,
00453                                         nsIContent* aAnonymousContent,
00454                                         PRBool aNotify)
00455 {
00456   if (!mAttributeTable)
00457     return;
00458   nsPRUint32Key nskey(aNameSpaceID);
00459   nsObjectHashtable *attributesNS = NS_STATIC_CAST(nsObjectHashtable*, 
00460                                                    mAttributeTable->Get(&nskey));
00461   if (!attributesNS)
00462     return;
00463 
00464   nsISupportsKey key(aAttribute);
00465   nsXBLAttributeEntry* xblAttr = NS_STATIC_CAST(nsXBLAttributeEntry*,
00466                                                 attributesNS->Get(&key));
00467   if (!xblAttr)
00468     return;
00469 
00470   // Iterate over the elements in the array.
00471   nsCOMPtr<nsIContent> content = GetImmediateChild(nsXBLAtoms::content);
00472   while (xblAttr) {
00473     nsIContent* element = xblAttr->GetElement();
00474 
00475     nsCOMPtr<nsIContent> realElement = LocateInstance(aChangedElement, content,
00476                                                       aAnonymousContent,
00477                                                       element);
00478 
00479     if (realElement) {
00480       nsIAtom* dstAttr = xblAttr->GetDstAttribute();
00481       PRInt32 dstNs = xblAttr->GetDstNameSpace();
00482 
00483       if (aRemoveFlag)
00484         realElement->UnsetAttr(dstNs, dstAttr, aNotify);
00485       else {
00486         PRBool attrPresent = PR_TRUE;
00487         nsAutoString value;
00488         // Check to see if the src attribute is xbl:text.  If so, then we need to obtain the 
00489         // children of the real element and get the text nodes' values.
00490         if (aAttribute == nsHTMLAtoms::text && aNameSpaceID == kNameSpaceID_XBL) {
00491           nsXBLBinding::GetTextData(aChangedElement, value);
00492           value.StripChar(PRUnichar('\n'));
00493           value.StripChar(PRUnichar('\r'));
00494           nsAutoString stripVal(value);
00495           stripVal.StripWhitespace();
00496           if (stripVal.IsEmpty()) 
00497             attrPresent = PR_FALSE;
00498         }    
00499         else {
00500           nsresult result = aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
00501           attrPresent = (result == NS_CONTENT_ATTR_NO_VALUE ||
00502                          result == NS_CONTENT_ATTR_HAS_VALUE);
00503         }
00504 
00505         if (attrPresent)
00506           realElement->SetAttr(dstNs, dstAttr, value, aNotify);
00507       }
00508 
00509       // See if we're the <html> tag in XUL, and see if value is being
00510       // set or unset on us.  We may also be a tag that is having
00511       // xbl:text set on us.
00512 
00513       if ((dstAttr == nsHTMLAtoms::text && dstNs == kNameSpaceID_XBL) ||
00514           realElement->GetNodeInfo()->Equals(nsHTMLAtoms::html,
00515                                              kNameSpaceID_XUL) &&
00516           dstAttr == nsHTMLAtoms::value) {
00517         // Flush out all our kids.
00518         PRUint32 childCount = realElement->GetChildCount();
00519         for (PRUint32 i = 0; i < childCount; i++)
00520           realElement->RemoveChildAt(0, aNotify);
00521 
00522         if (!aRemoveFlag) {
00523           // Construct a new text node and insert it.
00524           nsAutoString value;
00525           aChangedElement->GetAttr(aNameSpaceID, aAttribute, value);
00526           if (!value.IsEmpty()) {
00527             nsCOMPtr<nsITextContent> textContent;
00528             NS_NewTextNode(getter_AddRefs(textContent),
00529                            realElement->GetNodeInfo()->NodeInfoManager());
00530             if (!textContent) {
00531               continue;
00532             }
00533 
00534             textContent->SetText(value, PR_TRUE);
00535             realElement->AppendChildTo(textContent, PR_TRUE);
00536           }
00537         }
00538       }
00539     }
00540 
00541     xblAttr = xblAttr->GetNext();
00542   }
00543 }
00544 
00545 struct InsertionData {
00546   nsXBLBinding* mBinding;
00547   nsXBLPrototypeBinding* mPrototype;
00548 
00549   InsertionData(nsXBLBinding* aBinding,
00550                 nsXBLPrototypeBinding* aPrototype) 
00551     :mBinding(aBinding), mPrototype(aPrototype) {};
00552 };
00553 
00554 PRBool PR_CALLBACK InstantiateInsertionPoint(nsHashKey* aKey, void* aData, void* aClosure)
00555 {
00556   nsXBLInsertionPointEntry* entry = NS_STATIC_CAST(nsXBLInsertionPointEntry*, aData);
00557   InsertionData* data = NS_STATIC_CAST(InsertionData*, aClosure);
00558   nsXBLBinding* binding = data->mBinding;
00559   nsXBLPrototypeBinding* proto = data->mPrototype;
00560 
00561   // Get the insertion parent.
00562   nsIContent* content = entry->GetInsertionParent();
00563   PRUint32 index = entry->GetInsertionIndex();
00564   nsIContent* defContent = entry->GetDefaultContent();
00565 
00566   // Locate the real content.
00567   nsIContent *instanceRoot = binding->GetAnonymousContent();
00568   nsIContent *templRoot = proto->GetImmediateChild(nsXBLAtoms::content);
00569   nsIContent *realContent = proto->LocateInstance(nsnull, templRoot,
00570                                                   instanceRoot, content);
00571   if (!realContent)
00572     realContent = binding->GetBoundElement();
00573 
00574   // Now that we have the real content, look it up in our table.
00575   nsVoidArray* points;
00576   binding->GetInsertionPointsFor(realContent, &points);
00577   nsXBLInsertionPoint* insertionPoint = nsnull;
00578   PRInt32 count = points->Count();
00579   PRInt32 i = 0;
00580   PRInt32 currIndex = 0;  
00581   
00582   for ( ; i < count; i++) {
00583     nsXBLInsertionPoint* currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, points->ElementAt(i));
00584     currIndex = currPoint->GetInsertionIndex();
00585     if (currIndex == (PRInt32)index) {
00586       // This is a match. Break out of the loop and set our variable.
00587       insertionPoint = currPoint;
00588       break;
00589     }
00590     
00591     if (currIndex > (PRInt32)index)
00592       // There was no match. Break.
00593       break;
00594   }
00595 
00596   if (!insertionPoint) {
00597     // We need to make a new insertion point.
00598     insertionPoint = new nsXBLInsertionPoint(realContent, index, defContent);
00599     if (insertionPoint) {
00600       NS_ADDREF(insertionPoint);
00601       points->InsertElementAt(insertionPoint, i);
00602     }
00603   }
00604 
00605   return PR_TRUE;
00606 }
00607 
00608 void
00609 nsXBLPrototypeBinding::InstantiateInsertionPoints(nsXBLBinding* aBinding)
00610 {
00611   InsertionData data(aBinding, this);
00612   if (mInsertionPointTable)
00613     mInsertionPointTable->Enumerate(InstantiateInsertionPoint, &data);
00614 }
00615 
00616 nsIContent*
00617 nsXBLPrototypeBinding::GetInsertionPoint(nsIContent* aBoundElement,
00618                                          nsIContent* aCopyRoot,
00619                                          nsIContent* aChild,
00620                                          PRUint32* aIndex)
00621 {
00622   if (!mInsertionPointTable)
00623     return nsnull;
00624 
00625   nsISupportsKey key(aChild->Tag());
00626   nsXBLInsertionPointEntry* entry = NS_STATIC_CAST(nsXBLInsertionPointEntry*, mInsertionPointTable->Get(&key));
00627   if (!entry) {
00628     nsISupportsKey key2(nsXBLAtoms::children);
00629     entry = NS_STATIC_CAST(nsXBLInsertionPointEntry*, mInsertionPointTable->Get(&key2));
00630   }
00631 
00632   nsIContent *realContent = nsnull;
00633   if (entry) {
00634     nsIContent* content = entry->GetInsertionParent();
00635     *aIndex = entry->GetInsertionIndex();
00636     nsIContent* templContent = GetImmediateChild(nsXBLAtoms::content);
00637     realContent = LocateInstance(nsnull, templContent, aCopyRoot, content);
00638   }
00639   else {
00640     // We got nothin'.  Bail.
00641     return nsnull;
00642   }
00643 
00644   return realContent ? realContent : aBoundElement;
00645 }
00646 
00647 nsIContent*
00648 nsXBLPrototypeBinding::GetSingleInsertionPoint(nsIContent* aBoundElement,
00649                                                nsIContent* aCopyRoot,
00650                                                PRUint32* aIndex,
00651                                                PRBool* aMultipleInsertionPoints)
00652 { 
00653   *aMultipleInsertionPoints = PR_FALSE;
00654   *aIndex = 0;
00655 
00656   if (!mInsertionPointTable)
00657     return nsnull;
00658 
00659   if (mInsertionPointTable->Count() != 1) {
00660     *aMultipleInsertionPoints = PR_TRUE;
00661     return nsnull;
00662   }
00663 
00664   nsISupportsKey key(nsXBLAtoms::children);
00665   nsXBLInsertionPointEntry* entry =
00666     NS_STATIC_CAST(nsXBLInsertionPointEntry*, mInsertionPointTable->Get(&key));
00667 
00668   if (!entry) {
00669     // The only insertion point specified was actually a filtered insertion
00670     // point. This means (strictly speaking) that we actually have multiple
00671     // insertion points: the filtered one and a generic insertion point
00672     // (content that doesn't match the filter will just go right underneath the
00673     // bound element).
00674 
00675     *aMultipleInsertionPoints = PR_TRUE;
00676     *aIndex = 0;
00677     return nsnull;
00678   }
00679 
00680   *aMultipleInsertionPoints = PR_FALSE;
00681   *aIndex = entry->GetInsertionIndex();
00682 
00683   nsIContent* templContent = GetImmediateChild(nsXBLAtoms::content);
00684   nsIContent* content = entry->GetInsertionParent();
00685   nsIContent *realContent = LocateInstance(nsnull, templContent, aCopyRoot,
00686                                            content);
00687 
00688   return realContent ? realContent : aBoundElement;
00689 }
00690 
00691 void
00692 nsXBLPrototypeBinding::SetBaseTag(PRInt32 aNamespaceID, nsIAtom* aTag)
00693 {
00694   mBaseNameSpaceID = aNamespaceID;
00695   mBaseTag = aTag;
00696 }
00697 
00698 nsIAtom*
00699 nsXBLPrototypeBinding::GetBaseTag(PRInt32* aNamespaceID)
00700 {
00701   if (mBaseTag) {
00702     *aNamespaceID = mBaseNameSpaceID;
00703     return mBaseTag;
00704   }
00705 
00706   return nsnull;
00707 }
00708 
00709 PRBool
00710 nsXBLPrototypeBinding::ImplementsInterface(REFNSIID aIID) const
00711 {
00712   // Check our IID table.
00713   if (mInterfaceTable) {
00714     nsIIDKey key(aIID);
00715     nsCOMPtr<nsISupports> supports = getter_AddRefs(NS_STATIC_CAST(nsISupports*, 
00716                                                                    mInterfaceTable->Get(&key)));
00717     return supports != nsnull;
00718   }
00719 
00720   return PR_FALSE;
00721 }
00722 
00723 // Internal helpers ///////////////////////////////////////////////////////////////////////
00724 
00725 nsIContent*
00726 nsXBLPrototypeBinding::GetImmediateChild(nsIAtom* aTag)
00727 {
00728   PRUint32 childCount = mBinding->GetChildCount();
00729 
00730   for (PRUint32 i = 0; i < childCount; i++) {
00731     nsIContent *child = mBinding->GetChildAt(i);
00732     nsINodeInfo *childNodeInfo = child->GetNodeInfo();
00733 
00734     if (childNodeInfo && childNodeInfo->Equals(aTag, kNameSpaceID_XBL)) {
00735       return child;
00736     }
00737   }
00738 
00739   return nsnull;
00740 }
00741  
00742 nsresult
00743 nsXBLPrototypeBinding::InitClass(const nsCString& aClassName,
00744                                  JSContext * aContext, JSObject * aGlobal,
00745                                  JSObject * aScriptObject,
00746                                  void ** aClassObject)
00747 {
00748   NS_ENSURE_ARG_POINTER(aClassObject); 
00749 
00750   *aClassObject = nsnull;
00751 
00752   return nsXBLBinding::DoInitJSClass(aContext, aGlobal, aScriptObject,
00753                                      aClassName, aClassObject);
00754 }
00755 
00756 nsIContent*
00757 nsXBLPrototypeBinding::LocateInstance(nsIContent* aBoundElement,
00758                                       nsIContent* aTemplRoot,
00759                                       nsIContent* aCopyRoot, 
00760                                       nsIContent* aTemplChild)
00761 {
00762   // XXX We will get in trouble if the binding instantiation deviates from the template
00763   // in the prototype.
00764   if (aTemplChild == aTemplRoot || !aTemplChild)
00765     return nsnull;
00766 
00767   nsCOMPtr<nsIContent> templParent = aTemplChild->GetParent();
00768   nsCOMPtr<nsIContent> childPoint;
00769   
00770   if (aBoundElement) {
00771     nsINodeInfo *ni = templParent->GetNodeInfo();
00772 
00773     if (ni->Equals(nsXBLAtoms::children, kNameSpaceID_XBL)) {
00774       childPoint = templParent;
00775       templParent = childPoint->GetParent();
00776     }
00777   }
00778 
00779   if (!templParent)
00780     return nsnull;
00781 
00782   nsIContent* result = nsnull;
00783   nsIContent *copyParent;
00784 
00785   if (templParent == aTemplRoot)
00786     copyParent = aCopyRoot;
00787   else
00788     copyParent = LocateInstance(aBoundElement, aTemplRoot, aCopyRoot, templParent);
00789   
00790   if (childPoint && aBoundElement) {
00791     // First we have to locate this insertion point and use its index and its
00792     // count to detemine our precise position within the template.
00793     nsIDocument* doc = aBoundElement->GetOwnerDoc();
00794     nsXBLBinding *binding = doc->BindingManager()->GetBinding(aBoundElement);
00795     nsIContent *anonContent = nsnull;
00796 
00797     while (binding) {
00798       anonContent = binding->GetAnonymousContent();
00799       if (anonContent)
00800         break;
00801 
00802       binding = binding->GetBaseBinding();
00803     }
00804 
00805     nsVoidArray* points;
00806     if (anonContent == copyParent)
00807       binding->GetInsertionPointsFor(aBoundElement, &points);
00808     else
00809       binding->GetInsertionPointsFor(copyParent, &points);
00810     PRInt32 count = points->Count();
00811     for (PRInt32 i = 0; i < count; i++) {
00812       // Next we have to find the real insertion point for this proto insertion
00813       // point.  If it does not contain any default content, then we should 
00814       // return null, since the content is not in the clone.
00815       nsXBLInsertionPoint* currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, points->ElementAt(i));
00816       nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
00817       if (defContent == childPoint) {
00818         // Now check to see if we even built default content at this
00819         // insertion point.
00820         defContent = currPoint->GetDefaultContent();
00821         if (defContent) {
00822           // Find out the index of the template element within the <children> elt.
00823           PRInt32 index = childPoint->IndexOf(aTemplChild);
00824           
00825           // Now we just have to find the corresponding elt underneath the cloned
00826           // default content.
00827           result = defContent->GetChildAt(index);
00828         } 
00829         break;
00830       }
00831     }
00832   }
00833   else if (copyParent)
00834   {
00835     PRInt32 index = templParent->IndexOf(aTemplChild);
00836     result = copyParent->GetChildAt(index);
00837   }
00838 
00839   return result;
00840 }
00841 
00842 struct nsXBLAttrChangeData
00843 {
00844   nsXBLPrototypeBinding* mProto;
00845   nsIContent* mBoundElement;
00846   nsIContent* mContent;
00847   PRInt32 mSrcNamespace;
00848 
00849   nsXBLAttrChangeData(nsXBLPrototypeBinding* aProto,
00850                       nsIContent* aElt, nsIContent* aContent) 
00851   :mProto(aProto), mBoundElement(aElt), mContent(aContent) {};
00852 };
00853 
00854 PRBool PR_CALLBACK SetAttrs(nsHashKey* aKey, void* aData, void* aClosure)
00855 {
00856   nsXBLAttributeEntry* entry = NS_STATIC_CAST(nsXBLAttributeEntry*, aData);
00857   nsXBLAttrChangeData* changeData = NS_STATIC_CAST(nsXBLAttrChangeData*, aClosure);
00858 
00859   nsIAtom* src = entry->GetSrcAttribute();
00860   PRInt32 srcNs = changeData->mSrcNamespace;
00861   nsAutoString value;
00862   PRBool attrPresent = PR_TRUE;
00863 
00864   if (src == nsHTMLAtoms::text && srcNs == kNameSpaceID_XBL) {
00865     nsXBLBinding::GetTextData(changeData->mBoundElement, value);
00866     value.StripChar(PRUnichar('\n'));
00867     value.StripChar(PRUnichar('\r'));
00868     nsAutoString stripVal(value);
00869     stripVal.StripWhitespace();
00870 
00871     if (stripVal.IsEmpty()) 
00872       attrPresent = PR_FALSE;
00873   }
00874   else {
00875     nsresult result = changeData->mBoundElement->GetAttr(srcNs, src, value);
00876     attrPresent = (result == NS_CONTENT_ATTR_NO_VALUE ||
00877                    result == NS_CONTENT_ATTR_HAS_VALUE);
00878   }
00879 
00880   if (attrPresent) {
00881     nsIContent* content =
00882       changeData->mProto->GetImmediateChild(nsXBLAtoms::content);
00883 
00884     nsXBLAttributeEntry* curr = entry;
00885     while (curr) {
00886       nsIAtom* dst = curr->GetDstAttribute();
00887       PRInt32 dstNs = curr->GetDstNameSpace();
00888       nsIContent* element = curr->GetElement();
00889 
00890       nsIContent *realElement =
00891         changeData->mProto->LocateInstance(changeData->mBoundElement, content,
00892                                            changeData->mContent, element);
00893 
00894       if (realElement) {
00895         realElement->SetAttr(dstNs, dst, value, PR_FALSE);
00896 
00897         if ((dst == nsHTMLAtoms::text && dstNs == kNameSpaceID_XBL) ||
00898             (realElement->GetNodeInfo()->Equals(nsHTMLAtoms::html,
00899                                                 kNameSpaceID_XUL) &&
00900              dst == nsHTMLAtoms::value && !value.IsEmpty())) {
00901 
00902           nsCOMPtr<nsITextContent> textContent;
00903           NS_NewTextNode(getter_AddRefs(textContent),
00904                          realElement->GetNodeInfo()->NodeInfoManager());
00905           if (!textContent) {
00906             continue;
00907           }
00908 
00909           textContent->SetText(value, PR_TRUE);
00910           realElement->AppendChildTo(textContent, PR_TRUE);
00911         }
00912       }
00913 
00914       curr = curr->GetNext();
00915     }
00916   }
00917 
00918   return PR_TRUE;
00919 }
00920 
00921 PRBool PR_CALLBACK SetAttrsNS(nsHashKey* aKey, void* aData, void* aClosure)
00922 {
00923   if (aData && aClosure) {
00924     nsPRUint32Key * key = NS_STATIC_CAST(nsPRUint32Key*, aKey);
00925     nsObjectHashtable* xblAttributes =
00926       NS_STATIC_CAST(nsObjectHashtable*, aData);
00927     nsXBLAttrChangeData * changeData = NS_STATIC_CAST(nsXBLAttrChangeData *,
00928                                                       aClosure);
00929     changeData->mSrcNamespace = key->GetValue();
00930     xblAttributes->Enumerate(SetAttrs, (void*)changeData);
00931   }
00932   return PR_TRUE;
00933 }
00934 
00935 void
00936 nsXBLPrototypeBinding::SetInitialAttributes(nsIContent* aBoundElement, nsIContent* aAnonymousContent)
00937 {
00938   if (mAttributeTable) {
00939     nsXBLAttrChangeData data(this, aBoundElement, aAnonymousContent);
00940     mAttributeTable->Enumerate(SetAttrsNS, (void*)&data);
00941   }
00942 }
00943 
00944 nsIStyleRuleProcessor*
00945 nsXBLPrototypeBinding::GetRuleProcessor()
00946 {
00947   if (mResources) {
00948     return mResources->mRuleProcessor;
00949   }
00950   
00951   return nsnull;
00952 }
00953 
00954 nsCOMArray<nsICSSStyleSheet>*
00955 nsXBLPrototypeBinding::GetStyleSheets()
00956 {
00957   if (mResources) {
00958     return &mResources->mStyleSheetList;
00959   }
00960 
00961   return nsnull;
00962 }
00963 
00964 PRBool
00965 nsXBLPrototypeBinding::ShouldBuildChildFrames() const
00966 {
00967   if (!mAttributeTable)
00968     return PR_TRUE;
00969   nsPRUint32Key nskey(kNameSpaceID_XBL);
00970   nsObjectHashtable* xblAttributes =
00971     NS_STATIC_CAST(nsObjectHashtable*, mAttributeTable->Get(&nskey));
00972   if (xblAttributes) {
00973     nsISupportsKey key(nsHTMLAtoms::text);
00974     void* entry = xblAttributes->Get(&key);
00975     return !entry;
00976   }
00977 
00978   return PR_TRUE;
00979 }
00980 
00981 static PRBool PR_CALLBACK
00982 DeleteAttributeEntry(nsHashKey* aKey, void* aData, void* aClosure)
00983 {
00984   nsXBLAttributeEntry::Destroy(NS_STATIC_CAST(nsXBLAttributeEntry*, aData));
00985   return PR_TRUE;
00986 }
00987 
00988 static PRBool PR_CALLBACK
00989 DeleteAttributeTable(nsHashKey* aKey, void* aData, void* aClosure)
00990 {
00991   delete NS_STATIC_CAST(nsObjectHashtable*, aData);
00992   return PR_TRUE;
00993 }
00994 
00995 void
00996 nsXBLPrototypeBinding::ConstructAttributeTable(nsIContent* aElement)
00997 {
00998   // Don't add entries for <children> elements, since those will get
00999   // removed from the DOM when we construct the insertion point table.
01000   nsINodeInfo* nodeInfo = aElement->GetNodeInfo();
01001   if (nodeInfo && !nodeInfo->Equals(nsXBLAtoms::children,
01002                                     kNameSpaceID_XBL)) {
01003     nsAutoString inherits;
01004     aElement->GetAttr(kNameSpaceID_XBL, nsXBLAtoms::inherits, inherits);
01005 
01006     if (!inherits.IsEmpty()) {
01007       if (!mAttributeTable) {
01008         mAttributeTable = new nsObjectHashtable(nsnull, nsnull,
01009                                                 DeleteAttributeTable,
01010                                                 nsnull, 4);
01011         if (!mAttributeTable)
01012           return;
01013       }
01014 
01015       // The user specified at least one attribute.
01016       char* str = ToNewCString(inherits);
01017       char* newStr;
01018       // XXX We should use a strtok function that tokenizes PRUnichars
01019       // so that we don't have to convert from Unicode to ASCII and then back
01020 
01021       char* token = nsCRT::strtok( str, ", ", &newStr );
01022       while( token != NULL ) {
01023         // Build an atom out of this attribute.
01024         nsCOMPtr<nsIAtom> atom;
01025         PRInt32 atomNsID = kNameSpaceID_None;
01026         nsCOMPtr<nsIAtom> attribute;
01027         PRInt32 attributeNsID = kNameSpaceID_None;
01028 
01029         // Figure out if this token contains a :.
01030         nsAutoString attrTok; attrTok.AssignWithConversion(token);
01031         PRInt32 index = attrTok.Find("=", PR_TRUE);
01032         nsresult rv;
01033         if (index != -1) {
01034           // This attribute maps to something different.
01035           nsAutoString left, right;
01036           attrTok.Left(left, index);
01037           attrTok.Right(right, attrTok.Length()-index-1);
01038 
01039           rv = nsContentUtils::SplitQName(aElement, left, &attributeNsID,
01040                                           getter_AddRefs(attribute));
01041           if (NS_FAILED(rv))
01042             return;
01043 
01044           rv = nsContentUtils::SplitQName(aElement, right, &atomNsID,
01045                                           getter_AddRefs(atom));
01046           if (NS_FAILED(rv))
01047             return;
01048         }
01049         else {
01050           nsAutoString tok;
01051           tok.AssignWithConversion(token);
01052           rv = nsContentUtils::SplitQName(aElement, tok, &atomNsID, 
01053                                           getter_AddRefs(atom));
01054           if (NS_FAILED(rv))
01055             return;
01056           attribute = atom;
01057           attributeNsID = atomNsID;
01058         }
01059 
01060         nsPRUint32Key nskey(atomNsID);
01061         nsObjectHashtable* attributesNS =
01062           NS_STATIC_CAST(nsObjectHashtable*, mAttributeTable->Get(&nskey));
01063         if (!attributesNS) {
01064           attributesNS = new nsObjectHashtable(nsnull, nsnull,
01065                                                DeleteAttributeEntry,
01066                                                nsnull, 4);
01067           if (!attributesNS)
01068             return;
01069 
01070           mAttributeTable->Put(&nskey, attributesNS);
01071         }
01072       
01073         // Create an XBL attribute entry.
01074         nsXBLAttributeEntry* xblAttr =
01075           nsXBLAttributeEntry::Create(atom, attribute, attributeNsID, aElement);
01076 
01077         // Now we should see if some element within our anonymous
01078         // content is already observing this attribute.
01079         nsISupportsKey key(atom);
01080         nsXBLAttributeEntry* entry = NS_STATIC_CAST(nsXBLAttributeEntry*,
01081                                                     attributesNS->Get(&key));
01082 
01083         if (!entry) {
01084           // Put it in the table.
01085           attributesNS->Put(&key, xblAttr);
01086         } else {
01087           while (entry->GetNext())
01088             entry = entry->GetNext();
01089 
01090           entry->SetNext(xblAttr);
01091         }
01092 
01093         // Now remove the inherits attribute from the element so that it doesn't
01094         // show up on clones of the element.  It is used
01095         // by the template only, and we don't need it anymore.
01096         // XXXdwh Don't do this for XUL elements, since it faults them into heavyweight
01097         // elements. Should nuke from the prototype instead.
01098         // aElement->UnsetAttr(kNameSpaceID_XBL, nsXBLAtoms::inherits, PR_FALSE);
01099 
01100         token = nsCRT::strtok( newStr, ", ", &newStr );
01101       }
01102 
01103       nsMemory::Free(str);
01104     }
01105   }
01106 
01107   // Recur into our children.
01108   PRUint32 childCount = aElement->GetChildCount();
01109   for (PRUint32 i = 0; i < childCount; i++) {
01110     ConstructAttributeTable(aElement->GetChildAt(i));
01111   }
01112 }
01113 
01114 static PRBool PR_CALLBACK
01115 DeleteInsertionPointEntry(nsHashKey* aKey, void* aData, void* aClosure)
01116 {
01117   NS_STATIC_CAST(nsXBLInsertionPointEntry*, aData)->Release();
01118   return PR_TRUE;
01119 }
01120 
01121 void 
01122 nsXBLPrototypeBinding::ConstructInsertionTable(nsIContent* aContent)
01123 {
01124   nsCOMArray<nsIContent> childrenElements;
01125   GetNestedChildren(nsXBLAtoms::children, kNameSpaceID_XBL, aContent,
01126                     childrenElements);
01127 
01128   PRInt32 count = childrenElements.Count();
01129   if (count == 0)
01130     return;
01131 
01132   mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
01133                                                DeleteInsertionPointEntry,
01134                                                nsnull, 4);
01135   if (!mInsertionPointTable)
01136     return;
01137 
01138   PRInt32 i;
01139   for (i = 0; i < count; i++) {
01140     nsIContent* child = childrenElements[i];
01141     nsIContent* parent = child->GetParent(); 
01142 
01143     // Create an XBL insertion point entry.
01144     nsXBLInsertionPointEntry* xblIns = nsXBLInsertionPointEntry::Create(parent);
01145 
01146     nsAutoString includes;
01147     child->GetAttr(kNameSpaceID_None, nsXBLAtoms::includes, includes);
01148     if (includes.IsEmpty()) {
01149       nsISupportsKey key(nsXBLAtoms::children);
01150       xblIns->AddRef();
01151       mInsertionPointTable->Put(&key, xblIns);
01152     }
01153     else {
01154       // The user specified at least one attribute.
01155       char* str = ToNewCString(includes);
01156       char* newStr;
01157       // XXX We should use a strtok function that tokenizes PRUnichar's
01158       // so that we don't have to convert from Unicode to ASCII and then back
01159 
01160       char* token = nsCRT::strtok( str, "| ", &newStr );
01161       while( token != NULL ) {
01162         nsAutoString tok;
01163         tok.AssignWithConversion(token);
01164 
01165         // Build an atom out of this string.
01166         nsCOMPtr<nsIAtom> atom = do_GetAtom(tok);
01167            
01168         nsISupportsKey key(atom);
01169         xblIns->AddRef();
01170         mInsertionPointTable->Put(&key, xblIns);
01171           
01172         token = nsCRT::strtok( newStr, "| ", &newStr );
01173       }
01174 
01175       nsMemory::Free(str);
01176     }
01177 
01178     // Compute the index of the <children> element.  This index is
01179     // equal to the index of the <children> in the template minus the #
01180     // of previous insertion point siblings removed.  Because our childrenElements
01181     // array was built in a DFS that went from left-to-right through siblings,
01182     // if we dynamically obtain our index each time, then the removals of previous
01183     // siblings will cause the index to adjust (and we won't have to take that into
01184     // account explicitly).
01185     PRInt32 index = parent->IndexOf(child);
01186     xblIns->SetInsertionIndex((PRUint32)index);
01187 
01188     // Now remove the <children> element from the template.  This ensures that the
01189     // binding instantiation will not contain a clone of the <children> element when
01190     // it clones the binding template.
01191     parent->RemoveChildAt(index, PR_FALSE);
01192 
01193     // See if the insertion point contains default content.  Default content must
01194     // be cached in our insertion point entry, since it will need to be cloned
01195     // in situations where no content ends up being placed at the insertion point.
01196     PRUint32 defaultCount = child->GetChildCount();
01197     if (defaultCount > 0) {
01198       // Annotate the insertion point with our default content.
01199       xblIns->SetDefaultContent(child);
01200 
01201       // Reconnect back to our parent for access later.  This makes "inherits" easier
01202       // to work with on default content.
01203       // XXXbz this is somewhat screwed up, since it's sort of like anonymous
01204       // content... but not.
01205       nsresult rv =
01206         child->BindToTree(parent->GetCurrentDoc(), parent, nsnull, PR_FALSE);
01207       if (NS_FAILED(rv)) {
01208         // Well... now what?  Just unbind and bail out, I guess...
01209         // XXXbz This really shouldn't be a void method!
01210         child->UnbindFromTree();
01211         return;
01212       }
01213     }
01214   }
01215 }
01216 
01217 nsresult
01218 nsXBLPrototypeBinding::ConstructInterfaceTable(const nsAString& aImpls)
01219 {
01220   if (!aImpls.IsEmpty()) {
01221     // Obtain the interface info manager that can tell us the IID
01222     // for a given interface name.
01223     nsCOMPtr<nsIInterfaceInfoManager> infoManager = getter_AddRefs(XPTI_GetInterfaceInfoManager());
01224     if (!infoManager)
01225       return NS_ERROR_FAILURE;
01226 
01227     // Create the table.
01228     if (!mInterfaceTable)
01229       mInterfaceTable = new nsSupportsHashtable(4);
01230 
01231     // The user specified at least one attribute.
01232     NS_ConvertUCS2toUTF8 utf8impl(aImpls);
01233     char* str = utf8impl.BeginWriting();
01234     char* newStr;
01235     // XXX We should use a strtok function that tokenizes PRUnichars
01236     // so that we don't have to convert from Unicode to ASCII and then back
01237 
01238     char* token = nsCRT::strtok( str, ", ", &newStr );
01239     while( token != NULL ) {
01240       // get the InterfaceInfo for the name
01241       nsCOMPtr<nsIInterfaceInfo> iinfo;
01242       infoManager->GetInfoForName(token, getter_AddRefs(iinfo));
01243 
01244       if (iinfo) {
01245         // obtain an IID.
01246         nsIID* iid = nsnull;
01247         iinfo->GetInterfaceIID(&iid);
01248 
01249         if (iid) {
01250           // We found a valid iid.  Add it to our table.
01251           nsIIDKey key(*iid);
01252           mInterfaceTable->Put(&key, mBinding);
01253 
01254           // this block adds the parent interfaces of each interface
01255           // defined in the xbl definition (implements="nsI...")
01256           nsCOMPtr<nsIInterfaceInfo> parentInfo;
01257           // if it has a parent, add it to the table
01258           while (NS_SUCCEEDED(iinfo->GetParent(getter_AddRefs(parentInfo))) && parentInfo) {
01259             // free the nsMemory::Clone()ed iid
01260             nsMemory::Free(iid);
01261 
01262             // get the iid
01263             parentInfo->GetInterfaceIID(&iid);
01264 
01265             // don't add nsISupports to the table
01266             if (!iid || iid->Equals(NS_GET_IID(nsISupports)))
01267               break;
01268 
01269             // add the iid to the table
01270             nsIIDKey parentKey(*iid);
01271             mInterfaceTable->Put(&parentKey, mBinding);
01272 
01273             // look for the next parent
01274             iinfo = parentInfo;
01275           }
01276         }
01277 
01278         // free the nsMemory::Clone()ed iid
01279         if (iid)
01280           nsMemory::Free(iid);
01281       }
01282 
01283       token = nsCRT::strtok( newStr, ", ", &newStr );
01284     }
01285   }
01286 
01287   return NS_OK;
01288 }
01289 
01290 void
01291 nsXBLPrototypeBinding::GetNestedChildren(nsIAtom* aTag, PRInt32 aNamespace,
01292                                          nsIContent* aContent,
01293                                          nsCOMArray<nsIContent> & aList)
01294 {
01295   PRUint32 childCount = aContent->GetChildCount();
01296 
01297   for (PRUint32 i = 0; i < childCount; i++) {
01298     nsIContent *child = aContent->GetChildAt(i);
01299 
01300     nsINodeInfo *nodeInfo = child->GetNodeInfo();
01301     if (nodeInfo && nodeInfo->Equals(aTag, aNamespace)) {
01302       aList.AppendObject(child);
01303     }
01304     else
01305       GetNestedChildren(aTag, aNamespace, child, aList);
01306   }
01307 }
01308 
01309 nsresult
01310 nsXBLPrototypeBinding::AddResourceListener(nsIContent* aBoundElement)
01311 {
01312   if (!mResources)
01313     return NS_ERROR_FAILURE; // Makes no sense to add a listener when the binding
01314                              // has no resources.
01315 
01316   mResources->AddResourceListener(aBoundElement);
01317   return NS_OK;
01318 }
01319 
01320 void
01321 nsXBLPrototypeBinding::CreateKeyHandlers()
01322 {
01323   nsXBLPrototypeHandler* curr = mPrototypeHandler;
01324   while (curr) {
01325     nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
01326     if (eventAtom == nsXBLAtoms::keyup ||
01327         eventAtom == nsXBLAtoms::keydown ||
01328         eventAtom == nsXBLAtoms::keypress) {
01329       PRUint8 phase = curr->GetPhase();
01330       PRUint8 type = curr->GetType();
01331 
01332       PRInt32 count = mKeyHandlers.Count();
01333       PRInt32 i;
01334       nsXBLKeyEventHandler* handler = nsnull;
01335       for (i = 0; i < count; ++i) {
01336         handler = mKeyHandlers[i];
01337         if (handler->Matches(eventAtom, phase, type))
01338           break;
01339       }
01340 
01341       if (i == count) {
01342         nsRefPtr<nsXBLKeyEventHandler> newHandler;
01343         NS_NewXBLKeyEventHandler(eventAtom, phase, type,
01344                                  getter_AddRefs(newHandler));
01345         if (newHandler)
01346           mKeyHandlers.AppendObject(newHandler);
01347         handler = newHandler;
01348       }
01349 
01350       if (handler)
01351         handler->AddProtoHandler(curr);
01352     }
01353 
01354     curr = curr->GetNextHandler();
01355   }
01356 }