Back to index

lightning-sunbird  0.9+nobinonly
nsXULDocument.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim: set ts=4 sw=4 et tw=80: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Chris Waterson <waterson@netscape.com>
00025  *   Ben Goodger <ben@netscape.com>
00026  *   Pete Collins <petejc@collab.net>
00027  *   Dan Rosen <dr@netscape.com>
00028  *   Johnny Stenback <jst@netscape.com>
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK ***** */
00043 
00044 /*
00045 
00046   An implementation for the XUL document. This implementation serves
00047   as the basis for generating an NGLayout content model.
00048 
00049   Notes
00050   -----
00051 
00052   1. We do some monkey business in the document observer methods to`
00053      keep the element map in sync for HTML elements. Why don't we just
00054      do it for _all_ elements? Well, in the case of XUL elements,
00055      which may be lazily created during frame construction, the
00056      document observer methods will never be called because we'll be
00057      adding the XUL nodes into the content model "quietly".
00058 
00059   2. The "element map" maps an RDF resource to the elements whose 'id'
00060      or 'ref' attributes refer to that resource. We re-use the element
00061      map to support the HTML-like 'getElementById()' method.
00062 
00063 */
00064 
00065 // Note the ALPHABETICAL ORDERING
00066 #include "nsXULDocument.h"
00067 
00068 #include "nsDOMError.h"
00069 #include "nsIBoxObject.h"
00070 #include "nsIChromeRegistry.h"
00071 #include "nsIContentSink.h" // for NS_CONTENT_ID_COUNTER_BASE
00072 #include "nsIScrollableView.h"
00073 #include "nsIContentViewer.h"
00074 #include "nsGUIEvent.h"
00075 #include "nsIDOMNSUIEvent.h"
00076 #include "nsIDOMXULElement.h"
00077 #include "nsIPrivateDOMEvent.h"
00078 #include "nsIRDFNode.h"
00079 #include "nsIRDFRemoteDataSource.h"
00080 #include "nsIRDFService.h"
00081 #include "nsIStreamListener.h"
00082 #include "nsITextContent.h"
00083 #include "nsITimer.h"
00084 #include "nsIDocShell.h"
00085 #include "nsXULAtoms.h"
00086 #include "nsHTMLAtoms.h"
00087 #include "nsIXULContentSink.h"
00088 #include "nsXULContentUtils.h"
00089 #include "nsIXULOverlayProvider.h"
00090 #include "nsIXULPrototypeCache.h"
00091 #include "nsNetUtil.h"
00092 #include "nsParserCIID.h"
00093 #include "nsPIBoxObject.h"
00094 #include "nsRDFCID.h"
00095 #include "nsILocalStore.h"
00096 #include "nsXPIDLString.h"
00097 #include "nsPIDOMWindow.h"
00098 #include "nsXULCommandDispatcher.h"
00099 #include "nsXULDocument.h"
00100 #include "nsXULElement.h"
00101 #include "prlog.h"
00102 #include "rdf.h"
00103 #include "nsIFrame.h"
00104 #include "nsIXBLService.h"
00105 #include "nsCExternalHandlerService.h"
00106 #include "nsMimeTypes.h"
00107 #include "nsIFastLoadService.h"
00108 #include "nsIObjectInputStream.h"
00109 #include "nsIObjectOutputStream.h"
00110 #include "nsIFocusController.h"
00111 #include "nsContentList.h"
00112 #include "nsIScriptGlobalObject.h"
00113 #include "nsIScriptGlobalObjectOwner.h"
00114 #include "nsIScriptSecurityManager.h"
00115 #include "nsNodeInfoManager.h"
00116 #include "nsContentCreatorFunctions.h"
00117 #include "nsContentUtils.h"
00118 #include "nsIParser.h"
00119 #include "nsIParserService.h"
00120 #include "nsICSSStyleSheet.h"
00121 #include "nsIScriptError.h"
00122 
00123 //----------------------------------------------------------------------
00124 //
00125 // CIDs
00126 //
00127 
00128 static NS_DEFINE_CID(kLocalStoreCID,             NS_LOCALSTORE_CID);
00129 static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
00130 static NS_DEFINE_CID(kRDFServiceCID,             NS_RDFSERVICE_CID);
00131 static NS_DEFINE_CID(kXULPrototypeCacheCID,      NS_XULPROTOTYPECACHE_CID);
00132 
00133 static PRBool IsChromeURI(nsIURI* aURI)
00134 {
00135     // why is this check a member function of nsXULDocument? -gagan
00136     PRBool isChrome = PR_FALSE;
00137     if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome)
00138         return PR_TRUE;
00139     return PR_FALSE;
00140 }
00141 
00142 //----------------------------------------------------------------------
00143 //
00144 // Miscellaneous Constants
00145 //
00146 
00147 const nsForwardReference::Phase nsForwardReference::kPasses[] = {
00148     nsForwardReference::eConstruction,
00149     nsForwardReference::eHookup,
00150     nsForwardReference::eDone
00151 };
00152 
00153 const PRUint32 kMaxAttrNameLength = 512;
00154 const PRUint32 kMaxAttributeLength = 4096;
00155 
00156 //----------------------------------------------------------------------
00157 //
00158 // Statics
00159 //
00160 
00161 PRInt32 nsXULDocument::gRefCnt = 0;
00162 
00163 nsIRDFService* nsXULDocument::gRDFService;
00164 nsIRDFResource* nsXULDocument::kNC_persist;
00165 nsIRDFResource* nsXULDocument::kNC_attribute;
00166 nsIRDFResource* nsXULDocument::kNC_value;
00167 
00168 nsIXULPrototypeCache* nsXULDocument::gXULCache;
00169 
00170 PRLogModuleInfo* nsXULDocument::gXULLog;
00171 
00172 class nsProxyLoadStream : public nsIInputStream
00173 {
00174 private:
00175     const char* mBuffer;
00176     PRUint32    mSize;
00177     PRUint32    mIndex;
00178 
00179 public:
00180     nsProxyLoadStream(void) : mBuffer(nsnull)
00181     {
00182     }
00183 
00184     virtual ~nsProxyLoadStream(void)
00185     {
00186     }
00187 
00188     // nsISupports
00189     NS_DECL_ISUPPORTS
00190 
00191     // nsIBaseStream
00192     NS_IMETHOD Close(void)
00193     {
00194         return NS_OK;
00195     }
00196 
00197     // nsIInputStream
00198     NS_IMETHOD Available(PRUint32 *aLength)
00199     {
00200         *aLength = mSize - mIndex;
00201         return NS_OK;
00202     }
00203 
00204     NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount)
00205     {
00206         PRUint32 readCount = 0;
00207         while (mIndex < mSize && aCount > 0) {
00208             *aBuf = mBuffer[mIndex];
00209             ++aBuf;
00210             ++mIndex;
00211             readCount++;
00212             --aCount;
00213         }
00214         *aReadCount = readCount;
00215         return NS_OK;
00216     }
00217 
00218     NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure,
00219                             PRUint32 count, PRUint32 *_retval)
00220     {
00221         NS_NOTREACHED("ReadSegments");
00222         return NS_ERROR_NOT_IMPLEMENTED;
00223     }
00224 
00225     NS_IMETHOD IsNonBlocking(PRBool *aNonBlocking)
00226     {
00227         *aNonBlocking = PR_TRUE;
00228         return NS_OK;
00229     }
00230 
00231     // Implementation
00232     void SetBuffer(const char* aBuffer, PRUint32 aSize)
00233     {
00234         mBuffer = aBuffer;
00235         mSize = aSize;
00236         mIndex = 0;
00237     }
00238 };
00239 
00240 NS_IMPL_ISUPPORTS1(nsProxyLoadStream, nsIInputStream)
00241 
00242 //----------------------------------------------------------------------
00243 //
00244 // PlaceholderRequest
00245 //
00246 //   This is a dummy request implementation that we add to the load
00247 //   group. It ensures that EndDocumentLoad() in the docshell doesn't
00248 //   fire before we've finished building the complete document content
00249 //   model.
00250 //
00251 
00252 class PlaceHolderRequest : public nsIChannel
00253 {
00254 protected:
00255     PlaceHolderRequest();
00256     virtual ~PlaceHolderRequest();
00257 
00258     static PRInt32 gRefCnt;
00259     static nsIURI* gURI;
00260 
00261     nsCOMPtr<nsILoadGroup> mLoadGroup;
00262 
00263 public:
00264     static nsresult
00265     Create(nsIRequest** aResult);
00266 
00267     NS_DECL_ISUPPORTS
00268 
00269     // nsIRequest
00270     NS_IMETHOD GetName(nsACString &result) {
00271         return NS_ERROR_NOT_IMPLEMENTED;
00272     }
00273     NS_IMETHOD IsPending(PRBool *_retval) { *_retval = PR_TRUE; return NS_OK; }
00274     NS_IMETHOD GetStatus(nsresult *status) { *status = NS_OK; return NS_OK; }
00275     NS_IMETHOD Cancel(nsresult status)  { return NS_OK; }
00276     NS_IMETHOD Suspend(void) { return NS_OK; }
00277     NS_IMETHOD Resume(void)  { return NS_OK; }
00278     NS_IMETHOD GetLoadGroup(nsILoadGroup * *aLoadGroup) { *aLoadGroup = mLoadGroup; NS_IF_ADDREF(*aLoadGroup); return NS_OK; }
00279     NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup) { mLoadGroup = aLoadGroup; return NS_OK; }
00280     NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_NORMAL; return NS_OK; }
00281     NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
00282 
00283     // nsIChannel
00284     NS_IMETHOD GetOriginalURI(nsIURI* *aOriginalURI) { *aOriginalURI = gURI; NS_ADDREF(*aOriginalURI); return NS_OK; }
00285     NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI) { gURI = aOriginalURI; NS_ADDREF(gURI); return NS_OK; }
00286     NS_IMETHOD GetURI(nsIURI* *aURI) { *aURI = gURI; NS_ADDREF(*aURI); return NS_OK; }
00287     NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; }
00288     NS_IMETHOD Open(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; }
00289     NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; }
00290     NS_IMETHOD GetOwner(nsISupports * *aOwner) { *aOwner = nsnull; return NS_OK; }
00291     NS_IMETHOD SetOwner(nsISupports * aOwner) { return NS_OK; }
00292     NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) { *aNotificationCallbacks = nsnull; return NS_OK; }
00293     NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks) { return NS_OK; }
00294     NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) { *aSecurityInfo = nsnull; return NS_OK; }
00295     NS_IMETHOD GetContentType(nsACString &aContentType) { aContentType.Truncate(); return NS_OK; }
00296     NS_IMETHOD SetContentType(const nsACString &aContentType) { return NS_OK; }
00297     NS_IMETHOD GetContentCharset(nsACString &aContentCharset) { aContentCharset.Truncate(); return NS_OK; }
00298     NS_IMETHOD SetContentCharset(const nsACString &aContentCharset) { return NS_OK; }
00299     NS_IMETHOD GetContentLength(PRInt32 *aContentLength) { return NS_OK; }
00300     NS_IMETHOD SetContentLength(PRInt32 aContentLength) { return NS_OK; }
00301 };
00302 
00303 PRInt32 PlaceHolderRequest::gRefCnt;
00304 nsIURI* PlaceHolderRequest::gURI;
00305 
00306 NS_IMPL_ADDREF(PlaceHolderRequest)
00307 NS_IMPL_RELEASE(PlaceHolderRequest)
00308 NS_IMPL_QUERY_INTERFACE2(PlaceHolderRequest, nsIRequest, nsIChannel)
00309 
00310 nsresult
00311 PlaceHolderRequest::Create(nsIRequest** aResult)
00312 {
00313     PlaceHolderRequest* request = new PlaceHolderRequest();
00314     if (! request)
00315         return NS_ERROR_OUT_OF_MEMORY;
00316 
00317     *aResult = request;
00318     NS_ADDREF(*aResult);
00319     return NS_OK;
00320 }
00321 
00322 
00323 PlaceHolderRequest::PlaceHolderRequest()
00324 {
00325 
00326     if (gRefCnt++ == 0) {
00327         nsresult rv;
00328         rv = NS_NewURI(&gURI, NS_LITERAL_CSTRING("about:xul-master-placeholder"), nsnull);
00329         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create about:xul-master-placeholder");
00330     }
00331 }
00332 
00333 
00334 PlaceHolderRequest::~PlaceHolderRequest()
00335 {
00336     if (--gRefCnt == 0) {
00337         NS_IF_RELEASE(gURI);
00338     }
00339 }
00340 
00341 //----------------------------------------------------------------------
00342 
00343 struct BroadcasterMapEntry : public PLDHashEntryHdr {
00344     nsIDOMElement*   mBroadcaster; // [WEAK]
00345     nsSmallVoidArray mListeners;   // [OWNING] of BroadcastListener objects
00346 };
00347 
00348 struct BroadcastListener {
00349     nsIDOMElement*    mListener; // [WEAK] XXXwaterson crash waiting to happen!
00350     nsCOMPtr<nsIAtom> mAttribute;
00351 };
00352 
00353 //----------------------------------------------------------------------
00354 //
00355 // ctors & dtors
00356 //
00357 
00358     // NOTE! nsDocument::operator new() zeroes out all members, so
00359     // don't bother initializing members to 0.
00360 
00361 nsXULDocument::nsXULDocument(void)
00362     : mResolutionPhase(nsForwardReference::eStart),
00363       mState(eState_Master)
00364 {
00365 
00366     // NOTE! nsDocument::operator new() zeroes out all members, so don't
00367     // bother initializing members to 0.
00368 
00369     // Override the default in nsDocument
00370     mCharacterSet.AssignLiteral("UTF-8");
00371 
00372     mDefaultElementType = kNameSpaceID_XUL;
00373 }
00374 
00375 nsXULDocument::~nsXULDocument()
00376 {
00377     mCommandDispatcher->Disconnect();
00378 
00379     NS_ASSERTION(mNextSrcLoadWaiter == nsnull,
00380         "unreferenced document still waiting for script source to load?");
00381 
00382     // Notify our observers here, we can't let the nsDocument
00383     // destructor do that for us since some of the observers are
00384     // deleted by the time we get there.
00385     NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentWillBeDestroyed, (this));
00386     mObservers.Clear();
00387 
00388     // In case we failed somewhere early on and the forward observer
00389     // decls never got resolved.
00390     DestroyForwardReferences();
00391 
00392     // Destroy our broadcaster map.
00393     if (mBroadcasterMap) {
00394         PL_DHashTableDestroy(mBroadcasterMap);
00395     }
00396 
00397     if (mLocalStore) {
00398         nsCOMPtr<nsIRDFRemoteDataSource> remote =
00399             do_QueryInterface(mLocalStore);
00400         if (remote)
00401             remote->Flush();
00402     }
00403 
00404     delete mTemplateBuilderTable;
00405 
00406     if (--gRefCnt == 0) {
00407         NS_IF_RELEASE(gRDFService);
00408 
00409         NS_IF_RELEASE(kNC_persist);
00410         NS_IF_RELEASE(kNC_attribute);
00411         NS_IF_RELEASE(kNC_value);
00412 
00413         if (gXULCache) {
00414             // Remove the current document here from the FastLoad table in
00415             // case the document did not make it past StartLayout in
00416             // ResumeWalk. The FastLoad table must be clear of entries so
00417             // that the FastLoad file footer can be properly written.
00418             if (mDocumentURI)
00419                 gXULCache->RemoveFromFastLoadSet(mDocumentURI);
00420 
00421             NS_RELEASE(gXULCache);
00422         }
00423     }
00424 
00425     // The destructor of nsDocument will delete references to style
00426     // sheets, but we don't want that if we're a popup document, so
00427     // then we'll clear the stylesheets array here to prevent that
00428     // from happening.
00429     if (mIsPopup) {
00430         mStyleSheets.Clear();
00431         mStyleAttrStyleSheet = nsnull;
00432         mAttrStyleSheet = nsnull;
00433     }
00434 
00435     // This is done in nsDocument::~nsDocument() too, but since this
00436     // call ends up calling back into the document through virtual
00437     // methods (nsIDocument::GetPrincipal()) we must do it here before
00438     // we go out of nsXULDocument's destructor.
00439     if (mNodeInfoManager) {
00440         mNodeInfoManager->DropDocumentReference();
00441     }
00442 }
00443 
00444 nsresult
00445 NS_NewXULDocument(nsIXULDocument** result)
00446 {
00447     NS_PRECONDITION(result != nsnull, "null ptr");
00448     if (! result)
00449         return NS_ERROR_NULL_POINTER;
00450 
00451     nsXULDocument* doc = new nsXULDocument();
00452     if (! doc)
00453         return NS_ERROR_OUT_OF_MEMORY;
00454 
00455     NS_ADDREF(doc);
00456 
00457     nsresult rv;
00458     if (NS_FAILED(rv = doc->Init())) {
00459         NS_RELEASE(doc);
00460         return rv;
00461     }
00462 
00463     *result = doc;
00464     return NS_OK;
00465 }
00466 
00467 
00468 //----------------------------------------------------------------------
00469 //
00470 // nsISupports interface
00471 //
00472 
00473 NS_IMPL_ADDREF_INHERITED(nsXULDocument, nsXMLDocument)
00474 NS_IMPL_RELEASE_INHERITED(nsXULDocument, nsXMLDocument)
00475 
00476 
00477 // QueryInterface implementation for nsXULDocument
00478 NS_INTERFACE_MAP_BEGIN(nsXULDocument)
00479     NS_INTERFACE_MAP_ENTRY(nsIXULDocument)
00480     NS_INTERFACE_MAP_ENTRY(nsIDOMXULDocument)
00481     NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
00482     NS_INTERFACE_MAP_ENTRY(nsIDOMXULDocument2)
00483     NS_INTERFACE_MAP_ENTRY(nsIDOMXULDocument_MOZILLA_1_8_BRANCH)
00484     NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XULDocument)
00485 NS_INTERFACE_MAP_END_INHERITING(nsXMLDocument)
00486 
00487 
00488 //----------------------------------------------------------------------
00489 //
00490 // nsIDocument interface
00491 //
00492 
00493 void
00494 nsXULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
00495 {
00496     NS_NOTREACHED("Reset");
00497 }
00498 
00499 void
00500 nsXULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup)
00501 {
00502     NS_NOTREACHED("ResetToURI");
00503 }
00504 
00505 // Override the nsDocument.cpp method to keep from returning the
00506 // "cached XUL" type which is completely internal and may confuse
00507 // people
00508 NS_IMETHODIMP
00509 nsXULDocument::GetContentType(nsAString& aContentType)
00510 {
00511     aContentType.AssignLiteral("application/vnd.mozilla.xul+xml");
00512     return NS_OK;
00513 }
00514 
00515 void
00516 nsXULDocument::SetContentType(const nsAString& aContentType)
00517 {
00518     NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
00519                  "xul-documents always has content-type application/vnd.mozilla.xul+xml");
00520     // Don't do anything, xul always has the mimetype
00521     // application/vnd.mozilla.xul+xml
00522 }
00523 
00524 nsresult
00525 nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
00526                                  nsILoadGroup* aLoadGroup,
00527                                  nsISupports* aContainer,
00528                                  nsIStreamListener **aDocListener,
00529                                  PRBool aReset, nsIContentSink* aSink)
00530 {
00531     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
00532 
00533     mDocumentTitle.SetIsVoid(PR_TRUE);
00534 
00535     mChannel = aChannel;
00536 
00537     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
00538     // XXXbz this code is repeated from nsDocument::Reset; we
00539     // really need to refactor this part better.
00540     nsLoadFlags loadFlags = 0;
00541     nsresult rv = aChannel->GetLoadFlags(&loadFlags);
00542     if (NS_SUCCEEDED(rv)) {
00543         if (loadFlags & nsIChannel::LOAD_REPLACE) {
00544             rv = aChannel->GetURI(getter_AddRefs(mDocumentURI));
00545         } else {
00546             rv = aChannel->GetOriginalURI(getter_AddRefs(mDocumentURI));
00547         }
00548     }
00549     NS_ENSURE_SUCCESS(rv, rv);
00550     
00551     rv = ResetStylesheetsToURI(mDocumentURI);
00552     if (NS_FAILED(rv)) return rv;
00553 
00554     RetrieveRelevantHeaders(aChannel);
00555 
00556     // Look in the chrome cache: we've got this puppy loaded
00557     // already.
00558     nsCOMPtr<nsIXULPrototypeDocument> proto;
00559     if (IsChromeURI(mDocumentURI))
00560         gXULCache->GetPrototype(mDocumentURI, getter_AddRefs(proto));
00561 
00562     // Same comment as nsChromeProtocolHandler::NewChannel and
00563     // nsXULDocument::ResumeWalk
00564     // - Ben Goodger
00565     //
00566     // We don't abort on failure here because there are too many valid
00567     // cases that can return failure, and the null-ness of |proto| is enough
00568     // to trigger the fail-safe parse-from-disk solution. Example failure cases
00569     // (for reference) include:
00570     //
00571     // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
00572     //                         parse from disk
00573     // other: the FastLoad cache file, XUL.mfl, could not be found, probably
00574     //        due to being accessed before a profile has been selected (e.g.
00575     //        loading chrome for the profile manager itself). This must be
00576     //        parsed from disk.
00577 
00578     if (proto) {
00579         // If we're racing with another document to load proto, wait till the
00580         // load has finished loading before trying to add cloned style sheets.
00581         // nsXULDocument::EndLoad will call proto->NotifyLoadDone, which will
00582         // find all racing documents and notify them via OnPrototypeLoadDone,
00583         // which will add style sheet clones to each document.
00584         PRBool loaded;
00585         rv = proto->AwaitLoadDone(this, &loaded);
00586         if (NS_FAILED(rv)) return rv;
00587 
00588         mMasterPrototype = mCurrentPrototype = proto;
00589 
00590         // Add cloned style sheet references only if the prototype has in
00591         // fact already loaded.  It may still be loading when we hit the XUL
00592         // prototype cache.
00593         if (loaded) {
00594             rv = AddPrototypeSheets();
00595             if (NS_FAILED(rv)) return rv;
00596         }
00597 
00598         // We need a listener, even if proto is not yet loaded, in which
00599         // event the listener's OnStopRequest method does nothing, and all
00600         // the interesting work happens below nsXULDocument::EndLoad, from
00601         // the call there to mCurrentPrototype->NotifyLoadDone().
00602         *aDocListener = new CachedChromeStreamListener(this, loaded);
00603         if (! *aDocListener)
00604             return NS_ERROR_OUT_OF_MEMORY;
00605     }
00606     else {
00607         PRBool useXULCache;
00608         gXULCache->GetEnabled(&useXULCache);
00609         PRBool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
00610 
00611 
00612         // It's just a vanilla document load. Create a parser to deal
00613         // with the stream n' stuff.
00614 
00615         nsCOMPtr<nsIParser> parser;
00616         rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
00617                            getter_AddRefs(parser));
00618         if (NS_FAILED(rv)) return rv;
00619 
00620         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
00621         // so we don't have to re-check whether the cache is enabled all
00622         // the time.
00623         mIsWritingFastLoad = useXULCache;
00624 
00625         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
00626         NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
00627         if (NS_FAILED(rv)) return rv;
00628 
00629         *aDocListener = listener;
00630 
00631         parser->Parse(mDocumentURI);
00632 
00633         // Put the current prototype, created under PrepareToLoad, into the
00634         // XUL prototype cache now.  We can't do this under PrepareToLoad or
00635         // overlay loading will break; search for PutPrototype in ResumeWalk
00636         // and see the comment there.
00637         if (fillXULCache) {
00638             rv = gXULCache->PutPrototype(mCurrentPrototype);
00639             if (NS_FAILED(rv)) return rv;
00640         }
00641     }
00642 
00643     NS_IF_ADDREF(*aDocListener);
00644     return NS_OK;
00645 }
00646 
00647 nsIPrincipal*
00648 nsXULDocument::GetPrincipal()
00649 {
00650     NS_ASSERTION(mMasterPrototype, "Missing master prototype. See bug 169036");
00651     NS_ENSURE_TRUE(mMasterPrototype, nsnull);
00652 
00653     return mMasterPrototype->GetDocumentPrincipal();
00654 }
00655 
00656 void
00657 nsXULDocument::SetPrincipal(nsIPrincipal *aPrincipal)
00658 {
00659     NS_NOTREACHED("SetPrincipal");
00660 }
00661 
00662 
00663 void
00664 nsXULDocument::EndLoad()
00665 {
00666     nsresult rv;
00667 
00668     // Whack the prototype document into the cache so that the next
00669     // time somebody asks for it, they don't need to load it by hand.
00670 
00671     nsCOMPtr<nsIURI> uri;
00672     rv = mCurrentPrototype->GetURI(getter_AddRefs(uri));
00673     if (NS_FAILED(rv)) return;
00674 
00675     PRBool isChrome = IsChromeURI(uri);
00676 
00677     // Remember if the XUL cache is on
00678     PRBool useXULCache;
00679     gXULCache->GetEnabled(&useXULCache);
00680 
00681     // If the current prototype is an overlay document (non-master prototype)
00682     // and we're filling the FastLoad disk cache, tell the cache we're done
00683     // loading it, and write the prototype.
00684     if (useXULCache && mIsWritingFastLoad &&
00685         mMasterPrototype != mCurrentPrototype &&
00686         isChrome)
00687         gXULCache->WritePrototype(mCurrentPrototype);
00688 
00689     if (isChrome) {
00690         nsCOMPtr<nsIXULOverlayProvider> reg =
00691             do_GetService(NS_CHROMEREGISTRY_CONTRACTID);
00692         nsICSSLoader* cssLoader = CSSLoader();
00693         
00694         if (reg) {
00695             nsCOMPtr<nsISimpleEnumerator> overlays;
00696             reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
00697 
00698             PRBool moreSheets;
00699             nsCOMPtr<nsISupports> next;
00700             nsCOMPtr<nsIURI> sheetURI;
00701             nsCOMPtr<nsICSSStyleSheet> sheet;
00702 
00703             while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
00704                    moreSheets) {
00705                 overlays->GetNext(getter_AddRefs(next));
00706 
00707                 sheetURI = do_QueryInterface(next);
00708                 if (!uri) {
00709                     NS_ERROR("Chrome registry handed me a non-nsIURI object!");
00710                     continue;
00711                 }
00712 
00713                 if (useXULCache && IsChromeURI(sheetURI)) {
00714                     mCurrentPrototype->AddStyleSheetReference(sheetURI);
00715                 }
00716 
00717                 cssLoader->LoadAgentSheet(sheetURI, getter_AddRefs(sheet));
00718                 if (!sheet) {
00719                     NS_WARNING("Couldn't load chrome style overlay.");
00720                     continue;
00721                 }
00722 
00723                 AddStyleSheet(sheet);
00724             }
00725         }
00726 
00727         if (useXULCache) {
00728             // If it's a 'chrome:' prototype document, then notify any
00729             // documents that raced to load the prototype, and awaited
00730             // its load completion via proto->AwaitLoadDone().
00731             rv = mCurrentPrototype->NotifyLoadDone();
00732             if (NS_FAILED(rv)) return;
00733         }
00734     }
00735 
00736     // Now walk the prototype to build content.
00737     rv = PrepareToWalk();
00738     if (NS_FAILED(rv)) return;
00739 
00740     ResumeWalk();
00741 }
00742 
00743 // Called back from nsXULPrototypeDocument::NotifyLoadDone for each XUL
00744 // document that raced to start the same prototype document load, lost
00745 // the race, but hit the XUL prototype cache because the winner filled
00746 // the cache with the not-yet-loaded prototype object.
00747 NS_IMETHODIMP
00748 nsXULDocument::OnPrototypeLoadDone()
00749 {
00750     nsresult rv;
00751 
00752     // Need to clone style sheet references now, as we couldn't do that
00753     // in StartDocumentLoad, because the prototype may not have finished
00754     // loading at that point.
00755     rv = AddPrototypeSheets();
00756     if (NS_FAILED(rv)) return rv;
00757 
00758     // Now we must do for each secondary or later XUL document (those that
00759     // lost the race to start the prototype document load) what is done by
00760     // nsCachedChromeStreamListener::OnStopRequest for the primary document
00761     // (the one that won the race to start the prototype load).
00762     rv = PrepareToWalk();
00763     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
00764     if (NS_FAILED(rv)) return rv;
00765 
00766     return ResumeWalk();
00767 }
00768 
00769 
00770 PR_STATIC_CALLBACK(PRBool)
00771 ClearPresentationStuff(nsHashKey *aKey, void *aData, void* aClosure)
00772 {
00773     nsISupports *supp = NS_STATIC_CAST(nsISupports *, aData);
00774     nsCOMPtr<nsPIBoxObject> boxObject(do_QueryInterface(supp));
00775 
00776     if (boxObject) {
00777         boxObject->InvalidatePresentationStuff();
00778     }
00779 
00780     return PR_TRUE;
00781 }
00782 
00783 NS_IMETHODIMP
00784 nsXULDocument::OnHide()
00785 {
00786     if (mBoxObjectTable) {
00787         mBoxObjectTable->Enumerate(ClearPresentationStuff, nsnull);
00788     }
00789 
00790     return NS_OK;
00791 }
00792 
00793 PR_STATIC_CALLBACK(void)
00794 ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
00795 {
00796     BroadcasterMapEntry* entry =
00797         NS_STATIC_CAST(BroadcasterMapEntry*, aEntry);
00798     for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
00799         delete (BroadcastListener*)entry->mListeners[i];
00800     }
00801 
00802     // N.B. that we need to manually run the dtor because we
00803     // constructed the nsSmallVoidArray object in-place.
00804     entry->mListeners.~nsSmallVoidArray();
00805 }
00806 
00807 static PRBool
00808 CanBroadcast(PRInt32 aNameSpaceID, nsIAtom* aAttribute)
00809 {
00810     // Don't push changes to the |id|, |ref|, or |persist| attribute.
00811     if (aNameSpaceID == kNameSpaceID_None) {
00812         if ((aAttribute == nsXULAtoms::id) ||
00813             (aAttribute == nsXULAtoms::ref) ||
00814             (aAttribute == nsXULAtoms::persist)) {
00815             return PR_FALSE;
00816         }
00817     }
00818     return PR_TRUE;
00819 }
00820 
00821 void
00822 nsXULDocument::SynchronizeBroadcastListener(nsIDOMElement   *aBroadcaster,
00823                                             nsIDOMElement   *aListener,
00824                                             const nsAString &aAttr)
00825 {
00826     nsCOMPtr<nsIContent> broadcaster = do_QueryInterface(aBroadcaster);
00827     nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
00828 
00829     if (aAttr.EqualsLiteral("*")) {
00830         PRUint32 count = broadcaster->GetAttrCount();
00831         while (count-- > 0) {
00832             PRInt32 nameSpaceID;
00833             nsCOMPtr<nsIAtom> name;
00834             nsCOMPtr<nsIAtom> prefix;
00835             broadcaster->GetAttrNameAt(count, &nameSpaceID,
00836                                        getter_AddRefs(name),
00837                                        getter_AddRefs(prefix));
00838 
00839             // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
00840             if (! CanBroadcast(nameSpaceID, name))
00841                 continue;
00842 
00843             nsAutoString value;
00844             broadcaster->GetAttr(nameSpaceID, name, value);
00845             listener->SetAttr(nameSpaceID, name, prefix, value, PR_FALSE);
00846 
00847 #if 0
00848             // XXX we don't fire the |onbroadcast| handler during
00849             // initial hookup: doing so would potentially run the
00850             // |onbroadcast| handler before the |onload| handler,
00851             // which could define JS properties that mask XBL
00852             // properties, etc.
00853             ExecuteOnBroadcastHandlerFor(broadcaster, aListener, name);
00854 #endif
00855         }
00856     }
00857     else {
00858         // Find out if the attribute is even present at all.
00859         nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr);
00860 
00861         nsAutoString value;
00862         nsresult rv = broadcaster->GetAttr(kNameSpaceID_None, name, value);
00863 
00864         if (rv == NS_CONTENT_ATTR_NO_VALUE ||
00865             rv == NS_CONTENT_ATTR_HAS_VALUE) {
00866             listener->SetAttr(kNameSpaceID_None, name, value, PR_FALSE);
00867         }
00868         else {
00869             listener->UnsetAttr(kNameSpaceID_None, name, PR_FALSE);
00870         }
00871 
00872 #if 0
00873         // XXX we don't fire the |onbroadcast| handler during initial
00874         // hookup: doing so would potentially run the |onbroadcast|
00875         // handler before the |onload| handler, which could define JS
00876         // properties that mask XBL properties, etc.
00877         ExecuteOnBroadcastHandlerFor(broadcaster, aListener, name);
00878 #endif
00879     }
00880 }
00881 
00882 NS_IMETHODIMP
00883 nsXULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
00884                                        nsIDOMElement* aListener,
00885                                        const nsAString& aAttr)
00886 {
00887     NS_ENSURE_ARG(aBroadcaster && aListener);
00888     
00889     nsresult rv =
00890         nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
00891                                         aBroadcaster);
00892 
00893     if (NS_FAILED(rv)) {
00894         return rv;
00895     }
00896 
00897     rv = nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
00898                                          aListener);
00899 
00900     if (NS_FAILED(rv)) {
00901         return rv;
00902     }
00903 
00904     static PLDHashTableOps gOps = {
00905         PL_DHashAllocTable,
00906         PL_DHashFreeTable,
00907         PL_DHashGetKeyStub,
00908         PL_DHashVoidPtrKeyStub,
00909         PL_DHashMatchEntryStub,
00910         PL_DHashMoveEntryStub,
00911         ClearBroadcasterMapEntry,
00912         PL_DHashFinalizeStub,
00913         nsnull
00914     };
00915 
00916     if (! mBroadcasterMap) {
00917         mBroadcasterMap =
00918             PL_NewDHashTable(&gOps, nsnull, sizeof(BroadcasterMapEntry),
00919                              PL_DHASH_MIN_SIZE);
00920 
00921         if (! mBroadcasterMap)
00922             return NS_ERROR_OUT_OF_MEMORY;
00923     }
00924 
00925     BroadcasterMapEntry* entry =
00926         NS_STATIC_CAST(BroadcasterMapEntry*,
00927                        PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
00928                                             PL_DHASH_LOOKUP));
00929 
00930     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
00931         entry =
00932             NS_STATIC_CAST(BroadcasterMapEntry*,
00933                            PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
00934                                                 PL_DHASH_ADD));
00935 
00936         if (! entry)
00937             return NS_ERROR_OUT_OF_MEMORY;
00938 
00939         entry->mBroadcaster = aBroadcaster;
00940 
00941         // N.B. placement new to construct the nsSmallVoidArray object
00942         // in-place
00943         new (&entry->mListeners) nsSmallVoidArray();
00944     }
00945 
00946     // Only add the listener if it's not there already!
00947     nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
00948 
00949     BroadcastListener* bl;
00950     for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
00951         bl = NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
00952 
00953         if ((bl->mListener == aListener) && (bl->mAttribute == attr))
00954             return NS_OK;
00955     }
00956 
00957     bl = new BroadcastListener;
00958     if (! bl)
00959         return NS_ERROR_OUT_OF_MEMORY;
00960 
00961     bl->mListener  = aListener;
00962     bl->mAttribute = attr;
00963 
00964     entry->mListeners.AppendElement(bl);
00965 
00966     SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
00967     return NS_OK;
00968 }
00969 
00970 NS_IMETHODIMP
00971 nsXULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
00972                                           nsIDOMElement* aListener,
00973                                           const nsAString& aAttr)
00974 {
00975     // If we haven't added any broadcast listeners, then there sure
00976     // aren't any to remove.
00977     if (! mBroadcasterMap)
00978         return NS_OK;
00979 
00980     BroadcasterMapEntry* entry =
00981         NS_STATIC_CAST(BroadcasterMapEntry*,
00982                        PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
00983                                             PL_DHASH_LOOKUP));
00984 
00985     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
00986         nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
00987         for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
00988             BroadcastListener* bl =
00989                 NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
00990 
00991             if ((bl->mListener == aListener) && (bl->mAttribute == attr)) {
00992                 entry->mListeners.RemoveElementAt(i);
00993                 delete bl;
00994 
00995                 if (entry->mListeners.Count() == 0)
00996                     PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
00997                                          PL_DHASH_REMOVE);
00998 
00999                 SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
01000 
01001                 break;
01002             }
01003         }
01004     }
01005 
01006     return NS_OK;
01007 }
01008 
01009 nsresult
01010 nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
01011                                             nsIDOMElement* aListener,
01012                                             nsIAtom* aAttr)
01013 {
01014     // Now we execute the onchange handler in the context of the
01015     // observer. We need to find the observer in order to
01016     // execute the handler.
01017 
01018     nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
01019     PRUint32 count = listener->GetChildCount();
01020     for (PRUint32 i = 0; i < count; ++i) {
01021         // Look for an <observes> element beneath the listener. This
01022         // ought to have an |element| attribute that refers to
01023         // aBroadcaster, and an |attribute| element that tells us what
01024         // attriubtes we're listening for.
01025         nsIContent *child = listener->GetChildAt(i);
01026 
01027         nsINodeInfo *ni = child->GetNodeInfo();
01028         if (!ni || !ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL))
01029             continue;
01030 
01031         // Is this the element that was listening to us?
01032         nsAutoString listeningToID;
01033         child->GetAttr(kNameSpaceID_None, nsXULAtoms::element, listeningToID);
01034 
01035         nsAutoString broadcasterID;
01036         aBroadcaster->GetAttr(kNameSpaceID_None, nsXULAtoms::id, broadcasterID);
01037 
01038         if (listeningToID != broadcasterID)
01039             continue;
01040 
01041         // We are observing the broadcaster, but is this the right
01042         // attribute?
01043         nsAutoString listeningToAttribute;
01044         child->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute,
01045                        listeningToAttribute);
01046 
01047         if (!aAttr->Equals(listeningToAttribute) &&
01048             !listeningToAttribute.EqualsLiteral("*")) {
01049             continue;
01050         }
01051 
01052         // This is the right <observes> element. Execute the
01053         // |onbroadcast| event handler
01054         nsEvent event(PR_TRUE, NS_XUL_BROADCAST);
01055 
01056         PRInt32 j = mPresShells.Count();
01057         while (--j >= 0) {
01058             nsCOMPtr<nsIPresShell> shell =
01059                 NS_STATIC_CAST(nsIPresShell*, mPresShells[j]);
01060 
01061             nsCOMPtr<nsPresContext> aPresContext = shell->GetPresContext();
01062 
01063             // Handle the DOM event
01064             nsEventStatus status = nsEventStatus_eIgnore;
01065             child->HandleDOMEvent(aPresContext, &event, nsnull,
01066                                   NS_EVENT_FLAG_INIT, &status);
01067         }
01068     }
01069 
01070     return NS_OK;
01071 }
01072 
01073 void
01074 nsXULDocument::AttributeChanged(nsIContent* aElement, PRInt32 aNameSpaceID,
01075                                 nsIAtom* aAttribute, PRInt32 aModType)
01076 {
01077     nsresult rv;
01078 
01079     // First see if we need to update our element map.
01080     if ((aAttribute == nsXULAtoms::id) || (aAttribute == nsXULAtoms::ref)) {
01081 
01082         rv = mElementMap.Enumerate(RemoveElementsFromMapByContent, aElement);
01083         if (NS_FAILED(rv)) return;
01084 
01085         // That'll have removed _both_ the 'ref' and 'id' entries from
01086         // the map. So add 'em back now.
01087         rv = AddElementToMap(aElement);
01088         if (NS_FAILED(rv)) return;
01089     }
01090 
01091     // Synchronize broadcast listeners
01092     if (mBroadcasterMap && CanBroadcast(aNameSpaceID, aAttribute)) {
01093         nsCOMPtr<nsIDOMElement> domele = do_QueryInterface(aElement);
01094         BroadcasterMapEntry* entry =
01095             NS_STATIC_CAST(BroadcasterMapEntry*,
01096                            PL_DHashTableOperate(mBroadcasterMap, domele.get(),
01097                                                 PL_DHASH_LOOKUP));
01098 
01099         if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
01100             // We've got listeners: push the value.
01101             nsAutoString value;
01102             rv = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
01103 
01104             nsCOMArray<nsIContent> listenerArray;
01105             PRInt32 i;
01106             for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
01107                 BroadcastListener* bl =
01108                     NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
01109 
01110                 if ((bl->mAttribute == aAttribute) ||
01111                     (bl->mAttribute == nsXULAtoms::_star)) {
01112                     nsCOMPtr<nsIContent> listener
01113                         = do_QueryInterface(bl->mListener);
01114                     listenerArray.AppendObject(listener);
01115                 }
01116             }
01117             for (i = 0; i < listenerArray.Count(); ++i) {
01118                 nsIContent* listener = listenerArray[i];
01119                  if (rv == NS_CONTENT_ATTR_NO_VALUE ||
01120                      rv == NS_CONTENT_ATTR_HAS_VALUE) {
01121                      listener->SetAttr(kNameSpaceID_None, aAttribute, value,
01122                                        PR_TRUE);
01123                  }
01124                  else {
01125                      listener->UnsetAttr(kNameSpaceID_None, aAttribute,
01126                                          PR_TRUE);
01127                  }
01128 
01129                 nsCOMPtr<nsIDOMElement> listenerEl = do_QueryInterface(listener);
01130                 ExecuteOnBroadcastHandlerFor(aElement, listenerEl, aAttribute);
01131             }
01132         }
01133     }
01134 
01135     // Now notify external observers
01136     NS_DOCUMENT_NOTIFY_OBSERVERS(AttributeChanged,
01137                                  (this, aElement, aNameSpaceID,
01138                                   aAttribute, aModType));
01139 
01140     // See if there is anything we need to persist in the localstore.
01141     //
01142     // XXX Namespace handling broken :-(
01143     nsAutoString persist;
01144     rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist);
01145     if (NS_FAILED(rv)) return;
01146 
01147     if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
01148         nsAutoString attr;
01149         rv = aAttribute->ToString(attr);
01150         if (NS_FAILED(rv)) return;
01151 
01152         if (persist.Find(attr) >= 0) {
01153             rv = Persist(aElement, kNameSpaceID_None, aAttribute);
01154             if (NS_FAILED(rv)) return;
01155         }
01156     }
01157 }
01158 
01159 void
01160 nsXULDocument::ContentAppended(nsIContent* aContainer,
01161                                PRInt32 aNewIndexInContainer)
01162 {
01163     // First update our element map
01164     PRUint32 count = aContainer->GetChildCount();
01165 
01166     for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
01167         nsresult rv = AddSubtreeToDocument(aContainer->GetChildAt(i));
01168         if (NS_FAILED(rv))
01169             return;
01170     }
01171 
01172     nsXMLDocument::ContentAppended(aContainer, aNewIndexInContainer);
01173 }
01174 
01175 void
01176 nsXULDocument::ContentInserted(nsIContent* aContainer,
01177                                nsIContent* aChild,
01178                                PRInt32 aIndexInContainer)
01179 {
01180     nsresult rv = AddSubtreeToDocument(aChild);
01181     if (NS_FAILED(rv))
01182         return;
01183 
01184     nsXMLDocument::ContentInserted(aContainer, aChild,
01185                                    aIndexInContainer);
01186 }
01187 
01188 void
01189 nsXULDocument::ContentRemoved(nsIContent* aContainer,
01190                               nsIContent* aChild,
01191                               PRInt32 aIndexInContainer)
01192 {
01193     nsresult rv;
01194     rv = RemoveSubtreeFromDocument(aChild);
01195     if (NS_FAILED(rv))
01196         return;
01197 
01198     nsXMLDocument::ContentRemoved(aContainer, aChild,
01199                                   aIndexInContainer);
01200 }
01201 
01202 nsresult
01203 nsXULDocument::HandleDOMEvent(nsPresContext* aPresContext,
01204                               nsEvent* aEvent,
01205                               nsIDOMEvent** aDOMEvent,
01206                               PRUint32 aFlags,
01207                               nsEventStatus* aEventStatus)
01208 {
01209     // Make sure to tell the event that dispatch has started.
01210     NS_MARK_EVENT_DISPATCH_STARTED(aEvent);
01211 
01212     nsresult ret = NS_OK;
01213     nsIDOMEvent* domEvent = nsnull;
01214     PRBool externalDOMEvent = PR_FALSE;
01215 
01216     if (NS_EVENT_FLAG_INIT & aFlags) {
01217         if (aDOMEvent) {
01218             if (*aDOMEvent) {
01219                 externalDOMEvent = PR_TRUE;
01220             }
01221         }
01222         else {
01223             aDOMEvent = &domEvent;
01224         }
01225         aEvent->flags |= aFlags;
01226         aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
01227         aFlags |= NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE;
01228     }
01229 
01230     // Capturing stage
01231     if (NS_EVENT_FLAG_CAPTURE & aFlags && mScriptGlobalObject) {
01232         mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
01233                                             aFlags & NS_EVENT_CAPTURE_MASK,
01234                                             aEventStatus);
01235     }
01236 
01237     // Local handling stage
01238     if (mListenerManager) {
01239         aEvent->flags |= aFlags;
01240         mListenerManager->HandleEvent(aPresContext, aEvent, aDOMEvent,
01241                                       this, aFlags, aEventStatus);
01242         aEvent->flags &= ~aFlags;
01243     }
01244 
01245     // Bubbling stage
01246     if (NS_EVENT_FLAG_BUBBLE & aFlags && mScriptGlobalObject) {
01247         mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
01248                                             aFlags & NS_EVENT_BUBBLE_MASK,
01249                                             aEventStatus);
01250     }
01251 
01252     if (NS_EVENT_FLAG_INIT & aFlags) {
01253         // We're leaving the DOM event loop so if we created a DOM
01254         // event, release here.
01255         if (*aDOMEvent && !externalDOMEvent) {
01256             nsrefcnt rc;
01257             NS_RELEASE2(*aDOMEvent, rc);
01258             if (0 != rc) {
01259                 // Okay, so someone in the DOM loop (a listener, JS
01260                 // object) still has a ref to the DOM Event but the
01261                 // internal data hasn't been malloc'd.  Force a copy
01262                 // of the data here so the DOM Event is still valid.
01263                 nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
01264                     do_QueryInterface(*aDOMEvent);
01265 
01266                 if (privateEvent) {
01267                     privateEvent->DuplicatePrivateData();
01268                 }
01269             }
01270         }
01271         aDOMEvent = nsnull;
01272 
01273         // Now that we're done with this event, remove the flag that says
01274         // we're in the process of dispatching this event.
01275         NS_MARK_EVENT_DISPATCH_DONE(aEvent);
01276     }
01277 
01278     return ret;
01279 }
01280 
01281 
01282 //----------------------------------------------------------------------
01283 //
01284 // nsIXULDocument interface
01285 //
01286 
01287 NS_IMETHODIMP
01288 nsXULDocument::AddElementForID(const nsAString& aID, nsIContent* aElement)
01289 {
01290     NS_PRECONDITION(aElement != nsnull, "null ptr");
01291     if (! aElement)
01292         return NS_ERROR_NULL_POINTER;
01293 
01294     mElementMap.Add(aID, aElement);
01295     return NS_OK;
01296 }
01297 
01298 
01299 NS_IMETHODIMP
01300 nsXULDocument::RemoveElementForID(const nsAString& aID, nsIContent* aElement)
01301 {
01302     NS_PRECONDITION(aElement != nsnull, "null ptr");
01303     if (! aElement)
01304         return NS_ERROR_NULL_POINTER;
01305 
01306     mElementMap.Remove(aID, aElement);
01307     return NS_OK;
01308 }
01309 
01310 NS_IMETHODIMP
01311 nsXULDocument::GetElementsForID(const nsAString& aID,
01312                                 nsISupportsArray* aElements)
01313 {
01314     NS_PRECONDITION(aElements != nsnull, "null ptr");
01315     if (! aElements)
01316         return NS_ERROR_NULL_POINTER;
01317 
01318     mElementMap.Find(aID, aElements);
01319     return NS_OK;
01320 }
01321 
01322 NS_IMETHODIMP
01323 nsXULDocument::AddForwardReference(nsForwardReference* aRef)
01324 {
01325     if (mResolutionPhase < aRef->GetPhase()) {
01326         mForwardReferences.AppendElement(aRef);
01327     }
01328     else {
01329         NS_ERROR("forward references have already been resolved");
01330         delete aRef;
01331     }
01332 
01333     return NS_OK;
01334 }
01335 
01336 
01337 NS_IMETHODIMP
01338 nsXULDocument::ResolveForwardReferences()
01339 {
01340     if (mResolutionPhase == nsForwardReference::eDone)
01341         return NS_OK;
01342 
01343     // Resolve each outstanding 'forward' reference. We iterate
01344     // through the list of forward references until no more forward
01345     // references can be resolved. This annealing process is
01346     // guaranteed to converge because we've "closed the gate" to new
01347     // forward references.
01348 
01349     const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
01350     while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
01351         PRInt32 previous = 0;
01352         while (mForwardReferences.Count() && mForwardReferences.Count() != previous) {
01353             previous = mForwardReferences.Count();
01354 
01355             for (PRInt32 i = 0; i < mForwardReferences.Count(); ++i) {
01356                 nsForwardReference* fwdref = NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
01357 
01358                 if (fwdref->GetPhase() == *pass) {
01359                     nsForwardReference::Result result = fwdref->Resolve();
01360 
01361                     switch (result) {
01362                     case nsForwardReference::eResolve_Succeeded:
01363                     case nsForwardReference::eResolve_Error:
01364                         mForwardReferences.RemoveElementAt(i);
01365                         delete fwdref;
01366 
01367                         // fixup because we removed from list
01368                         --i;
01369                         break;
01370 
01371                     case nsForwardReference::eResolve_Later:
01372                         // do nothing. we'll try again later
01373                         ;
01374                     }
01375                 }
01376             }
01377         }
01378 
01379         ++pass;
01380     }
01381 
01382     DestroyForwardReferences();
01383     return NS_OK;
01384 }
01385 
01386 NS_IMETHODIMP
01387 nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument)
01388 {
01389     mMasterPrototype = aDocument;
01390     return NS_OK;
01391 }
01392 
01393 NS_IMETHODIMP
01394 nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument)
01395 {
01396     *aDocument = mMasterPrototype;
01397     NS_IF_ADDREF(*aDocument);
01398     return NS_OK;
01399 }
01400 
01401 NS_IMETHODIMP
01402 nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument)
01403 {
01404     mCurrentPrototype = aDocument;
01405     return NS_OK;
01406 }
01407 
01408 //----------------------------------------------------------------------
01409 //
01410 // nsIDOMDocument interface
01411 //
01412 
01413 NS_IMETHODIMP
01414 nsXULDocument::GetElementsByAttribute(const nsAString& aAttribute,
01415                                       const nsAString& aValue,
01416                                       nsIDOMNodeList** aReturn)
01417 {
01418     nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
01419     NS_ENSURE_TRUE(attrAtom, NS_ERROR_OUT_OF_MEMORY);
01420 
01421     nsContentList *list = new nsContentList(this,
01422                                             MatchAttribute,
01423                                             aValue,
01424                                             nsnull,
01425                                             PR_TRUE,
01426                                             attrAtom,
01427                                             kNameSpaceID_Unknown);
01428     NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY);
01429 
01430     NS_ADDREF(*aReturn = list);
01431     return NS_OK;
01432 }
01433 
01434 
01435 NS_IMETHODIMP
01436 nsXULDocument::Persist(const nsAString& aID,
01437                        const nsAString& aAttr)
01438 {
01439     // If we're currently reading persisted attributes out of the
01440     // localstore, _don't_ re-enter and try to set them again!
01441     if (mApplyingPersistedAttrs)
01442         return NS_OK;
01443 
01444     nsresult rv;
01445 
01446     nsCOMPtr<nsIDOMElement> domelement;
01447     rv = GetElementById(aID, getter_AddRefs(domelement));
01448     if (NS_FAILED(rv)) return rv;
01449 
01450     if (! domelement)
01451         return NS_OK;
01452 
01453     nsCOMPtr<nsIContent> element = do_QueryInterface(domelement);
01454     NS_ASSERTION(element != nsnull, "null ptr");
01455     if (! element)
01456         return NS_ERROR_UNEXPECTED;
01457 
01458     nsCOMPtr<nsIAtom> tag;
01459     PRInt32 nameSpaceID;
01460 
01461     nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
01462     if (ni) {
01463         tag = ni->NameAtom();
01464         nameSpaceID = ni->NamespaceID();
01465     }
01466     else {
01467         // Make sure that this QName is going to be valid.
01468         nsIParserService *parserService =
01469             nsContentUtils::GetParserServiceWeakRef();
01470         NS_ASSERTION(parserService, "Running scripts during shutdown?");
01471 
01472         const PRUnichar *colon;
01473         rv = parserService->CheckQName(PromiseFlatString(aAttr), PR_TRUE, &colon);
01474         if (NS_FAILED(rv)) {
01475             // There was an invalid character or it was malformed.
01476             return NS_ERROR_INVALID_ARG;
01477         }
01478 
01479         if (colon) {
01480             // We don't really handle namespace qualifiers in attribute names.
01481             return NS_ERROR_NOT_IMPLEMENTED;
01482         }
01483 
01484         tag = do_GetAtom(aAttr);
01485         NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
01486 
01487         nameSpaceID = kNameSpaceID_None;
01488     }
01489 
01490     rv = Persist(element, nameSpaceID, tag);
01491     if (NS_FAILED(rv)) return rv;
01492 
01493     return NS_OK;
01494 }
01495 
01496 
01497 nsresult
01498 nsXULDocument::Persist(nsIContent* aElement, PRInt32 aNameSpaceID,
01499                        nsIAtom* aAttribute)
01500 {
01501     // First make sure we _have_ a local store to stuff the persisted
01502     // information into. (We might not have one if profile information
01503     // hasn't been loaded yet...)
01504     if (! mLocalStore)
01505         return NS_OK;
01506 
01507     nsresult rv;
01508 
01509     nsCOMPtr<nsIRDFResource> element;
01510     rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
01511     if (NS_FAILED(rv)) return rv;
01512 
01513     // No ID, so nothing to persist.
01514     if (! element)
01515         return NS_OK;
01516 
01517     // Ick. Construct a property from the attribute. Punt on
01518     // namespaces for now.
01519     const char* attrstr;
01520     rv = aAttribute->GetUTF8String(&attrstr);
01521     if (NS_FAILED(rv)) return rv;
01522 
01523     // Don't bother with unreasonable attributes. We clamp long values,
01524     // but truncating attribute names turns it into a different attribute
01525     // so there's no point in persisting anything at all
01526     if (!attrstr || strlen(attrstr) > kMaxAttrNameLength) {
01527         NS_WARNING("Can't persist, Attribute name too long");
01528         return NS_ERROR_ILLEGAL_VALUE;
01529     }
01530 
01531     nsCOMPtr<nsIRDFResource> attr;
01532     rv = gRDFService->GetResource(nsDependentCString(attrstr),
01533                                   getter_AddRefs(attr));
01534     if (NS_FAILED(rv)) return rv;
01535 
01536     // Turn the value into a literal
01537     nsAutoString valuestr;
01538     rv = aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
01539     if (NS_FAILED(rv)) return rv;
01540 
01541     // prevent over-long attributes that choke the parser (bug 319846)
01542     // (can't simply Truncate without testing, it's implemented
01543     // using SetLength and will grow a short string)
01544     if (valuestr.Length() > kMaxAttributeLength) {
01545         NS_WARNING("Truncating persisted attribute value");
01546         valuestr.Truncate(kMaxAttributeLength);
01547     }
01548 
01549     PRBool novalue = (rv != NS_CONTENT_ATTR_HAS_VALUE);
01550 
01551     // See if there was an old value...
01552     nsCOMPtr<nsIRDFNode> oldvalue;
01553     rv = mLocalStore->GetTarget(element, attr, PR_TRUE, getter_AddRefs(oldvalue));
01554     if (NS_FAILED(rv)) return rv;
01555 
01556     if (oldvalue && novalue) {
01557         // ...there was an oldvalue, and they've removed it. XXXThis
01558         // handling isn't quite right...
01559         rv = mLocalStore->Unassert(element, attr, oldvalue);
01560     }
01561     else {
01562         // Now either 'change' or 'assert' based on whether there was
01563         // an old value.
01564         nsCOMPtr<nsIRDFLiteral> newvalue;
01565         rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
01566         if (NS_FAILED(rv)) return rv;
01567 
01568         if (oldvalue) {
01569             if (oldvalue != newvalue)
01570                 rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
01571             else
01572                 rv = NS_OK;
01573         }
01574         else {
01575             rv = mLocalStore->Assert(element, attr, newvalue, PR_TRUE);
01576         }
01577     }
01578 
01579     if (NS_FAILED(rv)) return rv;
01580 
01581     // Add it to the persisted set for this document (if it's not
01582     // there already).
01583     {
01584         nsCAutoString docurl;
01585         rv = mDocumentURI->GetSpec(docurl);
01586         if (NS_FAILED(rv)) return rv;
01587 
01588         nsCOMPtr<nsIRDFResource> doc;
01589         rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
01590         if (NS_FAILED(rv)) return rv;
01591 
01592         PRBool hasAssertion;
01593         rv = mLocalStore->HasAssertion(doc, kNC_persist, element, PR_TRUE, &hasAssertion);
01594         if (NS_FAILED(rv)) return rv;
01595 
01596         if (! hasAssertion) {
01597             rv = mLocalStore->Assert(doc, kNC_persist, element, PR_TRUE);
01598             if (NS_FAILED(rv)) return rv;
01599         }
01600     }
01601 
01602     return NS_OK;
01603 }
01604 
01605 
01606 
01607 nsresult
01608 nsXULDocument::DestroyForwardReferences()
01609 {
01610     for (PRInt32 i = mForwardReferences.Count() - 1; i >= 0; --i) {
01611         nsForwardReference* fwdref =
01612             NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
01613         delete fwdref;
01614     }
01615 
01616     mForwardReferences.Clear();
01617     return NS_OK;
01618 }
01619 
01620 
01621 nsresult
01622 nsXULDocument::GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth,
01623                                   PRInt32* aHeight)
01624 {
01625     nsresult result = NS_OK;
01626     nsSize size;
01627     nsIFrame* frame;
01628 
01629     FlushPendingNotifications(Flush_Layout);
01630 
01631     if (mRootContent) {
01632         result = aShell->GetPrimaryFrameFor(mRootContent, &frame);
01633     } else {
01634         frame = nsnull;
01635     }
01636     if (NS_SUCCEEDED(result) && frame) {
01637         nsIView* view = frame->GetView();
01638         // If we have a view check if it's scrollable. If not,
01639         // just use the view size itself
01640         if (view) {
01641             nsIScrollableView* scrollableView = view->ToScrollableView();
01642 
01643             if (scrollableView) {
01644                 scrollableView->GetScrolledView(view);
01645             }
01646 
01647             nsRect r = view->GetBounds();
01648             size.height = r.height;
01649             size.width = r.width;
01650         }
01651         // If we don't have a view, use the frame size
01652         else {
01653             size = frame->GetSize();
01654         }
01655 
01656         // Convert from twips to pixels
01657         float scale = aShell->GetPresContext()->TwipsToPixels();
01658 
01659         *aWidth = NSTwipsToIntPixels(size.width, scale);
01660         *aHeight = NSTwipsToIntPixels(size.height, scale);
01661     }
01662     else {
01663         *aWidth = 0;
01664         *aHeight = 0;
01665     }
01666 
01667     return result;
01668 }
01669 
01670 NS_IMETHODIMP
01671 nsXULDocument::GetWidth(PRInt32* aWidth)
01672 {
01673     NS_ENSURE_ARG_POINTER(aWidth);
01674 
01675     nsresult rv = NS_OK;
01676 
01677     // We make the assumption that the first presentation shell
01678     // is the one for which we need information.
01679     nsIPresShell *shell = GetShellAt(0);
01680     if (shell) {
01681         PRInt32 width, height;
01682 
01683         rv = GetPixelDimensions(shell, &width, &height);
01684         *aWidth = width;
01685     } else
01686         *aWidth = 0;
01687 
01688     return rv;
01689 }
01690 
01691 NS_IMETHODIMP
01692 nsXULDocument::GetHeight(PRInt32* aHeight)
01693 {
01694     NS_ENSURE_ARG_POINTER(aHeight);
01695 
01696     nsresult rv = NS_OK;
01697 
01698     // We make the assumption that the first presentation shell
01699     // is the one for which we need information.
01700     nsIPresShell *shell = GetShellAt(0);
01701     if (shell) {
01702         PRInt32 width, height;
01703 
01704         rv = GetPixelDimensions(shell, &width, &height);
01705         *aHeight = height;
01706     } else
01707         *aHeight = 0;
01708 
01709     return rv;
01710 }
01711 
01712 //----------------------------------------------------------------------
01713 //
01714 // nsIDOMXULDocument interface
01715 //
01716 
01717 NS_IMETHODIMP
01718 nsXULDocument::GetPopupNode(nsIDOMNode** aNode)
01719 {
01720     // Get popup node.
01721     nsresult rv = TrustedGetPopupNode(aNode); // addref happens here
01722 
01723     if (NS_SUCCEEDED(rv) && *aNode && !nsContentUtils::CanCallerAccess(*aNode)) {
01724         NS_RELEASE(*aNode);
01725         return NS_ERROR_DOM_SECURITY_ERR;
01726     }
01727 
01728     return rv;
01729 }
01730 
01731 NS_IMETHODIMP
01732 nsXULDocument::TrustedGetPopupNode(nsIDOMNode** aNode)
01733 {
01734     // Get the focus controller.
01735     nsCOMPtr<nsIFocusController> focusController;
01736     GetFocusController(getter_AddRefs(focusController));
01737     NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
01738 
01739     // Get the popup node.
01740     return focusController->GetPopupNode(aNode); // addref happens here
01741 }
01742 
01743 NS_IMETHODIMP
01744 nsXULDocument::SetPopupNode(nsIDOMNode* aNode)
01745 {
01746     nsresult rv;
01747 
01748     // get focus controller
01749     nsCOMPtr<nsIFocusController> focusController;
01750     GetFocusController(getter_AddRefs(focusController));
01751     NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
01752     // set popup node
01753     rv = focusController->SetPopupNode(aNode);
01754 
01755     return rv;
01756 }
01757 
01758 NS_IMETHODIMP
01759 nsXULDocument::GetTrustedPopupEvent(nsIDOMEvent** aEvent)
01760 {
01761     nsresult rv;
01762 
01763     nsCOMPtr<nsIFocusController> focusController;
01764     GetFocusController(getter_AddRefs(focusController));
01765     NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
01766 
01767     nsCOMPtr<nsIFocusController_MOZILLA_1_8_BRANCH> focusControllerBranch =
01768         do_QueryInterface(focusController, &rv);
01769     NS_ENSURE_SUCCESS(rv, rv);
01770     rv = focusControllerBranch->GetPopupEvent(aEvent);
01771 
01772     return rv;
01773 }
01774 
01775 NS_IMETHODIMP
01776 nsXULDocument::SetTrustedPopupEvent(nsIDOMEvent* aEvent)
01777 {
01778     nsresult rv;
01779 
01780     nsCOMPtr<nsIFocusController> focusController;
01781     GetFocusController(getter_AddRefs(focusController));
01782     NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
01783 
01784     nsCOMPtr<nsIFocusController_MOZILLA_1_8_BRANCH> focusControllerBranch =
01785         do_QueryInterface(focusController, &rv);
01786     NS_ENSURE_SUCCESS(rv, rv);
01787     rv = focusControllerBranch->SetPopupEvent(aEvent);
01788 
01789     return rv;
01790 }
01791 
01792 // Returns the rangeOffset element from the popupEvent. This is for chrome
01793 // callers only.
01794 NS_IMETHODIMP
01795 nsXULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
01796 {
01797     NS_ENSURE_ARG_POINTER(aRangeParent);
01798     *aRangeParent = nsnull;
01799 
01800     nsCOMPtr<nsIDOMEvent> event;
01801     nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
01802     NS_ENSURE_SUCCESS(rv, rv);
01803     if (! event)
01804         return NS_ERROR_UNEXPECTED; // no event active
01805 
01806     nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
01807     NS_ENSURE_SUCCESS(rv, rv);
01808     rv = uiEvent->GetRangeParent(aRangeParent); // addrefs
01809 
01810     if (NS_SUCCEEDED(rv) && *aRangeParent &&
01811             !nsContentUtils::CanCallerAccess(*aRangeParent)) {
01812         NS_RELEASE(*aRangeParent);
01813         return NS_ERROR_DOM_SECURITY_ERR;
01814     }
01815     return rv;
01816 }
01817 
01818 // Returns the rangeOffset element from the popupEvent. We check the rangeParent
01819 // to determine if the caller has rights to access to the data.
01820 NS_IMETHODIMP
01821 nsXULDocument::GetPopupRangeOffset(PRInt32* aRangeOffset)
01822 {
01823     NS_ENSURE_ARG_POINTER(aRangeOffset);
01824 
01825     nsCOMPtr<nsIDOMEvent> event;
01826     nsresult rv = GetTrustedPopupEvent(getter_AddRefs(event));
01827     NS_ENSURE_SUCCESS(rv, rv);
01828     if (! event)
01829         return NS_ERROR_UNEXPECTED; // no event active
01830 
01831     nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(event, &rv);
01832     NS_ENSURE_SUCCESS(rv, rv);
01833 
01834     nsCOMPtr<nsIDOMNode> parent;
01835     rv = uiEvent->GetRangeParent(getter_AddRefs(parent));
01836     NS_ENSURE_SUCCESS(rv, rv);
01837 
01838     if (parent && !nsContentUtils::CanCallerAccess(parent))
01839         return NS_ERROR_DOM_SECURITY_ERR;
01840 
01841     return uiEvent->GetRangeOffset(aRangeOffset);
01842 }
01843 
01844 NS_IMETHODIMP
01845 nsXULDocument::GetTooltipNode(nsIDOMNode** aNode)
01846 {
01847     if (mTooltipNode && !nsContentUtils::CanCallerAccess(mTooltipNode)) {
01848         return NS_ERROR_DOM_SECURITY_ERR;
01849     }
01850     *aNode = mTooltipNode;
01851     NS_IF_ADDREF(*aNode);
01852     return NS_OK;
01853 }
01854 
01855 NS_IMETHODIMP
01856 nsXULDocument::TrustedGetTooltipNode(nsIDOMNode** aNode)
01857 {
01858     NS_IF_ADDREF(*aNode = mTooltipNode);
01859     return NS_OK;
01860 }
01861 
01862 NS_IMETHODIMP
01863 nsXULDocument::SetTooltipNode(nsIDOMNode* aNode)
01864 {
01865     mTooltipNode = aNode;
01866     return NS_OK;
01867 }
01868 
01869 
01870 NS_IMETHODIMP
01871 nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
01872 {
01873     *aTracker = mCommandDispatcher;
01874     NS_IF_ADDREF(*aTracker);
01875     return NS_OK;
01876 }
01877 
01878 NS_IMETHODIMP
01879 nsXULDocument::GetElementById(const nsAString& aId,
01880                               nsIDOMElement** aReturn)
01881 {
01882     NS_ENSURE_ARG_POINTER(aReturn);
01883     *aReturn = nsnull;
01884 
01885     NS_WARN_IF_FALSE(!aId.IsEmpty(),"getElementById(\"\"), fix caller?");
01886     if (aId.IsEmpty())
01887       return NS_OK;
01888 
01889     nsresult rv;
01890 
01891     nsCOMPtr<nsIContent> element;
01892     rv = mElementMap.FindFirst(aId, getter_AddRefs(element));
01893     if (NS_FAILED(rv)) return rv;
01894 
01895     if (element) {
01896         rv = CallQueryInterface(element, aReturn);
01897     }
01898 
01899     return rv;
01900 }
01901 
01902 nsresult
01903 nsXULDocument::AddElementToDocumentPre(nsIContent* aElement)
01904 {
01905     // Do a bunch of work that's necessary when an element gets added
01906     // to the XUL Document.
01907     nsresult rv;
01908 
01909     // 1. Add the element to the resource-to-element map
01910     rv = AddElementToMap(aElement);
01911     if (NS_FAILED(rv)) return rv;
01912 
01913     // 2. If the element is a 'command updater' (i.e., has a
01914     // "commandupdater='true'" attribute), then add the element to the
01915     // document's command dispatcher
01916     nsAutoString value;
01917     rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
01918                            value);
01919     if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
01920         value.EqualsLiteral("true")) {
01921         rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
01922         if (NS_FAILED(rv)) return rv;
01923     }
01924 
01925     // 3. Check for a broadcaster hookup attribute, in which case
01926     // we'll hook the node up as a listener on a broadcaster.
01927     PRBool listener, resolved;
01928     rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
01929     if (NS_FAILED(rv)) return rv;
01930 
01931     // If it's not there yet, we may be able to defer hookup until
01932     // later.
01933     if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
01934         BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
01935         if (! hookup)
01936             return NS_ERROR_OUT_OF_MEMORY;
01937 
01938         rv = AddForwardReference(hookup);
01939         if (NS_FAILED(rv)) return rv;
01940     }
01941 
01942     return NS_OK;
01943 }
01944 
01945 nsresult
01946 nsXULDocument::AddElementToDocumentPost(nsIContent* aElement)
01947 {
01948     nsINodeInfo *ni = aElement->GetNodeInfo();
01949 
01950     // We need to pay special attention to the keyset tag to set up a listener
01951     if (ni && ni->Equals(nsXULAtoms::keyset, kNameSpaceID_XUL)) {
01952         // Create our XUL key listener and hook it up.
01953         nsCOMPtr<nsIXBLService> xblService(do_GetService("@mozilla.org/xbl;1"));
01954         if (xblService) {
01955             nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(aElement));
01956             xblService->AttachGlobalKeyHandler(rec);
01957         }
01958     }
01959 
01960     // See if we need to attach a XUL template to this node
01961     PRBool needsHookup;
01962     nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
01963     if (NS_FAILED(rv))
01964         return rv;
01965 
01966     if (needsHookup) {
01967         if (mResolutionPhase == nsForwardReference::eDone) {
01968             rv = CreateTemplateBuilder(aElement);
01969             if (NS_FAILED(rv))
01970                 return rv;
01971         }
01972         else {
01973             TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
01974             if (! hookup)
01975                 return NS_ERROR_OUT_OF_MEMORY;
01976 
01977             rv = AddForwardReference(hookup);
01978             if (NS_FAILED(rv))
01979                 return rv;
01980         }
01981     }
01982 
01983     return NS_OK;
01984 }
01985 
01986 NS_IMETHODIMP
01987 nsXULDocument::AddSubtreeToDocument(nsIContent* aElement)
01988 {
01989     nsresult rv;
01990 
01991     // Do pre-order addition magic
01992     rv = AddElementToDocumentPre(aElement);
01993     if (NS_FAILED(rv)) return rv;
01994 
01995     // Recurse to children
01996     nsXULElement *xulcontent = nsXULElement::FromContent(aElement);
01997 
01998     PRUint32 count =
01999         xulcontent ? xulcontent->PeekChildCount() : aElement->GetChildCount();
02000 
02001     while (count-- > 0) {
02002         rv = AddSubtreeToDocument(aElement->GetChildAt(count));
02003         if (NS_FAILED(rv))
02004             return rv;
02005     }
02006 
02007     // Do post-order addition magic
02008     return AddElementToDocumentPost(aElement);
02009 }
02010 
02011 NS_IMETHODIMP
02012 nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement)
02013 {
02014     // Do a bunch of cleanup to remove an element from the XUL
02015     // document.
02016     nsresult rv;
02017 
02018     // 1. Remove any children from the document.
02019     PRUint32 count = aElement->GetChildCount();
02020 
02021     while (count-- > 0) {
02022         rv = RemoveSubtreeFromDocument(aElement->GetChildAt(count));
02023         if (NS_FAILED(rv))
02024             return rv;
02025     }
02026 
02027     // 2. Remove the element from the resource-to-element map
02028     rv = RemoveElementFromMap(aElement);
02029     if (NS_FAILED(rv)) return rv;
02030 
02031     // 3. If the element is a 'command updater', then remove the
02032     // element from the document's command dispatcher.
02033     nsAutoString value;
02034     rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
02035                            value);
02036     if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
02037         value.EqualsLiteral("true")) {
02038         nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
02039         NS_ASSERTION(domelement != nsnull, "not a DOM element");
02040         if (! domelement)
02041             return NS_ERROR_UNEXPECTED;
02042 
02043         rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
02044         if (NS_FAILED(rv)) return rv;
02045     }
02046 
02047     // 4. Remove the element from our broadcaster map, since it is no longer
02048     // in the document.
02049     nsCOMPtr<nsIDOMElement> broadcaster, listener;
02050     nsAutoString attribute, broadcasterID;
02051     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
02052                          broadcasterID, attribute, getter_AddRefs(broadcaster));
02053     if (rv == NS_FINDBROADCASTER_FOUND) {
02054         RemoveBroadcastListenerFor(broadcaster, listener, attribute);
02055     }
02056 
02057     return NS_OK;
02058 }
02059 
02060 NS_IMETHODIMP
02061 nsXULDocument::SetTemplateBuilderFor(nsIContent* aContent,
02062                                      nsIXULTemplateBuilder* aBuilder)
02063 {
02064     if (! mTemplateBuilderTable) {
02065         mTemplateBuilderTable = new nsSupportsHashtable();
02066         if (! mTemplateBuilderTable)
02067             return NS_ERROR_OUT_OF_MEMORY;
02068     }
02069 
02070     nsISupportsKey key(aContent);
02071 
02072     if (aContent) {
02073         mTemplateBuilderTable->Put(&key, aBuilder);
02074     }
02075     else {
02076         mTemplateBuilderTable->Remove(&key);
02077     }
02078 
02079     return NS_OK;
02080 }
02081 
02082 NS_IMETHODIMP
02083 nsXULDocument::GetTemplateBuilderFor(nsIContent* aContent,
02084                                      nsIXULTemplateBuilder** aResult)
02085 {
02086     if (mTemplateBuilderTable) {
02087         nsISupportsKey key(aContent);
02088         *aResult = NS_STATIC_CAST(nsIXULTemplateBuilder*,
02089                                   mTemplateBuilderTable->Get(&key));
02090     }
02091     else
02092         *aResult = nsnull;
02093 
02094     return NS_OK;
02095 }
02096 
02097 // Attributes that are used with getElementById() and the
02098 // resource-to-element map.
02099 nsIAtom** nsXULDocument::kIdentityAttrs[] =
02100 {
02101     &nsXULAtoms::id,
02102     &nsXULAtoms::ref,
02103     nsnull
02104 };
02105 
02106 nsresult
02107 nsXULDocument::AddElementToMap(nsIContent* aElement)
02108 {
02109     // Look at the element's 'id' and 'ref' attributes, and if set,
02110     // add pointers in the resource-to-element map to the element.
02111     nsresult rv;
02112 
02113     for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
02114         nsAutoString value;
02115         rv = aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value);
02116         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get attribute");
02117         if (NS_FAILED(rv)) return rv;
02118 
02119         if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
02120             rv = mElementMap.Add(value, aElement);
02121             if (NS_FAILED(rv)) return rv;
02122         }
02123     }
02124 
02125     return NS_OK;
02126 }
02127 
02128 
02129 nsresult
02130 nsXULDocument::RemoveElementFromMap(nsIContent* aElement)
02131 {
02132     // Remove the element from the resource-to-element map.
02133     nsresult rv;
02134 
02135     for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
02136         nsAutoString value;
02137         rv = aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value);
02138         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get attribute");
02139         if (NS_FAILED(rv)) return rv;
02140 
02141         if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
02142             rv = mElementMap.Remove(value, aElement);
02143             if (NS_FAILED(rv)) return rv;
02144         }
02145     }
02146 
02147     return NS_OK;
02148 }
02149 
02150 
02151 PRIntn
02152 nsXULDocument::RemoveElementsFromMapByContent(const PRUnichar* aID,
02153                                               nsIContent* aElement,
02154                                               void* aClosure)
02155 {
02156     nsIContent* content = NS_REINTERPRET_CAST(nsIContent*, aClosure);
02157     return (aElement == content) ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;
02158 }
02159 
02160 
02161 
02162 //----------------------------------------------------------------------
02163 //
02164 // nsIDOMNode interface
02165 //
02166 
02167 NS_IMETHODIMP
02168 nsXULDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
02169 {
02170     // We don't allow cloning of a document
02171     *aReturn = nsnull;
02172     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
02173 }
02174 
02175 
02176 //----------------------------------------------------------------------
02177 //
02178 // Implementation methods
02179 //
02180 
02181 nsresult
02182 nsXULDocument::Init()
02183 {
02184     nsresult rv = nsXMLDocument::Init();
02185     NS_ENSURE_SUCCESS(rv, rv);
02186 
02187     // Create our command dispatcher and hook it up.
02188     rv = nsXULCommandDispatcher::Create(this,
02189                                         getter_AddRefs(mCommandDispatcher));
02190     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a focus tracker");
02191     if (NS_FAILED(rv)) return rv;
02192 
02193     // this _could_ fail; e.g., if we've tried to grab the local store
02194     // before profiles have initialized. If so, no big deal; nothing
02195     // will persist.
02196     mLocalStore = do_GetService(kLocalStoreCID);
02197 
02198     // Create a new nsISupportsArray for dealing with overlay references
02199     rv = NS_NewISupportsArray(getter_AddRefs(mUnloadedOverlays));
02200     if (NS_FAILED(rv)) return rv;
02201 
02202     if (gRefCnt++ == 0) {
02203         // Keep the RDF service cached in a member variable to make using
02204         // it a bit less painful
02205         rv = CallGetService(kRDFServiceCID, &gRDFService);
02206         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
02207         if (NS_FAILED(rv)) return rv;
02208 
02209         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
02210                                  &kNC_persist);
02211         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
02212                                  &kNC_attribute);
02213         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
02214                                  &kNC_value);
02215 
02216         rv = CallGetService(kXULPrototypeCacheCID, &gXULCache);
02217         if (NS_FAILED(rv)) return rv;
02218     }
02219 
02220 #ifdef PR_LOGGING
02221     if (! gXULLog)
02222         gXULLog = PR_NewLogModule("nsXULDocument");
02223 #endif
02224 
02225     return NS_OK;
02226 }
02227 
02228 
02229 nsresult
02230 nsXULDocument::StartLayout(void)
02231 {
02232     if (!mRootContent) {
02233 #ifdef PR_LOGGING
02234         if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
02235             nsCAutoString urlspec;
02236             mDocumentURI->GetSpec(urlspec);
02237 
02238             PR_LOG(gXULLog, PR_LOG_WARNING,
02239                    ("xul: unable to layout '%s'; no root content", urlspec.get()));
02240         }
02241 #endif
02242         return NS_OK;
02243     }
02244 
02245     PRUint32 count = GetNumberOfShells();
02246     for (PRUint32 i = 0; i < count; ++i) {
02247         nsIPresShell *shell = GetShellAt(i);
02248 
02249         // Resize-reflow this time
02250         nsPresContext *cx = shell->GetPresContext();
02251         NS_ASSERTION(cx != nsnull, "no pres context");
02252         if (! cx)
02253             return NS_ERROR_UNEXPECTED;
02254 
02255         nsCOMPtr<nsISupports> container = cx->GetContainer();
02256         NS_ASSERTION(container != nsnull, "pres context has no container");
02257         if (! container)
02258             return NS_ERROR_UNEXPECTED;
02259 
02260         nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
02261         NS_ASSERTION(docShell != nsnull, "container is not a docshell");
02262         if (! docShell)
02263             return NS_ERROR_UNEXPECTED;
02264 
02265         nsRect r = cx->GetVisibleArea();
02266 
02267         // Trigger a refresh before the call to InitialReflow(),
02268         // because the view manager's UpdateView() function is
02269         // dropping dirty rects if refresh is disabled rather than
02270         // accumulating them until refresh is enabled and then
02271         // triggering a repaint...
02272         nsIViewManager* vm = shell->GetViewManager();
02273         if (vm) {
02274             nsCOMPtr<nsIContentViewer> contentViewer;
02275             nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
02276             if (NS_SUCCEEDED(rv) && (contentViewer != nsnull)) {
02277                 PRBool enabled;
02278                 contentViewer->GetEnableRendering(&enabled);
02279                 if (enabled) {
02280                     vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
02281                 }
02282             }
02283         }
02284 
02285         shell->InitialReflow(r.width, r.height);
02286 
02287         // Start observing the document _after_ we do the initial
02288         // reflow. Otherwise, we'll get into an trouble trying to
02289         // create kids before the root frame is established.
02290         shell->BeginObservingDocument();
02291     }
02292 
02293     return NS_OK;
02294 }
02295 
02296 
02297 /* static */
02298 PRBool
02299 nsXULDocument::MatchAttribute(nsIContent* aContent,
02300                               PRInt32 aNamespaceID,
02301                               nsIAtom* aAttrName,
02302                               const nsAString& aAttrValue)
02303 {
02304     NS_PRECONDITION(aContent, "Must have content node to work with!");
02305   
02306     if (aNamespaceID != kNameSpaceID_Unknown) {
02307         // Getting attrs is expensive, so use HasAttr() first.
02308         if (!aContent->HasAttr(aNamespaceID, aAttrName)) {
02309             return PR_FALSE;
02310         }
02311 
02312         if (aAttrValue.EqualsLiteral("*")) {
02313             // Wildcard.  We already know we have this attr, so we match
02314             return PR_TRUE;
02315         }
02316 
02317         nsAutoString value;
02318         nsresult rv = aContent->GetAttr(aNamespaceID, aAttrName, value);
02319 
02320         return NS_SUCCEEDED(rv) && value.Equals(aAttrValue);
02321     }
02322 
02323 
02324     // Qualified name match. This takes more work.
02325 
02326     PRUint32 count = aContent->GetAttrCount();
02327     for (PRUint32 i = 0; i < count; ++i) {
02328         PRInt32 namespaceID;
02329         nsCOMPtr<nsIAtom> name;
02330         nsCOMPtr<nsIAtom> prefix;
02331 
02332 #ifdef DEBUG
02333         nsresult rv =
02334 #endif
02335         aContent->GetAttrNameAt(i, &namespaceID, getter_AddRefs(name),
02336                                 getter_AddRefs(prefix));
02337         NS_ASSERTION(NS_SUCCEEDED(rv), "GetAttrCount lied?");
02338         
02339         PRBool nameMatch;
02340         if (!prefix) {
02341             nameMatch = name == aAttrName;
02342         } else {
02343             nsAutoString nameStr;
02344             nsAutoString prefixStr;
02345             name->ToString(nameStr);
02346             prefix->ToString(prefixStr);
02347             nameMatch = aAttrName->Equals(prefixStr + NS_LITERAL_STRING(":") +
02348                                           nameStr);
02349         }
02350 
02351         if (nameMatch) {
02352             if (aAttrValue.EqualsLiteral("*")) {
02353                 return PR_TRUE;
02354             }
02355 
02356             nsAutoString value;
02357             nsresult rv = aContent->GetAttr(namespaceID, name, value);
02358 
02359             return NS_SUCCEEDED(rv) && value.Equals(aAttrValue);
02360         }
02361     }
02362 
02363     return PR_FALSE;
02364 }
02365 
02366 nsresult
02367 nsXULDocument::PrepareToLoad(nsISupports* aContainer,
02368                              const char* aCommand,
02369                              nsIChannel* aChannel,
02370                              nsILoadGroup* aLoadGroup,
02371                              nsIParser** aResult)
02372 {
02373     nsresult rv;
02374 
02375     // Get the document's principal
02376     nsCOMPtr<nsISupports> owner;
02377     rv = aChannel->GetOwner(getter_AddRefs(owner));
02378     if (NS_FAILED(rv)) return rv;
02379 
02380     nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
02381 
02382     return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
02383 }
02384 
02385 
02386 nsresult
02387 nsXULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
02388                                       nsIPrincipal* aDocumentPrincipal,
02389                                       nsIParser** aResult)
02390 {
02391     nsresult rv;
02392 
02393     // Create a new prototype document.
02394     rv = NS_NewXULPrototypeDocument(nsnull,
02395                                     NS_GET_IID(nsIXULPrototypeDocument),
02396                                     getter_AddRefs(mCurrentPrototype));
02397     if (NS_FAILED(rv)) return rv;
02398 
02399     // Bootstrap the master document prototype.
02400     if (! mMasterPrototype) {
02401         mMasterPrototype = mCurrentPrototype;
02402         mMasterPrototype->SetDocumentPrincipal(aDocumentPrincipal);
02403     }
02404 
02405     rv = mCurrentPrototype->SetURI(aURI);
02406     if (NS_FAILED(rv)) return rv;
02407 
02408     // Create a XUL content sink, a parser, and kick off a load for
02409     // the overlay.
02410     nsCOMPtr<nsIXULContentSink> sink;
02411     rv = NS_NewXULContentSink(getter_AddRefs(sink));
02412     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create XUL content sink");
02413     if (NS_FAILED(rv)) return rv;
02414 
02415     rv = sink->Init(this, mCurrentPrototype);
02416     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
02417     if (NS_FAILED(rv)) return rv;
02418 
02419     nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
02420     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
02421     if (NS_FAILED(rv)) return rv;
02422 
02423     parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
02424                        eViewSource);
02425 
02426     parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
02427                                kCharsetFromDocTypeDefault);
02428     parser->SetContentSink(sink); // grabs a reference to the parser
02429 
02430     *aResult = parser;
02431     NS_ADDREF(*aResult);
02432     return NS_OK;
02433 }
02434 
02435 
02436 nsresult
02437 nsXULDocument::ApplyPersistentAttributes()
02438 {
02439     // Add all of the 'persisted' attributes into the content
02440     // model.
02441     if (! mLocalStore)
02442         return NS_OK;
02443 
02444     mApplyingPersistedAttrs = PR_TRUE;
02445 
02446     nsresult rv;
02447     nsCOMPtr<nsISupportsArray> elements;
02448     rv = NS_NewISupportsArray(getter_AddRefs(elements));
02449     if (NS_FAILED(rv)) return rv;
02450 
02451     nsCAutoString docurl;
02452     mDocumentURI->GetSpec(docurl);
02453 
02454     nsCOMPtr<nsIRDFResource> doc;
02455     gRDFService->GetResource(docurl, getter_AddRefs(doc));
02456 
02457     nsCOMPtr<nsISimpleEnumerator> persisted;
02458     mLocalStore->GetTargets(doc, kNC_persist, PR_TRUE, getter_AddRefs(persisted));
02459 
02460     while (1) {
02461         PRBool hasmore = PR_FALSE;
02462         persisted->HasMoreElements(&hasmore);
02463         if (! hasmore)
02464             break;
02465 
02466         nsCOMPtr<nsISupports> isupports;
02467         persisted->GetNext(getter_AddRefs(isupports));
02468 
02469         nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
02470         if (! resource) {
02471             NS_WARNING("expected element to be a resource");
02472             continue;
02473         }
02474 
02475         const char *uri;
02476         resource->GetValueConst(&uri);
02477         if (! uri)
02478             continue;
02479 
02480         nsAutoString id;
02481         nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id);
02482 
02483         if (id.IsEmpty())
02484             continue;
02485 
02486         // This will clear the array if there are no elements.
02487         GetElementsForID(id, elements);
02488 
02489         PRUint32 cnt = 0;
02490         elements->Count(&cnt);
02491         if (! cnt)
02492             continue;
02493 
02494         ApplyPersistentAttributesToElements(resource, elements);
02495     }
02496 
02497     mApplyingPersistedAttrs = PR_FALSE;
02498 
02499     return NS_OK;
02500 }
02501 
02502 
02503 nsresult
02504 nsXULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
02505                                                    nsISupportsArray* aElements)
02506 {
02507     nsresult rv;
02508 
02509     nsCOMPtr<nsISimpleEnumerator> attrs;
02510     rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
02511     if (NS_FAILED(rv)) return rv;
02512 
02513     while (1) {
02514         PRBool hasmore;
02515         rv = attrs->HasMoreElements(&hasmore);
02516         if (NS_FAILED(rv)) return rv;
02517 
02518         if (! hasmore)
02519             break;
02520 
02521         nsCOMPtr<nsISupports> isupports;
02522         rv = attrs->GetNext(getter_AddRefs(isupports));
02523         if (NS_FAILED(rv)) return rv;
02524 
02525         nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
02526         if (! property) {
02527             NS_WARNING("expected a resource");
02528             continue;
02529         }
02530 
02531         const char* attrname;
02532         rv = property->GetValueConst(&attrname);
02533         if (NS_FAILED(rv)) return rv;
02534 
02535         nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
02536         if (! attr)
02537             return NS_ERROR_OUT_OF_MEMORY;
02538 
02539         // XXX could hang namespace off here, as well...
02540 
02541         nsCOMPtr<nsIRDFNode> node;
02542         rv = mLocalStore->GetTarget(aResource, property, PR_TRUE,
02543                                     getter_AddRefs(node));
02544         if (NS_FAILED(rv)) return rv;
02545 
02546         nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
02547         if (! literal) {
02548             NS_WARNING("expected a literal");
02549             continue;
02550         }
02551 
02552         const PRUnichar* value;
02553         rv = literal->GetValueConst(&value);
02554         if (NS_FAILED(rv)) return rv;
02555 
02556         nsDependentString wrapper(value);
02557 
02558         PRUint32 cnt;
02559         rv = aElements->Count(&cnt);
02560         if (NS_FAILED(rv)) return rv;
02561 
02562         for (PRInt32 i = PRInt32(cnt) - 1; i >= 0; --i) {
02563             nsISupports* isupports2 = aElements->ElementAt(i);
02564             if (! isupports2)
02565                 continue;
02566 
02567             nsCOMPtr<nsIContent> element = do_QueryInterface(isupports2);
02568             NS_RELEASE(isupports2);
02569 
02570             rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
02571                                   attr,
02572                                   wrapper,
02573                                   PR_TRUE);
02574         }
02575     }
02576 
02577     return NS_OK;
02578 }
02579 
02580 //----------------------------------------------------------------------
02581 //
02582 // nsXULDocument::ContextStack
02583 //
02584 
02585 nsXULDocument::ContextStack::ContextStack()
02586     : mTop(nsnull), mDepth(0)
02587 {
02588 }
02589 
02590 nsXULDocument::ContextStack::~ContextStack()
02591 {
02592     while (mTop) {
02593         Entry* doomed = mTop;
02594         mTop = mTop->mNext;
02595         NS_IF_RELEASE(doomed->mElement);
02596         delete doomed;
02597     }
02598 }
02599 
02600 nsresult
02601 nsXULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
02602                                   nsIContent* aElement)
02603 {
02604     Entry* entry = new Entry;
02605     if (! entry)
02606         return NS_ERROR_OUT_OF_MEMORY;
02607 
02608     entry->mPrototype = aPrototype;
02609     entry->mElement   = aElement;
02610     NS_IF_ADDREF(entry->mElement);
02611     entry->mIndex     = 0;
02612 
02613     entry->mNext = mTop;
02614     mTop = entry;
02615 
02616     ++mDepth;
02617     return NS_OK;
02618 }
02619 
02620 nsresult
02621 nsXULDocument::ContextStack::Pop()
02622 {
02623     if (mDepth == 0)
02624         return NS_ERROR_UNEXPECTED;
02625 
02626     Entry* doomed = mTop;
02627     mTop = mTop->mNext;
02628     --mDepth;
02629 
02630     NS_IF_RELEASE(doomed->mElement);
02631     delete doomed;
02632     return NS_OK;
02633 }
02634 
02635 nsresult
02636 nsXULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
02637                                            nsIContent** aElement,
02638                                            PRInt32* aIndex)
02639 {
02640     if (mDepth == 0)
02641         return NS_ERROR_UNEXPECTED;
02642 
02643     *aPrototype = mTop->mPrototype;
02644     *aElement   = mTop->mElement;
02645     NS_IF_ADDREF(*aElement);
02646     *aIndex     = mTop->mIndex;
02647 
02648     return NS_OK;
02649 }
02650 
02651 
02652 nsresult
02653 nsXULDocument::ContextStack::SetTopIndex(PRInt32 aIndex)
02654 {
02655     if (mDepth == 0)
02656         return NS_ERROR_UNEXPECTED;
02657 
02658     mTop->mIndex = aIndex;
02659     return NS_OK;
02660 }
02661 
02662 
02663 PRBool
02664 nsXULDocument::ContextStack::IsInsideXULTemplate()
02665 {
02666     if (mDepth) {
02667         for (nsIContent* element = mTop->mElement; element;
02668              element = element->GetParent()) {
02669 
02670             nsINodeInfo *ni = element->GetNodeInfo();
02671 
02672             if (ni && ni->Equals(nsXULAtoms::Template, kNameSpaceID_XUL)) {
02673                 return PR_TRUE;
02674             }
02675         }
02676     }
02677     return PR_FALSE;
02678 }
02679 
02680 
02681 //----------------------------------------------------------------------
02682 //
02683 // Content model walking routines
02684 //
02685 
02686 nsresult
02687 nsXULDocument::PrepareToWalk()
02688 {
02689     // Prepare to walk the mCurrentPrototype
02690     nsresult rv;
02691 
02692     // Keep an owning reference to the prototype document so that its
02693     // elements aren't yanked from beneath us.
02694     mPrototypes.AppendObject(mCurrentPrototype);
02695 
02696     // Push the overlay references onto our overlay processing
02697     // stack. GetOverlayReferences() will return an ordered array of
02698     // overlay references...
02699     nsCOMPtr<nsISupportsArray> overlays;
02700     rv = mCurrentPrototype->GetOverlayReferences(getter_AddRefs(overlays));
02701     if (NS_FAILED(rv)) return rv;
02702 
02703     // ...and we preserve this ordering by appending to our
02704     // mUnloadedOverlays array in reverse order
02705     PRUint32 count;
02706     overlays->Count(&count);
02707     for (PRInt32 i = count - 1; i >= 0; --i) {
02708         nsISupports* isupports = overlays->ElementAt(i);
02709         mUnloadedOverlays->AppendElement(isupports);
02710         NS_IF_RELEASE(isupports);
02711     }
02712 
02713 
02714     // Now check the chrome registry for any additional overlays.
02715     rv = AddChromeOverlays();
02716 
02717     // Get the prototype's root element and initialize the context
02718     // stack for the prototype walk.
02719     nsXULPrototypeElement* proto;
02720     rv = mCurrentPrototype->GetRootElement(&proto);
02721     if (NS_FAILED(rv)) return rv;
02722 
02723 
02724     if (! proto) {
02725 #ifdef PR_LOGGING
02726         if (PR_LOG_TEST(gXULLog, PR_LOG_ERROR)) {
02727             nsCOMPtr<nsIURI> url;
02728             rv = mCurrentPrototype->GetURI(getter_AddRefs(url));
02729             if (NS_FAILED(rv)) return rv;
02730 
02731             nsCAutoString urlspec;
02732             rv = url->GetSpec(urlspec);
02733             if (NS_FAILED(rv)) return rv;
02734 
02735             PR_LOG(gXULLog, PR_LOG_ERROR,
02736                    ("xul: error parsing '%s'", urlspec.get()));
02737         }
02738 #endif
02739 
02740         return NS_OK;
02741     }
02742 
02743     // Do one-time initialization if we're preparing to walk the
02744     // master document's prototype.
02745     nsCOMPtr<nsIContent> root;
02746 
02747     if (mState == eState_Master) {
02748         rv = CreateElementFromPrototype(proto, getter_AddRefs(root));
02749         if (NS_FAILED(rv)) return rv;
02750 
02751         rv = SetRootContent(root);
02752         if (NS_FAILED(rv)) return rv;
02753         
02754         // Add the root element to the XUL document's ID-to-element map.
02755         rv = AddElementToMap(root);
02756         if (NS_FAILED(rv)) return rv;
02757 
02758         // Add a dummy channel to the load group as a placeholder for the document
02759         // load
02760         rv = PlaceHolderRequest::Create(getter_AddRefs(mPlaceHolderRequest));
02761         if (NS_FAILED(rv)) return rv;
02762 
02763         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
02764 
02765         if (group) {
02766             rv = mPlaceHolderRequest->SetLoadGroup(group);
02767             if (NS_FAILED(rv)) return rv;
02768             rv = group->AddRequest(mPlaceHolderRequest, nsnull);
02769             if (NS_FAILED(rv)) return rv;
02770         }
02771     }
02772 
02773     // There'd better not be anything on the context stack at this
02774     // point! This is the basis case for our "induction" in
02775     // ResumeWalk(), below, which'll assume that there's always a
02776     // content element on the context stack if either 1) we're in the
02777     // "master" document, or 2) we're in an overlay, and we've got
02778     // more than one prototype element (the single, root "overlay"
02779     // element) on the stack.
02780     NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
02781     if (mContextStack.Depth() != 0)
02782         return NS_ERROR_UNEXPECTED;
02783 
02784     rv = mContextStack.Push(proto, root);
02785     if (NS_FAILED(rv)) return rv;
02786 
02787     return NS_OK;
02788 }
02789 
02790 
02791 nsresult
02792 nsXULDocument::AddChromeOverlays()
02793 {
02794     nsresult rv;
02795 
02796     nsCOMPtr<nsIURI> docUri;
02797     rv = mCurrentPrototype->GetURI(getter_AddRefs(docUri));
02798     NS_ENSURE_SUCCESS(rv, rv);
02799 
02800     /* overlays only apply to chrome, skip all content URIs */
02801     if (!IsChromeURI(docUri)) return NS_OK;
02802 
02803     nsCOMPtr<nsIXULOverlayProvider> chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
02804     // In embedding situations, the chrome registry may not provide overlays,
02805     // or even exist at all; that's OK.
02806     NS_ENSURE_TRUE(chromeReg, NS_OK);
02807 
02808     nsCOMPtr<nsISimpleEnumerator> overlays;
02809     rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
02810     NS_ENSURE_SUCCESS(rv, rv);
02811 
02812     PRBool moreOverlays;
02813     nsCOMPtr<nsISupports> next;
02814     nsCOMPtr<nsIURI> uri;
02815 
02816     while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
02817            moreOverlays) {
02818         rv = overlays->GetNext(getter_AddRefs(next));
02819         if (NS_FAILED(rv) || !next) continue;
02820 
02821         uri = do_QueryInterface(next);
02822         if (!uri) {
02823             NS_ERROR("Chrome registry handed me a non-nsIURI object!");
02824             continue;
02825         }
02826 
02827         mUnloadedOverlays->AppendElement(uri);
02828     }
02829 
02830     return NS_OK;
02831 }
02832 
02833 NS_IMETHODIMP
02834 nsXULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver)
02835 {
02836     nsresult rv;
02837 
02838     nsCOMPtr<nsIURI> uri;
02839     rv = NS_NewURI(getter_AddRefs(uri), aURL, nsnull);
02840     if (NS_FAILED(rv)) return rv;
02841 
02842     if (aObserver) {
02843         nsIObserver* obs = nsnull;
02844         NS_ENSURE_TRUE(mOverlayLoadObservers.IsInitialized() || mOverlayLoadObservers.Init(), 
02845                        NS_ERROR_OUT_OF_MEMORY);
02846         
02847         obs = mOverlayLoadObservers.GetWeak(uri);
02848 
02849         if (obs) {
02850             // We don't support loading the same overlay twice into the same
02851             // document - that doesn't make sense anyway.
02852             return NS_ERROR_FAILURE;
02853         }
02854         mOverlayLoadObservers.Put(uri, aObserver);
02855     }
02856     PRBool shouldReturn;
02857     rv = LoadOverlayInternal(uri, PR_TRUE, &shouldReturn);
02858     if (NS_FAILED(rv) && mOverlayLoadObservers.IsInitialized())
02859         mOverlayLoadObservers.Remove(uri); // remove the observer if LoadOverlayInternal generated an error
02860     return rv;
02861 }
02862 
02863 nsresult
02864 nsXULDocument::LoadOverlayInternal(nsIURI* aURI, PRBool aIsDynamic, PRBool* aShouldReturn)
02865 {
02866     nsresult rv;
02867 
02868     *aShouldReturn = PR_FALSE;
02869 
02870     nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
02871     NS_ENSURE_SUCCESS(rv, rv);
02872 
02873 #ifdef PR_LOGGING
02874     if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
02875         nsCAutoString urlspec;
02876         aURI->GetSpec(urlspec);
02877 
02878         PR_LOG(gXULLog, PR_LOG_DEBUG,
02879                 ("xul: loading overlay %s", urlspec.get()));
02880     }
02881 #endif
02882 
02883     mResolutionPhase = nsForwardReference::eStart;
02884 
02885     // Chrome documents are allowed to load overlays from anywhere.
02886     // In all other cases, the overlay is only allowed to load if
02887     // the master document and prototype document have the same origin.
02888 
02889     if (!IsChromeURI(mDocumentURI)) {
02890         // Make sure we're allowed to load this overlay.
02891         rv = secMan->CheckSameOriginURI(mDocumentURI, aURI);
02892         if (NS_FAILED(rv)) return rv;
02893     }
02894 
02895     // Look in the prototype cache for the prototype document with
02896     // the specified overlay URI.
02897     PRBool overlayIsChrome = IsChromeURI(aURI);
02898     if (overlayIsChrome)
02899         gXULCache->GetPrototype(aURI, getter_AddRefs(mCurrentPrototype));
02900     else
02901         mCurrentPrototype = nsnull;
02902 
02903     // Same comment as nsChromeProtocolHandler::NewChannel and
02904     // nsXULDocument::StartDocumentLoad
02905     // - Ben Goodger
02906     //
02907     // We don't abort on failure here because there are too many valid
02908     // cases that can return failure, and the null-ness of |proto| is
02909     // enough to trigger the fail-safe parse-from-disk solution.
02910     // Example failure cases (for reference) include:
02911     //
02912     // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
02913     //                         parse from disk
02914     // other: the FastLoad file, XUL.mfl, could not be found, probably
02915     //        due to being accessed before a profile has been selected
02916     //        (e.g. loading chrome for the profile manager itself).
02917     //        The .xul file must be parsed from disk.
02918 
02919     PRBool useXULCache;
02920     gXULCache->GetEnabled(&useXULCache);
02921     mIsWritingFastLoad = useXULCache;
02922 
02923     if (useXULCache && mCurrentPrototype) {
02924         PRBool loaded;
02925         rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
02926         if (NS_FAILED(rv)) return rv;
02927 
02928         if (! loaded) {
02929             // Return to the main event loop and eagerly await the
02930             // prototype overlay load's completion. When the content
02931             // sink completes, it will trigger an EndLoad(), which'll
02932             // wind us back up here, in ResumeWalk().
02933             *aShouldReturn = PR_TRUE;
02934             return NS_OK;
02935         }
02936 
02937         // Found the overlay's prototype in the cache, fully loaded.
02938         rv = AddPrototypeSheets();
02939         if (NS_FAILED(rv)) return rv;
02940 
02941         // Now prepare to walk the prototype to create its content
02942         rv = PrepareToWalk();
02943         if (NS_FAILED(rv)) return rv;
02944 
02945         PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
02946 
02947         // If this is a dynamic overlay and we have the prototype in the chrome 
02948         // cache already, we must manually call ResumeWalk.
02949         if (aIsDynamic)
02950             return ResumeWalk();
02951     }
02952     else {
02953         // Not there. Initiate a load.
02954         PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
02955 
02956         nsCOMPtr<nsIParser> parser;
02957         rv = PrepareToLoadPrototype(aURI, "view", nsnull, getter_AddRefs(parser));
02958         if (NS_FAILED(rv)) return rv;
02959 
02960         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
02961         // so we don't have to re-check whether the cache is enabled all
02962         // the time.
02963         mIsWritingFastLoad = useXULCache;
02964 
02965         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
02966         if (! listener)
02967             return NS_ERROR_UNEXPECTED;
02968 
02969         // Add an observer to the parser; this'll get called when
02970         // Necko fires its On[Start|Stop]Request() notifications,
02971         // and will let us recover from a missing overlay.
02972         ParserObserver* parserObserver = new ParserObserver(this);
02973         if (! parserObserver)
02974             return NS_ERROR_OUT_OF_MEMORY;
02975 
02976         NS_ADDREF(parserObserver);
02977         parser->Parse(aURI, parserObserver);
02978         NS_RELEASE(parserObserver);
02979 
02980         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
02981         rv = NS_OpenURI(listener, nsnull, aURI, nsnull, group);
02982         if (NS_FAILED(rv)) {
02983             // Just move on to the next overlay.  NS_OpenURI could fail
02984             // just because a channel could not be opened, which can happen
02985             // if a file or chrome package does not exist.
02986             ReportMissingOverlay(aURI);
02987             return rv;
02988         }
02989 
02990         // If it's a 'chrome:' prototype document, then put it into
02991         // the prototype cache; other XUL documents will be reloaded
02992         // each time.  We must do this after NS_OpenURI and AsyncOpen,
02993         // or chrome code will wrongly create a cached chrome channel
02994         // instead of a real one.
02995         if (useXULCache && overlayIsChrome) {
02996             rv = gXULCache->PutPrototype(mCurrentPrototype);
02997             if (NS_FAILED(rv)) return rv;
02998         }
02999 
03000         // Return to the main event loop and eagerly await the
03001         // overlay load's completion. When the content sink
03002         // completes, it will trigger an EndLoad(), which'll wind
03003         // us back in ResumeWalk().
03004         if (!aIsDynamic)
03005             *aShouldReturn = PR_TRUE;
03006     }
03007     return NS_OK;
03008 }
03009 
03010 PR_STATIC_CALLBACK(PLDHashOperator)
03011 FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr<nsIObserver>& aObserver, void* aClosure)
03012 {
03013     aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get());
03014 
03015     typedef nsInterfaceHashtable<nsURIHashKey,nsIObserver> table;
03016     table* observers = NS_STATIC_CAST(table*, aClosure);
03017     observers->Remove(aKey);
03018 
03019     return PL_DHASH_REMOVE;
03020 }
03021 
03022 nsresult
03023 nsXULDocument::ResumeWalk()
03024 {
03025     // Walk the prototype and build the delegate content model. The
03026     // walk is performed in a top-down, left-to-right fashion. That
03027     // is, a parent is built before any of its children; a node is
03028     // only built after all of its siblings to the left are fully
03029     // constructed.
03030     //
03031     // It is interruptable so that transcluded documents (e.g.,
03032     // <html:script src="..." />) can be properly re-loaded if the
03033     // cached copy of the document becomes stale.
03034     nsresult rv;
03035     nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
03036     NS_ENSURE_SUCCESS(rv, rv);
03037 
03038     while (1) {
03039         // Begin (or resume) walking the current prototype.
03040 
03041         while (mContextStack.Depth() > 0) {
03042             // Look at the top of the stack to determine what we're
03043             // currently working on.
03044             nsXULPrototypeElement* proto;
03045             nsCOMPtr<nsIContent> element;
03046             PRInt32 indx;
03047             rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
03048             if (NS_FAILED(rv)) return rv;
03049 
03050             if (indx >= (PRInt32)proto->mNumChildren) {
03051                 if (element) {
03052                     // We've processed all of the prototype's children. If
03053                     // we're in the master prototype, do post-order
03054                     // document-level hookup. (An overlay will get its
03055                     // document hookup done when it's successfully
03056                     // resolved.)
03057                     if (mState == eState_Master) {
03058                         AddElementToDocumentPost(element);
03059                     }
03060 
03061 #ifdef MOZ_XTF
03062                     if (element->GetNameSpaceID() > kNameSpaceID_LastBuiltin) {
03063                         element->DoneAddingChildren();
03064                     }
03065 #endif
03066                 }
03067                 // Now pop the context stack back up to the parent
03068                 // element and continue the prototype walk.
03069                 mContextStack.Pop();
03070                 continue;
03071             }
03072 
03073             // Grab the next child, and advance the current context stack
03074             // to the next sibling to our right.
03075             nsXULPrototypeNode* childproto = proto->mChildren[indx];
03076             mContextStack.SetTopIndex(++indx);
03077 
03078             switch (childproto->mType) {
03079             case nsXULPrototypeNode::eType_Element: {
03080                 // An 'element', which may contain more content.
03081                 nsXULPrototypeElement* protoele =
03082                     NS_REINTERPRET_CAST(nsXULPrototypeElement*, childproto);
03083 
03084                 nsCOMPtr<nsIContent> child;
03085 
03086                 if ((mState == eState_Master) || (mContextStack.Depth() > 1)) {
03087                     // We're in the master document -or -we're in an
03088                     // overlay, and far enough down into the overlay's
03089                     // content that we can simply build the delegates
03090                     // and attach them to the parent node.
03091                     NS_ASSERTION(element != nsnull, "no element on context stack");
03092 
03093                     rv = CreateElementFromPrototype(protoele,
03094                                                     getter_AddRefs(child));
03095                     if (NS_FAILED(rv)) return rv;
03096 
03097                     // ...and append it to the content model.
03098                     rv = element->AppendChildTo(child, PR_FALSE);
03099                     if (NS_FAILED(rv)) return rv;
03100 
03101                     // do pre-order document-level hookup, but only if
03102                     // we're in the master document. For an overlay,
03103                     // this will happen when the overlay is
03104                     // successfully resolved.
03105                     if (mState == eState_Master)
03106                         AddElementToDocumentPre(child);
03107                 }
03108                 else {
03109                     // We're in the "first ply" of an overlay: the
03110                     // "hookup" nodes. Create an 'overlay' element so
03111                     // that we can continue to build content, and
03112                     // enter a forward reference so we can hook it up
03113                     // later.
03114                     rv = CreateOverlayElement(protoele, getter_AddRefs(child));
03115                     if (NS_FAILED(rv)) return rv;
03116                 }
03117 
03118                 // If it has children, push the element onto the context
03119                 // stack and begin to process them.
03120                 if (protoele->mNumChildren > 0) {
03121                     rv = mContextStack.Push(protoele, child);
03122                     if (NS_FAILED(rv)) return rv;
03123                 }
03124                 else if (mState == eState_Master) {
03125                     // If there are no children, and we're in the
03126                     // master document, do post-order document hookup
03127                     // immediately.
03128                     AddElementToDocumentPost(child);
03129                 }
03130             }
03131             break;
03132 
03133             case nsXULPrototypeNode::eType_Script: {
03134                 // A script reference. Execute the script immediately;
03135                 // this may have side effects in the content model.
03136                 nsXULPrototypeScript* scriptproto =
03137                     NS_REINTERPRET_CAST(nsXULPrototypeScript*, childproto);
03138 
03139                 if (scriptproto->mSrcURI) {
03140                     // A transcluded script reference; this may
03141                     // "block" our prototype walk if the script isn't
03142                     // cached, or the cached copy of the script is
03143                     // stale and must be reloaded.
03144                     PRBool blocked;
03145                     rv = LoadScript(scriptproto, &blocked);
03146                     // If the script cannot be loaded, just keep going!
03147 
03148                     if (NS_SUCCEEDED(rv) && blocked)
03149                         return NS_OK;
03150                 }
03151                 else if (scriptproto->mJSObject) {
03152                     // An inline script
03153                     rv = ExecuteScript(scriptproto->mJSObject);
03154                     if (NS_FAILED(rv)) return rv;
03155                 }
03156             }
03157             break;
03158 
03159             case nsXULPrototypeNode::eType_Text: {
03160                 // A simple text node.
03161 
03162                 if ((mState == eState_Master) || (mContextStack.Depth() > 1)) {
03163                     // We're in the master document -or -we're in an
03164                     // overlay, and far enough down into the overlay's
03165                     // content that we can simply build the delegates
03166                     // and attach them to the parent node.
03167                     NS_ASSERTION(element, "no element on context stack");
03168 
03169                     nsCOMPtr<nsITextContent> text;
03170                     rv = NS_NewTextNode(getter_AddRefs(text),
03171                                         mNodeInfoManager);
03172                     NS_ENSURE_SUCCESS(rv, rv);
03173 
03174                     nsXULPrototypeText* textproto =
03175                         NS_REINTERPRET_CAST(nsXULPrototypeText*, childproto);
03176                     text->SetText(textproto->mValue, PR_FALSE);
03177 
03178                     nsCOMPtr<nsIContent> child = do_QueryInterface(text);
03179                     if (! child)
03180                         return NS_ERROR_UNEXPECTED;
03181 
03182                     rv = element->AppendChildTo(child, PR_FALSE);
03183                     if (NS_FAILED(rv)) return rv;
03184                 }
03185             }
03186             break;
03187             }
03188         }
03189 
03190         // Once we get here, the context stack will have been
03191         // depleted. That means that the entire prototype has been
03192         // walked and content has been constructed.
03193 
03194         // If we're not already, mark us as now processing overlays.
03195         mState = eState_Overlay;
03196 
03197         PRUint32 count;
03198         mUnloadedOverlays->Count(&count);
03199 
03200         // If there are no overlay URIs, then we're done.
03201         if (! count)
03202             break;
03203 
03204         nsCOMPtr<nsIURI> uri =
03205             dont_AddRef(NS_REINTERPRET_CAST(nsIURI*, mUnloadedOverlays->ElementAt(count - 1)));
03206 
03207         mUnloadedOverlays->RemoveElementAt(count - 1);
03208 
03209 #ifdef PR_LOGGING
03210         if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
03211             nsCAutoString urlspec;
03212             uri->GetSpec(urlspec);
03213 
03214             PR_LOG(gXULLog, PR_LOG_DEBUG,
03215                    ("xul: loading overlay %s", urlspec.get()));
03216         }
03217 #endif
03218 
03219         // Chrome documents are allowed to load overlays from anywhere.
03220         // In all other cases, the overlay is only allowed to load if
03221         // the master document and prototype document have the same origin.
03222 
03223         if (!IsChromeURI(mDocumentURI)) {
03224             // Make sure we're allowed to load this overlay.
03225             rv = secMan->CheckSameOriginURI(mDocumentURI, uri);
03226             if (NS_FAILED(rv)) {
03227                 // move on to the next overlay
03228                 continue;
03229             }
03230         }
03231 
03232         // Look in the prototype cache for the prototype document with
03233         // the specified overlay URI.
03234         PRBool overlayIsChrome = IsChromeURI(uri);
03235         if (overlayIsChrome)
03236             gXULCache->GetPrototype(uri, getter_AddRefs(mCurrentPrototype));
03237         else
03238             mCurrentPrototype = nsnull;
03239 
03240         // Same comment as nsChromeProtocolHandler::NewChannel and
03241         // nsXULDocument::StartDocumentLoad
03242         // - Ben Goodger
03243         //
03244         // We don't abort on failure here because there are too many valid
03245         // cases that can return failure, and the null-ness of |proto| is
03246         // enough to trigger the fail-safe parse-from-disk solution.
03247         // Example failure cases (for reference) include:
03248         //
03249         // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
03250         //                         parse from disk
03251         // other: the FastLoad file, XUL.mfl, could not be found, probably
03252         //        due to being accessed before a profile has been selected
03253         //        (e.g. loading chrome for the profile manager itself).
03254         //        The .xul file must be parsed from disk.
03255 
03256         PRBool useXULCache;
03257         gXULCache->GetEnabled(&useXULCache);
03258 
03259         if (useXULCache && mCurrentPrototype) {
03260             PRBool loaded;
03261             rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
03262             if (NS_FAILED(rv)) return rv;
03263 
03264             if (! loaded) {
03265                 // Return to the main event loop and eagerly await the
03266                 // prototype overlay load's completion. When the content
03267                 // sink completes, it will trigger an EndLoad(), which'll
03268                 // wind us back up here, in ResumeWalk().
03269                 return NS_OK;
03270             }
03271 
03272             // Found the overlay's prototype in the cache, fully loaded.
03273             rv = AddPrototypeSheets();
03274             if (NS_FAILED(rv)) return rv;
03275 
03276             // Now prepare to walk the prototype to create its content
03277             rv = PrepareToWalk();
03278             if (NS_FAILED(rv)) return rv;
03279 
03280             PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
03281         }
03282         else {
03283             // Not there. Initiate a load.
03284             PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
03285 
03286             nsCOMPtr<nsIParser> parser;
03287             rv = PrepareToLoadPrototype(uri, "view", nsnull, getter_AddRefs(parser));
03288             if (NS_FAILED(rv)) return rv;
03289 
03290             // Predicate mIsWritingFastLoad on the XUL cache being enabled,
03291             // so we don't have to re-check whether the cache is enabled all
03292             // the time.
03293             mIsWritingFastLoad = useXULCache;
03294 
03295             nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
03296             if (! listener)
03297                 return NS_ERROR_UNEXPECTED;
03298 
03299             // Add an observer to the parser; this'll get called when
03300             // Necko fires its On[Start|Stop]Request() notifications,
03301             // and will let us recover from a missing overlay.
03302             ParserObserver* parserObserver = new ParserObserver(this);
03303             if (! parserObserver)
03304                 return NS_ERROR_OUT_OF_MEMORY;
03305 
03306             NS_ADDREF(parserObserver);
03307             parser->Parse(uri, parserObserver);
03308             NS_RELEASE(parserObserver);
03309 
03310             nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
03311             rv = NS_OpenURI(listener, nsnull, uri, nsnull, group);
03312             if (NS_FAILED(rv)) {
03313                 // Just move on to the next overlay.  NS_OpenURI could fail
03314                 // just because a channel could not be opened, which can happen
03315                 // if a file or chrome package does not exist.
03316                 ReportMissingOverlay(uri);
03317                 continue;
03318             }
03319 
03320             // If it's a 'chrome:' prototype document, then put it into
03321             // the prototype cache; other XUL documents will be reloaded
03322             // each time.  We must do this after NS_OpenURI and AsyncOpen,
03323             // or chrome code will wrongly create a cached chrome channel
03324             // instead of a real one.
03325             if (useXULCache && overlayIsChrome) {
03326                 rv = gXULCache->PutPrototype(mCurrentPrototype);
03327                 if (NS_FAILED(rv)) return rv;
03328             }
03329 
03330             // Return to the main event loop and eagerly await the
03331             // overlay load's completion. When the content sink
03332             // completes, it will trigger an EndLoad(), which'll wind
03333             // us back up here, in ResumeWalk().
03334             return NS_OK;
03335         }
03336     }
03337 
03338     // If we get here, there is nothing left for us to walk. The content
03339     // model is built and ready for layout.
03340     rv = ResolveForwardReferences();
03341     if (NS_FAILED(rv)) return rv;
03342 
03343     rv = ApplyPersistentAttributes();
03344     if (NS_FAILED(rv)) return rv;
03345 
03346     // XXXldb This is where we should really be setting the chromehidden
03347     // attribute.
03348 
03349     if (!mDocumentLoaded) {
03350         // Make sure we don't reenter here from StartLayout().  Note that
03351         // setting mDocumentLoaded to true here means that if StartLayout()
03352         // causes ResumeWalk() to be reentered, we'll take the other branch of
03353         // the |if (!mDocumentLoaded)| check above and since
03354         // mInitialLayoutComplete will be false will follow the else branch
03355         // there too.  See the big comment there for how such reentry can
03356         // happen.
03357         mDocumentLoaded = PR_TRUE;
03358 
03359         nsAutoString title;
03360         if (mRootContent) {
03361             mRootContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::title,
03362                                   title);
03363         }
03364         SetTitle(title);
03365 
03366         StartLayout();
03367 
03368         if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
03369             gXULCache->WritePrototype(mMasterPrototype);
03370 
03371         NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
03372 
03373         DispatchContentLoadedEvents();
03374 
03375         NS_ASSERTION(mPlaceHolderRequest, "Bug 119310, perhaps overlayinfo referenced a overlay that doesn't exist");
03376         if (mPlaceHolderRequest) {
03377             // Remove the placeholder channel; if we're the last channel in the
03378             // load group, this will fire the OnEndDocumentLoad() method in the
03379             // docshell, and run the onload handlers, etc.
03380             nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
03381             if (group) {
03382                 rv = group->RemoveRequest(mPlaceHolderRequest, nsnull, NS_OK);
03383                 if (NS_FAILED(rv)) return rv;
03384             }
03385         }
03386         mInitialLayoutComplete = PR_TRUE;
03387 
03388         // Walk the set of pending load notifications and notify any observers.
03389         // See below for detail.
03390         if (mPendingOverlayLoadNotifications.IsInitialized())
03391             mPendingOverlayLoadNotifications.Enumerate(FirePendingMergeNotification, (void*)&mOverlayLoadObservers);
03392     }
03393     else {
03394         if (mOverlayLoadObservers.IsInitialized()) {
03395             nsCOMPtr<nsIURI> overlayURI;
03396             mCurrentPrototype->GetURI(getter_AddRefs(overlayURI));
03397             nsCOMPtr<nsIObserver> obs;
03398             if (mInitialLayoutComplete) {
03399                 // We have completed initial layout, so just send the notification.
03400                 mOverlayLoadObservers.Get(overlayURI, getter_AddRefs(obs));
03401                 if (obs)
03402                     obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
03403                 mOverlayLoadObservers.Remove(overlayURI);
03404             }
03405             else {
03406                 // If we have not yet displayed the document for the first time 
03407                 // (i.e. we came in here as the result of a dynamic overlay load
03408                 // which was spawned by a binding-attached event caused by 
03409                 // StartLayout() on the master prototype - we must remember that
03410                 // this overlay has been merged and tell the listeners after 
03411                 // StartLayout() is completely finished rather than doing so 
03412                 // immediately - otherwise we may be executing code that needs to
03413                 // access XBL Binding implementations on nodes for which frames 
03414                 // have not yet been constructed because their bindings have not
03415                 // yet been attached. This can be a race condition because dynamic
03416                 // overlay loading can take varying amounts of time depending on
03417                 // whether or not the overlay prototype is in the XUL cache. The
03418                 // most likely effect of this bug is odd UI initialization due to
03419                 // methods and properties that do not work.
03420                 // XXXbz really, we shouldn't be firing binding constructors
03421                 // until after StartLayout returns!
03422 
03423                 NS_ENSURE_TRUE(mPendingOverlayLoadNotifications.IsInitialized() || mPendingOverlayLoadNotifications.Init(), 
03424                                NS_ERROR_OUT_OF_MEMORY);
03425                 
03426                 mPendingOverlayLoadNotifications.Get(overlayURI, getter_AddRefs(obs));
03427                 if (!obs) {
03428                     mOverlayLoadObservers.Get(overlayURI, getter_AddRefs(obs));
03429                     NS_ASSERTION(obs, "null overlay load observer?");
03430                     mPendingOverlayLoadNotifications.Put(overlayURI, obs);
03431                 }
03432             }
03433         }
03434     }
03435 
03436     return rv;
03437 }
03438 
03439 void
03440 nsXULDocument::ReportMissingOverlay(nsIURI* aURI)
03441 {
03442     NS_PRECONDITION(aURI, "Must have a URI");
03443     
03444     nsCAutoString spec;
03445     aURI->GetSpec(spec);
03446 
03447     NS_ConvertUTF8toUTF16 utfSpec(spec);
03448     const PRUnichar* params[] = { utfSpec.get() };
03449 
03450     nsContentUtils::ReportToConsole(nsContentUtils::eXUL_PROPERTIES,
03451                                     "MissingOverlay",
03452                                     params, NS_ARRAY_LENGTH(params),
03453                                     mDocumentURI,
03454                                     EmptyString(), /* source line */
03455                                     0, /* line number */
03456                                     0, /* column number */
03457                                     nsIScriptError::warningFlag,
03458                                     "XUL Document");
03459 }
03460 
03461 nsresult
03462 nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock)
03463 {
03464     // Load a transcluded script
03465     nsresult rv;
03466 
03467     PRBool isChromeDoc = IsChromeURI(mDocumentURI);
03468 
03469     if (isChromeDoc && aScriptProto->mJSObject) {
03470         rv = ExecuteScript(aScriptProto->mJSObject);
03471 
03472         // Ignore return value from execution, and don't block
03473         *aBlock = PR_FALSE;
03474         return NS_OK;
03475     }
03476 
03477     // Try the XUL script cache, in case two XUL documents source the same
03478     // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
03479     // XXXbe the cache relies on aScriptProto's GC root!
03480     PRBool useXULCache;
03481     gXULCache->GetEnabled(&useXULCache);
03482 
03483     if (isChromeDoc && useXULCache) {
03484         gXULCache->GetScript(aScriptProto->mSrcURI,
03485                              NS_REINTERPRET_CAST(void**, &aScriptProto->mJSObject));
03486 
03487         if (aScriptProto->mJSObject) {
03488             rv = ExecuteScript(aScriptProto->mJSObject);
03489 
03490             // Ignore return value from execution, and don't block
03491             *aBlock = PR_FALSE;
03492             return NS_OK;
03493         }
03494     }
03495 
03496     // Set the current script prototype so that OnStreamComplete can report
03497     // the right file if there are errors in the script.
03498     NS_ASSERTION(!mCurrentScriptProto,
03499                  "still loading a script when starting another load?");
03500     mCurrentScriptProto = aScriptProto;
03501 
03502     if (aScriptProto->mSrcLoading) {
03503         // Another XULDocument load has started, which is still in progress.
03504         // Remember to ResumeWalk this document when the load completes.
03505         mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
03506         aScriptProto->mSrcLoadWaiters = this;
03507         NS_ADDREF_THIS();
03508     }
03509     else {
03510         // Set mSrcLoading *before* calling NS_NewStreamLoader, in case the
03511         // stream completes (probably due to an error) within the activation
03512         // of NS_NewStreamLoader.
03513         aScriptProto->mSrcLoading = PR_TRUE;
03514 
03515         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
03516 
03517         // N.B., the loader will be released in OnStreamComplete
03518         nsIStreamLoader* loader;
03519         rv = NS_NewStreamLoader(&loader, aScriptProto->mSrcURI, this, nsnull, group);
03520         if (NS_FAILED(rv)) return rv;
03521     }
03522 
03523     // Block until OnStreamComplete resumes us.
03524     *aBlock = PR_TRUE;
03525     return NS_OK;
03526 }
03527 
03528 
03529 NS_IMETHODIMP
03530 nsXULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
03531                                 nsISupports* context,
03532                                 nsresult aStatus,
03533                                 PRUint32 stringLen,
03534                                 const PRUint8* string)
03535 {
03536     nsCOMPtr<nsIRequest> request;
03537     aLoader->GetRequest(getter_AddRefs(request));
03538     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
03539 
03540 #ifdef DEBUG
03541     // print a load error on bad status
03542     if (NS_FAILED(aStatus)) {
03543         if (channel) {
03544             nsCOMPtr<nsIURI> uri;
03545             channel->GetURI(getter_AddRefs(uri));
03546             if (uri) {
03547                 nsCAutoString uriSpec;
03548                 uri->GetSpec(uriSpec);
03549                 printf("Failed to load %s\n", uriSpec.get());
03550             }
03551         }
03552     }
03553 #endif
03554 
03555     // This is the completion routine that will be called when a
03556     // transcluded script completes. Compile and execute the script
03557     // if the load was successful, then continue building content
03558     // from the prototype.
03559     nsresult rv;
03560 
03561     NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
03562                  "script source not loading on unichar stream complete?");
03563 
03564     // Clear mCurrentScriptProto now, but save it first for use below in
03565     // the compile/execute code, and in the while loop that resumes walks
03566     // of other documents that raced to load this script
03567     nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
03568     mCurrentScriptProto = nsnull;
03569 
03570     // Clear the prototype's loading flag before executing the script or
03571     // resuming document walks, in case any of those control flows starts a
03572     // new script load.
03573     scriptProto->mSrcLoading = PR_FALSE;
03574 
03575     if (NS_SUCCEEDED(aStatus)) {
03576         // If the including XUL document is a FastLoad document, and we're
03577         // compiling an out-of-line script (one with src=...), then we must
03578         // be writing a new FastLoad file.  If we were reading this script
03579         // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
03580         // nsXULContentSink.cpp) would have already deserialized a non-null
03581         // script->mJSObject, causing control flow at the top of LoadScript
03582         // not to reach here.
03583         nsCOMPtr<nsIURI> uri = scriptProto->mSrcURI;
03584 
03585         // XXX should also check nsIHttpChannel::requestSucceeded
03586 
03587         nsString stringStr;
03588         rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen,
03589                                             EmptyString(), this, stringStr);
03590         if (NS_SUCCEEDED(rv)) {
03591             rv = scriptProto->Compile(stringStr.get(), stringStr.Length(),
03592                                       uri, 1, this, mCurrentPrototype);
03593         }
03594 
03595         aStatus = rv;
03596         if (NS_SUCCEEDED(rv) && scriptProto->mJSObject) {
03597             if (nsScriptLoader::ShouldExecuteScript(this, channel)) {
03598                 rv = ExecuteScript(scriptProto->mJSObject);
03599             }
03600 
03601             // If the XUL cache is enabled, save the script object there in
03602             // case different XUL documents source the same script.
03603             //
03604             // But don't save the script in the cache unless the master XUL
03605             // document URL is a chrome: URL.  It is valid for a URL such as
03606             // about:config to translate into a master document URL, whose
03607             // prototype document nodes -- including prototype scripts that
03608             // hold GC roots protecting their mJSObject pointers -- are not
03609             // cached in the XUL prototype cache.  See StartDocumentLoad,
03610             // the fillXULCache logic.
03611             //
03612             // A document such as about:config is free to load a script via
03613             // a URL such as chrome://global/content/config.js, and we must
03614             // not cache that script object without a prototype cache entry
03615             // containing a companion nsXULPrototypeScript node that owns a
03616             // GC root protecting the script object.  Otherwise, the script
03617             // cache entry will dangle once the uncached prototype document
03618             // is released when its owning nsXULDocument is unloaded.
03619             //
03620             // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
03621             // the true crime story.)
03622             PRBool useXULCache;
03623             gXULCache->GetEnabled(&useXULCache);
03624 
03625             if (useXULCache && IsChromeURI(mDocumentURI)) {
03626                 gXULCache->PutScript(scriptProto->mSrcURI,
03627                                      NS_REINTERPRET_CAST(void*, scriptProto->mJSObject));
03628             }
03629 
03630             if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
03631                 // If we are loading an overlay script, try to serialize
03632                 // it to the FastLoad file here.  Master scripts will be
03633                 // serialized when the master prototype document gets
03634                 // written, at the bottom of ResumeWalk.  That way, master
03635                 // out-of-line scripts are serialized in the same order that
03636                 // they'll be read, in the FastLoad file, which reduces the
03637                 // number of seeks that dump the underlying stream's buffer.
03638                 //
03639                 // Ignore the return value, as we don't need to propagate
03640                 // a failure to write to the FastLoad file, because this
03641                 // method aborts that whole process on error.
03642                 nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner =
03643                   do_QueryInterface(mCurrentPrototype);
03644                 nsIScriptGlobalObject* global = 
03645                   globalOwner->GetScriptGlobalObject();
03646 
03647                 NS_ASSERTION(global != nsnull, "master prototype w/o global?!");
03648                 if (global) {
03649                     nsIScriptContext *scriptContext = global->GetContext();
03650                     if (scriptContext)
03651                         scriptProto->SerializeOutOfLine(nsnull, scriptContext);
03652                 }
03653             }
03654         }
03655         // ignore any evaluation errors
03656     }
03657 
03658     // balance the addref we added in LoadScript()
03659     NS_RELEASE(aLoader);
03660 
03661     rv = ResumeWalk();
03662 
03663     // Load a pointer to the prototype-script's list of nsXULDocuments who
03664     // raced to load the same script
03665     nsXULDocument** docp = &scriptProto->mSrcLoadWaiters;
03666 
03667     // Resume walking other documents that waited for this one's load, first
03668     // executing the script we just compiled, in each doc's script context
03669     nsXULDocument* doc;
03670     while ((doc = *docp) != nsnull) {
03671         NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
03672                      "waiting for wrong script to load?");
03673         doc->mCurrentScriptProto = nsnull;
03674 
03675         // Unlink doc from scriptProto's list before executing and resuming
03676         *docp = doc->mNextSrcLoadWaiter;
03677         doc->mNextSrcLoadWaiter = nsnull;
03678 
03679         // Execute only if we loaded and compiled successfully, then resume
03680         if (NS_SUCCEEDED(aStatus) && scriptProto->mJSObject &&
03681             nsScriptLoader::ShouldExecuteScript(doc, channel)) {
03682            doc->ExecuteScript(scriptProto->mJSObject);
03683         }
03684         doc->ResumeWalk();
03685         NS_RELEASE(doc);
03686     }
03687 
03688     return rv;
03689 }
03690 
03691 
03692 nsresult
03693 nsXULDocument::ExecuteScript(JSObject* aScriptObject)
03694 {
03695     NS_PRECONDITION(aScriptObject != nsnull, "null ptr");
03696     if (! aScriptObject)
03697         return NS_ERROR_NULL_POINTER;
03698 
03699     // Execute the precompiled script with the given version
03700     nsresult rv = NS_ERROR_UNEXPECTED;
03701 
03702     NS_ASSERTION(mScriptGlobalObject != nsnull, "no script global object");
03703 
03704     nsCOMPtr<nsIScriptContext> context;
03705     if (mScriptGlobalObject && (context = mScriptGlobalObject->GetContext()))
03706         rv = context->ExecuteScript(aScriptObject,
03707                                     mScriptGlobalObject->GetGlobalJSObject(),
03708                                     nsnull, nsnull);
03709 
03710     return rv;
03711 }
03712 
03713 
03714 nsresult
03715 nsXULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
03716                                           nsIContent** aResult)
03717 {
03718     // Create a content model element from a prototype element.
03719     NS_PRECONDITION(aPrototype != nsnull, "null ptr");
03720     if (! aPrototype)
03721         return NS_ERROR_NULL_POINTER;
03722 
03723     *aResult = nsnull;
03724     nsresult rv = NS_OK;
03725 
03726 #ifdef PR_LOGGING
03727     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
03728         nsAutoString tagstr;
03729         aPrototype->mNodeInfo->GetQualifiedName(tagstr);
03730 
03731         nsCAutoString tagstrC;
03732         tagstrC.AssignWithConversion(tagstr);
03733         PR_LOG(gXULLog, PR_LOG_NOTICE,
03734                ("xul: creating <%s> from prototype",
03735                 tagstrC.get()));
03736     }
03737 #endif
03738 
03739     nsCOMPtr<nsIContent> result;
03740 
03741     if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
03742         // If it's a XUL element, it'll be lightweight until somebody
03743         // monkeys with it.
03744         rv = nsXULElement::Create(aPrototype, this, PR_TRUE, getter_AddRefs(result));
03745         if (NS_FAILED(rv)) return rv;
03746     }
03747     else {
03748         // If it's not a XUL element, it's gonna be heavyweight no matter
03749         // what. So we need to copy everything out of the prototype
03750         // into the element.  Get a nodeinfo from our nodeinfo manager
03751         // for this node.
03752         nsCOMPtr<nsINodeInfo> newNodeInfo;
03753         rv = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
03754                                            aPrototype->mNodeInfo->GetPrefixAtom(),
03755                                            aPrototype->mNodeInfo->NamespaceID(),
03756                                            getter_AddRefs(newNodeInfo));
03757         if (NS_FAILED(rv)) return rv;
03758         rv = NS_NewElement(getter_AddRefs(result), newNodeInfo->NamespaceID(),
03759                            newNodeInfo);
03760         if (NS_FAILED(rv)) return rv;
03761 
03762 #ifdef MOZ_XTF
03763         if (result && newNodeInfo->NamespaceID() > kNameSpaceID_LastBuiltin) {
03764             result->BeginAddingChildren();
03765         }
03766 #endif
03767 
03768         rv = AddAttributes(aPrototype, result);
03769         if (NS_FAILED(rv)) return rv;
03770     }
03771 
03772     result->SetContentID(mNextContentID++);
03773 
03774     result.swap(*aResult);
03775 
03776     return NS_OK;
03777 }
03778 
03779 nsresult
03780 nsXULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
03781                                     nsIContent** aResult)
03782 {
03783     nsresult rv;
03784 
03785     // This doesn't really do anything except create a placeholder
03786     // element. I'd use an XML element, but it gets its knickers in a
03787     // knot with DOM ranges when you try to remove its children.
03788     nsCOMPtr<nsIContent> element;
03789     rv = nsXULElement::Create(aPrototype, this, PR_FALSE,
03790                               getter_AddRefs(element));
03791     if (NS_FAILED(rv)) return rv;
03792 
03793     OverlayForwardReference* fwdref =
03794         new OverlayForwardReference(this, element);
03795     if (! fwdref)
03796         return NS_ERROR_OUT_OF_MEMORY;
03797 
03798     // transferring ownership to ya...
03799     rv = AddForwardReference(fwdref);
03800     if (NS_FAILED(rv)) return rv;
03801 
03802     *aResult = element;
03803     NS_ADDREF(*aResult);
03804     return NS_OK;
03805 }
03806 
03807 nsresult
03808 nsXULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
03809                              nsIContent* aElement)
03810 {
03811     nsresult rv;
03812 
03813     for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i) {
03814         nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
03815         nsAutoString  valueStr;
03816         protoattr->mValue.ToString(valueStr);
03817 
03818         rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
03819                                protoattr->mName.LocalName(),
03820                                protoattr->mName.GetPrefix(),
03821                                valueStr,
03822                                PR_FALSE);
03823         if (NS_FAILED(rv)) return rv;
03824     }
03825 
03826     return NS_OK;
03827 }
03828 
03829 
03830 nsresult
03831 nsXULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
03832                                           PRBool* aNeedsHookup)
03833 {
03834     // See if the element already has a `database' attribute. If it
03835     // does, then the template builder has already been created.
03836     //
03837     // XXX This approach will crash and burn (well, maybe not _that_
03838     // bad) if aElement is not a XUL element.
03839     //
03840     // XXXvarga Do we still want to support non XUL content?
03841     nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
03842     if (xulElement) {
03843         nsCOMPtr<nsIRDFCompositeDataSource> ds;
03844         xulElement->GetDatabase(getter_AddRefs(ds));
03845         if (ds) {
03846             *aNeedsHookup = PR_FALSE;
03847             return NS_OK;
03848         }
03849     }
03850 
03851     // Check aElement for a 'datasources' attribute, if it has
03852     // one a XUL template builder needs to be hooked up.
03853     *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
03854                                       nsXULAtoms::datasources);
03855     return NS_OK;
03856 }
03857 
03858 nsresult
03859 nsXULDocument::CreateTemplateBuilder(nsIContent* aElement)
03860 {
03861     // Check if need to construct a tree builder or content builder.
03862     PRBool isTreeBuilder = PR_FALSE;
03863 
03864     PRInt32 nameSpaceID;
03865     nsCOMPtr<nsIAtom> baseTag;
03866 
03867     nsCOMPtr<nsIXBLService> xblService = do_GetService("@mozilla.org/xbl;1");
03868     if (xblService) {
03869         xblService->ResolveTag(aElement, &nameSpaceID, getter_AddRefs(baseTag));
03870     }
03871     else {
03872         nsINodeInfo *ni = aElement->GetNodeInfo();
03873         nameSpaceID = ni->NamespaceID();
03874         baseTag = ni->NameAtom();
03875     }
03876 
03877     if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsXULAtoms::tree)) {
03878         // By default, we build content for a tree and then we attach
03879         // the tree content view. However, if the `dont-build-content'
03880         // flag is set, then we we'll attach a tree builder which
03881         // directly implements the tree view.
03882 
03883         nsAutoString flags;
03884         aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::flags, flags);
03885         if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
03886             isTreeBuilder = PR_TRUE;
03887         }
03888     }
03889 
03890     if (isTreeBuilder) {
03891         // Create and initialize a tree builder.
03892         nsCOMPtr<nsIXULTemplateBuilder> builder =
03893             do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
03894 
03895         if (! builder)
03896             return NS_ERROR_FAILURE;
03897 
03898         builder->Init(aElement);
03899 
03900         // Create a <treechildren> if one isn't there already.
03901         // XXXvarga what about attributes?
03902         nsCOMPtr<nsIContent> bodyContent;
03903         nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
03904                                           nsXULAtoms::treechildren,
03905                                           getter_AddRefs(bodyContent));
03906 
03907         if (! bodyContent) {
03908             // Get the document.
03909             nsIDocument *document = aElement->GetDocument();
03910             NS_ASSERTION(document, "no document");
03911             if (! document)
03912                 return NS_ERROR_UNEXPECTED;
03913 
03914             nsresult rv = document->CreateElem(nsXULAtoms::treechildren,
03915                                                nsnull, kNameSpaceID_XUL,
03916                                                PR_FALSE,
03917                                                getter_AddRefs(bodyContent));
03918             NS_ENSURE_SUCCESS(rv, rv);
03919 
03920             aElement->AppendChildTo(bodyContent, PR_FALSE);
03921         }
03922     }
03923     else {
03924         // Create and initialize a content builder.
03925         nsCOMPtr<nsIXULTemplateBuilder> builder
03926             = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
03927 
03928         if (! builder)
03929             return NS_ERROR_FAILURE;
03930 
03931         builder->Init(aElement);
03932 
03933         nsXULElement *xulContent = nsXULElement::FromContent(aElement);
03934         if (xulContent) {
03935             // Mark the XUL element as being lazy, so the template builder
03936             // will run when layout first asks for these nodes.
03937             xulContent->SetLazyState(nsXULElement::eChildrenMustBeRebuilt);
03938         }
03939         else {
03940             // Force construction of immediate template sub-content _now_.
03941             builder->CreateContents(aElement);
03942         }
03943     }
03944 
03945     return NS_OK;
03946 }
03947 
03948 
03949 nsresult
03950 nsXULDocument::AddPrototypeSheets()
03951 {
03952     // Add mCurrentPrototype's style sheets to the document.
03953     nsresult rv;
03954 
03955     nsCOMPtr<nsISupportsArray> sheets;
03956     rv = mCurrentPrototype->GetStyleSheetReferences(getter_AddRefs(sheets));
03957     if (NS_FAILED(rv)) return rv;
03958 
03959     PRUint32 count;
03960     sheets->Count(&count);
03961     for (PRUint32 i = 0; i < count; ++i) {
03962         nsISupports* isupports = sheets->ElementAt(i);
03963         nsCOMPtr<nsIURI> uri = do_QueryInterface(isupports);
03964         NS_IF_RELEASE(isupports);
03965 
03966         NS_ASSERTION(uri, "not a URI!!!");
03967         if (! uri)
03968             return NS_ERROR_UNEXPECTED;
03969 
03970         nsCAutoString spec;
03971         uri->GetAsciiSpec(spec);
03972 
03973         if (!IsChromeURI(uri)) {
03974             // These don't get to be in the prototype cache anyway...
03975             // and we can't load non-chrome sheets synchronously
03976             continue;
03977         }
03978 
03979         nsCOMPtr<nsICSSStyleSheet> sheet;
03980 
03981         // If the sheet is a chrome URL, then we can refetch the sheet
03982         // synchronously, since we know the sheet is local.  It's not
03983         // too late! :) If we're lucky, the loader will just pull it
03984         // from the prototype cache anyway.
03985         // Otherwise we just bail.  It shouldn't currently
03986         // be possible to get into this situation for any reason
03987         // other than a skin switch anyway (since skin switching is the
03988         // only system that partially invalidates the XUL cache).
03989         // - dwh
03990         //XXXbz we hit this code from fastload all the time.  Bug 183505.
03991         rv = CSSLoader()->LoadAgentSheet(uri, getter_AddRefs(sheet));
03992         // XXXldb We need to prevent bogus sheets from being held in the
03993         // prototype's list, but until then, don't propagate the failure
03994         // from LoadAgentSheet (and thus exit the loop).
03995         if (NS_SUCCEEDED(rv)) {
03996             AddStyleSheet(sheet);
03997         }
03998     }
03999 
04000     return NS_OK;
04001 }
04002 
04003 
04004 //----------------------------------------------------------------------
04005 //
04006 // nsXULDocument::OverlayForwardReference
04007 //
04008 
04009 nsForwardReference::Result
04010 nsXULDocument::OverlayForwardReference::Resolve()
04011 {
04012     // Resolve a forward reference from an overlay element; attempt to
04013     // hook it up into the main document.
04014     nsresult rv;
04015  
04016     PRBool notify = PR_FALSE;
04017     nsIPresShell *shell = mDocument->GetShellAt(0);
04018     if (shell)
04019         shell->GetDidInitialReflow(&notify);
04020 
04021     nsAutoString id;
04022     rv = mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
04023     if (NS_FAILED(rv)) return eResolve_Error;
04024 
04025     if (id.IsEmpty()) {
04026         // overlay had no id, use the root element
04027         if (!mDocument->mRootContent) {
04028             return eResolve_Error;
04029         }
04030 
04031         mDocument->InsertElement(mDocument->mRootContent, mOverlay, notify);
04032         mResolved = PR_TRUE;
04033         return eResolve_Succeeded;
04034     }
04035 
04036     nsCOMPtr<nsIDOMElement> domtarget;
04037     rv = mDocument->GetElementById(id, getter_AddRefs(domtarget));
04038     if (NS_FAILED(rv)) return eResolve_Error;
04039 
04040     // If we can't find the element in the document, defer the hookup
04041     // until later.
04042     if (! domtarget)
04043         return eResolve_Later;
04044 
04045     nsCOMPtr<nsIContent> target = do_QueryInterface(domtarget);
04046     NS_ASSERTION(target != nsnull, "not an nsIContent");
04047     if (! target)
04048         return eResolve_Error;
04049 
04050     rv = Merge(target, mOverlay, notify);
04051     if (NS_FAILED(rv)) return eResolve_Error;
04052 
04053     // Add child and any descendants to the element map
04054     rv = mDocument->AddSubtreeToDocument(target);
04055     if (NS_FAILED(rv)) return eResolve_Error;
04056 
04057 #ifdef PR_LOGGING
04058     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
04059         nsCAutoString idC;
04060         idC.AssignWithConversion(id);
04061         PR_LOG(gXULLog, PR_LOG_NOTICE,
04062                ("xul: overlay resolved '%s'",
04063                 idC.get()));
04064     }
04065 #endif
04066 
04067     mResolved = PR_TRUE;
04068     return eResolve_Succeeded;
04069 }
04070 
04071 
04072 
04073 nsresult
04074 nsXULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
04075                                               nsIContent* aOverlayNode, 
04076                                               PRBool aNotify)
04077 {
04078     // This function is given:
04079     // aTargetNode:  the node in the document whose 'id' attribute
04080     //               matches a toplevel node in our overlay.
04081     // aOverlayNode: the node in the overlay document that matches
04082     //               a node in the actual document.
04083     // aNotify:      whether or not content manipulation methods should
04084     //               use the aNotify parameter. After the initial 
04085     //               reflow (i.e. in the dynamic overlay merge case),
04086     //               we want all the content manipulation methods we
04087     //               call to notify so that frames are constructed 
04088     //               etc. Otherwise do not, since that's during initial
04089     //               document construction before StartLayout has been
04090     //               called which will do everything for us.
04091     //
04092     // This function merges the tree from the overlay into the tree in
04093     // the document, overwriting attributes and appending child content
04094     // nodes appropriately. (See XUL overlay reference for details)
04095 
04096     nsresult rv;
04097 
04098     // Merge attributes from the overlay content node to that of the
04099     // actual document.
04100     PRUint32 i, attrCount = aOverlayNode->GetAttrCount();
04101 
04102     for (i = 0; i < attrCount; ++i) {
04103         PRInt32 nameSpaceID;
04104         nsCOMPtr<nsIAtom> attr, prefix;
04105         rv = aOverlayNode->GetAttrNameAt(i, &nameSpaceID,
04106                                          getter_AddRefs(attr),
04107                                          getter_AddRefs(prefix));
04108         if (NS_FAILED(rv)) return rv;
04109 
04110         // We don't want to swap IDs, they should be the same.
04111         if (nameSpaceID == kNameSpaceID_None && attr.get() == nsXULAtoms::id)
04112             continue;
04113 
04114         nsAutoString value;
04115         rv = aOverlayNode->GetAttr(nameSpaceID, attr, value);
04116         if (NS_FAILED(rv)) return rv;
04117 
04118         // Element in the overlay has the 'removeelement' attribute set
04119         // so remove it from the actual document.
04120         if (attr == nsXULAtoms::removeelement &&
04121             value.EqualsLiteral("true")) {
04122 
04123             rv = RemoveElement(aTargetNode->GetParent(), aTargetNode);
04124             if (NS_FAILED(rv)) return rv;
04125 
04126             return NS_OK;
04127         }
04128 
04129         rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
04130         if (NS_FAILED(rv)) return rv;
04131     }
04132 
04133 
04134     // Walk our child nodes, looking for elements that have the 'id'
04135     // attribute set. If we find any, we must do a parent check in the
04136     // actual document to ensure that the structure matches that of
04137     // the actual document. If it does, we can call ourselves and attempt
04138     // to merge inside that subtree. If not, we just append the tree to
04139     // the parent like any other.
04140 
04141     PRUint32 childCount = aOverlayNode->GetChildCount();
04142 
04143     // This must be a strong reference since it will be the only
04144     // reference to a content object during part of this loop.
04145     nsCOMPtr<nsIContent> currContent;
04146 
04147     for (i = 0; i < childCount; ++i) {
04148         currContent = aOverlayNode->GetChildAt(0);
04149 
04150         nsAutoString id;
04151         rv = currContent->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
04152         if (NS_FAILED(rv)) return rv;
04153 
04154         nsCOMPtr<nsIDOMElement> nodeInDocument;
04155         if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
04156             nsCOMPtr<nsIDOMDocument> domDocument(
04157                         do_QueryInterface(aTargetNode->GetDocument()));
04158             if (!domDocument) return NS_ERROR_FAILURE;
04159 
04160             rv = domDocument->GetElementById(id, getter_AddRefs(nodeInDocument));
04161             if (NS_FAILED(rv)) return rv;
04162         }
04163 
04164         // The item has an 'id' attribute set, and we need to check with
04165         // the actual document to see if an item with this id exists at
04166         // this locale. If so, we want to merge the subtree under that
04167         // node. Otherwise, we just do an append as if the element had
04168         // no id attribute.
04169         if (nodeInDocument) {
04170             // Given two parents, aTargetNode and aOverlayNode, we want
04171             // to call merge on currContent if we find an associated
04172             // node in the document with the same id as currContent that
04173             // also has aTargetNode as its parent.
04174 
04175             nsAutoString documentParentID;
04176             rv = aTargetNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id,
04177                                       documentParentID);
04178             if (NS_FAILED(rv)) return rv;
04179 
04180             nsCOMPtr<nsIDOMNode> nodeParent;
04181             rv = nodeInDocument->GetParentNode(getter_AddRefs(nodeParent));
04182             if (NS_FAILED(rv)) return rv;
04183             nsCOMPtr<nsIDOMElement> elementParent(do_QueryInterface(nodeParent));
04184 
04185             nsAutoString parentID;
04186             elementParent->GetAttribute(NS_LITERAL_STRING("id"), parentID);
04187             if (parentID.Equals(documentParentID)) {
04188                 // The element matches. "Go Deep!"
04189                 nsCOMPtr<nsIContent> childDocumentContent(do_QueryInterface(nodeInDocument));
04190                 rv = Merge(childDocumentContent, currContent, aNotify);
04191                 if (NS_FAILED(rv)) return rv;
04192                 rv = aOverlayNode->RemoveChildAt(0, PR_FALSE);
04193                 if (NS_FAILED(rv)) return rv;
04194 
04195                 continue;
04196             }
04197         }
04198 
04199         rv = aOverlayNode->RemoveChildAt(0, PR_FALSE);
04200         if (NS_FAILED(rv)) return rv;
04201 
04202         rv = InsertElement(aTargetNode, currContent, aNotify);
04203         if (NS_FAILED(rv)) return rv;
04204     }
04205 
04206     return NS_OK;
04207 }
04208 
04209 
04210 
04211 nsXULDocument::OverlayForwardReference::~OverlayForwardReference()
04212 {
04213 #ifdef PR_LOGGING
04214     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
04215         nsAutoString id;
04216         mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
04217 
04218         nsCAutoString idC;
04219         idC.AssignWithConversion(id);
04220         PR_LOG(gXULLog, PR_LOG_WARNING,
04221                ("xul: overlay failed to resolve '%s'",
04222                 idC.get()));
04223     }
04224 #endif
04225 }
04226 
04227 
04228 //----------------------------------------------------------------------
04229 //
04230 // nsXULDocument::BroadcasterHookup
04231 //
04232 
04233 nsForwardReference::Result
04234 nsXULDocument::BroadcasterHookup::Resolve()
04235 {
04236     nsresult rv;
04237 
04238     PRBool listener;
04239     rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
04240     if (NS_FAILED(rv)) return eResolve_Error;
04241 
04242     return mResolved ? eResolve_Succeeded : eResolve_Later;
04243 }
04244 
04245 
04246 nsXULDocument::BroadcasterHookup::~BroadcasterHookup()
04247 {
04248 #ifdef PR_LOGGING
04249     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
04250         // Tell the world we failed
04251         nsresult rv;
04252 
04253         nsIAtom *tag = mObservesElement->Tag();
04254 
04255         nsAutoString broadcasterID;
04256         nsAutoString attribute;
04257 
04258         if (tag == nsXULAtoms::observes) {
04259             rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID);
04260             if (NS_FAILED(rv)) return;
04261 
04262             rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute);
04263             if (NS_FAILED(rv)) return;
04264         }
04265         else {
04266             rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID);
04267             if (NS_FAILED(rv)) return;
04268 
04269             attribute.AssignLiteral("*");
04270         }
04271 
04272         nsAutoString tagStr;
04273         rv = tag->ToString(tagStr);
04274         if (NS_FAILED(rv)) return;
04275 
04276         nsCAutoString tagstrC, attributeC,broadcasteridC;
04277         tagstrC.AssignWithConversion(tagStr);
04278         attributeC.AssignWithConversion(attribute);
04279         broadcasteridC.AssignWithConversion(broadcasterID);
04280         PR_LOG(gXULLog, PR_LOG_WARNING,
04281                ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
04282                 tagstrC.get(),
04283                 attributeC.get(),
04284                 broadcasteridC.get()));
04285     }
04286 #endif
04287 }
04288 
04289 
04290 //----------------------------------------------------------------------
04291 //
04292 // nsXULDocument::TemplateBuilderHookup
04293 //
04294 
04295 nsForwardReference::Result
04296 nsXULDocument::TemplateBuilderHookup::Resolve()
04297 {
04298     PRBool needsHookup;
04299     nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
04300     if (NS_FAILED(rv))
04301         return eResolve_Error;
04302 
04303     if (needsHookup) {
04304         rv = CreateTemplateBuilder(mElement);
04305         if (NS_FAILED(rv))
04306             return eResolve_Error;
04307     }
04308 
04309     return eResolve_Succeeded;
04310 }
04311 
04312 
04313 //----------------------------------------------------------------------
04314 
04315 nsresult
04316 nsXULDocument::FindBroadcaster(nsIContent* aElement,
04317                                nsIDOMElement** aListener,
04318                                nsString& aBroadcasterID,
04319                                nsString& aAttribute,
04320                                nsIDOMElement** aBroadcaster)
04321 {
04322     nsresult rv;
04323     nsINodeInfo *ni = aElement->GetNodeInfo();
04324     *aListener = nsnull;
04325     *aBroadcaster = nsnull;
04326 
04327     if (ni && ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL)) {
04328         // It's an <observes> element, which means that the actual
04329         // listener is the _parent_ node. This element should have an
04330         // 'element' attribute that specifies the ID of the
04331         // broadcaster element, and an 'attribute' element, which
04332         // specifies the name of the attribute to observe.
04333         nsIContent* parent = aElement->GetParent();
04334 
04335         // If we're still parented by an 'overlay' tag, then we haven't
04336         // made it into the real document yet. Defer hookup.
04337         if (parent->GetNodeInfo()->Equals(nsXULAtoms::overlay, kNameSpaceID_XUL)) {
04338             return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
04339         }
04340 
04341         if (NS_FAILED(CallQueryInterface(parent, aListener)))
04342             *aListener = nsnull;
04343 
04344         rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, aBroadcasterID);
04345         if (NS_FAILED(rv)) return rv;
04346 
04347         rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute,
04348                                aAttribute);
04349         if (NS_FAILED(rv)) return rv;
04350     }
04351     else {
04352         // It's a generic element, which means that we'll use the
04353         // value of the 'observes' attribute to determine the ID of
04354         // the broadcaster element, and we'll watch _all_ of its
04355         // values.
04356         rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, aBroadcasterID);
04357         if (NS_FAILED(rv)) return rv;
04358 
04359         // Bail if there's no aBroadcasterID
04360         if ((rv != NS_CONTENT_ATTR_HAS_VALUE) || (aBroadcasterID.IsEmpty())) {
04361             // Try the command attribute next.
04362             rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::command, aBroadcasterID);
04363             if (NS_FAILED(rv)) return rv;
04364 
04365             if (rv == NS_CONTENT_ATTR_HAS_VALUE && !aBroadcasterID.IsEmpty()) {
04366                 // We've got something in the command attribute.  We
04367                 // only treat this as a normal broadcaster if we are
04368                 // not a menuitem or a key.
04369 
04370                 if (ni->Equals(nsXULAtoms::menuitem, kNameSpaceID_XUL) ||
04371                     ni->Equals(nsXULAtoms::key, kNameSpaceID_XUL)) {
04372                 return NS_FINDBROADCASTER_NOT_FOUND;
04373               }
04374             }
04375             else {
04376               return NS_FINDBROADCASTER_NOT_FOUND;
04377             }
04378         }
04379 
04380         if (NS_FAILED(CallQueryInterface(aElement, aListener)))
04381             *aListener = nsnull;
04382 
04383         aAttribute.AssignLiteral("*");
04384     }
04385 
04386     // Make sure we got a valid listener.
04387     NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
04388 
04389     // Try to find the broadcaster element in the document.
04390     rv = GetElementById(aBroadcasterID, aBroadcaster);
04391     if (NS_FAILED(rv)) return rv;
04392 
04393     // If we can't find the broadcaster, then we'll need to defer the
04394     // hookup. We may need to resolve some of the other overlays
04395     // first.
04396     if (! *aBroadcaster) {
04397         return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
04398     }
04399 
04400     return NS_FINDBROADCASTER_FOUND;
04401 }
04402 
04403 nsresult
04404 nsXULDocument::CheckBroadcasterHookup(nsIContent* aElement,
04405                                       PRBool* aNeedsHookup,
04406                                       PRBool* aDidResolve)
04407 {
04408     // Resolve a broadcaster hookup. Look at the element that we're
04409     // trying to resolve: it could be an '<observes>' element, or just
04410     // a vanilla element with an 'observes' attribute on it.
04411     nsresult rv;
04412 
04413     *aDidResolve = PR_FALSE;
04414 
04415     nsCOMPtr<nsIDOMElement> listener;
04416     nsAutoString broadcasterID;
04417     nsAutoString attribute;
04418     nsCOMPtr<nsIDOMElement> broadcaster;
04419 
04420     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
04421                          broadcasterID, attribute, getter_AddRefs(broadcaster));
04422     switch (rv) {
04423         case NS_FINDBROADCASTER_NOT_FOUND:
04424             *aNeedsHookup = PR_FALSE;
04425             return NS_OK;
04426         case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
04427             *aNeedsHookup = PR_TRUE;
04428             return NS_OK;
04429         case NS_FINDBROADCASTER_FOUND:
04430             break;
04431         default:
04432             return rv;
04433     }
04434 
04435     rv = AddBroadcastListenerFor(broadcaster, listener, attribute);
04436     if (NS_FAILED(rv)) return rv;
04437 
04438 #ifdef PR_LOGGING
04439     // Tell the world we succeeded
04440     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
04441         nsCOMPtr<nsIContent> content =
04442             do_QueryInterface(listener);
04443 
04444         NS_ASSERTION(content != nsnull, "not an nsIContent");
04445         if (! content)
04446             return rv;
04447 
04448         nsAutoString tagStr;
04449         rv = content->Tag()->ToString(tagStr);
04450         if (NS_FAILED(rv)) return rv;
04451 
04452         nsCAutoString tagstrC, attributeC,broadcasteridC;
04453         tagstrC.AssignWithConversion(tagStr);
04454         attributeC.AssignWithConversion(attribute);
04455         broadcasteridC.AssignWithConversion(broadcasterID);
04456         PR_LOG(gXULLog, PR_LOG_NOTICE,
04457                ("xul: broadcaster hookup <%s attribute='%s'> to %s",
04458                 tagstrC.get(),
04459                 attributeC.get(),
04460                 broadcasteridC.get()));
04461     }
04462 #endif
04463 
04464     *aNeedsHookup = PR_FALSE;
04465     *aDidResolve = PR_TRUE;
04466     return NS_OK;
04467 }
04468 
04469 nsresult
04470 nsXULDocument::InsertElement(nsIContent* aParent, nsIContent* aChild, PRBool aNotify)
04471 {
04472     // Insert aChild appropriately into aParent, accounting for a
04473     // 'pos' attribute set on aChild.
04474     nsresult rv;
04475 
04476     nsAutoString posStr;
04477     PRBool wasInserted = PR_FALSE;
04478 
04479     // insert after an element of a given id
04480     rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, posStr);
04481     if (NS_FAILED(rv)) return rv;
04482     PRBool isInsertAfter = PR_TRUE;
04483 
04484     if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
04485         rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore,
04486                              posStr);
04487         if (NS_FAILED(rv)) return rv;
04488         isInsertAfter = PR_FALSE;
04489     }
04490 
04491     if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
04492         nsCOMPtr<nsIDOMDocument> domDocument(
04493                do_QueryInterface(aParent->GetDocument()));
04494         nsCOMPtr<nsIDOMElement> domElement;
04495 
04496         char* str = ToNewCString(posStr);
04497         char* rest;
04498         char* token = nsCRT::strtok(str, ", ", &rest);
04499 
04500         while (token) {
04501             rv = domDocument->GetElementById(NS_ConvertASCIItoUCS2(token),
04502                                              getter_AddRefs(domElement));
04503             if (domElement)
04504                 break;
04505 
04506             token = nsCRT::strtok(rest, ", ", &rest);
04507         }
04508         nsMemory::Free(str);
04509         if (NS_FAILED(rv))
04510             return rv;
04511 
04512         if (domElement) {
04513             nsCOMPtr<nsIContent> content(do_QueryInterface(domElement));
04514             NS_ASSERTION(content != nsnull, "null ptr");
04515             if (!content)
04516                 return NS_ERROR_UNEXPECTED;
04517 
04518             PRInt32 pos = aParent->IndexOf(content);
04519 
04520             if (pos != -1) {
04521                 pos = isInsertAfter ? pos + 1 : pos;
04522                 rv = aParent->InsertChildAt(aChild, pos, aNotify);
04523                 if (NS_FAILED(rv))
04524                     return rv;
04525 
04526                 wasInserted = PR_TRUE;
04527             }
04528         }
04529     }
04530 
04531     if (!wasInserted) {
04532 
04533         rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::position, posStr);
04534         if (NS_FAILED(rv)) return rv;
04535 
04536         if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
04537             // Positions are one-indexed.
04538             PRInt32 pos = posStr.ToInteger(NS_REINTERPRET_CAST(PRInt32*, &rv));
04539             // Note: if the insertion index (which is |pos - 1|) would be less
04540             // than 0 or greater than the number of children aParent has, then
04541             // don't insert, since the position is bogus.  Just skip on to
04542             // appending.
04543             if (NS_SUCCEEDED(rv) && pos > 0 &&
04544                 PRUint32(pos - 1) <= aParent->GetChildCount()) {
04545                 rv = aParent->InsertChildAt(aChild, pos - 1, aNotify);
04546                 if (NS_SUCCEEDED(rv))
04547                     wasInserted = PR_TRUE;
04548                 // If the insertion fails, then we should still
04549                 // attempt an append.  Thus, rather than returning rv
04550                 // immediately, we fall through to the final
04551                 // "catch-all" case that just does an AppendChildTo.
04552             }
04553         }
04554     }
04555 
04556     if (! wasInserted) {
04557         rv = aParent->AppendChildTo(aChild, aNotify);
04558         if (NS_FAILED(rv)) return rv;
04559     }
04560     return NS_OK;
04561 }
04562 
04563 nsresult
04564 nsXULDocument::RemoveElement(nsIContent* aParent, nsIContent* aChild)
04565 {
04566     PRInt32 nodeOffset = aParent->IndexOf(aChild);
04567 
04568     return aParent->RemoveChildAt(nodeOffset, PR_TRUE);
04569 }
04570 
04571 //----------------------------------------------------------------------
04572 //
04573 // CachedChromeStreamListener
04574 //
04575 
04576 nsXULDocument::CachedChromeStreamListener::CachedChromeStreamListener(nsXULDocument* aDocument, PRBool aProtoLoaded)
04577     : mDocument(aDocument),
04578       mProtoLoaded(aProtoLoaded)
04579 {
04580     NS_ADDREF(mDocument);
04581 }
04582 
04583 
04584 nsXULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
04585 {
04586     NS_RELEASE(mDocument);
04587 }
04588 
04589 
04590 NS_IMPL_ISUPPORTS2(nsXULDocument::CachedChromeStreamListener,
04591                    nsIRequestObserver, nsIStreamListener)
04592 
04593 NS_IMETHODIMP
04594 nsXULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
04595                                                           nsISupports* acontext)
04596 {
04597     return NS_OK;
04598 }
04599 
04600 
04601 NS_IMETHODIMP
04602 nsXULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
04603                                                          nsISupports* aContext,
04604                                                          nsresult aStatus)
04605 {
04606     if (! mProtoLoaded)
04607         return NS_OK;
04608 
04609     nsresult rv;
04610     rv = mDocument->PrepareToWalk();
04611     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
04612     if (NS_FAILED(rv)) return rv;
04613 
04614     return mDocument->ResumeWalk();
04615 }
04616 
04617 
04618 NS_IMETHODIMP
04619 nsXULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
04620                                                            nsISupports* aContext,
04621                                                            nsIInputStream* aInStr,
04622                                                            PRUint32 aSourceOffset,
04623                                                            PRUint32 aCount)
04624 {
04625     NS_NOTREACHED("CachedChromeStream doesn't receive data");
04626     return NS_ERROR_UNEXPECTED;
04627 }
04628 
04629 //----------------------------------------------------------------------
04630 //
04631 // ParserObserver
04632 //
04633 
04634 nsXULDocument::ParserObserver::ParserObserver(nsXULDocument* aDocument)
04635     : mDocument(aDocument)
04636 {
04637     NS_ADDREF(mDocument);
04638 }
04639 
04640 nsXULDocument::ParserObserver::~ParserObserver()
04641 {
04642     NS_IF_RELEASE(mDocument);
04643 }
04644 
04645 NS_IMPL_ISUPPORTS1(nsXULDocument::ParserObserver, nsIRequestObserver)
04646 
04647 NS_IMETHODIMP
04648 nsXULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
04649                                               nsISupports* aContext)
04650 {
04651     return NS_OK;
04652 }
04653 
04654 NS_IMETHODIMP
04655 nsXULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
04656                                              nsISupports* aContext,
04657                                              nsresult aStatus)
04658 {
04659     nsresult rv = NS_OK;
04660 
04661     if (NS_FAILED(aStatus)) {
04662         // If an overlay load fails, we need to nudge the prototype
04663         // walk along.
04664         nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
04665         if (aChannel) {
04666             nsCOMPtr<nsIURI> uri;
04667             aChannel->GetOriginalURI(getter_AddRefs(uri));
04668             if (uri) {
04669                 mDocument->ReportMissingOverlay(uri);
04670             }
04671         }
04672 
04673         rv = mDocument->ResumeWalk();
04674     }
04675 
04676     // Drop the reference to the document to break cycle between the
04677     // document, the parser, the content sink, and the parser
04678     // observer.
04679     NS_RELEASE(mDocument);
04680 
04681     return rv;
04682 }
04683 
04684 void
04685 nsXULDocument::GetFocusController(nsIFocusController** aFocusController)
04686 {
04687     nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryReferent(mDocumentContainer);
04688     nsCOMPtr<nsPIDOMWindow> windowPrivate = do_GetInterface(ir);
04689     if (windowPrivate) {
04690         NS_IF_ADDREF(*aFocusController = windowPrivate->GetRootFocusController());
04691     } else
04692         *aFocusController = nsnull;
04693 }