Back to index

lightning-sunbird  0.9+nobinonly
nsXBLContentSink.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  *   David Hyatt <hyatt@netscape.com> (Original Author)
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 "nsXBLContentSink.h"
00040 #include "nsIDocument.h"
00041 #include "nsIBindingManager.h"
00042 #include "nsIDOMNode.h"
00043 #include "nsIParser.h"
00044 #include "nsXBLAtoms.h"
00045 #include "nsINameSpaceManager.h"
00046 #include "nsHTMLAtoms.h"
00047 #include "nsLayoutAtoms.h"
00048 #include "nsHTMLTokens.h"
00049 #include "nsIURI.h"
00050 #include "nsTextFragment.h"
00051 #ifdef MOZ_XUL
00052 #include "nsXULElement.h"
00053 #endif
00054 #include "nsXULAtoms.h"
00055 #include "nsXBLProtoImplProperty.h"
00056 #include "nsXBLProtoImplMethod.h"
00057 #include "nsXBLProtoImplField.h"
00058 #include "nsXBLPrototypeBinding.h"
00059 #include "nsContentUtils.h"
00060 #include "nsIConsoleService.h"
00061 #include "nsIScriptError.h"
00062 #include "nsNodeInfoManager.h"
00063 #include "nsINodeInfo.h"
00064 #include "nsIPrincipal.h"
00065 
00066 nsresult
00067 NS_NewXBLContentSink(nsIXMLContentSink** aResult,
00068                      nsIDocument* aDoc,
00069                      nsIURI* aURI,
00070                      nsISupports* aContainer)
00071 {
00072   NS_ENSURE_ARG_POINTER(aResult);
00073 
00074   nsXBLContentSink* it;
00075   NS_NEWXPCOM(it, nsXBLContentSink);
00076   NS_ENSURE_TRUE(it, NS_ERROR_OUT_OF_MEMORY);
00077 
00078   nsCOMPtr<nsIXMLContentSink> kungFuDeathGrip = it;
00079   nsresult rv = it->Init(aDoc, aURI, aContainer);
00080   NS_ENSURE_SUCCESS(rv, rv);
00081 
00082   return CallQueryInterface(it, aResult);
00083 }
00084 
00085 nsXBLContentSink::nsXBLContentSink()
00086   : mState(eXBL_InDocument),
00087     mSecondaryState(eXBL_None),
00088     mDocInfo(nsnull),
00089     mIsChromeOrResource(PR_FALSE),
00090     mBinding(nsnull),
00091     mHandler(nsnull),
00092     mImplementation(nsnull),
00093     mImplMember(nsnull),
00094     mProperty(nsnull),
00095     mMethod(nsnull),
00096     mField(nsnull)
00097 {
00098   mPrettyPrintXML = PR_FALSE;
00099 }
00100 
00101 nsXBLContentSink::~nsXBLContentSink()
00102 {
00103 }
00104 
00105 nsresult
00106 nsXBLContentSink::Init(nsIDocument* aDoc,
00107                        nsIURI* aURI,
00108                        nsISupports* aContainer)
00109 {
00110   nsresult rv;
00111   rv = nsXMLContentSink::Init(aDoc, aURI, aContainer, nsnull);
00112   return rv;
00113 }
00114 
00115 nsresult
00116 nsXBLContentSink::FlushText(PRBool aCreateTextNode,
00117                             PRBool* aDidFlush)
00118 {
00119   if (mTextLength == 0) {
00120     if (aDidFlush)
00121       *aDidFlush = PR_FALSE;
00122     return NS_OK;
00123   }
00124 
00125   const nsASingleFragmentString& text = Substring(mText, mText+mTextLength);
00126   if (mState == eXBL_InHandlers) {
00127     NS_ASSERTION(mBinding, "Must have binding here");
00128     // Get the text and add it to the event handler.
00129     if (mSecondaryState == eXBL_InHandler)
00130       mHandler->AppendHandlerText(text);
00131     mTextLength = 0;
00132     if (aDidFlush)
00133       *aDidFlush = PR_TRUE;
00134     return NS_OK;
00135   }
00136   else if (mState == eXBL_InImplementation) {
00137     NS_ASSERTION(mBinding, "Must have binding here");
00138     if (mSecondaryState == eXBL_InConstructor ||
00139         mSecondaryState == eXBL_InDestructor) {
00140       // Construct a method for the constructor/destructor.
00141       nsXBLProtoImplMethod* method;
00142       if (mSecondaryState == eXBL_InConstructor)
00143         method = mBinding->GetConstructor();
00144       else
00145         method = mBinding->GetDestructor();
00146 
00147       // Get the text and add it to the constructor/destructor.
00148       method->AppendBodyText(text);
00149     }
00150     else if (mSecondaryState == eXBL_InGetter ||
00151              mSecondaryState == eXBL_InSetter) {
00152       // Get the text and add it to the getter/setter
00153       if (mSecondaryState == eXBL_InGetter)
00154         mProperty->AppendGetterText(text);
00155       else
00156         mProperty->AppendSetterText(text);
00157     }
00158     else if (mSecondaryState == eXBL_InBody) {
00159       // Get the text and add it to the method
00160       if (mMethod)
00161         mMethod->AppendBodyText(text);
00162     }
00163     else if (mSecondaryState == eXBL_InField) {
00164       // Get the text and add it to the method
00165       mField->AppendFieldText(text);
00166     }
00167     mTextLength = 0;
00168     if (aDidFlush)
00169       *aDidFlush = PR_TRUE;
00170     return NS_OK;
00171   }
00172 
00173   nsIContent* content = GetCurrentContent();
00174   if (content && (content->GetNodeInfo()->NamespaceEquals(kNameSpaceID_XBL) || (
00175       content->GetNodeInfo()->NamespaceEquals(kNameSpaceID_XUL) &&
00176       content->Tag() != nsXULAtoms::label &&
00177       content->Tag() != nsXULAtoms::description))) {
00178 
00179     PRBool isWS = PR_TRUE;
00180     if (mTextLength > 0) {
00181       const PRUnichar* cp = mText;
00182       const PRUnichar* end = mText + mTextLength;
00183       while (cp < end) {
00184         PRUnichar ch = *cp++;
00185         if (!XP_IS_SPACE(ch)) {
00186           isWS = PR_FALSE;
00187           break;
00188         }
00189       }
00190     }
00191 
00192     if (isWS && mTextLength > 0) {
00193       mTextLength = 0;
00194       if (aDidFlush)
00195         *aDidFlush = PR_TRUE;
00196       return NS_OK;
00197     }
00198   }
00199 
00200   return nsXMLContentSink::FlushText(aCreateTextNode, aDidFlush);
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsXBLContentSink::ReportError(const PRUnichar* aErrorText, 
00205                               const PRUnichar* aSourceText)
00206 {
00207   // XXX We should make sure the binding has no effect, but that it also
00208   // gets destroyed properly without leaking.  Perhaps we should even
00209   // ensure that the content that was bound is displayed with no
00210   // binding.
00211 
00212   // Report the error to the error console.
00213   nsCOMPtr<nsIConsoleService> consoleService =
00214     do_GetService(NS_CONSOLESERVICE_CONTRACTID);
00215   if (consoleService) {
00216     // XXX It would be nice if the parser didn't pre-format it for us,
00217     // because then we could create a instance of nsIScriptError and the
00218     // error console would format this much more nicely for us.
00219     // However, that would require duplicating even more code between
00220     // the XML content sink and the XUL content sink.
00221 
00222     consoleService->LogStringMessage(aErrorText);
00223   }
00224 
00225 #ifdef DEBUG
00226   // Report the error to stderr.
00227   fprintf(stderr,
00228           "\n%s\n%s\n\n",
00229           NS_LossyConvertUCS2toASCII(aErrorText).get(),
00230           NS_LossyConvertUCS2toASCII(aSourceText).get());
00231 #endif
00232 
00233   // Most of what this does won't be too useful, but whatever...
00234   return nsXMLContentSink::ReportError(aErrorText, aSourceText);
00235 }
00236 
00237 nsresult
00238 nsXBLContentSink::ReportUnexpectedElement(nsIAtom* aElementName,
00239                                           PRUint32 aLineNumber)
00240 {
00241   // XXX we should really somehow stop the parse and drop the binding
00242   // instead of just letting the XML sink build the content model like
00243   // we do...
00244   mState = eXBL_Error;
00245   nsAutoString elementName;
00246   aElementName->ToString(elementName);
00247 
00248   const PRUnichar* params[] = { elementName.get() };
00249 
00250   return nsContentUtils::ReportToConsole(nsContentUtils::eXBL_PROPERTIES,
00251                                          "UnexpectedElement",
00252                                          params, NS_ARRAY_LENGTH(params),
00253                                          mDocumentURI,
00254                                          EmptyString() /* source line */,
00255                                          aLineNumber, 0 /* column number */,
00256                                          nsIScriptError::errorFlag,
00257                                          "XBL Content Sink");
00258 }
00259 
00260 void
00261 nsXBLContentSink::AddMember(nsXBLProtoImplMember* aMember)
00262 {
00263   // Add this member to our chain.
00264   if (mImplMember)
00265     mImplMember->SetNext(aMember); // Already have a chain. Just append to the end.
00266   else
00267     mImplementation->SetMemberList(aMember); // We're the first member in the chain.
00268 
00269   mImplMember = aMember; // Adjust our pointer to point to the new last member in the chain.
00270 }
00271 
00272 NS_IMETHODIMP 
00273 nsXBLContentSink::HandleStartElement(const PRUnichar *aName, 
00274                                      const PRUnichar **aAtts, 
00275                                      PRUint32 aAttsCount, 
00276                                      PRInt32 aIndex, 
00277                                      PRUint32 aLineNumber)
00278 {
00279   nsresult rv = nsXMLContentSink::HandleStartElement(aName,aAtts,aAttsCount,aIndex,aLineNumber);
00280   if (NS_FAILED(rv))
00281     return rv;
00282 
00283   if (mState == eXBL_InBinding && !mBinding) {
00284     rv = ConstructBinding();
00285     if (NS_FAILED(rv))
00286       return rv;
00287     
00288     // mBinding may still be null, if the binding had no id.  If so,
00289     // we'll deal with that later in the sink.
00290   }
00291 
00292   return rv;
00293 }
00294 
00295 NS_IMETHODIMP 
00296 nsXBLContentSink::HandleEndElement(const PRUnichar *aName)
00297 {
00298   FlushText();
00299 
00300   if (mState != eXBL_InDocument) {
00301     PRInt32 nameSpaceID;
00302     nsCOMPtr<nsIAtom> prefix, localName;
00303     nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix),
00304                                    getter_AddRefs(localName), &nameSpaceID);
00305 
00306     if (nameSpaceID == kNameSpaceID_XBL) {
00307       if (mState == eXBL_Error) {
00308         // Check whether we've opened this tag before; we may not have if
00309         // it was a real XBL tag before the error occured.
00310         if (!GetCurrentContent()->GetNodeInfo()->Equals(localName,
00311                                                         nameSpaceID)) {
00312           // OK, this tag was never opened as far as the XML sink is
00313           // concerned.  Just drop the HandleEndElement
00314           return NS_OK;
00315         }
00316       }
00317       else if (mState == eXBL_InHandlers) {
00318         if (localName == nsXBLAtoms::handlers) {
00319           mState = eXBL_InBinding;
00320           mHandler = nsnull;
00321         }
00322         else if (localName == nsXBLAtoms::handler)
00323           mSecondaryState = eXBL_None;
00324         return NS_OK;
00325       }
00326       else if (mState == eXBL_InResources) {
00327         if (localName == nsXBLAtoms::resources)
00328           mState = eXBL_InBinding;
00329         return NS_OK;
00330       }
00331       else if (mState == eXBL_InImplementation) {
00332         if (localName == nsXBLAtoms::implementation)
00333           mState = eXBL_InBinding;
00334         else if (localName == nsXBLAtoms::property) {
00335           mSecondaryState = eXBL_None;
00336           mProperty = nsnull;
00337         }
00338         else if (localName == nsXBLAtoms::method) {
00339           mSecondaryState = eXBL_None;
00340           mMethod = nsnull;
00341         }
00342         else if (localName == nsXBLAtoms::field) {
00343           mSecondaryState = eXBL_None;
00344           mField = nsnull;
00345         }
00346         else if (localName == nsXBLAtoms::constructor ||
00347                  localName == nsXBLAtoms::destructor)
00348           mSecondaryState = eXBL_None;
00349         else if (localName == nsXBLAtoms::getter ||
00350                  localName == nsXBLAtoms::setter)
00351           mSecondaryState = eXBL_InProperty;
00352         else if (localName == nsXBLAtoms::parameter ||
00353                  localName == nsXBLAtoms::body)
00354           mSecondaryState = eXBL_InMethod;
00355         return NS_OK;
00356       }
00357       else if (mState == eXBL_InBindings &&
00358                localName == nsXBLAtoms::bindings) {
00359         mState = eXBL_InDocument;
00360       }
00361       
00362       nsresult rv = nsXMLContentSink::HandleEndElement(aName);
00363       if (NS_FAILED(rv))
00364         return rv;
00365 
00366       if (mState == eXBL_InBinding && localName == nsXBLAtoms::binding) {
00367         mState = eXBL_InBindings;
00368         if (mBinding) {  // See comment in HandleStartElement()
00369           mBinding->Initialize();
00370           mBinding = nsnull; // Clear our current binding ref.
00371         }
00372       }
00373 
00374       return NS_OK;
00375     }
00376   }
00377 
00378   return nsXMLContentSink::HandleEndElement(aName);
00379 }
00380 
00381 NS_IMETHODIMP 
00382 nsXBLContentSink::HandleCDataSection(const PRUnichar *aData, 
00383                                      PRUint32 aLength)
00384 {
00385   if (mState == eXBL_InHandlers || mState == eXBL_InImplementation)
00386     return AddText(aData, aLength);
00387   return nsXMLContentSink::HandleCDataSection(aData, aLength);
00388 }
00389 
00390 #define ENSURE_XBL_STATE(_cond)                                                       \
00391   PR_BEGIN_MACRO                                                                      \
00392     if (!(_cond)) { ReportUnexpectedElement(aTagName, aLineNumber); return PR_TRUE; } \
00393   PR_END_MACRO
00394 
00395 PRBool 
00396 nsXBLContentSink::OnOpenContainer(const PRUnichar **aAtts, 
00397                                   PRUint32 aAttsCount, 
00398                                   PRInt32 aNameSpaceID, 
00399                                   nsIAtom* aTagName,
00400                                   PRUint32 aLineNumber)
00401 {
00402   if (mState == eXBL_Error) {
00403     return PR_TRUE;
00404   }
00405   
00406   if (aNameSpaceID != kNameSpaceID_XBL) {
00407     // Construct non-XBL nodes
00408     return PR_TRUE;
00409   }
00410 
00411   PRBool ret = PR_TRUE;
00412   if (aTagName == nsXBLAtoms::bindings) {
00413     ENSURE_XBL_STATE(mState == eXBL_InDocument);
00414       
00415     NS_NewXBLDocumentInfo(mDocument, &mDocInfo);
00416     if (!mDocInfo) {
00417       mState = eXBL_Error;
00418       return PR_TRUE;
00419     }
00420 
00421     mDocument->BindingManager()->PutXBLDocumentInfo(mDocInfo);
00422 
00423     nsIURI *uri = mDocument->GetDocumentURI();
00424       
00425     PRBool isChrome = PR_FALSE;
00426     PRBool isRes = PR_FALSE;
00427 
00428     uri->SchemeIs("chrome", &isChrome);
00429     uri->SchemeIs("resource", &isRes);
00430     mIsChromeOrResource = isChrome || isRes;
00431       
00432     nsIXBLDocumentInfo* info = mDocInfo;
00433     NS_RELEASE(info); // We keep a weak ref. We've created a cycle between doc/binding manager/doc info.
00434     mState = eXBL_InBindings;
00435   }
00436   else if (aTagName == nsXBLAtoms::binding) {
00437     ENSURE_XBL_STATE(mState == eXBL_InBindings);
00438     mState = eXBL_InBinding;
00439   }
00440   else if (aTagName == nsXBLAtoms::handlers) {
00441     ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
00442     mState = eXBL_InHandlers;
00443     ret = PR_FALSE;
00444   }
00445   else if (aTagName == nsXBLAtoms::handler) {
00446     ENSURE_XBL_STATE(mState == eXBL_InHandlers);
00447     mSecondaryState = eXBL_InHandler;
00448     ConstructHandler(aAtts, aLineNumber);
00449     ret = PR_FALSE;
00450   }
00451   else if (aTagName == nsXBLAtoms::resources) {
00452     ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
00453     mState = eXBL_InResources;
00454     // Note that this mState will cause us to return false, so no need
00455     // to set ret to false.
00456   }
00457   else if (aTagName == nsXBLAtoms::stylesheet || aTagName == nsXBLAtoms::image) {
00458     ENSURE_XBL_STATE(mState == eXBL_InResources);
00459     NS_ASSERTION(mBinding, "Must have binding here");
00460     ConstructResource(aAtts, aTagName);
00461   }
00462   else if (aTagName == nsXBLAtoms::implementation) {
00463     ENSURE_XBL_STATE(mState == eXBL_InBinding && mBinding);
00464     mState = eXBL_InImplementation;
00465     ConstructImplementation(aAtts);
00466     // Note that this mState will cause us to return false, so no need
00467     // to set ret to false.
00468   }
00469   else if (aTagName == nsXBLAtoms::constructor) {
00470     ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
00471                      mSecondaryState == eXBL_None);
00472     NS_ASSERTION(mBinding, "Must have binding here");
00473       
00474     mSecondaryState = eXBL_InConstructor;
00475     nsXBLProtoImplAnonymousMethod* newMethod =
00476       new nsXBLProtoImplAnonymousMethod();
00477     if (newMethod) {
00478       newMethod->SetLineNumber(aLineNumber);
00479       mBinding->SetConstructor(newMethod);
00480       AddMember(newMethod);
00481     }
00482   }
00483   else if (aTagName == nsXBLAtoms::destructor) {
00484     ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
00485                      mSecondaryState == eXBL_None);
00486     NS_ASSERTION(mBinding, "Must have binding here");
00487     mSecondaryState = eXBL_InDestructor;
00488     nsXBLProtoImplAnonymousMethod* newMethod =
00489       new nsXBLProtoImplAnonymousMethod();
00490     if (newMethod) {
00491       newMethod->SetLineNumber(aLineNumber);
00492       mBinding->SetDestructor(newMethod);
00493       AddMember(newMethod);
00494     }
00495   }
00496   else if (aTagName == nsXBLAtoms::field) {
00497     ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
00498                      mSecondaryState == eXBL_None);
00499     NS_ASSERTION(mBinding, "Must have binding here");
00500     mSecondaryState = eXBL_InField;
00501     ConstructField(aAtts, aLineNumber);
00502   }
00503   else if (aTagName == nsXBLAtoms::property) {
00504     ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
00505                      mSecondaryState == eXBL_None);
00506     NS_ASSERTION(mBinding, "Must have binding here");
00507     mSecondaryState = eXBL_InProperty;
00508     ConstructProperty(aAtts);
00509   }
00510   else if (aTagName == nsXBLAtoms::getter) {
00511     ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
00512     NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
00513     mProperty->SetGetterLineNumber(aLineNumber);
00514     mSecondaryState = eXBL_InGetter;
00515   }
00516   else if (aTagName == nsXBLAtoms::setter) {
00517     ENSURE_XBL_STATE(mSecondaryState == eXBL_InProperty && mProperty);
00518     NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
00519     mProperty->SetSetterLineNumber(aLineNumber);
00520     mSecondaryState = eXBL_InSetter;
00521   }
00522   else if (aTagName == nsXBLAtoms::method) {
00523     ENSURE_XBL_STATE(mState == eXBL_InImplementation &&
00524                      mSecondaryState == eXBL_None);
00525     NS_ASSERTION(mBinding, "Must have binding here");
00526     mSecondaryState = eXBL_InMethod;
00527     ConstructMethod(aAtts);
00528   }
00529   else if (aTagName == nsXBLAtoms::parameter) {
00530     ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
00531     NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
00532     ConstructParameter(aAtts);
00533   }
00534   else if (aTagName == nsXBLAtoms::body) {
00535     ENSURE_XBL_STATE(mSecondaryState == eXBL_InMethod && mMethod);
00536     NS_ASSERTION(mState == eXBL_InImplementation, "Unexpected state");
00537     // stash away the line number
00538     mMethod->SetLineNumber(aLineNumber);
00539     mSecondaryState = eXBL_InBody;
00540   }
00541 
00542   return ret && mState != eXBL_InResources && mState != eXBL_InImplementation;
00543 }
00544 
00545 #undef ENSURE_XBL_STATE
00546 
00547 nsresult
00548 nsXBLContentSink::ConstructBinding()
00549 {
00550   nsCOMPtr<nsIContent> binding = GetCurrentContent();
00551   nsAutoString id;
00552   binding->GetAttr(kNameSpaceID_None, nsHTMLAtoms::id, id);
00553   NS_ConvertUTF16toUTF8 cid(id);
00554 
00555   nsresult rv = NS_OK;
00556   
00557   if (!cid.IsEmpty()) {
00558     mBinding = new nsXBLPrototypeBinding();
00559     if (!mBinding)
00560       return NS_ERROR_OUT_OF_MEMORY;
00561       
00562     rv = mBinding->Init(cid, mDocInfo, binding);
00563     if (NS_SUCCEEDED(rv)) {
00564       mDocInfo->SetPrototypeBinding(cid, mBinding);
00565       binding->UnsetAttr(kNameSpaceID_None, nsHTMLAtoms::id, PR_FALSE);
00566     } else {
00567       delete mBinding;
00568       mBinding = nsnull;
00569     }
00570   }
00571 
00572   return rv;
00573 }
00574 
00575 static PRBool
00576 FindValue(const PRUnichar **aAtts, nsIAtom *aAtom, const PRUnichar **aResult)
00577 {
00578   nsCOMPtr<nsIAtom> prefix, localName;
00579   for (; *aAtts; aAtts += 2) {
00580     PRInt32 nameSpaceID;
00581     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
00582                                    getter_AddRefs(localName), &nameSpaceID);
00583 
00584     // Is this attribute one of the ones we care about?
00585     if (nameSpaceID == kNameSpaceID_None && localName == aAtom) {
00586       *aResult = aAtts[1];
00587 
00588       return PR_TRUE;
00589     }
00590   }
00591 
00592   return PR_FALSE;
00593 }
00594 
00595 void
00596 nsXBLContentSink::ConstructHandler(const PRUnichar **aAtts, PRUint32 aLineNumber)
00597 {
00598   const PRUnichar* event          = nsnull;
00599   const PRUnichar* modifiers      = nsnull;
00600   const PRUnichar* button         = nsnull;
00601   const PRUnichar* clickcount     = nsnull;
00602   const PRUnichar* keycode        = nsnull;
00603   const PRUnichar* charcode       = nsnull;
00604   const PRUnichar* phase          = nsnull;
00605   const PRUnichar* command        = nsnull;
00606   const PRUnichar* action         = nsnull;
00607   const PRUnichar* group          = nsnull;
00608   const PRUnichar* preventdefault = nsnull;
00609   const PRUnichar* allowuntrusted = nsnull;
00610 
00611   nsCOMPtr<nsIAtom> prefix, localName;
00612   for (; *aAtts; aAtts += 2) {
00613     PRInt32 nameSpaceID;
00614     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
00615                                    getter_AddRefs(localName), &nameSpaceID);
00616 
00617     if (nameSpaceID != kNameSpaceID_None) {
00618       continue;
00619     }
00620 
00621     // Is this attribute one of the ones we care about?
00622     if (localName == nsXBLAtoms::event)
00623       event = aAtts[1];
00624     else if (localName == nsXBLAtoms::modifiers)
00625       modifiers = aAtts[1];
00626     else if (localName == nsXBLAtoms::button)
00627       button = aAtts[1];
00628     else if (localName == nsXBLAtoms::clickcount)
00629       clickcount = aAtts[1];
00630     else if (localName == nsXBLAtoms::keycode)
00631       keycode = aAtts[1];
00632     else if (localName == nsXBLAtoms::key || localName == nsXBLAtoms::charcode)
00633       charcode = aAtts[1];
00634     else if (localName == nsXBLAtoms::phase)
00635       phase = aAtts[1];
00636     else if (localName == nsXBLAtoms::command)
00637       command = aAtts[1];
00638     else if (localName == nsXBLAtoms::action)
00639       action = aAtts[1];
00640     else if (localName == nsXBLAtoms::group)
00641       group = aAtts[1];
00642     else if (localName == nsXBLAtoms::preventdefault)
00643       preventdefault = aAtts[1];
00644     else if (localName == nsXBLAtoms::allowuntrusted)
00645       allowuntrusted = aAtts[1];
00646   }
00647 
00648   if (command && !mIsChromeOrResource) {
00649     // Make sure the XBL doc is chrome or resource if we have a command
00650     // shorthand syntax.
00651     mState = eXBL_Error;
00652     return; // Don't even make this handler.
00653   }
00654 
00655   // All of our pointers are now filled in. Construct our handler with all of
00656   // these parameters.
00657   nsXBLPrototypeHandler* newHandler;
00658   newHandler = new nsXBLPrototypeHandler(event, phase, action, command,
00659                                          keycode, charcode, modifiers, button,
00660                                          clickcount, group, preventdefault,
00661                                          allowuntrusted, mBinding);
00662 
00663   if (newHandler) {
00664     newHandler->SetLineNumber(aLineNumber);
00665     
00666     // Add this handler to our chain of handlers.
00667     if (mHandler) {
00668       // Already have a chain. Just append to the end.
00669       mHandler->SetNextHandler(newHandler);
00670     }
00671     else {
00672       // We're the first handler in the chain.
00673       mBinding->SetPrototypeHandlers(newHandler);
00674     }
00675     // Adjust our mHandler pointer to point to the new last handler in the
00676     // chain.
00677     mHandler = newHandler;
00678   } else {
00679     mState = eXBL_Error;
00680   }
00681 }
00682 
00683 void
00684 nsXBLContentSink::ConstructResource(const PRUnichar **aAtts,
00685                                     nsIAtom* aResourceType)
00686 {
00687   if (!mBinding)
00688     return;
00689 
00690   const PRUnichar* src = nsnull;
00691   if (FindValue(aAtts, nsHTMLAtoms::src, &src)) {
00692     mBinding->AddResource(aResourceType, nsDependentString(src));
00693   }
00694 }
00695 
00696 void
00697 nsXBLContentSink::ConstructImplementation(const PRUnichar **aAtts)
00698 {
00699   mImplementation = nsnull;
00700   mImplMember = nsnull;
00701       
00702   if (!mBinding)
00703     return;
00704 
00705   const PRUnichar* name = nsnull;
00706 
00707   nsCOMPtr<nsIAtom> prefix, localName;
00708   for (; *aAtts; aAtts += 2) {
00709     PRInt32 nameSpaceID;
00710     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
00711                                    getter_AddRefs(localName), &nameSpaceID);
00712 
00713     if (nameSpaceID != kNameSpaceID_None) {
00714       continue;
00715     }
00716 
00717     // Is this attribute one of the ones we care about?
00718     if (localName == nsXBLAtoms::name) {
00719       name = aAtts[1];
00720     }
00721     else if (localName == nsXBLAtoms::implements) {
00722       // Only allow implementation of interfaces via XBL if the principal of
00723       // our XBL document has UniversalXPConnect privileges.  No principal
00724       // means no privs!
00725       
00726       nsIPrincipal* principal = mDocument->GetPrincipal();
00727       if (principal) {
00728         // XXX this api is so badly tied to JS it's not even funny.  We don't
00729         // have a concept of enabling capabilities on a per-principal basis,
00730         // but only on a per-principal-and-JS-stackframe basis!  So for now
00731         // this is basically equivalent to testing that we have the system
00732         // principal, since there is no JS stackframe in sight here...
00733         PRBool hasUniversalXPConnect;
00734         nsresult rv = principal->IsCapabilityEnabled("UniversalXPConnect",
00735                                                      nsnull,
00736                                                      &hasUniversalXPConnect);
00737         if (NS_SUCCEEDED(rv) && hasUniversalXPConnect) {
00738           mBinding->ConstructInterfaceTable(nsDependentString(aAtts[1]));
00739         }
00740       }
00741     }
00742   }
00743 
00744   NS_NewXBLProtoImpl(mBinding, name, &mImplementation);
00745 }
00746 
00747 void
00748 nsXBLContentSink::ConstructField(const PRUnichar **aAtts, PRUint32 aLineNumber)
00749 {
00750   const PRUnichar* name     = nsnull;
00751   const PRUnichar* readonly = nsnull;
00752 
00753   nsCOMPtr<nsIAtom> prefix, localName;
00754   for (; *aAtts; aAtts += 2) {
00755     PRInt32 nameSpaceID;
00756     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
00757                                    getter_AddRefs(localName), &nameSpaceID);
00758 
00759     if (nameSpaceID != kNameSpaceID_None) {
00760       continue;
00761     }
00762 
00763     // Is this attribute one of the ones we care about?
00764     if (localName == nsXBLAtoms::name) {
00765       name = aAtts[1];
00766     }
00767     else if (localName == nsXBLAtoms::readonly) {
00768       readonly = aAtts[1];
00769     }
00770   }
00771 
00772   // All of our pointers are now filled in. Construct our field with all of
00773   // these parameters.
00774   mField = new nsXBLProtoImplField(name, readonly);
00775   if (mField) {
00776     mField->SetLineNumber(aLineNumber);
00777     AddMember(mField);
00778   }
00779 }
00780 
00781 void
00782 nsXBLContentSink::ConstructProperty(const PRUnichar **aAtts)
00783 {
00784   const PRUnichar* name     = nsnull;
00785   const PRUnichar* readonly = nsnull;
00786   const PRUnichar* onget    = nsnull;
00787   const PRUnichar* onset    = nsnull;
00788 
00789   nsCOMPtr<nsIAtom> prefix, localName;
00790   for (; *aAtts; aAtts += 2) {
00791     PRInt32 nameSpaceID;
00792     nsContentUtils::SplitExpatName(aAtts[0], getter_AddRefs(prefix),
00793                                    getter_AddRefs(localName), &nameSpaceID);
00794 
00795     if (nameSpaceID != kNameSpaceID_None) {
00796       continue;
00797     }
00798 
00799     // Is this attribute one of the ones we care about?
00800     if (localName == nsXBLAtoms::name) {
00801       name = aAtts[1];
00802     }
00803     else if (localName == nsXBLAtoms::readonly) {
00804       readonly = aAtts[1];
00805     }
00806     else if (localName == nsXBLAtoms::onget) {
00807       onget = aAtts[1];
00808     }
00809     else if (localName == nsXBLAtoms::onset) {
00810       onset = aAtts[1];
00811     }
00812   }
00813 
00814   if (name) {
00815     // All of our pointers are now filled in. Construct our property with all of
00816     // these parameters.
00817     mProperty = new nsXBLProtoImplProperty(name, onget, onset, readonly);
00818     if (mProperty) {
00819       AddMember(mProperty);
00820     }
00821   }
00822 }
00823 
00824 void
00825 nsXBLContentSink::ConstructMethod(const PRUnichar **aAtts)
00826 {
00827   mMethod = nsnull;
00828 
00829   const PRUnichar* name = nsnull;
00830   if (FindValue(aAtts, nsXBLAtoms::name, &name)) {
00831     mMethod = new nsXBLProtoImplMethod(name);
00832   }
00833 
00834   if (mMethod) {
00835     AddMember(mMethod);
00836   }
00837 }
00838 
00839 void
00840 nsXBLContentSink::ConstructParameter(const PRUnichar **aAtts)
00841 {
00842   if (!mMethod)
00843     return;
00844 
00845   const PRUnichar* name = nsnull;
00846   if (FindValue(aAtts, nsXBLAtoms::name, &name)) {
00847     mMethod->AddParameter(nsDependentString(name));
00848   }
00849 }
00850 
00851 nsresult
00852 nsXBLContentSink::CreateElement(const PRUnichar** aAtts, PRUint32 aAttsCount,
00853                                 nsINodeInfo* aNodeInfo, PRUint32 aLineNumber,
00854                                 nsIContent** aResult, PRBool* aAppendContent)
00855 {
00856 #ifdef MOZ_XUL
00857   if (!aNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
00858 #endif
00859     return nsXMLContentSink::CreateElement(aAtts, aAttsCount, aNodeInfo,
00860                                            aLineNumber, aResult,
00861                                            aAppendContent);
00862 #ifdef MOZ_XUL
00863   }
00864 
00865   *aAppendContent = PR_TRUE;
00866   nsXULPrototypeElement* prototype = new nsXULPrototypeElement();
00867   if (!prototype)
00868     return NS_ERROR_OUT_OF_MEMORY;
00869 
00870   prototype->mNodeInfo = aNodeInfo;
00871 
00872   AddAttributesToXULPrototype(aAtts, aAttsCount, prototype);
00873 
00874   nsresult rv = nsXULElement::Create(prototype, mDocument, PR_FALSE, aResult);
00875 
00876   // XUL prototype elements start with a refcnt of 1 to represent
00877   // ownership by the XUL prototype document.  In our case we have no
00878   // prototype document, so release that reference.  The Create call
00879   // above took a reference.
00880   prototype->Release();
00881 
00882   return rv;
00883 #endif
00884 }
00885 
00886 nsresult 
00887 nsXBLContentSink::AddAttributes(const PRUnichar** aAtts,
00888                                 nsIContent* aContent)
00889 {
00890   if (aContent->IsContentOfType(nsIContent::eXUL))
00891     return NS_OK; // Nothing to do, since the proto already has the attrs.
00892 
00893   return nsXMLContentSink::AddAttributes(aAtts, aContent);
00894 }
00895 
00896 #ifdef MOZ_XUL
00897 nsresult
00898 nsXBLContentSink::AddAttributesToXULPrototype(const PRUnichar **aAtts, 
00899                                               PRUint32 aAttsCount, 
00900                                               nsXULPrototypeElement* aElement)
00901 {
00902   // Add tag attributes to the element
00903   nsresult rv;
00904 
00905   // Create storage for the attributes
00906   nsXULPrototypeAttribute* attrs = nsnull;
00907   if (aAttsCount > 0) {
00908     attrs = new nsXULPrototypeAttribute[aAttsCount];
00909     if (!attrs)
00910       return NS_ERROR_OUT_OF_MEMORY;
00911   }
00912 
00913   aElement->mAttributes    = attrs;
00914   aElement->mNumAttributes = aAttsCount;
00915 
00916   // Copy the attributes into the prototype
00917   nsCOMPtr<nsIAtom> prefix, localName;
00918 
00919   PRUint32 i;  
00920   for (i = 0; i < aAttsCount; ++i) {
00921     PRInt32 nameSpaceID;
00922     nsContentUtils::SplitExpatName(aAtts[i * 2], getter_AddRefs(prefix),
00923                                    getter_AddRefs(localName), &nameSpaceID);
00924 
00925     if (nameSpaceID == kNameSpaceID_None) {
00926       attrs[i].mName.SetTo(localName);
00927     }
00928     else {
00929       nsCOMPtr<nsINodeInfo> ni;
00930       mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID,
00931                                     getter_AddRefs(ni));
00932       attrs[i].mName.SetTo(ni);
00933     }
00934     
00935     rv = aElement->SetAttrAt(i, nsDependentString(aAtts[i * 2 + 1]),
00936                              mDocumentURI); 
00937     NS_ENSURE_SUCCESS(rv, rv);
00938   }
00939 
00940   return NS_OK;
00941 }
00942 #endif