content/xul/document/src/XULDocument.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=4 sw=4 et tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 /*
     9   An implementation for the XUL document. This implementation serves
    10   as the basis for generating an NGLayout content model.
    12   Notes
    13   -----
    15   1. We do some monkey business in the document observer methods to`
    16      keep the element map in sync for HTML elements. Why don't we just
    17      do it for _all_ elements? Well, in the case of XUL elements,
    18      which may be lazily created during frame construction, the
    19      document observer methods will never be called because we'll be
    20      adding the XUL nodes into the content model "quietly".
    22 */
    24 #include "mozilla/ArrayUtils.h"
    26 // Note the ALPHABETICAL ORDERING
    27 #include "XULDocument.h"
    29 #include "nsError.h"
    30 #include "nsIBoxObject.h"
    31 #include "nsIChromeRegistry.h"
    32 #include "nsView.h"
    33 #include "nsViewManager.h"
    34 #include "nsIContentViewer.h"
    35 #include "nsIDOMXULElement.h"
    36 #include "nsIRDFNode.h"
    37 #include "nsIRDFRemoteDataSource.h"
    38 #include "nsIRDFService.h"
    39 #include "nsIStreamListener.h"
    40 #include "nsITimer.h"
    41 #include "nsDocShell.h"
    42 #include "nsGkAtoms.h"
    43 #include "nsXMLContentSink.h"
    44 #include "nsXULContentSink.h"
    45 #include "nsXULContentUtils.h"
    46 #include "nsIXULOverlayProvider.h"
    47 #include "nsNetUtil.h"
    48 #include "nsParserCIID.h"
    49 #include "nsPIBoxObject.h"
    50 #include "nsRDFCID.h"
    51 #include "nsILocalStore.h"
    52 #include "nsXPIDLString.h"
    53 #include "nsPIDOMWindow.h"
    54 #include "nsPIWindowRoot.h"
    55 #include "nsXULCommandDispatcher.h"
    56 #include "nsXULElement.h"
    57 #include "prlog.h"
    58 #include "rdf.h"
    59 #include "nsIFrame.h"
    60 #include "nsXBLService.h"
    61 #include "nsCExternalHandlerService.h"
    62 #include "nsMimeTypes.h"
    63 #include "nsIObjectInputStream.h"
    64 #include "nsIObjectOutputStream.h"
    65 #include "nsContentList.h"
    66 #include "nsIScriptGlobalObject.h"
    67 #include "nsIScriptSecurityManager.h"
    68 #include "nsNodeInfoManager.h"
    69 #include "nsContentCreatorFunctions.h"
    70 #include "nsContentUtils.h"
    71 #include "nsIParser.h"
    72 #include "nsCharsetSource.h"
    73 #include "nsIParserService.h"
    74 #include "nsCSSStyleSheet.h"
    75 #include "mozilla/css/Loader.h"
    76 #include "nsIScriptError.h"
    77 #include "nsIStyleSheetLinkingElement.h"
    78 #include "nsIObserverService.h"
    79 #include "nsNodeUtils.h"
    80 #include "nsIDocShellTreeOwner.h"
    81 #include "nsIXULWindow.h"
    82 #include "nsXULPopupManager.h"
    83 #include "nsCCUncollectableMarker.h"
    84 #include "nsURILoader.h"
    85 #include "mozilla/BasicEvents.h"
    86 #include "mozilla/dom/Element.h"
    87 #include "mozilla/dom/ProcessingInstruction.h"
    88 #include "mozilla/dom/XULDocumentBinding.h"
    89 #include "mozilla/EventDispatcher.h"
    90 #include "mozilla/Preferences.h"
    91 #include "nsTextNode.h"
    92 #include "nsJSUtils.h"
    93 #include "mozilla/dom/URL.h"
    95 using namespace mozilla;
    96 using namespace mozilla::dom;
    98 //----------------------------------------------------------------------
    99 //
   100 // CIDs
   101 //
   103 static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
   105 static bool IsOverlayAllowed(nsIURI* aURI)
   106 {
   107     bool canOverlay = false;
   108     if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay)
   109         return true;
   110     if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay)
   111         return true;
   112     return false;
   113 }
   115 //----------------------------------------------------------------------
   116 //
   117 // Miscellaneous Constants
   118 //
   120 const nsForwardReference::Phase nsForwardReference::kPasses[] = {
   121     nsForwardReference::eConstruction,
   122     nsForwardReference::eHookup,
   123     nsForwardReference::eDone
   124 };
   126 const uint32_t kMaxAttrNameLength = 512;
   127 const uint32_t kMaxAttributeLength = 4096;
   129 //----------------------------------------------------------------------
   130 //
   131 // Statics
   132 //
   134 int32_t XULDocument::gRefCnt = 0;
   136 nsIRDFService* XULDocument::gRDFService;
   137 nsIRDFResource* XULDocument::kNC_persist;
   138 nsIRDFResource* XULDocument::kNC_attribute;
   139 nsIRDFResource* XULDocument::kNC_value;
   141 PRLogModuleInfo* XULDocument::gXULLog;
   143 //----------------------------------------------------------------------
   145 struct BroadcasterMapEntry : public PLDHashEntryHdr {
   146     Element*         mBroadcaster; // [WEAK]
   147     nsSmallVoidArray mListeners;   // [OWNING] of BroadcastListener objects
   148 };
   150 struct BroadcastListener {
   151     nsWeakPtr mListener;
   152     nsCOMPtr<nsIAtom> mAttribute;
   153 };
   155 Element*
   156 nsRefMapEntry::GetFirstElement()
   157 {
   158     return static_cast<Element*>(mRefContentList.SafeElementAt(0));
   159 }
   161 void
   162 nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements)
   163 {
   164     for (int32_t i = 0; i < mRefContentList.Count(); ++i) {
   165         aElements->AppendObject(static_cast<nsIContent*>(mRefContentList[i]));
   166     }
   167 }
   169 bool
   170 nsRefMapEntry::AddElement(Element* aElement)
   171 {
   172     if (mRefContentList.IndexOf(aElement) >= 0)
   173         return true;
   174     return mRefContentList.AppendElement(aElement);
   175 }
   177 bool
   178 nsRefMapEntry::RemoveElement(Element* aElement)
   179 {
   180     mRefContentList.RemoveElement(aElement);
   181     return mRefContentList.Count() == 0;
   182 }
   184 //----------------------------------------------------------------------
   185 //
   186 // ctors & dtors
   187 //
   189 namespace mozilla {
   190 namespace dom {
   192 XULDocument::XULDocument(void)
   193     : XMLDocument("application/vnd.mozilla.xul+xml"),
   194       mDocLWTheme(Doc_Theme_Uninitialized),
   195       mState(eState_Master),
   196       mResolutionPhase(nsForwardReference::eStart)
   197 {
   198     // NOTE! nsDocument::operator new() zeroes out all members, so don't
   199     // bother initializing members to 0.
   201     // Override the default in nsDocument
   202     mCharacterSet.AssignLiteral("UTF-8");
   204     mDefaultElementType = kNameSpaceID_XUL;
   205     mIsXUL = true;
   207     mDelayFrameLoaderInitialization = true;
   209     mAllowXULXBL = eTriTrue;
   210 }
   212 XULDocument::~XULDocument()
   213 {
   214     NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
   215         "unreferenced document still waiting for script source to load?");
   217     // In case we failed somewhere early on and the forward observer
   218     // decls never got resolved.
   219     mForwardReferences.Clear();
   220     // Likewise for any references we have to IDs where we might
   221     // look for persisted data:
   222     mPersistenceIds.Clear();
   224     // Destroy our broadcaster map.
   225     if (mBroadcasterMap) {
   226         PL_DHashTableDestroy(mBroadcasterMap);
   227     }
   229     if (mLocalStore) {
   230         nsCOMPtr<nsIRDFRemoteDataSource> remote =
   231             do_QueryInterface(mLocalStore);
   232         if (remote)
   233             remote->Flush();
   234     }
   236     delete mTemplateBuilderTable;
   238     Preferences::UnregisterCallback(XULDocument::DirectionChanged,
   239                                     "intl.uidirection.", this);
   241     if (--gRefCnt == 0) {
   242         NS_IF_RELEASE(gRDFService);
   244         NS_IF_RELEASE(kNC_persist);
   245         NS_IF_RELEASE(kNC_attribute);
   246         NS_IF_RELEASE(kNC_value);
   247     }
   249     if (mOffThreadCompileStringBuf) {
   250       js_free(mOffThreadCompileStringBuf);
   251     }
   252 }
   254 } // namespace dom
   255 } // namespace mozilla
   257 nsresult
   258 NS_NewXULDocument(nsIXULDocument** result)
   259 {
   260     NS_PRECONDITION(result != nullptr, "null ptr");
   261     if (! result)
   262         return NS_ERROR_NULL_POINTER;
   264     XULDocument* doc = new XULDocument();
   265     if (! doc)
   266         return NS_ERROR_OUT_OF_MEMORY;
   268     NS_ADDREF(doc);
   270     nsresult rv;
   271     if (NS_FAILED(rv = doc->Init())) {
   272         NS_RELEASE(doc);
   273         return rv;
   274     }
   276     *result = doc;
   277     return NS_OK;
   278 }
   281 namespace mozilla {
   282 namespace dom {
   284 //----------------------------------------------------------------------
   285 //
   286 // nsISupports interface
   287 //
   289 static PLDHashOperator
   290 TraverseTemplateBuilders(nsISupports* aKey, nsIXULTemplateBuilder* aData,
   291                          void* aContext)
   292 {
   293     nsCycleCollectionTraversalCallback *cb =
   294         static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   296     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable key");
   297     cb->NoteXPCOMChild(aKey);
   298     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable value");
   299     cb->NoteXPCOMChild(aData);
   301     return PL_DHASH_NEXT;
   302 }
   304 static PLDHashOperator
   305 TraverseObservers(nsIURI* aKey, nsIObserver* aData, void* aContext)
   306 {
   307     nsCycleCollectionTraversalCallback *cb =
   308         static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   310     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value");
   311     cb->NoteXPCOMChild(aData);
   313     return PL_DHASH_NEXT;
   314 }
   316 NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
   318 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
   319     NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
   320                  "Shouldn't traverse XULDocument!");
   321     // XXX tmp->mForwardReferences?
   322     // XXX tmp->mContextStack?
   324     // An element will only have a template builder as long as it's in the
   325     // document, so we'll traverse the table here instead of from the element.
   326     if (tmp->mTemplateBuilderTable)
   327         tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb);
   329     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
   330     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
   331     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
   332     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes);
   333     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
   335     if (tmp->mOverlayLoadObservers) {
   336         tmp->mOverlayLoadObservers->EnumerateRead(TraverseObservers, &cb);
   337     }
   338     if (tmp->mPendingOverlayLoadNotifications) {
   339         tmp->mPendingOverlayLoadNotifications->EnumerateRead(TraverseObservers, &cb);
   340     }
   341 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   343 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
   344     delete tmp->mTemplateBuilderTable;
   345     tmp->mTemplateBuilderTable = nullptr;
   347     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
   348     //XXX We should probably unlink all the objects we traverse.
   349 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   351 NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument)
   352 NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
   355 // QueryInterface implementation for XULDocument
   356 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument)
   357     NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument,
   358                                  nsIDOMXULDocument, nsIStreamLoaderObserver,
   359                                  nsICSSLoaderObserver, nsIOffThreadScriptReceiver)
   360 NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument)
   363 //----------------------------------------------------------------------
   364 //
   365 // nsIDocument interface
   366 //
   368 void
   369 XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
   370 {
   371     NS_NOTREACHED("Reset");
   372 }
   374 void
   375 XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
   376                         nsIPrincipal* aPrincipal)
   377 {
   378     NS_NOTREACHED("ResetToURI");
   379 }
   381 void
   382 XULDocument::SetContentType(const nsAString& aContentType)
   383 {
   384     NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
   385                  "xul-documents always has content-type application/vnd.mozilla.xul+xml");
   386     // Don't do anything, xul always has the mimetype
   387     // application/vnd.mozilla.xul+xml
   388 }
   390 // This is called when the master document begins loading, whether it's
   391 // being cached or not.
   392 nsresult
   393 XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
   394                                nsILoadGroup* aLoadGroup,
   395                                nsISupports* aContainer,
   396                                nsIStreamListener **aDocListener,
   397                                bool aReset, nsIContentSink* aSink)
   398 {
   399 #ifdef PR_LOGGING
   400     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
   402         nsCOMPtr<nsIURI> uri;
   403         nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
   404         if (NS_SUCCEEDED(rv)) {
   405             nsAutoCString urlspec;
   406             rv = uri->GetSpec(urlspec);
   407             if (NS_SUCCEEDED(rv)) {
   408                 PR_LOG(gXULLog, PR_LOG_WARNING,
   409                        ("xul: load document '%s'", urlspec.get()));
   410             }
   411         }
   412     }
   413 #endif
   414     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
   415     // we'll possibly need to reset our content type afterwards.
   416     mStillWalking = true;
   417     mMayStartLayout = false;
   418     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
   420     mChannel = aChannel;
   422     mHaveInputEncoding = true;
   424     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
   425     nsresult rv =
   426         NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
   427     NS_ENSURE_SUCCESS(rv, rv);
   429     ResetStylesheetsToURI(mDocumentURI);
   431     RetrieveRelevantHeaders(aChannel);
   433     // Look in the chrome cache: we've got this puppy loaded
   434     // already.
   435     nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
   436             nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
   437             nullptr;
   439     // Same comment as nsChromeProtocolHandler::NewChannel and
   440     // XULDocument::ResumeWalk
   441     // - Ben Goodger
   442     //
   443     // We don't abort on failure here because there are too many valid
   444     // cases that can return failure, and the null-ness of |proto| is enough
   445     // to trigger the fail-safe parse-from-disk solution. Example failure cases
   446     // (for reference) include:
   447     //
   448     // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
   449     //                         parse from disk
   450     // other: the startup cache file could not be found, probably
   451     //        due to being accessed before a profile has been selected (e.g.
   452     //        loading chrome for the profile manager itself). This must be
   453     //        parsed from disk.
   455     if (proto) {
   456         // If we're racing with another document to load proto, wait till the
   457         // load has finished loading before trying to add cloned style sheets.
   458         // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
   459         // find all racing documents and notify them via OnPrototypeLoadDone,
   460         // which will add style sheet clones to each document.
   461         bool loaded;
   462         rv = proto->AwaitLoadDone(this, &loaded);
   463         if (NS_FAILED(rv)) return rv;
   465         mMasterPrototype = mCurrentPrototype = proto;
   467         // Set up the right principal on ourselves.
   468         SetPrincipal(proto->DocumentPrincipal());
   470         // We need a listener, even if proto is not yet loaded, in which
   471         // event the listener's OnStopRequest method does nothing, and all
   472         // the interesting work happens below XULDocument::EndLoad, from
   473         // the call there to mCurrentPrototype->NotifyLoadDone().
   474         *aDocListener = new CachedChromeStreamListener(this, loaded);
   475         if (! *aDocListener)
   476             return NS_ERROR_OUT_OF_MEMORY;
   477     }
   478     else {
   479         bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
   480         bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
   483         // It's just a vanilla document load. Create a parser to deal
   484         // with the stream n' stuff.
   486         nsCOMPtr<nsIParser> parser;
   487         rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
   488                            getter_AddRefs(parser));
   489         if (NS_FAILED(rv)) return rv;
   491         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
   492         // so we don't have to re-check whether the cache is enabled all
   493         // the time.
   494         mIsWritingFastLoad = useXULCache;
   496         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
   497         NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
   498         if (NS_FAILED(rv)) return rv;
   500         *aDocListener = listener;
   502         parser->Parse(mDocumentURI);
   504         // Put the current prototype, created under PrepareToLoad, into the
   505         // XUL prototype cache now.  We can't do this under PrepareToLoad or
   506         // overlay loading will break; search for PutPrototype in ResumeWalk
   507         // and see the comment there.
   508         if (fillXULCache) {
   509             nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
   510         }
   511     }
   513     NS_IF_ADDREF(*aDocListener);
   514     return NS_OK;
   515 }
   517 // This gets invoked after a prototype for this document or one of
   518 // its overlays is fully built in the content sink.
   519 void
   520 XULDocument::EndLoad()
   521 {
   522     // This can happen if an overlay fails to load
   523     if (!mCurrentPrototype)
   524         return;
   526     nsresult rv;
   528     // Whack the prototype document into the cache so that the next
   529     // time somebody asks for it, they don't need to load it by hand.
   531     nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
   532     bool isChrome = IsChromeURI(uri);
   534     // Remember if the XUL cache is on
   535     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
   537     // If the current prototype is an overlay document (non-master prototype)
   538     // and we're filling the FastLoad disk cache, tell the cache we're done
   539     // loading it, and write the prototype. The master prototype is put into
   540     // the cache earlier in XULDocument::StartDocumentLoad.
   541     if (useXULCache && mIsWritingFastLoad && isChrome &&
   542         mMasterPrototype != mCurrentPrototype) {
   543         nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
   544     }
   546     if (IsOverlayAllowed(uri)) {
   547         nsCOMPtr<nsIXULOverlayProvider> reg =
   548             mozilla::services::GetXULOverlayProviderService();
   550         if (reg) {
   551             nsCOMPtr<nsISimpleEnumerator> overlays;
   552             rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
   553             if (NS_FAILED(rv)) return;
   555             bool moreSheets;
   556             nsCOMPtr<nsISupports> next;
   557             nsCOMPtr<nsIURI> sheetURI;
   559             while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
   560                    moreSheets) {
   561                 overlays->GetNext(getter_AddRefs(next));
   563                 sheetURI = do_QueryInterface(next);
   564                 if (!sheetURI) {
   565                     NS_ERROR("Chrome registry handed me a non-nsIURI object!");
   566                     continue;
   567                 }
   569                 if (IsChromeURI(sheetURI)) {
   570                     mCurrentPrototype->AddStyleSheetReference(sheetURI);
   571                 }
   572             }
   573         }
   575         if (isChrome && useXULCache) {
   576             // If it's a chrome prototype document, then notify any
   577             // documents that raced to load the prototype, and awaited
   578             // its load completion via proto->AwaitLoadDone().
   579             rv = mCurrentPrototype->NotifyLoadDone();
   580             if (NS_FAILED(rv)) return;
   581         }
   582     }
   584     OnPrototypeLoadDone(true);
   585 #ifdef PR_LOGGING
   586     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
   587         nsAutoCString urlspec;
   588         rv = uri->GetSpec(urlspec);
   589         if (NS_SUCCEEDED(rv)) {
   590             PR_LOG(gXULLog, PR_LOG_WARNING,
   591                    ("xul: Finished loading document '%s'", urlspec.get()));
   592         }
   593     }
   594 #endif
   595 }
   597 NS_IMETHODIMP
   598 XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
   599 {
   600     nsresult rv;
   602     // Add the style overlays from chrome registry, if any.
   603     rv = AddPrototypeSheets();
   604     if (NS_FAILED(rv)) return rv;
   606     rv = PrepareToWalk();
   607     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
   608     if (NS_FAILED(rv)) return rv;
   610     if (aResumeWalk) {
   611         rv = ResumeWalk();
   612     }
   613     return rv;
   614 }
   616 // called when an error occurs parsing a document
   617 bool
   618 XULDocument::OnDocumentParserError()
   619 {
   620   // don't report errors that are from overlays
   621   if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) {
   622     nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
   623     if (IsChromeURI(uri)) {
   624       nsCOMPtr<nsIObserverService> os =
   625         mozilla::services::GetObserverService();
   626       if (os)
   627         os->NotifyObservers(uri, "xul-overlay-parsererror",
   628                             EmptyString().get());
   629     }
   631     return false;
   632   }
   634   return true;
   635 }
   637 static void
   638 ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
   639 {
   640     BroadcasterMapEntry* entry =
   641         static_cast<BroadcasterMapEntry*>(aEntry);
   642     for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   643         delete (BroadcastListener*)entry->mListeners[i];
   644     }
   646     // N.B. that we need to manually run the dtor because we
   647     // constructed the nsSmallVoidArray object in-place.
   648     entry->mListeners.~nsSmallVoidArray();
   649 }
   651 static bool
   652 CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute)
   653 {
   654     // Don't push changes to the |id|, |ref|, |persist|, |command| or
   655     // |observes| attribute.
   656     if (aNameSpaceID == kNameSpaceID_None) {
   657         if ((aAttribute == nsGkAtoms::id) ||
   658             (aAttribute == nsGkAtoms::ref) ||
   659             (aAttribute == nsGkAtoms::persist) ||
   660             (aAttribute == nsGkAtoms::command) ||
   661             (aAttribute == nsGkAtoms::observes)) {
   662             return false;
   663         }
   664     }
   665     return true;
   666 }
   668 struct nsAttrNameInfo
   669 {
   670   nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) :
   671     mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
   672   nsAttrNameInfo(const nsAttrNameInfo& aOther) :
   673     mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
   674     mPrefix(aOther.mPrefix) {}
   675   int32_t           mNamespaceID;
   676   nsCOMPtr<nsIAtom> mName;
   677   nsCOMPtr<nsIAtom> mPrefix;
   678 };
   680 void
   681 XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
   682                                           Element *aListener,
   683                                           const nsAString &aAttr)
   684 {
   685     if (!nsContentUtils::IsSafeToRunScript()) {
   686         nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
   687                                                aAttr);
   688         mDelayedBroadcasters.AppendElement(delayedUpdate);
   689         MaybeBroadcast();
   690         return;
   691     }
   692     bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
   694     if (aAttr.EqualsLiteral("*")) {
   695         uint32_t count = aBroadcaster->GetAttrCount();
   696         nsTArray<nsAttrNameInfo> attributes(count);
   697         for (uint32_t i = 0; i < count; ++i) {
   698             const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
   699             int32_t nameSpaceID = attrName->NamespaceID();
   700             nsIAtom* name = attrName->LocalName();
   702             // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
   703             if (! CanBroadcast(nameSpaceID, name))
   704                 continue;
   706             attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
   707                                                     attrName->GetPrefix()));
   708         }
   710         count = attributes.Length();
   711         while (count-- > 0) {
   712             int32_t nameSpaceID = attributes[count].mNamespaceID;
   713             nsIAtom* name = attributes[count].mName;
   714             nsAutoString value;
   715             if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
   716               aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
   717                                  value, notify);
   718             }
   720 #if 0
   721             // XXX we don't fire the |onbroadcast| handler during
   722             // initial hookup: doing so would potentially run the
   723             // |onbroadcast| handler before the |onload| handler,
   724             // which could define JS properties that mask XBL
   725             // properties, etc.
   726             ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
   727 #endif
   728         }
   729     }
   730     else {
   731         // Find out if the attribute is even present at all.
   732         nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr);
   734         nsAutoString value;
   735         if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
   736             aListener->SetAttr(kNameSpaceID_None, name, value, notify);
   737         } else {
   738             aListener->UnsetAttr(kNameSpaceID_None, name, notify);
   739         }
   741 #if 0
   742         // XXX we don't fire the |onbroadcast| handler during initial
   743         // hookup: doing so would potentially run the |onbroadcast|
   744         // handler before the |onload| handler, which could define JS
   745         // properties that mask XBL properties, etc.
   746         ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
   747 #endif
   748     }
   749 }
   751 NS_IMETHODIMP
   752 XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
   753                                      nsIDOMElement* aListener,
   754                                      const nsAString& aAttr)
   755 {
   756     ErrorResult rv;
   757     nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
   758     nsCOMPtr<Element> listener = do_QueryInterface(aListener);
   759     NS_ENSURE_ARG(broadcaster && listener);
   760     AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv);
   761     return rv.ErrorCode();
   762 }
   764 void
   765 XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
   766                                      const nsAString& aAttr, ErrorResult& aRv)
   767 {
   768     nsresult rv =
   769         nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
   771     if (NS_FAILED(rv)) {
   772         aRv.Throw(rv);
   773         return;
   774     }
   776     rv = nsContentUtils::CheckSameOrigin(this, &aListener);
   778     if (NS_FAILED(rv)) {
   779         aRv.Throw(rv);
   780         return;
   781     }
   783     static const PLDHashTableOps gOps = {
   784         PL_DHashAllocTable,
   785         PL_DHashFreeTable,
   786         PL_DHashVoidPtrKeyStub,
   787         PL_DHashMatchEntryStub,
   788         PL_DHashMoveEntryStub,
   789         ClearBroadcasterMapEntry,
   790         PL_DHashFinalizeStub,
   791         nullptr
   792     };
   794     if (! mBroadcasterMap) {
   795         mBroadcasterMap =
   796             PL_NewDHashTable(&gOps, nullptr, sizeof(BroadcasterMapEntry),
   797                              PL_DHASH_MIN_SIZE);
   799         if (! mBroadcasterMap) {
   800             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   801             return;
   802         }
   803     }
   805     BroadcasterMapEntry* entry =
   806         static_cast<BroadcasterMapEntry*>
   807                    (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   808                                             PL_DHASH_LOOKUP));
   810     if (PL_DHASH_ENTRY_IS_FREE(entry)) {
   811         entry =
   812             static_cast<BroadcasterMapEntry*>
   813                        (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   814                                                 PL_DHASH_ADD));
   816         if (! entry) {
   817             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   818             return;
   819         }
   821         entry->mBroadcaster = &aBroadcaster;
   823         // N.B. placement new to construct the nsSmallVoidArray object
   824         // in-place
   825         new (&entry->mListeners) nsSmallVoidArray();
   826     }
   828     // Only add the listener if it's not there already!
   829     nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
   831     BroadcastListener* bl;
   832     for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   833         bl = static_cast<BroadcastListener*>(entry->mListeners[i]);
   835         nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
   837         if (blListener == &aListener && bl->mAttribute == attr)
   838             return;
   839     }
   841     bl = new BroadcastListener;
   843     bl->mListener  = do_GetWeakReference(&aListener);
   844     bl->mAttribute = attr;
   846     entry->mListeners.AppendElement(bl);
   848     SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
   849 }
   851 NS_IMETHODIMP
   852 XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
   853                                         nsIDOMElement* aListener,
   854                                         const nsAString& aAttr)
   855 {
   856     nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
   857     nsCOMPtr<Element> listener = do_QueryInterface(aListener);
   858     NS_ENSURE_ARG(broadcaster && listener);
   859     RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr);
   860     return NS_OK;
   861 }
   863 void
   864 XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
   865                                         Element& aListener,
   866                                         const nsAString& aAttr)
   867 {
   868     // If we haven't added any broadcast listeners, then there sure
   869     // aren't any to remove.
   870     if (! mBroadcasterMap)
   871         return;
   873     BroadcasterMapEntry* entry =
   874         static_cast<BroadcasterMapEntry*>
   875                    (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   876                                             PL_DHASH_LOOKUP));
   878     if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   879         nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
   880         for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   881             BroadcastListener* bl =
   882                 static_cast<BroadcastListener*>(entry->mListeners[i]);
   884             nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
   886             if (blListener == &aListener && bl->mAttribute == attr) {
   887                 entry->mListeners.RemoveElementAt(i);
   888                 delete bl;
   890                 if (entry->mListeners.Count() == 0)
   891                     PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   892                                          PL_DHASH_REMOVE);
   894                 break;
   895             }
   896         }
   897     }
   898 }
   900 nsresult
   901 XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
   902                                           Element* aListener,
   903                                           nsIAtom* aAttr)
   904 {
   905     // Now we execute the onchange handler in the context of the
   906     // observer. We need to find the observer in order to
   907     // execute the handler.
   909     for (nsIContent* child = aListener->GetFirstChild();
   910          child;
   911          child = child->GetNextSibling()) {
   913         // Look for an <observes> element beneath the listener. This
   914         // ought to have an |element| attribute that refers to
   915         // aBroadcaster, and an |attribute| element that tells us what
   916         // attriubtes we're listening for.
   917         if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL))
   918             continue;
   920         // Is this the element that was listening to us?
   921         nsAutoString listeningToID;
   922         child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
   924         nsAutoString broadcasterID;
   925         aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
   927         if (listeningToID != broadcasterID)
   928             continue;
   930         // We are observing the broadcaster, but is this the right
   931         // attribute?
   932         nsAutoString listeningToAttribute;
   933         child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
   934                        listeningToAttribute);
   936         if (!aAttr->Equals(listeningToAttribute) &&
   937             !listeningToAttribute.EqualsLiteral("*")) {
   938             continue;
   939         }
   941         // This is the right <observes> element. Execute the
   942         // |onbroadcast| event handler
   943         WidgetEvent event(true, NS_XUL_BROADCAST);
   945         nsCOMPtr<nsIPresShell> shell = GetShell();
   946         if (shell) {
   947             nsRefPtr<nsPresContext> aPresContext = shell->GetPresContext();
   949             // Handle the DOM event
   950             nsEventStatus status = nsEventStatus_eIgnore;
   951             EventDispatcher::Dispatch(child, aPresContext, &event, nullptr,
   952                                       &status);
   953         }
   954     }
   956     return NS_OK;
   957 }
   959 void
   960 XULDocument::AttributeWillChange(nsIDocument* aDocument,
   961                                  Element* aElement, int32_t aNameSpaceID,
   962                                  nsIAtom* aAttribute, int32_t aModType)
   963 {
   964     NS_ABORT_IF_FALSE(aElement, "Null content!");
   965     NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
   967     // XXXbz check aNameSpaceID, dammit!
   968     // See if we need to update our ref map.
   969     if (aAttribute == nsGkAtoms::ref ||
   970         (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) {
   971         // Might not need this, but be safe for now.
   972         nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   973         RemoveElementFromRefMap(aElement);
   974     }
   975 }
   977 void
   978 XULDocument::AttributeChanged(nsIDocument* aDocument,
   979                               Element* aElement, int32_t aNameSpaceID,
   980                               nsIAtom* aAttribute, int32_t aModType)
   981 {
   982     NS_ASSERTION(aDocument == this, "unexpected doc");
   984     // Might not need this, but be safe for now.
   985     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   987     // XXXbz check aNameSpaceID, dammit!
   988     // See if we need to update our ref map.
   989     if (aAttribute == nsGkAtoms::ref ||
   990         (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) {
   991         AddElementToRefMap(aElement);
   992     }
   994     nsresult rv;
   996     // Synchronize broadcast listeners
   997     if (mBroadcasterMap &&
   998         CanBroadcast(aNameSpaceID, aAttribute)) {
   999         BroadcasterMapEntry* entry =
  1000             static_cast<BroadcasterMapEntry*>
  1001                        (PL_DHashTableOperate(mBroadcasterMap, aElement,
  1002                                                 PL_DHASH_LOOKUP));
  1004         if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1005             // We've got listeners: push the value.
  1006             nsAutoString value;
  1007             bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
  1009             int32_t i;
  1010             for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
  1011                 BroadcastListener* bl =
  1012                     static_cast<BroadcastListener*>(entry->mListeners[i]);
  1014                 if ((bl->mAttribute == aAttribute) ||
  1015                     (bl->mAttribute == nsGkAtoms::_asterix)) {
  1016                     nsCOMPtr<Element> listenerEl
  1017                         = do_QueryReferent(bl->mListener);
  1018                     if (listenerEl) {
  1019                         nsAutoString currentValue;
  1020                         bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
  1021                                                            aAttribute,
  1022                                                            currentValue);
  1023                         // We need to update listener only if we're
  1024                         // (1) removing an existing attribute,
  1025                         // (2) adding a new attribute or
  1026                         // (3) changing the value of an attribute.
  1027                         bool needsAttrChange =
  1028                             attrSet != hasAttr || !value.Equals(currentValue);
  1029                         nsDelayedBroadcastUpdate delayedUpdate(aElement,
  1030                                                                listenerEl,
  1031                                                                aAttribute,
  1032                                                                value,
  1033                                                                attrSet,
  1034                                                                needsAttrChange);
  1036                         uint32_t index =
  1037                             mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
  1038                                 0, nsDelayedBroadcastUpdate::Comparator());
  1039                         if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
  1040                             if (mHandlingDelayedAttrChange) {
  1041                                 NS_WARNING("Broadcasting loop!");
  1042                                 continue;
  1044                             mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
  1047                         mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
  1054     // checks for modifications in broadcasters
  1055     bool listener, resolved;
  1056     CheckBroadcasterHookup(aElement, &listener, &resolved);
  1058     // See if there is anything we need to persist in the localstore.
  1059     //
  1060     // XXX Namespace handling broken :-(
  1061     nsAutoString persist;
  1062     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
  1063     if (!persist.IsEmpty()) {
  1064         // XXXldb This should check that it's a token, not just a substring.
  1065         if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
  1066             rv = Persist(aElement, kNameSpaceID_None, aAttribute);
  1067             if (NS_FAILED(rv)) return;
  1072 void
  1073 XULDocument::ContentAppended(nsIDocument* aDocument,
  1074                              nsIContent* aContainer,
  1075                              nsIContent* aFirstNewContent,
  1076                              int32_t aNewIndexInContainer)
  1078     NS_ASSERTION(aDocument == this, "unexpected doc");
  1080     // Might not need this, but be safe for now.
  1081     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1083     // Update our element map
  1084     nsresult rv = NS_OK;
  1085     for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
  1086          cur = cur->GetNextSibling()) {
  1087         rv = AddSubtreeToDocument(cur);
  1091 void
  1092 XULDocument::ContentInserted(nsIDocument* aDocument,
  1093                              nsIContent* aContainer,
  1094                              nsIContent* aChild,
  1095                              int32_t aIndexInContainer)
  1097     NS_ASSERTION(aDocument == this, "unexpected doc");
  1099     // Might not need this, but be safe for now.
  1100     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1102     AddSubtreeToDocument(aChild);
  1105 void
  1106 XULDocument::ContentRemoved(nsIDocument* aDocument,
  1107                             nsIContent* aContainer,
  1108                             nsIContent* aChild,
  1109                             int32_t aIndexInContainer,
  1110                             nsIContent* aPreviousSibling)
  1112     NS_ASSERTION(aDocument == this, "unexpected doc");
  1114     // Might not need this, but be safe for now.
  1115     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1117     RemoveSubtreeFromDocument(aChild);
  1120 //----------------------------------------------------------------------
  1121 //
  1122 // nsIXULDocument interface
  1123 //
  1125 void
  1126 XULDocument::GetElementsForID(const nsAString& aID,
  1127                               nsCOMArray<nsIContent>& aElements)
  1129     aElements.Clear();
  1131     nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
  1132     if (entry) {
  1133         entry->AppendAllIdContent(&aElements);
  1135     nsRefMapEntry *refEntry = mRefMap.GetEntry(aID);
  1136     if (refEntry) {
  1137         refEntry->AppendAll(&aElements);
  1141 nsresult
  1142 XULDocument::AddForwardReference(nsForwardReference* aRef)
  1144     if (mResolutionPhase < aRef->GetPhase()) {
  1145         if (!mForwardReferences.AppendElement(aRef)) {
  1146             delete aRef;
  1147             return NS_ERROR_OUT_OF_MEMORY;
  1150     else {
  1151         NS_ERROR("forward references have already been resolved");
  1152         delete aRef;
  1155     return NS_OK;
  1158 nsresult
  1159 XULDocument::ResolveForwardReferences()
  1161     if (mResolutionPhase == nsForwardReference::eDone)
  1162         return NS_OK;
  1164     NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart,
  1165                  "nested ResolveForwardReferences()");
  1167     // Resolve each outstanding 'forward' reference. We iterate
  1168     // through the list of forward references until no more forward
  1169     // references can be resolved. This annealing process is
  1170     // guaranteed to converge because we've "closed the gate" to new
  1171     // forward references.
  1173     const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
  1174     while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
  1175         uint32_t previous = 0;
  1176         while (mForwardReferences.Length() &&
  1177                mForwardReferences.Length() != previous) {
  1178             previous = mForwardReferences.Length();
  1180             for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) {
  1181                 nsForwardReference* fwdref = mForwardReferences[i];
  1183                 if (fwdref->GetPhase() == *pass) {
  1184                     nsForwardReference::Result result = fwdref->Resolve();
  1186                     switch (result) {
  1187                     case nsForwardReference::eResolve_Succeeded:
  1188                     case nsForwardReference::eResolve_Error:
  1189                         mForwardReferences.RemoveElementAt(i);
  1191                         // fixup because we removed from list
  1192                         --i;
  1193                         break;
  1195                     case nsForwardReference::eResolve_Later:
  1196                         // do nothing. we'll try again later
  1200                     if (mResolutionPhase == nsForwardReference::eStart) {
  1201                         // Resolve() loaded a dynamic overlay,
  1202                         // (see XULDocument::LoadOverlayInternal()).
  1203                         // Return for now, we will be called again.
  1204                         return NS_OK;
  1210         ++pass;
  1213     mForwardReferences.Clear();
  1214     return NS_OK;
  1217 //----------------------------------------------------------------------
  1218 //
  1219 // nsIDOMDocument interface
  1220 //
  1222 NS_IMETHODIMP
  1223 XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  1224                                     const nsAString& aValue,
  1225                                     nsIDOMNodeList** aReturn)
  1227     *aReturn = GetElementsByAttribute(aAttribute, aValue).take();
  1228     return NS_OK;
  1231 already_AddRefed<nsINodeList>
  1232 XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  1233                                     const nsAString& aValue)
  1235     nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
  1236     void* attrValue = new nsString(aValue);
  1237     nsRefPtr<nsContentList> list = new nsContentList(this,
  1238                                             MatchAttribute,
  1239                                             nsContentUtils::DestroyMatchString,
  1240                                             attrValue,
  1241                                             true,
  1242                                             attrAtom,
  1243                                             kNameSpaceID_Unknown);
  1245     return list.forget();
  1248 NS_IMETHODIMP
  1249 XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1250                                       const nsAString& aAttribute,
  1251                                       const nsAString& aValue,
  1252                                       nsIDOMNodeList** aReturn)
  1254     ErrorResult rv;
  1255     *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute,
  1256                                         aValue, rv).take();
  1257     return rv.ErrorCode();
  1260 already_AddRefed<nsINodeList>
  1261 XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1262                                       const nsAString& aAttribute,
  1263                                       const nsAString& aValue,
  1264                                       ErrorResult& aRv)
  1266     nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
  1267     void* attrValue = new nsString(aValue);
  1269     int32_t nameSpaceId = kNameSpaceID_Wildcard;
  1270     if (!aNamespaceURI.EqualsLiteral("*")) {
  1271       nsresult rv =
  1272         nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
  1273                                                               nameSpaceId);
  1274       if (NS_FAILED(rv)) {
  1275           aRv.Throw(rv);
  1276           return nullptr;
  1280     nsRefPtr<nsContentList> list = new nsContentList(this,
  1281                                             MatchAttribute,
  1282                                             nsContentUtils::DestroyMatchString,
  1283                                             attrValue,
  1284                                             true,
  1285                                             attrAtom,
  1286                                             nameSpaceId);
  1287     return list.forget();
  1290 NS_IMETHODIMP
  1291 XULDocument::Persist(const nsAString& aID,
  1292                      const nsAString& aAttr)
  1294     // If we're currently reading persisted attributes out of the
  1295     // localstore, _don't_ re-enter and try to set them again!
  1296     if (mApplyingPersistedAttrs)
  1297         return NS_OK;
  1299     Element* element = nsDocument::GetElementById(aID);
  1300     if (!element)
  1301         return NS_OK;
  1303     nsCOMPtr<nsIAtom> tag;
  1304     int32_t nameSpaceID;
  1306     nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
  1307     nsresult rv;
  1308     if (ni) {
  1309         tag = ni->NameAtom();
  1310         nameSpaceID = ni->NamespaceID();
  1312     else {
  1313         // Make sure that this QName is going to be valid.
  1314         const char16_t *colon;
  1315         rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon);
  1317         if (NS_FAILED(rv)) {
  1318             // There was an invalid character or it was malformed.
  1319             return NS_ERROR_INVALID_ARG;
  1322         if (colon) {
  1323             // We don't really handle namespace qualifiers in attribute names.
  1324             return NS_ERROR_NOT_IMPLEMENTED;
  1327         tag = do_GetAtom(aAttr);
  1328         NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
  1330         nameSpaceID = kNameSpaceID_None;
  1333     rv = Persist(element, nameSpaceID, tag);
  1334     if (NS_FAILED(rv)) return rv;
  1336     return NS_OK;
  1339 nsresult
  1340 XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
  1341                      nsIAtom* aAttribute)
  1343     // For non-chrome documents, persistance is simply broken
  1344     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  1345         return NS_ERROR_NOT_AVAILABLE;
  1347     // First make sure we _have_ a local store to stuff the persisted
  1348     // information into. (We might not have one if profile information
  1349     // hasn't been loaded yet...)
  1350     if (!mLocalStore)
  1351         return NS_OK;
  1353     nsresult rv;
  1355     nsCOMPtr<nsIRDFResource> element;
  1356     rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
  1357     if (NS_FAILED(rv)) return rv;
  1359     // No ID, so nothing to persist.
  1360     if (! element)
  1361         return NS_OK;
  1363     // Ick. Construct a property from the attribute. Punt on
  1364     // namespaces for now.
  1365     // Don't bother with unreasonable attributes. We clamp long values,
  1366     // but truncating attribute names turns it into a different attribute
  1367     // so there's no point in persisting anything at all
  1368     nsAtomCString attrstr(aAttribute);
  1369     if (attrstr.Length() > kMaxAttrNameLength) {
  1370         NS_WARNING("Can't persist, Attribute name too long");
  1371         return NS_ERROR_ILLEGAL_VALUE;
  1374     nsCOMPtr<nsIRDFResource> attr;
  1375     rv = gRDFService->GetResource(attrstr,
  1376                                   getter_AddRefs(attr));
  1377     if (NS_FAILED(rv)) return rv;
  1379     // Turn the value into a literal
  1380     nsAutoString valuestr;
  1381     aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
  1383     // prevent over-long attributes that choke the parser (bug 319846)
  1384     // (can't simply Truncate without testing, it's implemented
  1385     // using SetLength and will grow a short string)
  1386     if (valuestr.Length() > kMaxAttributeLength) {
  1387         NS_WARNING("Truncating persisted attribute value");
  1388         valuestr.Truncate(kMaxAttributeLength);
  1391     // See if there was an old value...
  1392     nsCOMPtr<nsIRDFNode> oldvalue;
  1393     rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue));
  1394     if (NS_FAILED(rv)) return rv;
  1396     if (oldvalue && valuestr.IsEmpty()) {
  1397         // ...there was an oldvalue, and they've removed it. XXXThis
  1398         // handling isn't quite right...
  1399         rv = mLocalStore->Unassert(element, attr, oldvalue);
  1401     else {
  1402         // Now either 'change' or 'assert' based on whether there was
  1403         // an old value.
  1404         nsCOMPtr<nsIRDFLiteral> newvalue;
  1405         rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
  1406         if (NS_FAILED(rv)) return rv;
  1408         if (oldvalue) {
  1409             if (oldvalue != newvalue)
  1410                 rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
  1411             else
  1412                 rv = NS_OK;
  1414         else {
  1415             rv = mLocalStore->Assert(element, attr, newvalue, true);
  1419     if (NS_FAILED(rv)) return rv;
  1421     // Add it to the persisted set for this document (if it's not
  1422     // there already).
  1424         nsAutoCString docurl;
  1425         rv = mDocumentURI->GetSpec(docurl);
  1426         if (NS_FAILED(rv)) return rv;
  1428         nsCOMPtr<nsIRDFResource> doc;
  1429         rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
  1430         if (NS_FAILED(rv)) return rv;
  1432         bool hasAssertion;
  1433         rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion);
  1434         if (NS_FAILED(rv)) return rv;
  1436         if (! hasAssertion) {
  1437             rv = mLocalStore->Assert(doc, kNC_persist, element, true);
  1438             if (NS_FAILED(rv)) return rv;
  1442     return NS_OK;
  1446 nsresult
  1447 XULDocument::GetViewportSize(int32_t* aWidth,
  1448                              int32_t* aHeight)
  1450     *aWidth = *aHeight = 0;
  1452     FlushPendingNotifications(Flush_Layout);
  1454     nsIPresShell *shell = GetShell();
  1455     NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
  1457     nsIFrame* frame = shell->GetRootFrame();
  1458     NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
  1460     nsSize size = frame->GetSize();
  1462     *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
  1463     *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
  1465     return NS_OK;
  1468 NS_IMETHODIMP
  1469 XULDocument::GetWidth(int32_t* aWidth)
  1471     NS_ENSURE_ARG_POINTER(aWidth);
  1473     int32_t height;
  1474     return GetViewportSize(aWidth, &height);
  1477 int32_t
  1478 XULDocument::GetWidth(ErrorResult& aRv)
  1480     int32_t width;
  1481     aRv = GetWidth(&width);
  1482     return width;
  1485 NS_IMETHODIMP
  1486 XULDocument::GetHeight(int32_t* aHeight)
  1488     NS_ENSURE_ARG_POINTER(aHeight);
  1490     int32_t width;
  1491     return GetViewportSize(&width, aHeight);
  1494 int32_t
  1495 XULDocument::GetHeight(ErrorResult& aRv)
  1497     int32_t height;
  1498     aRv = GetHeight(&height);
  1499     return height;
  1502 JSObject*
  1503 GetScopeObjectOfNode(nsIDOMNode* node)
  1505     MOZ_ASSERT(node, "Must not be called with null.");
  1507     // Window root occasionally keeps alive a node of a document whose
  1508     // window is already dead. If in this brief period someone calls
  1509     // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
  1510     // because it will not know which scope this node belongs to. Returning
  1511     // an orphan node like that to JS would be a bug anyway, so to avoid
  1512     // this, let's do the same check as nsNodeSH::PreCreate does to
  1513     // determine the scope and if it fails let's just return null in
  1514     // XULDocument::GetPopupNode.
  1515     nsCOMPtr<nsINode> inode = do_QueryInterface(node);
  1516     MOZ_ASSERT(inode, "How can this happen?");
  1518     nsIDocument* doc = inode->OwnerDoc();
  1519     MOZ_ASSERT(inode, "This should never happen.");
  1521     nsIGlobalObject* global = doc->GetScopeObject();
  1522     return global ? global->GetGlobalJSObject() : nullptr;
  1525 //----------------------------------------------------------------------
  1526 //
  1527 // nsIDOMXULDocument interface
  1528 //
  1530 NS_IMETHODIMP
  1531 XULDocument::GetPopupNode(nsIDOMNode** aNode)
  1533     *aNode = nullptr;
  1535     nsCOMPtr<nsIDOMNode> node;
  1536     nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1537     if (rootWin)
  1538         node = rootWin->GetPopupNode(); // addref happens here
  1540     if (!node) {
  1541         nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1542         if (pm) {
  1543             node = pm->GetLastTriggerPopupNode(this);
  1547     if (node && nsContentUtils::CanCallerAccess(node)
  1548         && GetScopeObjectOfNode(node)) {
  1549         node.swap(*aNode);
  1552     return NS_OK;
  1555 already_AddRefed<nsINode>
  1556 XULDocument::GetPopupNode()
  1558     nsCOMPtr<nsIDOMNode> node;
  1559     DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node));
  1560     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1561     nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1562     return retval.forget();
  1565 NS_IMETHODIMP
  1566 XULDocument::SetPopupNode(nsIDOMNode* aNode)
  1568     if (aNode) {
  1569         // only allow real node objects
  1570         nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1571         NS_ENSURE_ARG(node);
  1574     nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1575     if (rootWin)
  1576         rootWin->SetPopupNode(aNode); // addref happens here
  1578     return NS_OK;
  1581 void
  1582 XULDocument::SetPopupNode(nsINode* aNode)
  1584     nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode));
  1585     DebugOnly<nsresult> rv = SetPopupNode(node);
  1586     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1589 // Returns the rangeOffset element from the XUL Popup Manager. This is for
  1590 // chrome callers only.
  1591 NS_IMETHODIMP
  1592 XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
  1594     NS_ENSURE_ARG_POINTER(aRangeParent);
  1595     *aRangeParent = nullptr;
  1597     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1598     if (!pm)
  1599         return NS_ERROR_FAILURE;
  1601     int32_t offset;
  1602     pm->GetMouseLocation(aRangeParent, &offset);
  1604     if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) {
  1605         NS_RELEASE(*aRangeParent);
  1606         return NS_ERROR_DOM_SECURITY_ERR;
  1609     return NS_OK;
  1612 already_AddRefed<nsINode>
  1613 XULDocument::GetPopupRangeParent(ErrorResult& aRv)
  1615     nsCOMPtr<nsIDOMNode> node;
  1616     aRv = GetPopupRangeParent(getter_AddRefs(node));
  1617     nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1618     return retval.forget();
  1622 // Returns the rangeOffset element from the XUL Popup Manager. We check the
  1623 // rangeParent to determine if the caller has rights to access to the data.
  1624 NS_IMETHODIMP
  1625 XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset)
  1627     ErrorResult rv;
  1628     *aRangeOffset = GetPopupRangeOffset(rv);
  1629     return rv.ErrorCode();
  1632 int32_t
  1633 XULDocument::GetPopupRangeOffset(ErrorResult& aRv)
  1635     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1636     if (!pm) {
  1637         aRv.Throw(NS_ERROR_FAILURE);
  1638         return 0;
  1641     int32_t offset;
  1642     nsCOMPtr<nsIDOMNode> parent;
  1643     pm->GetMouseLocation(getter_AddRefs(parent), &offset);
  1645     if (parent && !nsContentUtils::CanCallerAccess(parent)) {
  1646         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1647         return 0;
  1649     return offset;
  1652 NS_IMETHODIMP
  1653 XULDocument::GetTooltipNode(nsIDOMNode** aNode)
  1655     *aNode = nullptr;
  1657     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1658     if (pm) {
  1659         nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
  1660         if (node && nsContentUtils::CanCallerAccess(node))
  1661             node.swap(*aNode);
  1664     return NS_OK;
  1667 already_AddRefed<nsINode>
  1668 XULDocument::GetTooltipNode()
  1670     nsCOMPtr<nsIDOMNode> node;
  1671     DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node));
  1672     MOZ_ASSERT(NS_SUCCEEDED(rv));
  1673     nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1674     return retval.forget();
  1677 NS_IMETHODIMP
  1678 XULDocument::SetTooltipNode(nsIDOMNode* aNode)
  1680     // do nothing
  1681     return NS_OK;
  1685 NS_IMETHODIMP
  1686 XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
  1688     *aTracker = mCommandDispatcher;
  1689     NS_IF_ADDREF(*aTracker);
  1690     return NS_OK;
  1693 Element*
  1694 XULDocument::GetElementById(const nsAString& aId)
  1696     if (!CheckGetElementByIdArg(aId))
  1697         return nullptr;
  1699     nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
  1700     if (entry) {
  1701         Element* element = entry->GetIdElement();
  1702         if (element)
  1703             return element;
  1706     nsRefMapEntry* refEntry = mRefMap.GetEntry(aId);
  1707     if (refEntry) {
  1708         NS_ASSERTION(refEntry->GetFirstElement(),
  1709                      "nsRefMapEntries should have nonempty content lists");
  1710         return refEntry->GetFirstElement();
  1712     return nullptr;
  1715 nsresult
  1716 XULDocument::AddElementToDocumentPre(Element* aElement)
  1718     // Do a bunch of work that's necessary when an element gets added
  1719     // to the XUL Document.
  1720     nsresult rv;
  1722     // 1. Add the element to the resource-to-element map. Also add it to
  1723     // the id map, since it seems this can be called when creating
  1724     // elements from prototypes.
  1725     nsIAtom* id = aElement->GetID();
  1726     if (id) {
  1727         // FIXME: Shouldn't BindToTree take care of this?
  1728         nsAutoScriptBlocker scriptBlocker;
  1729         AddToIdTable(aElement, id);
  1731     rv = AddElementToRefMap(aElement);
  1732     if (NS_FAILED(rv)) return rv;
  1734     // 2. If the element is a 'command updater' (i.e., has a
  1735     // "commandupdater='true'" attribute), then add the element to the
  1736     // document's command dispatcher
  1737     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1738                               nsGkAtoms::_true, eCaseMatters)) {
  1739         rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
  1740         if (NS_FAILED(rv)) return rv;
  1743     // 3. Check for a broadcaster hookup attribute, in which case
  1744     // we'll hook the node up as a listener on a broadcaster.
  1745     bool listener, resolved;
  1746     rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
  1747     if (NS_FAILED(rv)) return rv;
  1749     // If it's not there yet, we may be able to defer hookup until
  1750     // later.
  1751     if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
  1752         BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
  1753         if (! hookup)
  1754             return NS_ERROR_OUT_OF_MEMORY;
  1756         rv = AddForwardReference(hookup);
  1757         if (NS_FAILED(rv)) return rv;
  1760     return NS_OK;
  1763 nsresult
  1764 XULDocument::AddElementToDocumentPost(Element* aElement)
  1766     // We need to pay special attention to the keyset tag to set up a listener
  1767     if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1768         // Create our XUL key listener and hook it up.
  1769         nsXBLService::AttachGlobalKeyHandler(aElement);
  1772     // See if we need to attach a XUL template to this node
  1773     bool needsHookup;
  1774     nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
  1775     if (NS_FAILED(rv))
  1776         return rv;
  1778     if (needsHookup) {
  1779         if (mResolutionPhase == nsForwardReference::eDone) {
  1780             rv = CreateTemplateBuilder(aElement);
  1781             if (NS_FAILED(rv))
  1782                 return rv;
  1784         else {
  1785             TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
  1786             if (! hookup)
  1787                 return NS_ERROR_OUT_OF_MEMORY;
  1789             rv = AddForwardReference(hookup);
  1790             if (NS_FAILED(rv))
  1791                 return rv;
  1795     return NS_OK;
  1798 NS_IMETHODIMP
  1799 XULDocument::AddSubtreeToDocument(nsIContent* aContent)
  1801     NS_ASSERTION(aContent->GetCurrentDoc() == this, "Element not in doc!");
  1802     // From here on we only care about elements.
  1803     if (!aContent->IsElement()) {
  1804         return NS_OK;
  1807     Element* aElement = aContent->AsElement();
  1809     // Do pre-order addition magic
  1810     nsresult rv = AddElementToDocumentPre(aElement);
  1811     if (NS_FAILED(rv)) return rv;
  1813     // Recurse to children
  1814     for (nsIContent* child = aElement->GetLastChild();
  1815          child;
  1816          child = child->GetPreviousSibling()) {
  1818         rv = AddSubtreeToDocument(child);
  1819         if (NS_FAILED(rv))
  1820             return rv;
  1823     // Do post-order addition magic
  1824     return AddElementToDocumentPost(aElement);
  1827 NS_IMETHODIMP
  1828 XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
  1830     // From here on we only care about elements.
  1831     if (!aContent->IsElement()) {
  1832         return NS_OK;
  1835     Element* aElement = aContent->AsElement();
  1837     // Do a bunch of cleanup to remove an element from the XUL
  1838     // document.
  1839     nsresult rv;
  1841     if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1842         nsXBLService::DetachGlobalKeyHandler(aElement);
  1845     // 1. Remove any children from the document.
  1846     for (nsIContent* child = aElement->GetLastChild();
  1847          child;
  1848          child = child->GetPreviousSibling()) {
  1850         rv = RemoveSubtreeFromDocument(child);
  1851         if (NS_FAILED(rv))
  1852             return rv;
  1855     // 2. Remove the element from the resource-to-element map.
  1856     // Also remove it from the id map, since we added it in
  1857     // AddElementToDocumentPre().
  1858     RemoveElementFromRefMap(aElement);
  1859     nsIAtom* id = aElement->GetID();
  1860     if (id) {
  1861         // FIXME: Shouldn't UnbindFromTree take care of this?
  1862         nsAutoScriptBlocker scriptBlocker;
  1863         RemoveFromIdTable(aElement, id);
  1866     // 3. If the element is a 'command updater', then remove the
  1867     // element from the document's command dispatcher.
  1868     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1869                               nsGkAtoms::_true, eCaseMatters)) {
  1870         nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
  1871         NS_ASSERTION(domelement != nullptr, "not a DOM element");
  1872         if (! domelement)
  1873             return NS_ERROR_UNEXPECTED;
  1875         rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
  1876         if (NS_FAILED(rv)) return rv;
  1879     // 4. Remove the element from our broadcaster map, since it is no longer
  1880     // in the document.
  1881     nsCOMPtr<Element> broadcaster, listener;
  1882     nsAutoString attribute, broadcasterID;
  1883     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  1884                          broadcasterID, attribute, getter_AddRefs(broadcaster));
  1885     if (rv == NS_FINDBROADCASTER_FOUND) {
  1886         RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
  1889     return NS_OK;
  1892 NS_IMETHODIMP
  1893 XULDocument::SetTemplateBuilderFor(nsIContent* aContent,
  1894                                    nsIXULTemplateBuilder* aBuilder)
  1896     if (! mTemplateBuilderTable) {
  1897         if (!aBuilder) {
  1898             return NS_OK;
  1900         mTemplateBuilderTable = new BuilderTable;
  1903     if (aBuilder) {
  1904         mTemplateBuilderTable->Put(aContent, aBuilder);
  1906     else {
  1907         mTemplateBuilderTable->Remove(aContent);
  1910     return NS_OK;
  1913 NS_IMETHODIMP
  1914 XULDocument::GetTemplateBuilderFor(nsIContent* aContent,
  1915                                    nsIXULTemplateBuilder** aResult)
  1917     if (mTemplateBuilderTable) {
  1918         mTemplateBuilderTable->Get(aContent, aResult);
  1920     else
  1921         *aResult = nullptr;
  1923     return NS_OK;
  1926 static void
  1927 GetRefMapAttribute(Element* aElement, nsAutoString* aValue)
  1929     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue);
  1930     if (aValue->IsEmpty() && !aElement->GetIDAttributeName()) {
  1931         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, *aValue);
  1935 nsresult
  1936 XULDocument::AddElementToRefMap(Element* aElement)
  1938     // Look at the element's 'ref' attribute, and if set,
  1939     // add an entry in the resource-to-element map to the element.
  1940     nsAutoString value;
  1941     GetRefMapAttribute(aElement, &value);
  1942     if (!value.IsEmpty()) {
  1943         nsRefMapEntry *entry = mRefMap.PutEntry(value);
  1944         if (!entry)
  1945             return NS_ERROR_OUT_OF_MEMORY;
  1946         if (!entry->AddElement(aElement))
  1947             return NS_ERROR_OUT_OF_MEMORY;
  1950     return NS_OK;
  1953 void
  1954 XULDocument::RemoveElementFromRefMap(Element* aElement)
  1956     // Remove the element from the resource-to-element map.
  1957     nsAutoString value;
  1958     GetRefMapAttribute(aElement, &value);
  1959     if (!value.IsEmpty()) {
  1960         nsRefMapEntry *entry = mRefMap.GetEntry(value);
  1961         if (!entry)
  1962             return;
  1963         if (entry->RemoveElement(aElement)) {
  1964             mRefMap.RawRemoveEntry(entry);
  1969 //----------------------------------------------------------------------
  1970 //
  1971 // nsIDOMNode interface
  1972 //
  1974 nsresult
  1975 XULDocument::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
  1977     // We don't allow cloning of a XUL document
  1978     *aResult = nullptr;
  1979     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
  1983 //----------------------------------------------------------------------
  1984 //
  1985 // Implementation methods
  1986 //
  1988 nsresult
  1989 XULDocument::Init()
  1991     nsresult rv = XMLDocument::Init();
  1992     NS_ENSURE_SUCCESS(rv, rv);
  1994     // Create our command dispatcher and hook it up.
  1995     mCommandDispatcher = new nsXULCommandDispatcher(this);
  1996     NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
  1998     // this _could_ fail; e.g., if we've tried to grab the local store
  1999     // before profiles have initialized. If so, no big deal; nothing
  2000     // will persist.
  2001     mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID);
  2003     if (gRefCnt++ == 0) {
  2004         // Keep the RDF service cached in a member variable to make using
  2005         // it a bit less painful
  2006         rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
  2007         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
  2008         if (NS_FAILED(rv)) return rv;
  2010         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
  2011                                  &kNC_persist);
  2012         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
  2013                                  &kNC_attribute);
  2014         gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
  2015                                  &kNC_value);
  2017         // ensure that the XUL prototype cache is instantiated successfully,
  2018         // so that we can use nsXULPrototypeCache::GetInstance() without
  2019         // null-checks in the rest of the class.
  2020         nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  2021         if (!cache) {
  2022           NS_ERROR("Could not instantiate nsXULPrototypeCache");
  2023           return NS_ERROR_FAILURE;
  2027     Preferences::RegisterCallback(XULDocument::DirectionChanged,
  2028                                   "intl.uidirection.", this);
  2030 #ifdef PR_LOGGING
  2031     if (! gXULLog)
  2032         gXULLog = PR_NewLogModule("XULDocument");
  2033 #endif
  2035     return NS_OK;
  2039 nsresult
  2040 XULDocument::StartLayout(void)
  2042     mMayStartLayout = true;
  2043     nsCOMPtr<nsIPresShell> shell = GetShell();
  2044     if (shell) {
  2045         // Resize-reflow this time
  2046         nsPresContext *cx = shell->GetPresContext();
  2047         NS_ASSERTION(cx != nullptr, "no pres context");
  2048         if (! cx)
  2049             return NS_ERROR_UNEXPECTED;
  2051         nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
  2052         NS_ASSERTION(docShell != nullptr, "container is not a docshell");
  2053         if (! docShell)
  2054             return NS_ERROR_UNEXPECTED;
  2056         nsresult rv = NS_OK;
  2057         nsRect r = cx->GetVisibleArea();
  2058         rv = shell->Initialize(r.width, r.height);
  2059         NS_ENSURE_SUCCESS(rv, rv);
  2062     return NS_OK;
  2065 /* static */
  2066 bool
  2067 XULDocument::MatchAttribute(nsIContent* aContent,
  2068                             int32_t aNamespaceID,
  2069                             nsIAtom* aAttrName,
  2070                             void* aData)
  2072     NS_PRECONDITION(aContent, "Must have content node to work with!");
  2073     nsString* attrValue = static_cast<nsString*>(aData);
  2074     if (aNamespaceID != kNameSpaceID_Unknown &&
  2075         aNamespaceID != kNameSpaceID_Wildcard) {
  2076         return attrValue->EqualsLiteral("*") ?
  2077             aContent->HasAttr(aNamespaceID, aAttrName) :
  2078             aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
  2079                                   eCaseMatters);
  2082     // Qualified name match. This takes more work.
  2084     uint32_t count = aContent->GetAttrCount();
  2085     for (uint32_t i = 0; i < count; ++i) {
  2086         const nsAttrName* name = aContent->GetAttrNameAt(i);
  2087         bool nameMatch;
  2088         if (name->IsAtom()) {
  2089             nameMatch = name->Atom() == aAttrName;
  2090         } else if (aNamespaceID == kNameSpaceID_Wildcard) {
  2091             nameMatch = name->NodeInfo()->Equals(aAttrName);
  2092         } else {
  2093             nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
  2096         if (nameMatch) {
  2097             return attrValue->EqualsLiteral("*") ||
  2098                 aContent->AttrValueIs(name->NamespaceID(), name->LocalName(),
  2099                                       *attrValue, eCaseMatters);
  2103     return false;
  2106 nsresult
  2107 XULDocument::PrepareToLoad(nsISupports* aContainer,
  2108                            const char* aCommand,
  2109                            nsIChannel* aChannel,
  2110                            nsILoadGroup* aLoadGroup,
  2111                            nsIParser** aResult)
  2113     // Get the document's principal
  2114     nsCOMPtr<nsIPrincipal> principal;
  2115     nsContentUtils::GetSecurityManager()->
  2116         GetChannelPrincipal(aChannel, getter_AddRefs(principal));
  2117     return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
  2121 nsresult
  2122 XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
  2123                                     nsIPrincipal* aDocumentPrincipal,
  2124                                     nsIParser** aResult)
  2126     nsresult rv;
  2128     // Create a new prototype document.
  2129     rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
  2130     if (NS_FAILED(rv)) return rv;
  2132     rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
  2133     if (NS_FAILED(rv)) {
  2134         mCurrentPrototype = nullptr;
  2135         return rv;
  2138     // Bootstrap the master document prototype.
  2139     if (! mMasterPrototype) {
  2140         mMasterPrototype = mCurrentPrototype;
  2141         // Set our principal based on the master proto.
  2142         SetPrincipal(aDocumentPrincipal);
  2145     // Create a XUL content sink, a parser, and kick off a load for
  2146     // the overlay.
  2147     nsRefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
  2148     if (!sink) return NS_ERROR_OUT_OF_MEMORY;
  2150     rv = sink->Init(this, mCurrentPrototype);
  2151     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
  2152     if (NS_FAILED(rv)) return rv;
  2154     nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
  2155     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
  2156     if (NS_FAILED(rv)) return rv;
  2158     parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
  2159                        eViewSource);
  2161     parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
  2162                                kCharsetFromDocTypeDefault);
  2163     parser->SetContentSink(sink); // grabs a reference to the parser
  2165     *aResult = parser;
  2166     NS_ADDREF(*aResult);
  2167     return NS_OK;
  2171 nsresult
  2172 XULDocument::ApplyPersistentAttributes()
  2174     // For non-chrome documents, persistance is simply broken
  2175     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  2176         return NS_ERROR_NOT_AVAILABLE;
  2178     // Add all of the 'persisted' attributes into the content
  2179     // model.
  2180     if (!mLocalStore)
  2181         return NS_OK;
  2183     mApplyingPersistedAttrs = true;
  2184     ApplyPersistentAttributesInternal();
  2185     mApplyingPersistedAttrs = false;
  2187     // After we've applied persistence once, we should only reapply
  2188     // it to nodes created by overlays
  2189     mRestrictPersistence = true;
  2190     mPersistenceIds.Clear();
  2192     return NS_OK;
  2196 nsresult 
  2197 XULDocument::ApplyPersistentAttributesInternal()
  2199     nsCOMArray<nsIContent> elements;
  2201     nsAutoCString docurl;
  2202     mDocumentURI->GetSpec(docurl);
  2204     nsCOMPtr<nsIRDFResource> doc;
  2205     gRDFService->GetResource(docurl, getter_AddRefs(doc));
  2207     nsCOMPtr<nsISimpleEnumerator> persisted;
  2208     mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted));
  2210     while (1) {
  2211         bool hasmore = false;
  2212         persisted->HasMoreElements(&hasmore);
  2213         if (! hasmore)
  2214             break;
  2216         nsCOMPtr<nsISupports> isupports;
  2217         persisted->GetNext(getter_AddRefs(isupports));
  2219         nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
  2220         if (! resource) {
  2221             NS_WARNING("expected element to be a resource");
  2222             continue;
  2225         const char *uri;
  2226         resource->GetValueConst(&uri);
  2227         if (! uri)
  2228             continue;
  2230         nsAutoString id;
  2231         nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id);
  2233         if (id.IsEmpty())
  2234             continue;
  2236         if (mRestrictPersistence && !mPersistenceIds.Contains(id))
  2237             continue;
  2239         // This will clear the array if there are no elements.
  2240         GetElementsForID(id, elements);
  2242         if (!elements.Count())
  2243             continue;
  2245         ApplyPersistentAttributesToElements(resource, elements);
  2248     return NS_OK;
  2252 nsresult
  2253 XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
  2254                                                  nsCOMArray<nsIContent>& aElements)
  2256     nsresult rv;
  2258     nsCOMPtr<nsISimpleEnumerator> attrs;
  2259     rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
  2260     if (NS_FAILED(rv)) return rv;
  2262     while (1) {
  2263         bool hasmore;
  2264         rv = attrs->HasMoreElements(&hasmore);
  2265         if (NS_FAILED(rv)) return rv;
  2267         if (! hasmore)
  2268             break;
  2270         nsCOMPtr<nsISupports> isupports;
  2271         rv = attrs->GetNext(getter_AddRefs(isupports));
  2272         if (NS_FAILED(rv)) return rv;
  2274         nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
  2275         if (! property) {
  2276             NS_WARNING("expected a resource");
  2277             continue;
  2280         const char* attrname;
  2281         rv = property->GetValueConst(&attrname);
  2282         if (NS_FAILED(rv)) return rv;
  2284         nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
  2285         if (! attr)
  2286             return NS_ERROR_OUT_OF_MEMORY;
  2288         // XXX could hang namespace off here, as well...
  2290         nsCOMPtr<nsIRDFNode> node;
  2291         rv = mLocalStore->GetTarget(aResource, property, true,
  2292                                     getter_AddRefs(node));
  2293         if (NS_FAILED(rv)) return rv;
  2295         nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
  2296         if (! literal) {
  2297             NS_WARNING("expected a literal");
  2298             continue;
  2301         const char16_t* value;
  2302         rv = literal->GetValueConst(&value);
  2303         if (NS_FAILED(rv)) return rv;
  2305         nsDependentString wrapper(value);
  2307         uint32_t cnt = aElements.Count();
  2309         for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
  2310             nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
  2311             if (!element)
  2312                 continue;
  2314             rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
  2315                                   attr,
  2316                                   wrapper,
  2317                                   true);
  2321     return NS_OK;
  2324 void
  2325 XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
  2327     uint32_t i, count = mPrototypes.Length();
  2328     for (i = 0; i < count; ++i) {
  2329         mPrototypes[i]->TraceProtos(aTrc, aGCNumber);
  2333 //----------------------------------------------------------------------
  2334 //
  2335 // XULDocument::ContextStack
  2336 //
  2338 XULDocument::ContextStack::ContextStack()
  2339     : mTop(nullptr), mDepth(0)
  2343 XULDocument::ContextStack::~ContextStack()
  2345     while (mTop) {
  2346         Entry* doomed = mTop;
  2347         mTop = mTop->mNext;
  2348         NS_IF_RELEASE(doomed->mElement);
  2349         delete doomed;
  2353 nsresult
  2354 XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
  2355                                 nsIContent* aElement)
  2357     Entry* entry = new Entry;
  2358     if (! entry)
  2359         return NS_ERROR_OUT_OF_MEMORY;
  2361     entry->mPrototype = aPrototype;
  2362     entry->mElement   = aElement;
  2363     NS_IF_ADDREF(entry->mElement);
  2364     entry->mIndex     = 0;
  2366     entry->mNext = mTop;
  2367     mTop = entry;
  2369     ++mDepth;
  2370     return NS_OK;
  2373 nsresult
  2374 XULDocument::ContextStack::Pop()
  2376     if (mDepth == 0)
  2377         return NS_ERROR_UNEXPECTED;
  2379     Entry* doomed = mTop;
  2380     mTop = mTop->mNext;
  2381     --mDepth;
  2383     NS_IF_RELEASE(doomed->mElement);
  2384     delete doomed;
  2385     return NS_OK;
  2388 nsresult
  2389 XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
  2390                                 nsIContent** aElement,
  2391                                 int32_t* aIndex)
  2393     if (mDepth == 0)
  2394         return NS_ERROR_UNEXPECTED;
  2396     *aPrototype = mTop->mPrototype;
  2397     *aElement   = mTop->mElement;
  2398     NS_IF_ADDREF(*aElement);
  2399     *aIndex     = mTop->mIndex;
  2401     return NS_OK;
  2405 nsresult
  2406 XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
  2408     if (mDepth == 0)
  2409         return NS_ERROR_UNEXPECTED;
  2411     mTop->mIndex = aIndex;
  2412     return NS_OK;
  2416 //----------------------------------------------------------------------
  2417 //
  2418 // Content model walking routines
  2419 //
  2421 nsresult
  2422 XULDocument::PrepareToWalk()
  2424     // Prepare to walk the mCurrentPrototype
  2425     nsresult rv;
  2427     // Keep an owning reference to the prototype document so that its
  2428     // elements aren't yanked from beneath us.
  2429     mPrototypes.AppendElement(mCurrentPrototype);
  2431     // Get the prototype's root element and initialize the context
  2432     // stack for the prototype walk.
  2433     nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
  2435     if (! proto) {
  2436 #ifdef PR_LOGGING
  2437         if (PR_LOG_TEST(gXULLog, PR_LOG_ERROR)) {
  2438             nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
  2440             nsAutoCString urlspec;
  2441             rv = url->GetSpec(urlspec);
  2442             if (NS_FAILED(rv)) return rv;
  2444             PR_LOG(gXULLog, PR_LOG_ERROR,
  2445                    ("xul: error parsing '%s'", urlspec.get()));
  2447 #endif
  2449         return NS_OK;
  2452     uint32_t piInsertionPoint = 0;
  2453     if (mState != eState_Master) {
  2454         int32_t indexOfRoot = IndexOf(GetRootElement());
  2455         NS_ASSERTION(indexOfRoot >= 0,
  2456                      "No root content when preparing to walk overlay!");
  2457         piInsertionPoint = indexOfRoot;
  2460     const nsTArray<nsRefPtr<nsXULPrototypePI> >& processingInstructions =
  2461         mCurrentPrototype->GetProcessingInstructions();
  2463     uint32_t total = processingInstructions.Length();
  2464     for (uint32_t i = 0; i < total; ++i) {
  2465         rv = CreateAndInsertPI(processingInstructions[i],
  2466                                this, piInsertionPoint + i);
  2467         if (NS_FAILED(rv)) return rv;
  2470     // Now check the chrome registry for any additional overlays.
  2471     rv = AddChromeOverlays();
  2472     if (NS_FAILED(rv)) return rv;
  2474     // Do one-time initialization if we're preparing to walk the
  2475     // master document's prototype.
  2476     nsRefPtr<Element> root;
  2478     if (mState == eState_Master) {
  2479         // Add the root element
  2480         rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
  2481         if (NS_FAILED(rv)) return rv;
  2483         rv = AppendChildTo(root, false);
  2484         if (NS_FAILED(rv)) return rv;
  2486         rv = AddElementToRefMap(root);
  2487         if (NS_FAILED(rv)) return rv;
  2489         // Block onload until we've finished building the complete
  2490         // document content model.
  2491         BlockOnload();
  2494     // There'd better not be anything on the context stack at this
  2495     // point! This is the basis case for our "induction" in
  2496     // ResumeWalk(), below, which'll assume that there's always a
  2497     // content element on the context stack if either 1) we're in the
  2498     // "master" document, or 2) we're in an overlay, and we've got
  2499     // more than one prototype element (the single, root "overlay"
  2500     // element) on the stack.
  2501     NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
  2502     if (mContextStack.Depth() != 0)
  2503         return NS_ERROR_UNEXPECTED;
  2505     rv = mContextStack.Push(proto, root);
  2506     if (NS_FAILED(rv)) return rv;
  2508     return NS_OK;
  2511 nsresult
  2512 XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
  2513                                nsINode* aParent, uint32_t aIndex)
  2515     NS_PRECONDITION(aProtoPI, "null ptr");
  2516     NS_PRECONDITION(aParent, "null ptr");
  2518     nsRefPtr<ProcessingInstruction> node =
  2519         NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
  2520                                        aProtoPI->mData);
  2522     nsresult rv;
  2523     if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
  2524         rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node);
  2525     } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) {
  2526         rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node);
  2527     } else {
  2528         // No special processing, just add the PI to the document.
  2529         rv = aParent->InsertChildAt(node, aIndex, false);
  2532     return rv;
  2535 nsresult
  2536 XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
  2537                                    nsINode* aParent,
  2538                                    uint32_t aIndex,
  2539                                    nsIContent* aPINode)
  2541     nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
  2542     NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
  2543                        "implement nsIStyleSheetLinkingElement!");
  2545     nsresult rv;
  2547     ssle->InitStyleLinkElement(false);
  2548     // We want to be notified when the style sheet finishes loading, so
  2549     // disable style sheet loading for now.
  2550     ssle->SetEnableUpdates(false);
  2551     ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
  2553     rv = aParent->InsertChildAt(aPINode, aIndex, false);
  2554     if (NS_FAILED(rv)) return rv;
  2556     ssle->SetEnableUpdates(true);
  2558     // load the stylesheet if necessary, passing ourselves as
  2559     // nsICSSObserver
  2560     bool willNotify;
  2561     bool isAlternate;
  2562     rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
  2563     if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
  2564         ++mPendingSheets;
  2567     // Ignore errors from UpdateStyleSheet; we don't want failure to
  2568     // do that to break the XUL document load.  But do propagate out
  2569     // NS_ERROR_OUT_OF_MEMORY.
  2570     if (rv == NS_ERROR_OUT_OF_MEMORY) {
  2571         return rv;
  2574     return NS_OK;
  2577 nsresult
  2578 XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
  2579                                 nsINode* aParent,
  2580                                 uint32_t aIndex,
  2581                                 nsIContent* aPINode)
  2583     nsresult rv;
  2585     rv = aParent->InsertChildAt(aPINode, aIndex, false);
  2586     if (NS_FAILED(rv)) return rv;
  2588     // xul-overlay PI is special only in prolog
  2589     if (!nsContentUtils::InProlog(aPINode)) {
  2590         return NS_OK;
  2593     nsAutoString href;
  2594     nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData,
  2595                                             nsGkAtoms::href,
  2596                                             href);
  2598     // If there was no href, we can't do anything with this PI
  2599     if (href.IsEmpty()) {
  2600         return NS_OK;
  2603     // Add the overlay to our list of overlays that need to be processed.
  2604     nsCOMPtr<nsIURI> uri;
  2606     rv = NS_NewURI(getter_AddRefs(uri), href, nullptr,
  2607                    mCurrentPrototype->GetURI());
  2608     if (NS_SUCCEEDED(rv)) {
  2609         // We insert overlays into mUnloadedOverlays at the same index in
  2610         // document order, so they end up in the reverse of the document
  2611         // order in mUnloadedOverlays.
  2612         // This is needed because the code in ResumeWalk loads the overlays
  2613         // by processing the last item of mUnloadedOverlays and removing it
  2614         // from the array.
  2615         mUnloadedOverlays.InsertElementAt(0, uri);
  2616         rv = NS_OK;
  2617     } else if (rv == NS_ERROR_MALFORMED_URI) {
  2618         // The URL is bad, move along. Don't propagate for now.
  2619         // XXX report this to the Error Console (bug 359846)
  2620         rv = NS_OK;
  2623     return rv;
  2626 nsresult
  2627 XULDocument::AddChromeOverlays()
  2629     nsresult rv;
  2631     nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI();
  2633     /* overlays only apply to chrome or about URIs */
  2634     if (!IsOverlayAllowed(docUri)) return NS_OK;
  2636     nsCOMPtr<nsIXULOverlayProvider> chromeReg =
  2637         mozilla::services::GetXULOverlayProviderService();
  2638     // In embedding situations, the chrome registry may not provide overlays,
  2639     // or even exist at all; that's OK.
  2640     NS_ENSURE_TRUE(chromeReg, NS_OK);
  2642     nsCOMPtr<nsISimpleEnumerator> overlays;
  2643     rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
  2644     NS_ENSURE_SUCCESS(rv, rv);
  2646     bool moreOverlays;
  2647     nsCOMPtr<nsISupports> next;
  2648     nsCOMPtr<nsIURI> uri;
  2650     while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
  2651            moreOverlays) {
  2653         rv = overlays->GetNext(getter_AddRefs(next));
  2654         if (NS_FAILED(rv) || !next) break;
  2656         uri = do_QueryInterface(next);
  2657         if (!uri) {
  2658             NS_ERROR("Chrome registry handed me a non-nsIURI object!");
  2659             continue;
  2662         // Same comment as in XULDocument::InsertXULOverlayPI
  2663         mUnloadedOverlays.InsertElementAt(0, uri);
  2666     return rv;
  2669 NS_IMETHODIMP
  2670 XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver)
  2672     nsresult rv;
  2674     nsCOMPtr<nsIURI> uri;
  2675     rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr);
  2676     if (NS_FAILED(rv)) return rv;
  2678     if (aObserver) {
  2679         nsIObserver* obs = nullptr;
  2680         if (!mOverlayLoadObservers) {
  2681           mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  2683         obs = mOverlayLoadObservers->GetWeak(uri);
  2685         if (obs) {
  2686             // We don't support loading the same overlay twice into the same
  2687             // document - that doesn't make sense anyway.
  2688             return NS_ERROR_FAILURE;
  2690         mOverlayLoadObservers->Put(uri, aObserver);
  2692     bool shouldReturn, failureFromContent;
  2693     rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent);
  2694     if (NS_FAILED(rv) && mOverlayLoadObservers)
  2695         mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error
  2696     return rv;
  2699 nsresult
  2700 XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
  2701                                  bool* aShouldReturn,
  2702                                  bool* aFailureFromContent)
  2704     nsresult rv;
  2706     *aShouldReturn = false;
  2707     *aFailureFromContent = false;
  2709 #ifdef PR_LOGGING
  2710     if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
  2711         nsAutoCString urlspec;
  2712         aURI->GetSpec(urlspec);
  2713         nsAutoCString parentDoc;
  2714         nsCOMPtr<nsIURI> uri;
  2715         nsresult rv = mChannel->GetOriginalURI(getter_AddRefs(uri));
  2716         if (NS_SUCCEEDED(rv))
  2717             rv = uri->GetSpec(parentDoc);
  2718         if (!(parentDoc.get()))
  2719             parentDoc = "";
  2721         PR_LOG(gXULLog, PR_LOG_DEBUG,
  2722                 ("xul: %s loading overlay %s", parentDoc.get(), urlspec.get()));
  2724 #endif
  2726     if (aIsDynamic)
  2727         mResolutionPhase = nsForwardReference::eStart;
  2729     // Chrome documents are allowed to load overlays from anywhere.
  2730     // In all other cases, the overlay is only allowed to load if
  2731     // the master document and prototype document have the same origin.
  2733     bool documentIsChrome = IsChromeURI(mDocumentURI);
  2734     if (!documentIsChrome) {
  2735         // Make sure we're allowed to load this overlay.
  2736         rv = NodePrincipal()->CheckMayLoad(aURI, true, false);
  2737         if (NS_FAILED(rv)) {
  2738             *aFailureFromContent = true;
  2739             return rv;
  2743     // Look in the prototype cache for the prototype document with
  2744     // the specified overlay URI. Only use the cache if the containing
  2745     // document is chrome otherwise it may not have a system principal and
  2746     // the cached document will, see bug 565610.
  2747     bool overlayIsChrome = IsChromeURI(aURI);
  2748     mCurrentPrototype = overlayIsChrome && documentIsChrome ?
  2749         nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr;
  2751     // Same comment as nsChromeProtocolHandler::NewChannel and
  2752     // XULDocument::StartDocumentLoad
  2753     // - Ben Goodger
  2754     //
  2755     // We don't abort on failure here because there are too many valid
  2756     // cases that can return failure, and the null-ness of |proto| is
  2757     // enough to trigger the fail-safe parse-from-disk solution.
  2758     // Example failure cases (for reference) include:
  2759     //
  2760     // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
  2761     //                         parse from disk
  2762     // other: the FastLoad file, XUL.mfl, could not be found, probably
  2763     //        due to being accessed before a profile has been selected
  2764     //        (e.g. loading chrome for the profile manager itself).
  2765     //        The .xul file must be parsed from disk.
  2767     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  2768     if (useXULCache && mCurrentPrototype) {
  2769         bool loaded;
  2770         rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
  2771         if (NS_FAILED(rv)) return rv;
  2773         if (! loaded) {
  2774             // Return to the main event loop and eagerly await the
  2775             // prototype overlay load's completion. When the content
  2776             // sink completes, it will trigger an EndLoad(), which'll
  2777             // wind us back up here, in ResumeWalk().
  2778             *aShouldReturn = true;
  2779             return NS_OK;
  2782         PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
  2784         // Found the overlay's prototype in the cache, fully loaded. If
  2785         // this is a dynamic overlay, this will call ResumeWalk.
  2786         // Otherwise, we'll return to ResumeWalk, which called us.
  2787         return OnPrototypeLoadDone(aIsDynamic);
  2789     else {
  2790         // Not there. Initiate a load.
  2791         PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
  2793         if (mIsGoingAway) {
  2794             PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: ...and document already destroyed"));
  2795             return NS_ERROR_NOT_AVAILABLE;
  2798         // We'll set the right principal on the proto doc when we get
  2799         // OnStartRequest from the parser, so just pass in a null principal for
  2800         // now.
  2801         nsCOMPtr<nsIParser> parser;
  2802         rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser));
  2803         if (NS_FAILED(rv)) return rv;
  2805         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
  2806         // so we don't have to re-check whether the cache is enabled all
  2807         // the time.
  2808         mIsWritingFastLoad = useXULCache;
  2810         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
  2811         if (! listener)
  2812             return NS_ERROR_UNEXPECTED;
  2814         // Add an observer to the parser; this'll get called when
  2815         // Necko fires its On[Start|Stop]Request() notifications,
  2816         // and will let us recover from a missing overlay.
  2817         ParserObserver* parserObserver =
  2818             new ParserObserver(this, mCurrentPrototype);
  2819         if (! parserObserver)
  2820             return NS_ERROR_OUT_OF_MEMORY;
  2822         NS_ADDREF(parserObserver);
  2823         parser->Parse(aURI, parserObserver);
  2824         NS_RELEASE(parserObserver);
  2826         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  2827         nsCOMPtr<nsIChannel> channel;
  2828         rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, group);
  2830         if (NS_SUCCEEDED(rv)) {
  2831             // Set the owner of the channel to be our principal so
  2832             // that the overlay's JSObjects etc end up being created
  2833             // with the right principal and in the correct
  2834             // compartment.
  2835             channel->SetOwner(NodePrincipal());
  2837             rv = channel->AsyncOpen(listener, nullptr);
  2840         if (NS_FAILED(rv)) {
  2841             // Abandon this prototype
  2842             mCurrentPrototype = nullptr;
  2844             // The parser won't get an OnStartRequest and
  2845             // OnStopRequest, so it needs a Terminate.
  2846             parser->Terminate();
  2848             // Just move on to the next overlay.  NS_OpenURI could fail
  2849             // just because a channel could not be opened, which can happen
  2850             // if a file or chrome package does not exist.
  2851             ReportMissingOverlay(aURI);
  2853             // XXX the error could indicate an internal error as well...
  2854             *aFailureFromContent = true;
  2855             return rv;
  2858         // If it's a 'chrome:' prototype document, then put it into
  2859         // the prototype cache; other XUL documents will be reloaded
  2860         // each time.  We must do this after NS_OpenURI and AsyncOpen,
  2861         // or chrome code will wrongly create a cached chrome channel
  2862         // instead of a real one. Prototypes are only cached when the
  2863         // document to be overlayed is chrome to avoid caching overlay
  2864         // scripts with incorrect principals, see bug 565610.
  2865         if (useXULCache && overlayIsChrome && documentIsChrome) {
  2866             nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
  2869         // Return to the main event loop and eagerly await the
  2870         // overlay load's completion. When the content sink
  2871         // completes, it will trigger an EndLoad(), which'll wind
  2872         // us back in ResumeWalk().
  2873         if (!aIsDynamic)
  2874             *aShouldReturn = true;
  2876     return NS_OK;
  2879 static PLDHashOperator
  2880 FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr<nsIObserver>& aObserver, void* aClosure)
  2882     aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get());
  2884     typedef nsInterfaceHashtable<nsURIHashKey,nsIObserver> table;
  2885     table* observers = static_cast<table*>(aClosure);
  2886     if (observers) {
  2887       observers->Remove(aKey);
  2890     return PL_DHASH_REMOVE;
  2893 nsresult
  2894 XULDocument::ResumeWalk()
  2896     // Walk the prototype and build the delegate content model. The
  2897     // walk is performed in a top-down, left-to-right fashion. That
  2898     // is, a parent is built before any of its children; a node is
  2899     // only built after all of its siblings to the left are fully
  2900     // constructed.
  2901     //
  2902     // It is interruptable so that transcluded documents (e.g.,
  2903     // <html:script src="..." />) can be properly re-loaded if the
  2904     // cached copy of the document becomes stale.
  2905     nsresult rv;
  2906     nsCOMPtr<nsIURI> overlayURI =
  2907         mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
  2909     while (1) {
  2910         // Begin (or resume) walking the current prototype.
  2912         while (mContextStack.Depth() > 0) {
  2913             // Look at the top of the stack to determine what we're
  2914             // currently working on.
  2915             // This will always be a node already constructed and
  2916             // inserted to the actual document.
  2917             nsXULPrototypeElement* proto;
  2918             nsCOMPtr<nsIContent> element;
  2919             int32_t indx; // all children of proto before indx (not
  2920                           // inclusive) have already been constructed
  2921             rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
  2922             if (NS_FAILED(rv)) return rv;
  2924             if (indx >= (int32_t)proto->mChildren.Length()) {
  2925                 if (element) {
  2926                     // We've processed all of the prototype's children. If
  2927                     // we're in the master prototype, do post-order
  2928                     // document-level hookup. (An overlay will get its
  2929                     // document hookup done when it's successfully
  2930                     // resolved.)
  2931                     if (mState == eState_Master) {
  2932                         AddElementToDocumentPost(element->AsElement());
  2934                         if (element->NodeInfo()->Equals(nsGkAtoms::style,
  2935                                                         kNameSpaceID_XHTML) ||
  2936                             element->NodeInfo()->Equals(nsGkAtoms::style,
  2937                                                         kNameSpaceID_SVG)) {
  2938                             // XXX sucks that we have to do this -
  2939                             // see bug 370111
  2940                             nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
  2941                                 do_QueryInterface(element);
  2942                             NS_ASSERTION(ssle, "<html:style> doesn't implement "
  2943                                                "nsIStyleSheetLinkingElement?");
  2944                             bool willNotify;
  2945                             bool isAlternate;
  2946                             ssle->UpdateStyleSheet(nullptr, &willNotify,
  2947                                                    &isAlternate);
  2951                 // Now pop the context stack back up to the parent
  2952                 // element and continue the prototype walk.
  2953                 mContextStack.Pop();
  2954                 continue;
  2957             // Grab the next child, and advance the current context stack
  2958             // to the next sibling to our right.
  2959             nsXULPrototypeNode* childproto = proto->mChildren[indx];
  2960             mContextStack.SetTopIndex(++indx);
  2962             // Whether we're in the "first ply" of an overlay:
  2963             // the "hookup" nodes. In the case !processingOverlayHookupNodes,
  2964             // we're in the master document -or- we're in an overlay, and far
  2965             // enough down into the overlay's content that we can simply build
  2966             // the delegates and attach them to the parent node.
  2967             bool processingOverlayHookupNodes = (mState == eState_Overlay) && 
  2968                                                   (mContextStack.Depth() == 1);
  2970             NS_ASSERTION(element || processingOverlayHookupNodes,
  2971                          "no element on context stack");
  2973             switch (childproto->mType) {
  2974             case nsXULPrototypeNode::eType_Element: {
  2975                 // An 'element', which may contain more content.
  2976                 nsXULPrototypeElement* protoele =
  2977                     static_cast<nsXULPrototypeElement*>(childproto);
  2979                 nsRefPtr<Element> child;
  2981                 if (!processingOverlayHookupNodes) {
  2982                     rv = CreateElementFromPrototype(protoele,
  2983                                                     getter_AddRefs(child),
  2984                                                     false);
  2985                     if (NS_FAILED(rv)) return rv;
  2987                     // ...and append it to the content model.
  2988                     rv = element->AppendChildTo(child, false);
  2989                     if (NS_FAILED(rv)) return rv;
  2991                     // If we're only restoring persisted things on
  2992                     // some elements, store the ID here to do that.
  2993                     if (mRestrictPersistence) {
  2994                         nsIAtom* id = child->GetID();
  2995                         if (id) {
  2996                             mPersistenceIds.PutEntry(nsDependentAtomString(id));
  3000                     // do pre-order document-level hookup, but only if
  3001                     // we're in the master document. For an overlay,
  3002                     // this will happen when the overlay is
  3003                     // successfully resolved.
  3004                     if (mState == eState_Master)
  3005                         AddElementToDocumentPre(child);
  3007                 else {
  3008                     // We're in the "first ply" of an overlay: the
  3009                     // "hookup" nodes. Create an 'overlay' element so
  3010                     // that we can continue to build content, and
  3011                     // enter a forward reference so we can hook it up
  3012                     // later.
  3013                     rv = CreateOverlayElement(protoele, getter_AddRefs(child));
  3014                     if (NS_FAILED(rv)) return rv;
  3017                 // If it has children, push the element onto the context
  3018                 // stack and begin to process them.
  3019                 if (protoele->mChildren.Length() > 0) {
  3020                     rv = mContextStack.Push(protoele, child);
  3021                     if (NS_FAILED(rv)) return rv;
  3023                 else {
  3024                     if (mState == eState_Master) {
  3025                         // If there are no children, and we're in the
  3026                         // master document, do post-order document hookup
  3027                         // immediately.
  3028                         AddElementToDocumentPost(child);
  3032             break;
  3034             case nsXULPrototypeNode::eType_Script: {
  3035                 // A script reference. Execute the script immediately;
  3036                 // this may have side effects in the content model.
  3037                 nsXULPrototypeScript* scriptproto =
  3038                     static_cast<nsXULPrototypeScript*>(childproto);
  3040                 if (scriptproto->mSrcURI) {
  3041                     // A transcluded script reference; this may
  3042                     // "block" our prototype walk if the script isn't
  3043                     // cached, or the cached copy of the script is
  3044                     // stale and must be reloaded.
  3045                     bool blocked;
  3046                     rv = LoadScript(scriptproto, &blocked);
  3047                     // If the script cannot be loaded, just keep going!
  3049                     if (NS_SUCCEEDED(rv) && blocked)
  3050                         return NS_OK;
  3052                 else if (scriptproto->GetScriptObject()) {
  3053                     // An inline script
  3054                     rv = ExecuteScript(scriptproto);
  3055                     if (NS_FAILED(rv)) return rv;
  3058             break;
  3060             case nsXULPrototypeNode::eType_Text: {
  3061                 // A simple text node.
  3063                 if (!processingOverlayHookupNodes) {
  3064                     // This does mean that text nodes that are direct children
  3065                     // of <overlay> get ignored.
  3067                     nsRefPtr<nsTextNode> text =
  3068                         new nsTextNode(mNodeInfoManager);
  3070                     nsXULPrototypeText* textproto =
  3071                         static_cast<nsXULPrototypeText*>(childproto);
  3072                     text->SetText(textproto->mValue, false);
  3074                     rv = element->AppendChildTo(text, false);
  3075                     NS_ENSURE_SUCCESS(rv, rv);
  3078             break;
  3080             case nsXULPrototypeNode::eType_PI: {
  3081                 nsXULPrototypePI* piProto =
  3082                     static_cast<nsXULPrototypePI*>(childproto);
  3084                 // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
  3085                 // outside the prolog, like they used to. Issue a warning.
  3087                 if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
  3088                     piProto->mTarget.EqualsLiteral("xul-overlay")) {
  3090                     const char16_t* params[] = { piProto->mTarget.get() };
  3092                     nsContentUtils::ReportToConsole(
  3093                                         nsIScriptError::warningFlag,
  3094                                         NS_LITERAL_CSTRING("XUL Document"), nullptr,
  3095                                         nsContentUtils::eXUL_PROPERTIES,
  3096                                         "PINotInProlog",
  3097                                         params, ArrayLength(params),
  3098                                         overlayURI);
  3101                 nsIContent* parent = processingOverlayHookupNodes ?
  3102                     GetRootElement() : element.get();
  3104                 if (parent) {
  3105                     // an inline script could have removed the root element
  3106                     rv = CreateAndInsertPI(piProto, parent,
  3107                                            parent->GetChildCount());
  3108                     NS_ENSURE_SUCCESS(rv, rv);
  3111             break;
  3113             default:
  3114                 NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
  3118         // Once we get here, the context stack will have been
  3119         // depleted. That means that the entire prototype has been
  3120         // walked and content has been constructed.
  3122         // If we're not already, mark us as now processing overlays.
  3123         mState = eState_Overlay;
  3125         // If there are no overlay URIs, then we're done.
  3126         uint32_t count = mUnloadedOverlays.Length();
  3127         if (! count)
  3128             break;
  3130         nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1];
  3131         mUnloadedOverlays.RemoveElementAt(count - 1);
  3133         bool shouldReturn, failureFromContent;
  3134         rv = LoadOverlayInternal(uri, false, &shouldReturn,
  3135                                  &failureFromContent);
  3136         if (failureFromContent)
  3137             // The failure |rv| was the result of a problem in the content
  3138             // rather than an unexpected problem in our implementation, so
  3139             // just continue with the next overlay.
  3140             continue;
  3141         if (NS_FAILED(rv))
  3142             return rv;
  3143         if (mOverlayLoadObservers) {
  3144             nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI);
  3145             if (obs) {
  3146                 // This overlay has an unloaded overlay, so it will never
  3147                 // notify. The best we can do is to notify for the unloaded
  3148                 // overlay instead, assuming nobody is already notifiable
  3149                 // for it. Note that this will confuse the observer.
  3150                 if (!mOverlayLoadObservers->GetWeak(uri))
  3151                     mOverlayLoadObservers->Put(uri, obs);
  3152                 mOverlayLoadObservers->Remove(overlayURI);
  3155         if (shouldReturn)
  3156             return NS_OK;
  3157         overlayURI.swap(uri);
  3160     // If we get here, there is nothing left for us to walk. The content
  3161     // model is built and ready for layout.
  3162     rv = ResolveForwardReferences();
  3163     if (NS_FAILED(rv)) return rv;
  3165     ApplyPersistentAttributes();
  3167     mStillWalking = false;
  3168     if (mPendingSheets == 0) {
  3169         rv = DoneWalking();
  3171     return rv;
  3174 nsresult
  3175 XULDocument::DoneWalking()
  3177     NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded");
  3178     NS_PRECONDITION(!mStillWalking, "walk not done");
  3180     // XXXldb This is where we should really be setting the chromehidden
  3181     // attribute.
  3183     uint32_t count = mOverlaySheets.Length();
  3184     for (uint32_t i = 0; i < count; ++i) {
  3185         AddStyleSheet(mOverlaySheets[i]);
  3187     mOverlaySheets.Clear();
  3189     if (!mDocumentLoaded) {
  3190         // Make sure we don't reenter here from StartLayout().  Note that
  3191         // setting mDocumentLoaded to true here means that if StartLayout()
  3192         // causes ResumeWalk() to be reentered, we'll take the other branch of
  3193         // the |if (!mDocumentLoaded)| check above and since
  3194         // mInitialLayoutComplete will be false will follow the else branch
  3195         // there too.  See the big comment there for how such reentry can
  3196         // happen.
  3197         mDocumentLoaded = true;
  3199         NotifyPossibleTitleChange(false);
  3201         // Before starting layout, check whether we're a toplevel chrome
  3202         // window.  If we are, set our chrome flags now, so that we don't have
  3203         // to restyle the whole frame tree after StartLayout.
  3204         nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
  3205         if (item) {
  3206             nsCOMPtr<nsIDocShellTreeOwner> owner;
  3207             item->GetTreeOwner(getter_AddRefs(owner));
  3208             nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
  3209             if (xulWin) {
  3210                 nsCOMPtr<nsIDocShell> xulWinShell;
  3211                 xulWin->GetDocShell(getter_AddRefs(xulWinShell));
  3212                 if (SameCOMIdentity(xulWinShell, item)) {
  3213                     // We're the chrome document!  Apply our chrome flags now.
  3214                     xulWin->ApplyChromeFlags();
  3219         StartLayout();
  3221         if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
  3222             nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype);
  3224         NS_ASSERTION(mDelayFrameLoaderInitialization,
  3225                      "mDelayFrameLoaderInitialization should be true!");
  3226         mDelayFrameLoaderInitialization = false;
  3227         NS_WARN_IF_FALSE(mUpdateNestLevel == 0,
  3228                          "Constructing XUL document in middle of an update?");
  3229         if (mUpdateNestLevel == 0) {
  3230             MaybeInitializeFinalizeFrameLoaders();
  3233         NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
  3235         // DispatchContentLoadedEvents undoes the onload-blocking we
  3236         // did in PrepareToWalk().
  3237         DispatchContentLoadedEvents();
  3239         mInitialLayoutComplete = true;
  3241         // Walk the set of pending load notifications and notify any observers.
  3242         // See below for detail.
  3243         if (mPendingOverlayLoadNotifications)
  3244             mPendingOverlayLoadNotifications->Enumerate(
  3245                 FirePendingMergeNotification, mOverlayLoadObservers.get());
  3247     else {
  3248         if (mOverlayLoadObservers) {
  3249             nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI();
  3250             nsCOMPtr<nsIObserver> obs;
  3251             if (mInitialLayoutComplete) {
  3252                 // We have completed initial layout, so just send the notification.
  3253                 mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  3254                 if (obs)
  3255                     obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
  3256                 mOverlayLoadObservers->Remove(overlayURI);
  3258             else {
  3259                 // If we have not yet displayed the document for the first time 
  3260                 // (i.e. we came in here as the result of a dynamic overlay load
  3261                 // which was spawned by a binding-attached event caused by 
  3262                 // StartLayout() on the master prototype - we must remember that
  3263                 // this overlay has been merged and tell the listeners after 
  3264                 // StartLayout() is completely finished rather than doing so 
  3265                 // immediately - otherwise we may be executing code that needs to
  3266                 // access XBL Binding implementations on nodes for which frames 
  3267                 // have not yet been constructed because their bindings have not
  3268                 // yet been attached. This can be a race condition because dynamic
  3269                 // overlay loading can take varying amounts of time depending on
  3270                 // whether or not the overlay prototype is in the XUL cache. The
  3271                 // most likely effect of this bug is odd UI initialization due to
  3272                 // methods and properties that do not work.
  3273                 // XXXbz really, we shouldn't be firing binding constructors
  3274                 // until after StartLayout returns!
  3276                 if (!mPendingOverlayLoadNotifications) {
  3277                     mPendingOverlayLoadNotifications =
  3278                         new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  3281                 mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs));
  3282                 if (!obs) {
  3283                     mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  3284                     NS_ASSERTION(obs, "null overlay load observer?");
  3285                     mPendingOverlayLoadNotifications->Put(overlayURI, obs);
  3291     return NS_OK;
  3294 NS_IMETHODIMP
  3295 XULDocument::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
  3296                               bool aWasAlternate,
  3297                               nsresult aStatus)
  3299     if (!aWasAlternate) {
  3300         // Don't care about when alternate sheets finish loading
  3302         NS_ASSERTION(mPendingSheets > 0,
  3303             "Unexpected StyleSheetLoaded notification");
  3305         --mPendingSheets;
  3307         if (!mStillWalking && mPendingSheets == 0) {
  3308             return DoneWalking();
  3312     return NS_OK;
  3315 void
  3316 XULDocument::MaybeBroadcast()
  3318     // Only broadcast when not in an update and when safe to run scripts.
  3319     if (mUpdateNestLevel == 0 &&
  3320         (mDelayedAttrChangeBroadcasts.Length() ||
  3321          mDelayedBroadcasters.Length())) {
  3322         if (!nsContentUtils::IsSafeToRunScript()) {
  3323             if (!mInDestructor) {
  3324                 nsContentUtils::AddScriptRunner(
  3325                     NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast));
  3327             return;
  3329         if (!mHandlingDelayedAttrChange) {
  3330             mHandlingDelayedAttrChange = true;
  3331             for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
  3332                 nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
  3333                 if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
  3334                     nsCOMPtr<nsIContent> listener =
  3335                         do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
  3336                     nsString value = mDelayedAttrChangeBroadcasts[i].mAttr;
  3337                     if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
  3338                         listener->SetAttr(kNameSpaceID_None, attrName, value,
  3339                                           true);
  3340                     } else {
  3341                         listener->UnsetAttr(kNameSpaceID_None, attrName,
  3342                                             true);
  3345                 ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
  3346                                              mDelayedAttrChangeBroadcasts[i].mListener,
  3347                                              attrName);
  3349             mDelayedAttrChangeBroadcasts.Clear();
  3350             mHandlingDelayedAttrChange = false;
  3353         uint32_t length = mDelayedBroadcasters.Length();
  3354         if (length) {
  3355             bool oldValue = mHandlingDelayedBroadcasters;
  3356             mHandlingDelayedBroadcasters = true;
  3357             nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
  3358             mDelayedBroadcasters.SwapElements(delayedBroadcasters);
  3359             for (uint32_t i = 0; i < length; ++i) {
  3360                 SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
  3361                                              delayedBroadcasters[i].mListener,
  3362                                              delayedBroadcasters[i].mAttr);
  3364             mHandlingDelayedBroadcasters = oldValue;
  3369 void
  3370 XULDocument::EndUpdate(nsUpdateType aUpdateType)
  3372     XMLDocument::EndUpdate(aUpdateType);
  3374     MaybeBroadcast();
  3377 void
  3378 XULDocument::ReportMissingOverlay(nsIURI* aURI)
  3380     NS_PRECONDITION(aURI, "Must have a URI");
  3382     nsAutoCString spec;
  3383     aURI->GetSpec(spec);
  3385     NS_ConvertUTF8toUTF16 utfSpec(spec);
  3386     const char16_t* params[] = { utfSpec.get() };
  3387     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  3388                                     NS_LITERAL_CSTRING("XUL Document"), this,
  3389                                     nsContentUtils::eXUL_PROPERTIES,
  3390                                     "MissingOverlay",
  3391                                     params, ArrayLength(params));
  3394 nsresult
  3395 XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
  3397     // Load a transcluded script
  3398     nsresult rv;
  3400     bool isChromeDoc = IsChromeURI(mDocumentURI);
  3402     if (isChromeDoc && aScriptProto->GetScriptObject()) {
  3403         rv = ExecuteScript(aScriptProto);
  3405         // Ignore return value from execution, and don't block
  3406         *aBlock = false;
  3407         return NS_OK;
  3410     // Try the XUL script cache, in case two XUL documents source the same
  3411     // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
  3412     // XXXbe the cache relies on aScriptProto's GC root!
  3413     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  3415     if (isChromeDoc && useXULCache) {
  3416         JSScript* newScriptObject =
  3417             nsXULPrototypeCache::GetInstance()->GetScript(
  3418                                    aScriptProto->mSrcURI);
  3419         if (newScriptObject) {
  3420             // The script language for a proto must remain constant - we
  3421             // can't just change it for this unexpected language.
  3422             aScriptProto->Set(newScriptObject);
  3425         if (aScriptProto->GetScriptObject()) {
  3426             rv = ExecuteScript(aScriptProto);
  3428             // Ignore return value from execution, and don't block
  3429             *aBlock = false;
  3430             return NS_OK;
  3434     // Allow security manager and content policies to veto the load. Note that
  3435     // at this point we already lost context information of the script.
  3436     rv = nsScriptLoader::ShouldLoadScript(
  3437                             this,
  3438                             static_cast<nsIDocument*>(this),
  3439                             aScriptProto->mSrcURI,
  3440                             NS_LITERAL_STRING("application/x-javascript"));
  3441     if (NS_FAILED(rv)) {
  3442       *aBlock = false;
  3443       return rv;
  3446     // Release script objects from FastLoad since we decided against using them
  3447     aScriptProto->UnlinkJSObjects();
  3449     // Set the current script prototype so that OnStreamComplete can report
  3450     // the right file if there are errors in the script.
  3451     NS_ASSERTION(!mCurrentScriptProto,
  3452                  "still loading a script when starting another load?");
  3453     mCurrentScriptProto = aScriptProto;
  3455     if (aScriptProto->mSrcLoading) {
  3456         // Another XULDocument load has started, which is still in progress.
  3457         // Remember to ResumeWalk this document when the load completes.
  3458         mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
  3459         aScriptProto->mSrcLoadWaiters = this;
  3460         NS_ADDREF_THIS();
  3462     else {
  3463         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  3465         // Note: the loader will keep itself alive while it's loading.
  3466         nsCOMPtr<nsIStreamLoader> loader;
  3467         rv = NS_NewStreamLoader(getter_AddRefs(loader), aScriptProto->mSrcURI,
  3468                                 this, nullptr, group);
  3469         if (NS_FAILED(rv)) {
  3470             mCurrentScriptProto = nullptr;
  3471             return rv;
  3474         aScriptProto->mSrcLoading = true;
  3477     // Block until OnStreamComplete resumes us.
  3478     *aBlock = true;
  3479     return NS_OK;
  3482 NS_IMETHODIMP
  3483 XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
  3484                               nsISupports* context,
  3485                               nsresult aStatus,
  3486                               uint32_t stringLen,
  3487                               const uint8_t* string)
  3489     nsCOMPtr<nsIRequest> request;
  3490     aLoader->GetRequest(getter_AddRefs(request));
  3491     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  3493 #ifdef DEBUG
  3494     // print a load error on bad status
  3495     if (NS_FAILED(aStatus)) {
  3496         if (channel) {
  3497             nsCOMPtr<nsIURI> uri;
  3498             channel->GetURI(getter_AddRefs(uri));
  3499             if (uri) {
  3500                 nsAutoCString uriSpec;
  3501                 uri->GetSpec(uriSpec);
  3502                 printf("Failed to load %s\n", uriSpec.get());
  3506 #endif
  3508     // This is the completion routine that will be called when a
  3509     // transcluded script completes. Compile and execute the script
  3510     // if the load was successful, then continue building content
  3511     // from the prototype.
  3512     nsresult rv = aStatus;
  3514     NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
  3515                  "script source not loading on unichar stream complete?");
  3516     if (!mCurrentScriptProto) {
  3517         // XXX Wallpaper for bug 270042
  3518         return NS_OK;
  3521     if (NS_SUCCEEDED(aStatus)) {
  3522         // If the including XUL document is a FastLoad document, and we're
  3523         // compiling an out-of-line script (one with src=...), then we must
  3524         // be writing a new FastLoad file.  If we were reading this script
  3525         // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
  3526         // nsXULContentSink.cpp) would have already deserialized a non-null
  3527         // script->mScriptObject, causing control flow at the top of LoadScript
  3528         // not to reach here.
  3529         nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
  3531         // XXX should also check nsIHttpChannel::requestSucceeded
  3533         MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
  3534                                             !mOffThreadCompileStringBuf),
  3535                    "XULDocument can't load multiple scripts at once");
  3537         rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen,
  3538                                             EmptyString(), this,
  3539                                             mOffThreadCompileStringBuf,
  3540                                             mOffThreadCompileStringLength);
  3541         if (NS_SUCCEEDED(rv)) {
  3542             // Attempt to give ownership of the buffer to the JS engine.  If
  3543             // we hit offthread compilation, however, we will have to take it
  3544             // back below in order to keep the memory alive until compilation
  3545             // completes.
  3546             JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
  3547                                           mOffThreadCompileStringLength,
  3548                                           JS::SourceBufferHolder::GiveOwnership);
  3549             mOffThreadCompileStringBuf = nullptr;
  3550             mOffThreadCompileStringLength = 0;
  3552             rv = mCurrentScriptProto->Compile(srcBuf,
  3553                                               uri, 1, this,
  3554                                               mMasterPrototype,
  3555                                               this);
  3556             if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) {
  3557                 // We will be notified via OnOffThreadCompileComplete when the
  3558                 // compile finishes. Keep the contents of the compiled script
  3559                 // alive until the compilation finishes.
  3560                 mOffThreadCompiling = true;
  3561                 // If the JS engine did not take the source buffer, then take
  3562                 // it back here to ensure it remains alive.
  3563                 mOffThreadCompileStringBuf = srcBuf.take();
  3564                 if (mOffThreadCompileStringBuf) {
  3565                   mOffThreadCompileStringLength = srcBuf.length();
  3567                 BlockOnload();
  3568                 return NS_OK;
  3573     return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
  3576 NS_IMETHODIMP
  3577 XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
  3579     // When compiling off thread the script will not have been attached to the
  3580     // script proto yet.
  3581     if (aScript && !mCurrentScriptProto->GetScriptObject())
  3582         mCurrentScriptProto->Set(aScript);
  3584     // Allow load events to be fired once off thread compilation finishes.
  3585     if (mOffThreadCompiling) {
  3586         mOffThreadCompiling = false;
  3587         UnblockOnload(false);
  3590     // After compilation finishes the script's characters are no longer needed.
  3591     if (mOffThreadCompileStringBuf) {
  3592       js_free(mOffThreadCompileStringBuf);
  3593       mOffThreadCompileStringBuf = nullptr;
  3594       mOffThreadCompileStringLength = 0;
  3597     // Clear mCurrentScriptProto now, but save it first for use below in
  3598     // the execute code, and in the while loop that resumes walks of other
  3599     // documents that raced to load this script.
  3600     nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
  3601     mCurrentScriptProto = nullptr;
  3603     // Clear the prototype's loading flag before executing the script or
  3604     // resuming document walks, in case any of those control flows starts a
  3605     // new script load.
  3606     scriptProto->mSrcLoading = false;
  3608     nsresult rv = aStatus;
  3609     if (NS_SUCCEEDED(rv)) {
  3610         rv = ExecuteScript(scriptProto);
  3612         // If the XUL cache is enabled, save the script object there in
  3613         // case different XUL documents source the same script.
  3614         //
  3615         // But don't save the script in the cache unless the master XUL
  3616         // document URL is a chrome: URL.  It is valid for a URL such as
  3617         // about:config to translate into a master document URL, whose
  3618         // prototype document nodes -- including prototype scripts that
  3619         // hold GC roots protecting their mJSObject pointers -- are not
  3620         // cached in the XUL prototype cache.  See StartDocumentLoad,
  3621         // the fillXULCache logic.
  3622         //
  3623         // A document such as about:config is free to load a script via
  3624         // a URL such as chrome://global/content/config.js, and we must
  3625         // not cache that script object without a prototype cache entry
  3626         // containing a companion nsXULPrototypeScript node that owns a
  3627         // GC root protecting the script object.  Otherwise, the script
  3628         // cache entry will dangle once the uncached prototype document
  3629         // is released when its owning XULDocument is unloaded.
  3630         //
  3631         // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
  3632         // the true crime story.)
  3633         bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  3635         if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->GetScriptObject()) {
  3636             nsXULPrototypeCache::GetInstance()->PutScript(
  3637                                scriptProto->mSrcURI,
  3638                                scriptProto->GetScriptObject());
  3641         if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
  3642             // If we are loading an overlay script, try to serialize
  3643             // it to the FastLoad file here.  Master scripts will be
  3644             // serialized when the master prototype document gets
  3645             // written, at the bottom of ResumeWalk.  That way, master
  3646             // out-of-line scripts are serialized in the same order that
  3647             // they'll be read, in the FastLoad file, which reduces the
  3648             // number of seeks that dump the underlying stream's buffer.
  3649             //
  3650             // Ignore the return value, as we don't need to propagate
  3651             // a failure to write to the FastLoad file, because this
  3652             // method aborts that whole process on error.
  3653             scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
  3655         // ignore any evaluation errors
  3658     rv = ResumeWalk();
  3660     // Load a pointer to the prototype-script's list of XULDocuments who
  3661     // raced to load the same script
  3662     XULDocument** docp = &scriptProto->mSrcLoadWaiters;
  3664     // Resume walking other documents that waited for this one's load, first
  3665     // executing the script we just compiled, in each doc's script context
  3666     XULDocument* doc;
  3667     while ((doc = *docp) != nullptr) {
  3668         NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
  3669                      "waiting for wrong script to load?");
  3670         doc->mCurrentScriptProto = nullptr;
  3672         // Unlink doc from scriptProto's list before executing and resuming
  3673         *docp = doc->mNextSrcLoadWaiter;
  3674         doc->mNextSrcLoadWaiter = nullptr;
  3676         // Execute only if we loaded and compiled successfully, then resume
  3677         if (NS_SUCCEEDED(aStatus) && scriptProto->GetScriptObject()) {
  3678             doc->ExecuteScript(scriptProto);
  3680         doc->ResumeWalk();
  3681         NS_RELEASE(doc);
  3684     return rv;
  3687 nsresult
  3688 XULDocument::ExecuteScript(nsIScriptContext * aContext,
  3689                            JS::Handle<JSScript*> aScriptObject)
  3691     NS_PRECONDITION(aScriptObject != nullptr && aContext != nullptr, "null ptr");
  3692     if (! aScriptObject || ! aContext)
  3693         return NS_ERROR_NULL_POINTER;
  3695     NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
  3697     // Execute the precompiled script with the given version
  3698     nsAutoMicroTask mt;
  3699     JSContext *cx = aContext->GetNativeContext();
  3700     AutoCxPusher pusher(cx);
  3701     JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
  3702     NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
  3703     NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK);
  3704     JS::ExposeObjectToActiveJS(global);
  3705     xpc_UnmarkGrayScript(aScriptObject);
  3706     JSAutoCompartment ac(cx, global);
  3708     // The script is in the compilation scope. Clone it into the target scope
  3709     // and execute it.
  3710     if (!JS::CloneAndExecuteScript(cx, global, aScriptObject))
  3711         nsJSUtils::ReportPendingException(cx);
  3712     return NS_OK;
  3715 nsresult
  3716 XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
  3718     NS_PRECONDITION(aScript != nullptr, "null ptr");
  3719     NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
  3720     NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
  3722     nsresult rv;
  3723     rv = mScriptGlobalObject->EnsureScriptEnvironment();
  3724     NS_ENSURE_SUCCESS(rv, rv);
  3726     nsCOMPtr<nsIScriptContext> context =
  3727       mScriptGlobalObject->GetScriptContext();
  3728     // failure getting a script context is fatal.
  3729     NS_ENSURE_TRUE(context != nullptr, NS_ERROR_UNEXPECTED);
  3731     if (aScript->GetScriptObject())
  3732         rv = ExecuteScript(context, aScript->GetScriptObject());
  3733     else
  3734         rv = NS_ERROR_UNEXPECTED;
  3735     return rv;
  3739 nsresult
  3740 XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
  3741                                         Element** aResult,
  3742                                         bool aIsRoot)
  3744     // Create a content model element from a prototype element.
  3745     NS_PRECONDITION(aPrototype != nullptr, "null ptr");
  3746     if (! aPrototype)
  3747         return NS_ERROR_NULL_POINTER;
  3749     *aResult = nullptr;
  3750     nsresult rv = NS_OK;
  3752 #ifdef PR_LOGGING
  3753     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  3754         PR_LOG(gXULLog, PR_LOG_NOTICE,
  3755                ("xul: creating <%s> from prototype",
  3756                 NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
  3758 #endif
  3760     nsRefPtr<Element> result;
  3762     if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
  3763         // If it's a XUL element, it'll be lightweight until somebody
  3764         // monkeys with it.
  3765         rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
  3766         if (NS_FAILED(rv)) return rv;
  3768     else {
  3769         // If it's not a XUL element, it's gonna be heavyweight no matter
  3770         // what. So we need to copy everything out of the prototype
  3771         // into the element.  Get a nodeinfo from our nodeinfo manager
  3772         // for this node.
  3773         nsCOMPtr<nsINodeInfo> newNodeInfo;
  3774         newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
  3775                                                     aPrototype->mNodeInfo->GetPrefixAtom(),
  3776                                                     aPrototype->mNodeInfo->NamespaceID(),
  3777                                                     nsIDOMNode::ELEMENT_NODE);
  3778         if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
  3779         nsCOMPtr<nsINodeInfo> xtfNi = newNodeInfo;
  3780         rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
  3781                            NOT_FROM_PARSER);
  3782         if (NS_FAILED(rv))
  3783             return rv;
  3785         rv = AddAttributes(aPrototype, result);
  3786         if (NS_FAILED(rv)) return rv;
  3789     result.swap(*aResult);
  3791     return NS_OK;
  3794 nsresult
  3795 XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
  3796                                   Element** aResult)
  3798     nsresult rv;
  3800     nsRefPtr<Element> element;
  3801     rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false);
  3802     if (NS_FAILED(rv)) return rv;
  3804     OverlayForwardReference* fwdref =
  3805         new OverlayForwardReference(this, element);
  3806     if (! fwdref)
  3807         return NS_ERROR_OUT_OF_MEMORY;
  3809     // transferring ownership to ya...
  3810     rv = AddForwardReference(fwdref);
  3811     if (NS_FAILED(rv)) return rv;
  3813     NS_ADDREF(*aResult = element);
  3814     return NS_OK;
  3817 nsresult
  3818 XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
  3819                            nsIContent* aElement)
  3821     nsresult rv;
  3823     for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
  3824         nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
  3825         nsAutoString  valueStr;
  3826         protoattr->mValue.ToString(valueStr);
  3828         rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
  3829                                protoattr->mName.LocalName(),
  3830                                protoattr->mName.GetPrefix(),
  3831                                valueStr,
  3832                                false);
  3833         if (NS_FAILED(rv)) return rv;
  3836     return NS_OK;
  3840 nsresult
  3841 XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
  3842                                         bool* aNeedsHookup)
  3844     // See if the element already has a `database' attribute. If it
  3845     // does, then the template builder has already been created.
  3846     //
  3847     // XXX This approach will crash and burn (well, maybe not _that_
  3848     // bad) if aElement is not a XUL element.
  3849     //
  3850     // XXXvarga Do we still want to support non XUL content?
  3851     nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
  3852     if (xulElement) {
  3853         nsCOMPtr<nsIRDFCompositeDataSource> ds;
  3854         xulElement->GetDatabase(getter_AddRefs(ds));
  3855         if (ds) {
  3856             *aNeedsHookup = false;
  3857             return NS_OK;
  3861     // Check aElement for a 'datasources' attribute, if it has
  3862     // one a XUL template builder needs to be hooked up.
  3863     *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
  3864                                       nsGkAtoms::datasources);
  3865     return NS_OK;
  3868 /* static */ nsresult
  3869 XULDocument::CreateTemplateBuilder(nsIContent* aElement)
  3871     // Check if need to construct a tree builder or content builder.
  3872     bool isTreeBuilder = false;
  3874     // return successful if the element is not is a document, as an inline
  3875     // script could have removed it
  3876     nsIDocument *document = aElement->GetCurrentDoc();
  3877     NS_ENSURE_TRUE(document, NS_OK);
  3879     int32_t nameSpaceID;
  3880     nsIAtom* baseTag = document->BindingManager()->
  3881       ResolveTag(aElement, &nameSpaceID);
  3883     if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) {
  3884         // By default, we build content for a tree and then we attach
  3885         // the tree content view. However, if the `dont-build-content'
  3886         // flag is set, then we we'll attach a tree builder which
  3887         // directly implements the tree view.
  3889         nsAutoString flags;
  3890         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
  3891         if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
  3892             isTreeBuilder = true;
  3896     if (isTreeBuilder) {
  3897         // Create and initialize a tree builder.
  3898         nsCOMPtr<nsIXULTemplateBuilder> builder =
  3899             do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
  3901         if (! builder)
  3902             return NS_ERROR_FAILURE;
  3904         builder->Init(aElement);
  3906         // Create a <treechildren> if one isn't there already.
  3907         // XXXvarga what about attributes?
  3908         nsCOMPtr<nsIContent> bodyContent;
  3909         nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
  3910                                           nsGkAtoms::treechildren,
  3911                                           getter_AddRefs(bodyContent));
  3913         if (! bodyContent) {
  3914             nsresult rv =
  3915                 document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren),
  3916                                      nullptr, kNameSpaceID_XUL,
  3917                                      getter_AddRefs(bodyContent));
  3918             NS_ENSURE_SUCCESS(rv, rv);
  3920             aElement->AppendChildTo(bodyContent, false);
  3923     else {
  3924         // Create and initialize a content builder.
  3925         nsCOMPtr<nsIXULTemplateBuilder> builder
  3926             = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
  3928         if (! builder)
  3929             return NS_ERROR_FAILURE;
  3931         builder->Init(aElement);
  3932         builder->CreateContents(aElement, false);
  3935     return NS_OK;
  3939 nsresult
  3940 XULDocument::AddPrototypeSheets()
  3942     nsresult rv;
  3944     const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences();
  3946     for (int32_t i = 0; i < sheets.Count(); i++) {
  3947         nsCOMPtr<nsIURI> uri = sheets[i];
  3949         nsRefPtr<nsCSSStyleSheet> incompleteSheet;
  3950         rv = CSSLoader()->LoadSheet(uri,
  3951                                     mCurrentPrototype->DocumentPrincipal(),
  3952                                     EmptyCString(), this,
  3953                                     getter_AddRefs(incompleteSheet));
  3955         // XXXldb We need to prevent bogus sheets from being held in the
  3956         // prototype's list, but until then, don't propagate the failure
  3957         // from LoadSheet (and thus exit the loop).
  3958         if (NS_SUCCEEDED(rv)) {
  3959             ++mPendingSheets;
  3960             if (!mOverlaySheets.AppendElement(incompleteSheet)) {
  3961                 return NS_ERROR_OUT_OF_MEMORY;
  3966     return NS_OK;
  3970 //----------------------------------------------------------------------
  3971 //
  3972 // XULDocument::OverlayForwardReference
  3973 //
  3975 nsForwardReference::Result
  3976 XULDocument::OverlayForwardReference::Resolve()
  3978     // Resolve a forward reference from an overlay element; attempt to
  3979     // hook it up into the main document.
  3980     nsresult rv;
  3981     nsCOMPtr<nsIContent> target;
  3983     nsIPresShell *shell = mDocument->GetShell();
  3984     bool notify = shell && shell->DidInitialize();
  3986     nsAutoString id;
  3987     mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  3988     if (id.IsEmpty()) {
  3989         // mOverlay is a direct child of <overlay> and has no id.
  3990         // Insert it under the root element in the base document.
  3991         Element* root = mDocument->GetRootElement();
  3992         if (!root) {
  3993             return eResolve_Error;
  3996         rv = mDocument->InsertElement(root, mOverlay, notify);
  3997         if (NS_FAILED(rv)) return eResolve_Error;
  3999         target = mOverlay;
  4001     else {
  4002         // The hook-up element has an id, try to match it with an element
  4003         // with the same id in the base document.
  4004         target = mDocument->GetElementById(id);
  4006         // If we can't find the element in the document, defer the hookup
  4007         // until later.
  4008         if (!target)
  4009             return eResolve_Later;
  4011         rv = Merge(target, mOverlay, notify);
  4012         if (NS_FAILED(rv)) return eResolve_Error;
  4015     // Check if 'target' is still in our document --- it might not be!
  4016     if (!notify && target->GetCurrentDoc() == mDocument) {
  4017         // Add child and any descendants to the element map
  4018         // XXX this is bogus, the content in 'target' might already be
  4019         // in the document
  4020         rv = mDocument->AddSubtreeToDocument(target);
  4021         if (NS_FAILED(rv)) return eResolve_Error;
  4024 #ifdef PR_LOGGING
  4025     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  4026         nsAutoCString idC;
  4027         idC.AssignWithConversion(id);
  4028         PR_LOG(gXULLog, PR_LOG_NOTICE,
  4029                ("xul: overlay resolved '%s'",
  4030                 idC.get()));
  4032 #endif
  4034     mResolved = true;
  4035     return eResolve_Succeeded;
  4040 nsresult
  4041 XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
  4042                                             nsIContent* aOverlayNode,
  4043                                             bool aNotify)
  4045     // This function is given:
  4046     // aTargetNode:  the node in the document whose 'id' attribute
  4047     //               matches a toplevel node in our overlay.
  4048     // aOverlayNode: the node in the overlay document that matches
  4049     //               a node in the actual document.
  4050     // aNotify:      whether or not content manipulation methods should
  4051     //               use the aNotify parameter. After the initial 
  4052     //               reflow (i.e. in the dynamic overlay merge case),
  4053     //               we want all the content manipulation methods we
  4054     //               call to notify so that frames are constructed 
  4055     //               etc. Otherwise do not, since that's during initial
  4056     //               document construction before StartLayout has been
  4057     //               called which will do everything for us.
  4058     //
  4059     // This function merges the tree from the overlay into the tree in
  4060     // the document, overwriting attributes and appending child content
  4061     // nodes appropriately. (See XUL overlay reference for details)
  4063     nsresult rv;
  4065     // Merge attributes from the overlay content node to that of the
  4066     // actual document.
  4067     uint32_t i;
  4068     const nsAttrName* name;
  4069     for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) {
  4070         // We don't want to swap IDs, they should be the same.
  4071         if (name->Equals(nsGkAtoms::id))
  4072             continue;
  4074         // In certain cases merging command or observes is unsafe, so don't.
  4075         if (!aNotify) {
  4076             if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes,
  4077                                                 kNameSpaceID_XUL))
  4078                 continue;
  4080             if (name->Equals(nsGkAtoms::observes) &&
  4081                 aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes))
  4082                 continue;
  4084             if (name->Equals(nsGkAtoms::command) &&
  4085                 aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) &&
  4086                 !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key,
  4087                                                  kNameSpaceID_XUL) &&
  4088                 !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem,
  4089                                                  kNameSpaceID_XUL))
  4090                 continue;
  4093         int32_t nameSpaceID = name->NamespaceID();
  4094         nsIAtom* attr = name->LocalName();
  4095         nsIAtom* prefix = name->GetPrefix();
  4097         nsAutoString value;
  4098         aOverlayNode->GetAttr(nameSpaceID, attr, value);
  4100         // Element in the overlay has the 'removeelement' attribute set
  4101         // so remove it from the actual document.
  4102         if (attr == nsGkAtoms::removeelement &&
  4103             value.EqualsLiteral("true")) {
  4105             nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode();
  4106             if (!parent) return NS_ERROR_FAILURE;
  4107             rv = RemoveElement(parent, aTargetNode);
  4108             if (NS_FAILED(rv)) return rv;
  4110             return NS_OK;
  4113         rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
  4114         if (!NS_FAILED(rv) && !aNotify)
  4115             rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode,
  4116                                                                 nameSpaceID,
  4117                                                                 attr, prefix,
  4118                                                                 value);
  4119         if (NS_FAILED(rv)) return rv;
  4123     // Walk our child nodes, looking for elements that have the 'id'
  4124     // attribute set. If we find any, we must do a parent check in the
  4125     // actual document to ensure that the structure matches that of
  4126     // the actual document. If it does, we can call ourselves and attempt
  4127     // to merge inside that subtree. If not, we just append the tree to
  4128     // the parent like any other.
  4130     uint32_t childCount = aOverlayNode->GetChildCount();
  4132     // This must be a strong reference since it will be the only
  4133     // reference to a content object during part of this loop.
  4134     nsCOMPtr<nsIContent> currContent;
  4136     for (i = 0; i < childCount; ++i) {
  4137         currContent = aOverlayNode->GetFirstChild();
  4139         nsIAtom *idAtom = currContent->GetID();
  4141         nsIContent *elementInDocument = nullptr;
  4142         if (idAtom) {
  4143             nsDependentAtomString id(idAtom);
  4145             if (!id.IsEmpty()) {
  4146                 nsIDocument *doc = aTargetNode->GetDocument();
  4147                 if (!doc) return NS_ERROR_FAILURE;
  4149                 elementInDocument = doc->GetElementById(id);
  4153         // The item has an 'id' attribute set, and we need to check with
  4154         // the actual document to see if an item with this id exists at
  4155         // this locale. If so, we want to merge the subtree under that
  4156         // node. Otherwise, we just do an append as if the element had
  4157         // no id attribute.
  4158         if (elementInDocument) {
  4159             // Given two parents, aTargetNode and aOverlayNode, we want
  4160             // to call merge on currContent if we find an associated
  4161             // node in the document with the same id as currContent that
  4162             // also has aTargetNode as its parent.
  4164             nsIContent *elementParent = elementInDocument->GetParent();
  4166             nsIAtom *parentID = elementParent->GetID();
  4167             if (parentID &&
  4168                 aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
  4169                                          nsDependentAtomString(parentID),
  4170                                          eCaseMatters)) {
  4171                 // The element matches. "Go Deep!"
  4172                 rv = Merge(elementInDocument, currContent, aNotify);
  4173                 if (NS_FAILED(rv)) return rv;
  4174                 aOverlayNode->RemoveChildAt(0, false);
  4176                 continue;
  4180         aOverlayNode->RemoveChildAt(0, false);
  4182         rv = InsertElement(aTargetNode, currContent, aNotify);
  4183         if (NS_FAILED(rv)) return rv;
  4186     return NS_OK;
  4191 XULDocument::OverlayForwardReference::~OverlayForwardReference()
  4193 #ifdef PR_LOGGING
  4194     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
  4195         nsAutoString id;
  4196         mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  4198         nsAutoCString idC;
  4199         idC.AssignWithConversion(id);
  4201         nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI();
  4202         nsAutoCString urlspec;
  4203         protoURI->GetSpec(urlspec);
  4205         nsCOMPtr<nsIURI> docURI;
  4206         nsAutoCString parentDoc;
  4207         nsresult rv = mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI));
  4208         if (NS_SUCCEEDED(rv))
  4209             docURI->GetSpec(parentDoc);
  4210         PR_LOG(gXULLog, PR_LOG_WARNING,
  4211                ("xul: %s overlay failed to resolve '%s' in %s",
  4212                 urlspec.get(), idC.get(), parentDoc.get()));
  4214 #endif
  4218 //----------------------------------------------------------------------
  4219 //
  4220 // XULDocument::BroadcasterHookup
  4221 //
  4223 nsForwardReference::Result
  4224 XULDocument::BroadcasterHookup::Resolve()
  4226     nsresult rv;
  4228     bool listener;
  4229     rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
  4230     if (NS_FAILED(rv)) return eResolve_Error;
  4232     return mResolved ? eResolve_Succeeded : eResolve_Later;
  4236 XULDocument::BroadcasterHookup::~BroadcasterHookup()
  4238 #ifdef PR_LOGGING
  4239     if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
  4240         // Tell the world we failed
  4241         nsIAtom *tag = mObservesElement->Tag();
  4243         nsAutoString broadcasterID;
  4244         nsAutoString attribute;
  4246         if (tag == nsGkAtoms::observes) {
  4247             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID);
  4248             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute);
  4250         else {
  4251             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID);
  4252             attribute.AssignLiteral("*");
  4255         nsAutoCString attributeC,broadcasteridC;
  4256         attributeC.AssignWithConversion(attribute);
  4257         broadcasteridC.AssignWithConversion(broadcasterID);
  4258         PR_LOG(gXULLog, PR_LOG_WARNING,
  4259                ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
  4260                 nsAtomCString(tag).get(),
  4261                 attributeC.get(),
  4262                 broadcasteridC.get()));
  4264 #endif
  4268 //----------------------------------------------------------------------
  4269 //
  4270 // XULDocument::TemplateBuilderHookup
  4271 //
  4273 nsForwardReference::Result
  4274 XULDocument::TemplateBuilderHookup::Resolve()
  4276     bool needsHookup;
  4277     nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
  4278     if (NS_FAILED(rv))
  4279         return eResolve_Error;
  4281     if (needsHookup) {
  4282         rv = CreateTemplateBuilder(mElement);
  4283         if (NS_FAILED(rv))
  4284             return eResolve_Error;
  4287     return eResolve_Succeeded;
  4291 //----------------------------------------------------------------------
  4293 nsresult
  4294 XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
  4295                                                  int32_t aNameSpaceID,
  4296                                                  nsIAtom* aAttribute,
  4297                                                  nsIAtom* aPrefix,
  4298                                                  const nsAString& aValue)
  4300     nsresult rv = NS_OK;
  4302     if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
  4303         return rv;
  4305     if (!aNode->IsElement())
  4306         return rv;
  4308     BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*>
  4309         (PL_DHashTableOperate(mBroadcasterMap, aNode->AsElement(), PL_DHASH_LOOKUP));
  4310     if (!PL_DHASH_ENTRY_IS_BUSY(entry))
  4311         return rv;
  4313     // We've got listeners: push the value.
  4314     int32_t i;
  4315     for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
  4316         BroadcastListener* bl = static_cast<BroadcastListener*>
  4317             (entry->mListeners[i]);
  4319         if ((bl->mAttribute != aAttribute) &&
  4320             (bl->mAttribute != nsGkAtoms::_asterix))
  4321             continue;
  4323         nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener);
  4324         if (l) {
  4325             rv = l->SetAttr(aNameSpaceID, aAttribute,
  4326                             aPrefix, aValue, false);
  4327             if (NS_FAILED(rv)) return rv;
  4330     return rv;
  4333 nsresult
  4334 XULDocument::FindBroadcaster(Element* aElement,
  4335                              Element** aListener,
  4336                              nsString& aBroadcasterID,
  4337                              nsString& aAttribute,
  4338                              Element** aBroadcaster)
  4340     nsINodeInfo *ni = aElement->NodeInfo();
  4341     *aListener = nullptr;
  4342     *aBroadcaster = nullptr;
  4344     if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
  4345         // It's an <observes> element, which means that the actual
  4346         // listener is the _parent_ node. This element should have an
  4347         // 'element' attribute that specifies the ID of the
  4348         // broadcaster element, and an 'attribute' element, which
  4349         // specifies the name of the attribute to observe.
  4350         nsIContent* parent = aElement->GetParent();
  4351         if (!parent) {
  4352              // <observes> is the root element
  4353             return NS_FINDBROADCASTER_NOT_FOUND;
  4356         // If we're still parented by an 'overlay' tag, then we haven't
  4357         // made it into the real document yet. Defer hookup.
  4358         if (parent->NodeInfo()->Equals(nsGkAtoms::overlay,
  4359                                        kNameSpaceID_XUL)) {
  4360             return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  4363         *aListener = parent->IsElement() ? parent->AsElement() : nullptr;
  4364         NS_IF_ADDREF(*aListener);
  4366         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
  4367         if (aBroadcasterID.IsEmpty()) {
  4368             return NS_FINDBROADCASTER_NOT_FOUND;
  4370         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
  4372     else {
  4373         // It's a generic element, which means that we'll use the
  4374         // value of the 'observes' attribute to determine the ID of
  4375         // the broadcaster element, and we'll watch _all_ of its
  4376         // values.
  4377         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
  4379         // Bail if there's no aBroadcasterID
  4380         if (aBroadcasterID.IsEmpty()) {
  4381             // Try the command attribute next.
  4382             aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
  4383             if (!aBroadcasterID.IsEmpty()) {
  4384                 // We've got something in the command attribute.  We
  4385                 // only treat this as a normal broadcaster if we are
  4386                 // not a menuitem or a key.
  4388                 if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
  4389                     ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
  4390                 return NS_FINDBROADCASTER_NOT_FOUND;
  4393             else {
  4394               return NS_FINDBROADCASTER_NOT_FOUND;
  4398         *aListener = aElement;
  4399         NS_ADDREF(*aListener);
  4401         aAttribute.AssignLiteral("*");
  4404     // Make sure we got a valid listener.
  4405     NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
  4407     // Try to find the broadcaster element in the document.
  4408     *aBroadcaster = GetElementById(aBroadcasterID);
  4410     // If we can't find the broadcaster, then we'll need to defer the
  4411     // hookup. We may need to resolve some of the other overlays
  4412     // first.
  4413     if (! *aBroadcaster) {
  4414         return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  4417     NS_ADDREF(*aBroadcaster);
  4419     return NS_FINDBROADCASTER_FOUND;
  4422 nsresult
  4423 XULDocument::CheckBroadcasterHookup(Element* aElement,
  4424                                     bool* aNeedsHookup,
  4425                                     bool* aDidResolve)
  4427     // Resolve a broadcaster hookup. Look at the element that we're
  4428     // trying to resolve: it could be an '<observes>' element, or just
  4429     // a vanilla element with an 'observes' attribute on it.
  4430     nsresult rv;
  4432     *aDidResolve = false;
  4434     nsCOMPtr<Element> listener;
  4435     nsAutoString broadcasterID;
  4436     nsAutoString attribute;
  4437     nsCOMPtr<Element> broadcaster;
  4439     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  4440                          broadcasterID, attribute, getter_AddRefs(broadcaster));
  4441     switch (rv) {
  4442         case NS_FINDBROADCASTER_NOT_FOUND:
  4443             *aNeedsHookup = false;
  4444             return NS_OK;
  4445         case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
  4446             *aNeedsHookup = true;
  4447             return NS_OK;
  4448         case NS_FINDBROADCASTER_FOUND:
  4449             break;
  4450         default:
  4451             return rv;
  4454     NS_ENSURE_ARG(broadcaster && listener);
  4455     ErrorResult domRv;
  4456     AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
  4457     if (domRv.Failed()) {
  4458         return domRv.ErrorCode();
  4461 #ifdef PR_LOGGING
  4462     // Tell the world we succeeded
  4463     if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  4464         nsCOMPtr<nsIContent> content =
  4465             do_QueryInterface(listener);
  4467         NS_ASSERTION(content != nullptr, "not an nsIContent");
  4468         if (! content)
  4469             return rv;
  4471         nsAutoCString attributeC,broadcasteridC;
  4472         attributeC.AssignWithConversion(attribute);
  4473         broadcasteridC.AssignWithConversion(broadcasterID);
  4474         PR_LOG(gXULLog, PR_LOG_NOTICE,
  4475                ("xul: broadcaster hookup <%s attribute='%s'> to %s",
  4476                 nsAtomCString(content->Tag()).get(),
  4477                 attributeC.get(),
  4478                 broadcasteridC.get()));
  4480 #endif
  4482     *aNeedsHookup = false;
  4483     *aDidResolve = true;
  4484     return NS_OK;
  4487 nsresult
  4488 XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild,
  4489                            bool aNotify)
  4491     // Insert aChild appropriately into aParent, accounting for a
  4492     // 'pos' attribute set on aChild.
  4494     nsAutoString posStr;
  4495     bool wasInserted = false;
  4497     // insert after an element of a given id
  4498     aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr);
  4499     bool isInsertAfter = true;
  4501     if (posStr.IsEmpty()) {
  4502         aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr);
  4503         isInsertAfter = false;
  4506     if (!posStr.IsEmpty()) {
  4507         nsIDocument *document = aParent->OwnerDoc();
  4509         nsIContent *content = nullptr;
  4511         char* str = ToNewCString(posStr);
  4512         char* rest;
  4513         char* token = nsCRT::strtok(str, ", ", &rest);
  4515         while (token) {
  4516             content = document->GetElementById(NS_ConvertASCIItoUTF16(token));
  4517             if (content)
  4518                 break;
  4520             token = nsCRT::strtok(rest, ", ", &rest);
  4522         nsMemory::Free(str);
  4524         if (content) {
  4525             int32_t pos = aParent->IndexOf(content);
  4527             if (pos != -1) {
  4528                 pos = isInsertAfter ? pos + 1 : pos;
  4529                 nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify);
  4530                 if (NS_FAILED(rv))
  4531                     return rv;
  4533                 wasInserted = true;
  4538     if (!wasInserted) {
  4540         aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr);
  4541         if (!posStr.IsEmpty()) {
  4542             nsresult rv;
  4543             // Positions are one-indexed.
  4544             int32_t pos = posStr.ToInteger(&rv);
  4545             // Note: if the insertion index (which is |pos - 1|) would be less
  4546             // than 0 or greater than the number of children aParent has, then
  4547             // don't insert, since the position is bogus.  Just skip on to
  4548             // appending.
  4549             if (NS_SUCCEEDED(rv) && pos > 0 &&
  4550                 uint32_t(pos - 1) <= aParent->GetChildCount()) {
  4551                 rv = aParent->InsertChildAt(aChild, pos - 1, aNotify);
  4552                 if (NS_SUCCEEDED(rv))
  4553                     wasInserted = true;
  4554                 // If the insertion fails, then we should still
  4555                 // attempt an append.  Thus, rather than returning rv
  4556                 // immediately, we fall through to the final
  4557                 // "catch-all" case that just does an AppendChildTo.
  4562     if (!wasInserted) {
  4563         return aParent->AppendChildTo(aChild, aNotify);
  4565     return NS_OK;
  4568 nsresult
  4569 XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild)
  4571     int32_t nodeOffset = aParent->IndexOf(aChild);
  4573     aParent->RemoveChildAt(nodeOffset, true);
  4574     return NS_OK;
  4577 //----------------------------------------------------------------------
  4578 //
  4579 // CachedChromeStreamListener
  4580 //
  4582 XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
  4583     : mDocument(aDocument),
  4584       mProtoLoaded(aProtoLoaded)
  4586     NS_ADDREF(mDocument);
  4590 XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
  4592     NS_RELEASE(mDocument);
  4596 NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
  4597                   nsIRequestObserver, nsIStreamListener)
  4599 NS_IMETHODIMP
  4600 XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
  4601                                                         nsISupports* acontext)
  4603     return NS_ERROR_PARSED_DATA_CACHED;
  4607 NS_IMETHODIMP
  4608 XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
  4609                                                        nsISupports* aContext,
  4610                                                        nsresult aStatus)
  4612     if (! mProtoLoaded)
  4613         return NS_OK;
  4615     return mDocument->OnPrototypeLoadDone(true);
  4619 NS_IMETHODIMP
  4620 XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
  4621                                                          nsISupports* aContext,
  4622                                                          nsIInputStream* aInStr,
  4623                                                          uint64_t aSourceOffset,
  4624                                                          uint32_t aCount)
  4626     NS_NOTREACHED("CachedChromeStream doesn't receive data");
  4627     return NS_ERROR_UNEXPECTED;
  4630 //----------------------------------------------------------------------
  4631 //
  4632 // ParserObserver
  4633 //
  4635 XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument,
  4636                                             nsXULPrototypeDocument* aPrototype)
  4637     : mDocument(aDocument), mPrototype(aPrototype)
  4641 XULDocument::ParserObserver::~ParserObserver()
  4645 NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver)
  4647 NS_IMETHODIMP
  4648 XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
  4649                                             nsISupports* aContext)
  4651     // Guard against buggy channels calling OnStartRequest multiple times.
  4652     if (mPrototype) {
  4653         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  4654         nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  4655         if (channel && secMan) {
  4656             nsCOMPtr<nsIPrincipal> principal;
  4657             secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
  4659             // Failure there is ok -- it'll just set a (safe) null principal
  4660             mPrototype->SetDocumentPrincipal(principal);
  4663         // Make sure to avoid cycles
  4664         mPrototype = nullptr;
  4667     return NS_OK;
  4670 NS_IMETHODIMP
  4671 XULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
  4672                                            nsISupports* aContext,
  4673                                            nsresult aStatus)
  4675     nsresult rv = NS_OK;
  4677     if (NS_FAILED(aStatus)) {
  4678         // If an overlay load fails, we need to nudge the prototype
  4679         // walk along.
  4680         nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
  4681         if (aChannel) {
  4682             nsCOMPtr<nsIURI> uri;
  4683             aChannel->GetOriginalURI(getter_AddRefs(uri));
  4684             if (uri) {
  4685                 mDocument->ReportMissingOverlay(uri);
  4689         rv = mDocument->ResumeWalk();
  4692     // Drop the reference to the document to break cycle between the
  4693     // document, the parser, the content sink, and the parser
  4694     // observer.
  4695     mDocument = nullptr;
  4697     return rv;
  4700 already_AddRefed<nsPIWindowRoot>
  4701 XULDocument::GetWindowRoot()
  4703     nsCOMPtr<nsIInterfaceRequestor> ir(mDocumentContainer);
  4704     nsCOMPtr<nsIDOMWindow> window(do_GetInterface(ir));
  4705     nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(window));
  4706     return piWin ? piWin->GetTopWindowRoot() : nullptr;
  4709 bool
  4710 XULDocument::IsDocumentRightToLeft()
  4712     // setting the localedir attribute on the root element forces a
  4713     // specific direction for the document.
  4714     Element* element = GetRootElement();
  4715     if (element) {
  4716         static nsIContent::AttrValuesArray strings[] =
  4717             {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
  4718         switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
  4719                                          strings, eCaseMatters)) {
  4720             case 0: return false;
  4721             case 1: return true;
  4722             default: break; // otherwise, not a valid value, so fall through
  4726     // otherwise, get the locale from the chrome registry and
  4727     // look up the intl.uidirection.<locale> preference
  4728     nsCOMPtr<nsIXULChromeRegistry> reg =
  4729         mozilla::services::GetXULChromeRegistryService();
  4730     if (!reg)
  4731         return false;
  4733     nsAutoCString package;
  4734     bool isChrome;
  4735     if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
  4736         isChrome) {
  4737         mDocumentURI->GetHostPort(package);
  4739     else {
  4740         // use the 'global' package for about and resource uris.
  4741         // otherwise, just default to left-to-right.
  4742         bool isAbout, isResource;
  4743         if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
  4744             isAbout) {
  4745             package.AssignLiteral("global");
  4747         else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
  4748             isResource) {
  4749             package.AssignLiteral("global");
  4751         else {
  4752             return false;
  4756     bool isRTL = false;
  4757     reg->IsLocaleRTL(package, &isRTL);
  4758     return isRTL;
  4761 void
  4762 XULDocument::ResetDocumentDirection()
  4764     DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
  4767 void
  4768 XULDocument::DirectionChanged(const char* aPrefName, void* aData)
  4770   // Reset the direction and restyle the document if necessary.
  4771   XULDocument* doc = (XULDocument *)aData;
  4772   if (doc) {
  4773       doc->ResetDocumentDirection();
  4777 int
  4778 XULDocument::GetDocumentLWTheme()
  4780     if (mDocLWTheme == Doc_Theme_Uninitialized) {
  4781         mDocLWTheme = Doc_Theme_None; // No lightweight theme by default
  4783         Element* element = GetRootElement();
  4784         nsAutoString hasLWTheme;
  4785         if (element &&
  4786             element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) &&
  4787             !(hasLWTheme.IsEmpty()) &&
  4788             hasLWTheme.EqualsLiteral("true")) {
  4789             mDocLWTheme = Doc_Theme_Neutral;
  4790             nsAutoString lwTheme;
  4791             element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
  4792             if (!(lwTheme.IsEmpty())) {
  4793                 if (lwTheme.EqualsLiteral("dark"))
  4794                     mDocLWTheme = Doc_Theme_Dark;
  4795                 else if (lwTheme.EqualsLiteral("bright"))
  4796                     mDocLWTheme = Doc_Theme_Bright;
  4800     return mDocLWTheme;
  4803 NS_IMETHODIMP
  4804 XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult)
  4806     ErrorResult rv;
  4807     nsCOMPtr<Element> el = do_QueryInterface(aElement);
  4808     *aResult = GetBoxObjectFor(el, rv).take();
  4809     return rv.ErrorCode();
  4812 JSObject*
  4813 XULDocument::WrapNode(JSContext *aCx)
  4815   return XULDocumentBinding::Wrap(aCx, this);
  4818 } // namespace dom
  4819 } // namespace mozilla

mercurial