LCOV - code coverage report
Current view: top level - dom/xul - XULDocument.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 527 1522 34.6 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /*
       8             : 
       9             :   An implementation for the XUL document. This implementation serves
      10             :   as the basis for generating an NGLayout content model.
      11             : 
      12             :   Notes
      13             :   -----
      14             : 
      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".
      21             : 
      22             : */
      23             : 
      24             : #include "mozilla/ArrayUtils.h"
      25             : 
      26             : #include "XULDocument.h"
      27             : 
      28             : #include "nsError.h"
      29             : #include "nsIBoxObject.h"
      30             : #include "nsIChromeRegistry.h"
      31             : #include "nsView.h"
      32             : #include "nsViewManager.h"
      33             : #include "nsIContentViewer.h"
      34             : #include "nsIStreamListener.h"
      35             : #include "nsITimer.h"
      36             : #include "nsDocShell.h"
      37             : #include "nsGkAtoms.h"
      38             : #include "nsXMLContentSink.h"
      39             : #include "nsXULContentSink.h"
      40             : #include "nsXULContentUtils.h"
      41             : #include "nsIXULOverlayProvider.h"
      42             : #include "nsIStringEnumerator.h"
      43             : #include "nsDocElementCreatedNotificationRunner.h"
      44             : #include "nsNetUtil.h"
      45             : #include "nsParserCIID.h"
      46             : #include "nsPIBoxObject.h"
      47             : #include "mozilla/dom/BoxObject.h"
      48             : #include "nsString.h"
      49             : #include "nsPIDOMWindow.h"
      50             : #include "nsPIWindowRoot.h"
      51             : #include "nsXULCommandDispatcher.h"
      52             : #include "nsXULElement.h"
      53             : #include "nsXULPrototypeCache.h"
      54             : #include "mozilla/Logging.h"
      55             : #include "nsIFrame.h"
      56             : #include "nsXBLService.h"
      57             : #include "nsCExternalHandlerService.h"
      58             : #include "nsMimeTypes.h"
      59             : #include "nsIObjectInputStream.h"
      60             : #include "nsIObjectOutputStream.h"
      61             : #include "nsContentList.h"
      62             : #include "nsISimpleEnumerator.h"
      63             : #include "nsIScriptGlobalObject.h"
      64             : #include "nsIScriptSecurityManager.h"
      65             : #include "nsNodeInfoManager.h"
      66             : #include "nsContentCreatorFunctions.h"
      67             : #include "nsContentUtils.h"
      68             : #include "nsIParser.h"
      69             : #include "nsCharsetSource.h"
      70             : #include "mozilla/StyleSheetInlines.h"
      71             : #include "mozilla/css/Loader.h"
      72             : #include "nsIScriptError.h"
      73             : #include "nsIStyleSheetLinkingElement.h"
      74             : #include "nsIObserverService.h"
      75             : #include "nsNodeUtils.h"
      76             : #include "nsIXULWindow.h"
      77             : #include "nsXULPopupManager.h"
      78             : #include "nsCCUncollectableMarker.h"
      79             : #include "nsURILoader.h"
      80             : #include "mozilla/BasicEvents.h"
      81             : #include "mozilla/dom/Element.h"
      82             : #include "mozilla/dom/NodeInfoInlines.h"
      83             : #include "mozilla/dom/ProcessingInstruction.h"
      84             : #include "mozilla/dom/ScriptSettings.h"
      85             : #include "mozilla/dom/XULDocumentBinding.h"
      86             : #include "mozilla/EventDispatcher.h"
      87             : #include "mozilla/LoadInfo.h"
      88             : #include "mozilla/Preferences.h"
      89             : #include "nsTextNode.h"
      90             : #include "nsJSUtils.h"
      91             : #include "mozilla/dom/URL.h"
      92             : #include "nsIContentPolicy.h"
      93             : #include "mozAutoDocUpdate.h"
      94             : #include "xpcpublic.h"
      95             : #include "mozilla/StyleSheet.h"
      96             : #include "mozilla/StyleSheetInlines.h"
      97             : #include "nsIConsoleService.h"
      98             : 
      99             : using namespace mozilla;
     100             : using namespace mozilla::dom;
     101             : 
     102             : //----------------------------------------------------------------------
     103             : //
     104             : // CIDs
     105             : //
     106             : 
     107             : static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
     108             : 
     109           0 : static bool IsOverlayAllowed(nsIURI* aURI)
     110             : {
     111           0 :     bool canOverlay = false;
     112           0 :     if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay)
     113             :         return true;
     114           0 :     if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay)
     115             :         return true;
     116           0 :     return false;
     117             : }
     118             : 
     119             : //----------------------------------------------------------------------
     120             : //
     121             : // Miscellaneous Constants
     122             : //
     123             : 
     124             : const nsForwardReference::Phase nsForwardReference::kPasses[] = {
     125             :     nsForwardReference::eConstruction,
     126             :     nsForwardReference::eHookup,
     127             :     nsForwardReference::eDone
     128             : };
     129             : 
     130             : //----------------------------------------------------------------------
     131             : //
     132             : // Statics
     133             : //
     134             : 
     135             : int32_t XULDocument::gRefCnt = 0;
     136             : 
     137             : LazyLogModule XULDocument::gXULLog("XULDocument");
     138             : 
     139             : //----------------------------------------------------------------------
     140             : 
     141           0 : struct BroadcastListener {
     142             :     nsWeakPtr mListener;
     143             :     RefPtr<nsAtom> mAttribute;
     144             : };
     145             : 
     146             : struct BroadcasterMapEntry : public PLDHashEntryHdr
     147             : {
     148             :     Element* mBroadcaster;  // [WEAK]
     149             :     nsTArray<BroadcastListener*> mListeners;  // [OWNING] of BroadcastListener objects
     150             : };
     151             : 
     152             : //----------------------------------------------------------------------
     153             : //
     154             : // ctors & dtors
     155             : //
     156             : 
     157             : namespace mozilla {
     158             : namespace dom {
     159             : 
     160           0 : XULDocument::XULDocument(void)
     161             :     : XMLDocument("application/vnd.mozilla.xul+xml"),
     162             :       mNextSrcLoadWaiter(nullptr),
     163             :       mApplyingPersistedAttrs(false),
     164             :       mIsWritingFastLoad(false),
     165             :       mDocumentLoaded(false),
     166             :       mStillWalking(false),
     167             :       mRestrictPersistence(false),
     168             :       mPendingSheets(0),
     169             :       mDocLWTheme(Doc_Theme_Uninitialized),
     170             :       mState(eState_Master),
     171             :       mCurrentScriptProto(nullptr),
     172             :       mOffThreadCompiling(false),
     173             :       mOffThreadCompileStringBuf(nullptr),
     174             :       mOffThreadCompileStringLength(0),
     175             :       mResolutionPhase(nsForwardReference::eStart),
     176             :       mBroadcasterMap(nullptr),
     177             :       mInitialLayoutComplete(false),
     178             :       mHandlingDelayedAttrChange(false),
     179           0 :       mHandlingDelayedBroadcasters(false)
     180             : {
     181             :     // Override the default in nsDocument
     182           0 :     mCharacterSet = UTF_8_ENCODING;
     183             : 
     184           0 :     mDefaultElementType = kNameSpaceID_XUL;
     185           0 :     mType = eXUL;
     186             : 
     187           0 :     mDelayFrameLoaderInitialization = true;
     188             : 
     189           0 :     mAllowXULXBL = eTriTrue;
     190           0 : }
     191             : 
     192           0 : XULDocument::~XULDocument()
     193             : {
     194           0 :     NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
     195             :         "unreferenced document still waiting for script source to load?");
     196             : 
     197             :     // In case we failed somewhere early on and the forward observer
     198             :     // decls never got resolved.
     199           0 :     mForwardReferences.Clear();
     200             :     // Likewise for any references we have to IDs where we might
     201             :     // look for persisted data:
     202           0 :     mPersistenceIds.Clear();
     203             : 
     204             :     // Destroy our broadcaster map.
     205           0 :     delete mBroadcasterMap;
     206             : 
     207             :     Preferences::UnregisterCallback(XULDocument::DirectionChanged,
     208           0 :                                     "intl.uidirection", this);
     209             : 
     210           0 :     if (mOffThreadCompileStringBuf) {
     211           0 :       js_free(mOffThreadCompileStringBuf);
     212             :     }
     213           0 : }
     214             : 
     215             : } // namespace dom
     216             : } // namespace mozilla
     217             : 
     218             : nsresult
     219           0 : NS_NewXULDocument(nsIDocument** result)
     220             : {
     221           0 :     MOZ_ASSERT(result != nullptr, "null ptr");
     222           0 :     if (! result)
     223             :         return NS_ERROR_NULL_POINTER;
     224             : 
     225           0 :     RefPtr<XULDocument> doc = new XULDocument();
     226             : 
     227             :     nsresult rv;
     228           0 :     if (NS_FAILED(rv = doc->Init())) {
     229             :         return rv;
     230             :     }
     231             : 
     232           0 :     doc.forget(result);
     233           0 :     return NS_OK;
     234             : }
     235             : 
     236             : 
     237             : namespace mozilla {
     238             : namespace dom {
     239             : 
     240             : //----------------------------------------------------------------------
     241             : //
     242             : // nsISupports interface
     243             : //
     244             : 
     245             : NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
     246             : 
     247           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
     248           0 :     NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
     249             :                  "Shouldn't traverse XULDocument!");
     250             :     // XXX tmp->mForwardReferences?
     251             :     // XXX tmp->mContextStack?
     252             : 
     253           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
     254           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
     255           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
     256           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
     257           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
     258             : 
     259           0 :     if (tmp->mOverlayLoadObservers) {
     260           0 :         for (auto iter = tmp->mOverlayLoadObservers->Iter();
     261           0 :              !iter.Done();
     262           0 :              iter.Next()) {
     263           0 :             NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOverlayLoadObservers value");
     264           0 :             cb.NoteXPCOMChild(iter.Data());
     265             :         }
     266             :     }
     267           0 :     if (tmp->mPendingOverlayLoadNotifications) {
     268           0 :         for (auto iter = tmp->mPendingOverlayLoadNotifications->Iter();
     269           0 :              !iter.Done();
     270           0 :              iter.Next()) {
     271           0 :             NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPendingOverlayLoadNotifications value");
     272           0 :             cb.NoteXPCOMChild(iter.Data());
     273             :         }
     274             :     }
     275             : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     276             : 
     277           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
     278           0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
     279           0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
     280             :     //XXX We should probably unlink all the objects we traverse.
     281           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     282             : 
     283           0 : NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULDocument,
     284             :                                              XMLDocument,
     285             :                                              nsIStreamLoaderObserver,
     286             :                                              nsICSSLoaderObserver,
     287             :                                              nsIOffThreadScriptReceiver)
     288             : 
     289             : 
     290             : //----------------------------------------------------------------------
     291             : //
     292             : // nsIDocument interface
     293             : //
     294             : 
     295             : void
     296           0 : XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
     297             : {
     298           0 :     NS_NOTREACHED("Reset");
     299           0 : }
     300             : 
     301             : void
     302           0 : XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
     303             :                         nsIPrincipal* aPrincipal)
     304             : {
     305           0 :     NS_NOTREACHED("ResetToURI");
     306           0 : }
     307             : 
     308             : void
     309           0 : XULDocument::SetContentType(const nsAString& aContentType)
     310             : {
     311           0 :     NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
     312             :                  "xul-documents always has content-type application/vnd.mozilla.xul+xml");
     313             :     // Don't do anything, xul always has the mimetype
     314             :     // application/vnd.mozilla.xul+xml
     315           0 : }
     316             : 
     317             : // This is called when the master document begins loading, whether it's
     318             : // being cached or not.
     319             : nsresult
     320           0 : XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
     321             :                                nsILoadGroup* aLoadGroup,
     322             :                                nsISupports* aContainer,
     323             :                                nsIStreamListener **aDocListener,
     324             :                                bool aReset, nsIContentSink* aSink)
     325             : {
     326           0 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
     327             : 
     328           0 :         nsCOMPtr<nsIURI> uri;
     329           0 :         nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
     330           0 :         if (NS_SUCCEEDED(rv)) {
     331           0 :             nsAutoCString urlspec;
     332           0 :             rv = uri->GetSpec(urlspec);
     333           0 :             if (NS_SUCCEEDED(rv)) {
     334           0 :                 MOZ_LOG(gXULLog, LogLevel::Warning,
     335             :                        ("xul: load document '%s'", urlspec.get()));
     336             :             }
     337             :         }
     338             :     }
     339             :     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
     340             :     // we'll possibly need to reset our content type afterwards.
     341           0 :     mStillWalking = true;
     342           0 :     mMayStartLayout = false;
     343           0 :     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
     344             : 
     345           0 :     mChannel = aChannel;
     346             : 
     347             :     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
     348             :     nsresult rv =
     349           0 :         NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
     350           0 :     NS_ENSURE_SUCCESS(rv, rv);
     351             : 
     352           0 :     mOriginalURI = mDocumentURI;
     353             : 
     354             :     // Get the document's principal
     355           0 :     nsCOMPtr<nsIPrincipal> principal;
     356             :     nsContentUtils::GetSecurityManager()->
     357           0 :         GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
     358           0 :     principal = MaybeDowngradePrincipal(principal);
     359             : 
     360           0 :     ResetStylesheetsToURI(mDocumentURI);
     361             : 
     362           0 :     RetrieveRelevantHeaders(aChannel);
     363             : 
     364             :     // Look in the chrome cache: we've got this puppy loaded
     365             :     // already.
     366           0 :     nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
     367           0 :             nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
     368           0 :             nullptr;
     369             : 
     370             :     // Same comment as nsChromeProtocolHandler::NewChannel and
     371             :     // XULDocument::ResumeWalk
     372             :     // - Ben Goodger
     373             :     //
     374             :     // We don't abort on failure here because there are too many valid
     375             :     // cases that can return failure, and the null-ness of |proto| is enough
     376             :     // to trigger the fail-safe parse-from-disk solution. Example failure cases
     377             :     // (for reference) include:
     378             :     //
     379             :     // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
     380             :     //                         parse from disk
     381             :     // other: the startup cache file could not be found, probably
     382             :     //        due to being accessed before a profile has been selected (e.g.
     383             :     //        loading chrome for the profile manager itself). This must be
     384             :     //        parsed from disk.
     385             : 
     386           0 :     if (proto) {
     387             :         // If we're racing with another document to load proto, wait till the
     388             :         // load has finished loading before trying to add cloned style sheets.
     389             :         // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
     390             :         // find all racing documents and notify them via OnPrototypeLoadDone,
     391             :         // which will add style sheet clones to each document.
     392             :         bool loaded;
     393           0 :         rv = proto->AwaitLoadDone(this, &loaded);
     394           0 :         if (NS_FAILED(rv)) return rv;
     395             : 
     396           0 :         mMasterPrototype = mCurrentPrototype = proto;
     397             : 
     398             :         // Set up the right principal on ourselves.
     399           0 :         SetPrincipal(proto->DocumentPrincipal());
     400             : 
     401             :         // We need a listener, even if proto is not yet loaded, in which
     402             :         // event the listener's OnStopRequest method does nothing, and all
     403             :         // the interesting work happens below XULDocument::EndLoad, from
     404             :         // the call there to mCurrentPrototype->NotifyLoadDone().
     405           0 :         *aDocListener = new CachedChromeStreamListener(this, loaded);
     406             :     }
     407             :     else {
     408           0 :         bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
     409           0 :         bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
     410             : 
     411             : 
     412             :         // It's just a vanilla document load. Create a parser to deal
     413             :         // with the stream n' stuff.
     414             : 
     415           0 :         nsCOMPtr<nsIParser> parser;
     416           0 :         rv = PrepareToLoadPrototype(mDocumentURI, aCommand, principal,
     417           0 :                                     getter_AddRefs(parser));
     418           0 :         if (NS_FAILED(rv)) return rv;
     419             : 
     420             :         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
     421             :         // so we don't have to re-check whether the cache is enabled all
     422             :         // the time.
     423           0 :         mIsWritingFastLoad = useXULCache;
     424             : 
     425           0 :         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
     426           0 :         NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
     427           0 :         if (NS_FAILED(rv)) return rv;
     428             : 
     429           0 :         *aDocListener = listener;
     430             : 
     431           0 :         parser->Parse(mDocumentURI);
     432             : 
     433             :         // Put the current prototype, created under PrepareToLoad, into the
     434             :         // XUL prototype cache now.  We can't do this under PrepareToLoad or
     435             :         // overlay loading will break; search for PutPrototype in ResumeWalk
     436             :         // and see the comment there.
     437           0 :         if (fillXULCache) {
     438           0 :             nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
     439             :         }
     440             :     }
     441             : 
     442           0 :     NS_IF_ADDREF(*aDocListener);
     443           0 :     return NS_OK;
     444             : }
     445             : 
     446             : // This gets invoked after a prototype for this document or one of
     447             : // its overlays is fully built in the content sink.
     448             : void
     449           0 : XULDocument::EndLoad()
     450             : {
     451             :     // This can happen if an overlay fails to load
     452           0 :     if (!mCurrentPrototype)
     453           0 :         return;
     454             : 
     455             :     nsresult rv;
     456             : 
     457             :     // Whack the prototype document into the cache so that the next
     458             :     // time somebody asks for it, they don't need to load it by hand.
     459             : 
     460           0 :     nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
     461           0 :     bool isChrome = IsChromeURI(uri);
     462             : 
     463             :     // Remember if the XUL cache is on
     464           0 :     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
     465             : 
     466             :     // If the current prototype is an overlay document (non-master prototype)
     467             :     // and we're filling the FastLoad disk cache, tell the cache we're done
     468             :     // loading it, and write the prototype. The master prototype is put into
     469             :     // the cache earlier in XULDocument::StartDocumentLoad.
     470           0 :     if (useXULCache && mIsWritingFastLoad && isChrome &&
     471           0 :         mMasterPrototype != mCurrentPrototype) {
     472           0 :         nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
     473             :     }
     474             : 
     475           0 :     if (IsOverlayAllowed(uri) && isChrome && useXULCache) {
     476             :         // If it's a chrome prototype document, then notify any
     477             :         // documents that raced to load the prototype, and awaited
     478             :         // its load completion via proto->AwaitLoadDone().
     479           0 :         rv = mCurrentPrototype->NotifyLoadDone();
     480           0 :         if (NS_FAILED(rv)) return;
     481             :     }
     482             : 
     483           2 :     OnPrototypeLoadDone(true);
     484           2 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
     485           0 :         nsAutoCString urlspec;
     486           0 :         rv = uri->GetSpec(urlspec);
     487           0 :         if (NS_SUCCEEDED(rv)) {
     488           0 :             MOZ_LOG(gXULLog, LogLevel::Warning,
     489             :                    ("xul: Finished loading document '%s'", urlspec.get()));
     490             :         }
     491             :     }
     492             : }
     493             : 
     494             : nsresult
     495           3 : XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
     496             : {
     497             :     nsresult rv;
     498             : 
     499           1 :     rv = PrepareToWalk();
     500           3 :     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
     501           3 :     if (NS_FAILED(rv)) return rv;
     502             : 
     503           3 :     if (aResumeWalk) {
     504           0 :         rv = ResumeWalk();
     505             :     }
     506             :     return rv;
     507             : }
     508             : 
     509             : // called when an error occurs parsing a document
     510             : bool
     511           0 : XULDocument::OnDocumentParserError()
     512             : {
     513             :   // don't report errors that are from overlays
     514           0 :   if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) {
     515           0 :     nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
     516           0 :     if (IsChromeURI(uri)) {
     517             :       nsCOMPtr<nsIObserverService> os =
     518           0 :         mozilla::services::GetObserverService();
     519           0 :       if (os)
     520           0 :         os->NotifyObservers(uri, "xul-overlay-parsererror",
     521           0 :                             EmptyString().get());
     522             :     }
     523             : 
     524             :     return false;
     525             :   }
     526             : 
     527             :   return true;
     528             : }
     529             : 
     530             : static void
     531           0 : ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
     532             : {
     533             :     BroadcasterMapEntry* entry =
     534           0 :         static_cast<BroadcasterMapEntry*>(aEntry);
     535           0 :     for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
     536           0 :         delete entry->mListeners[i];
     537             :     }
     538           0 :     entry->mListeners.Clear();
     539             : 
     540             :     // N.B. that we need to manually run the dtor because we
     541             :     // constructed the nsTArray object in-place.
     542           0 :     entry->mListeners.~nsTArray<BroadcastListener*>();
     543           0 : }
     544             : 
     545             : static bool
     546         351 : CanBroadcast(int32_t aNameSpaceID, nsAtom* aAttribute)
     547             : {
     548             :     // Don't push changes to the |id|, |persist|, |command| or
     549             :     // |observes| attribute.
     550           1 :     if (aNameSpaceID == kNameSpaceID_None) {
     551         608 :         if ((aAttribute == nsGkAtoms::id) ||
     552           1 :             (aAttribute == nsGkAtoms::persist) ||
     553           1 :             (aAttribute == nsGkAtoms::command) ||
     554           1 :             (aAttribute == nsGkAtoms::observes)) {
     555             :             return false;
     556             :         }
     557             :     }
     558         256 :     return true;
     559             : }
     560             : 
     561         288 : struct nsAttrNameInfo
     562             : {
     563         144 :   nsAttrNameInfo(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix) :
     564         144 :     mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
     565           0 :   nsAttrNameInfo(const nsAttrNameInfo& aOther) :
     566         144 :     mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
     567         144 :     mPrefix(aOther.mPrefix) {}
     568             :   int32_t           mNamespaceID;
     569             :   RefPtr<nsAtom> mName;
     570             :   RefPtr<nsAtom> mPrefix;
     571             : };
     572             : 
     573             : void
     574         112 : XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
     575             :                                           Element *aListener,
     576             :                                           const nsAString &aAttr)
     577             : {
     578         112 :     if (!nsContentUtils::IsSafeToRunScript()) {
     579             :         nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
     580           0 :                                                aAttr);
     581           5 :         mDelayedBroadcasters.AppendElement(delayedUpdate);
     582           5 :         MaybeBroadcast();
     583             :         return;
     584             :     }
     585           0 :     bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
     586             : 
     587           0 :     if (aAttr.EqualsLiteral("*")) {
     588           0 :         uint32_t count = aBroadcaster->GetAttrCount();
     589         267 :         nsTArray<nsAttrNameInfo> attributes(count);
     590         325 :         for (uint32_t i = 0; i < count; ++i) {
     591         236 :             const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
     592           0 :             int32_t nameSpaceID = attrName->NamespaceID();
     593         236 :             nsAtom* name = attrName->LocalName();
     594             : 
     595             :             // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
     596         236 :             if (! CanBroadcast(nameSpaceID, name))
     597             :                 continue;
     598             : 
     599           0 :             attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
     600           0 :                                                     attrName->GetPrefix()));
     601             :         }
     602             : 
     603          89 :         count = attributes.Length();
     604         377 :         while (count-- > 0) {
     605         288 :             int32_t nameSpaceID = attributes[count].mNamespaceID;
     606         288 :             nsAtom* name = attributes[count].mName;
     607         288 :             nsAutoString value;
     608           0 :             if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
     609         432 :               aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
     610         144 :                                  value, notify);
     611             :             }
     612             : 
     613             : #if 0
     614             :             // XXX we don't fire the |onbroadcast| handler during
     615             :             // initial hookup: doing so would potentially run the
     616             :             // |onbroadcast| handler before the |onload| handler,
     617             :             // which could define JS properties that mask XBL
     618             :             // properties, etc.
     619             :             ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
     620             : #endif
     621             :         }
     622             :     }
     623             :     else {
     624             :         // Find out if the attribute is even present at all.
     625           0 :         RefPtr<nsAtom> name = NS_Atomize(aAttr);
     626             : 
     627           0 :         nsAutoString value;
     628          18 :         if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
     629          10 :             aListener->SetAttr(kNameSpaceID_None, name, value, notify);
     630             :         } else {
     631          26 :             aListener->UnsetAttr(kNameSpaceID_None, name, notify);
     632             :         }
     633             : 
     634             : #if 0
     635             :         // XXX we don't fire the |onbroadcast| handler during initial
     636             :         // hookup: doing so would potentially run the |onbroadcast|
     637             :         // handler before the |onload| handler, which could define JS
     638             :         // properties that mask XBL properties, etc.
     639             :         ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
     640             : #endif
     641             :     }
     642             : }
     643             : 
     644             : void
     645         123 : XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
     646             :                                      const nsAString& aAttr, ErrorResult& aRv)
     647             : {
     648             :     nsresult rv =
     649         123 :         nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
     650             : 
     651         123 :     if (NS_FAILED(rv)) {
     652           0 :         aRv.Throw(rv);
     653           0 :         return;
     654             :     }
     655             : 
     656         123 :     rv = nsContentUtils::CheckSameOrigin(this, &aListener);
     657             : 
     658         123 :     if (NS_FAILED(rv)) {
     659           0 :         aRv.Throw(rv);
     660           0 :         return;
     661             :     }
     662             : 
     663             :     static const PLDHashTableOps gOps = {
     664             :         PLDHashTable::HashVoidPtrKeyStub,
     665             :         PLDHashTable::MatchEntryStub,
     666             :         PLDHashTable::MoveEntryStub,
     667             :         ClearBroadcasterMapEntry,
     668             :         nullptr
     669             :     };
     670             : 
     671         123 :     if (! mBroadcasterMap) {
     672           1 :         mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
     673             :     }
     674             : 
     675             :     auto entry = static_cast<BroadcasterMapEntry*>
     676         123 :                             (mBroadcasterMap->Search(&aBroadcaster));
     677         123 :     if (!entry) {
     678             :         entry = static_cast<BroadcasterMapEntry*>
     679           0 :                            (mBroadcasterMap->Add(&aBroadcaster, fallible));
     680             : 
     681          60 :         if (! entry) {
     682           0 :             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     683           0 :             return;
     684             :         }
     685             : 
     686           1 :         entry->mBroadcaster = &aBroadcaster;
     687             : 
     688             :         // N.B. placement new to construct the nsTArray object in-place
     689         120 :         new (&entry->mListeners) nsTArray<BroadcastListener*>();
     690             :     }
     691             : 
     692             :     // Only add the listener if it's not there already!
     693           1 :     RefPtr<nsAtom> attr = NS_Atomize(aAttr);
     694             : 
     695         345 :     for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
     696         230 :         BroadcastListener* bl = entry->mListeners[i];
     697         444 :         nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
     698             : 
     699         139 :         if (blListener == &aListener && bl->mAttribute == attr)
     700          16 :             return;
     701             :     }
     702             : 
     703         214 :     BroadcastListener* bl = new BroadcastListener;
     704         107 :     bl->mListener  = do_GetWeakReference(&aListener);
     705           0 :     bl->mAttribute = attr;
     706             : 
     707         107 :     entry->mListeners.AppendElement(bl);
     708             : 
     709         107 :     SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
     710             : }
     711             : 
     712             : void
     713           0 : XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
     714             :                                         Element& aListener,
     715             :                                         const nsAString& aAttr)
     716             : {
     717             :     // If we haven't added any broadcast listeners, then there sure
     718             :     // aren't any to remove.
     719           7 :     if (! mBroadcasterMap)
     720             :         return;
     721             : 
     722             :     auto entry = static_cast<BroadcasterMapEntry*>
     723           0 :                             (mBroadcasterMap->Search(&aBroadcaster));
     724           7 :     if (entry) {
     725          21 :         RefPtr<nsAtom> attr = NS_Atomize(aAttr);
     726          14 :         for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
     727           0 :             BroadcastListener* bl = entry->mListeners[i];
     728          21 :             nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
     729             : 
     730           0 :             if (blListener == &aListener && bl->mAttribute == attr) {
     731           0 :                 entry->mListeners.RemoveElementAt(i);
     732           7 :                 delete bl;
     733             : 
     734           0 :                 if (entry->mListeners.IsEmpty())
     735           0 :                     mBroadcasterMap->RemoveEntry(entry);
     736             : 
     737           0 :                 break;
     738             :             }
     739             :         }
     740             :     }
     741             : }
     742             : 
     743             : nsresult
     744          11 : XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
     745             :                                           Element* aListener,
     746             :                                           nsAtom* aAttr)
     747             : {
     748             :     // Now we execute the onchange handler in the context of the
     749             :     // observer. We need to find the observer in order to
     750             :     // execute the handler.
     751             : 
     752          11 :     for (nsIContent* child = aListener->GetFirstChild();
     753           0 :          child;
     754           0 :          child = child->GetNextSibling()) {
     755             : 
     756             :         // Look for an <observes> element beneath the listener. This
     757             :         // ought to have an |element| attribute that refers to
     758             :         // aBroadcaster, and an |attribute| element that tells us what
     759             :         // attriubtes we're listening for.
     760           0 :         if (!child->IsXULElement(nsGkAtoms::observes))
     761           0 :             continue;
     762             : 
     763             :         // Is this the element that was listening to us?
     764           0 :         nsAutoString listeningToID;
     765           0 :         child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
     766             : 
     767           0 :         nsAutoString broadcasterID;
     768           0 :         aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
     769             : 
     770           0 :         if (listeningToID != broadcasterID)
     771           0 :             continue;
     772             : 
     773             :         // We are observing the broadcaster, but is this the right
     774             :         // attribute?
     775           0 :         nsAutoString listeningToAttribute;
     776           0 :         child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
     777           0 :                                     listeningToAttribute);
     778             : 
     779           0 :         if (!aAttr->Equals(listeningToAttribute) &&
     780           0 :             !listeningToAttribute.EqualsLiteral("*")) {
     781             :             continue;
     782             :         }
     783             : 
     784             :         // This is the right <observes> element. Execute the
     785             :         // |onbroadcast| event handler
     786           0 :         WidgetEvent event(true, eXULBroadcast);
     787             : 
     788           0 :         RefPtr<nsPresContext> presContext = GetPresContext();
     789           0 :         if (presContext) {
     790             :           // Handle the DOM event
     791           0 :           nsEventStatus status = nsEventStatus_eIgnore;
     792           0 :           EventDispatcher::Dispatch(child, presContext, &event, nullptr,
     793           0 :                                     &status);
     794             :         }
     795             :     }
     796             : 
     797          11 :     return NS_OK;
     798             : }
     799             : 
     800             : static bool
     801           0 : ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute)
     802             : {
     803         207 :     if (aElement->IsXULElement(nsGkAtoms::window)) {
     804             :         // This is not an element of the top document, its owner is
     805             :         // not an nsXULWindow. Persist it.
     806           9 :         if (aElement->OwnerDoc()->GetParentDocument()) {
     807             :             return true;
     808             :         }
     809             :         // The following attributes of xul:window should be handled in
     810             :         // nsXULWindow::SavePersistentAttributes instead of here.
     811           0 :         if (aAttribute == nsGkAtoms::screenX ||
     812          18 :             aAttribute == nsGkAtoms::screenY ||
     813           0 :             aAttribute == nsGkAtoms::width ||
     814           0 :             aAttribute == nsGkAtoms::height ||
     815           7 :             aAttribute == nsGkAtoms::sizemode) {
     816             :             return false;
     817             :         }
     818             :     }
     819             :     return true;
     820             : }
     821             : 
     822             : void
     823           0 : XULDocument::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
     824             :                               nsAtom* aAttribute, int32_t aModType,
     825             :                               const nsAttrValue* aOldValue)
     826             : {
     827           0 :     NS_ASSERTION(aElement->OwnerDoc() == this, "unexpected doc");
     828             : 
     829             :     // Might not need this, but be safe for now.
     830         414 :     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     831             : 
     832             :     // Synchronize broadcast listeners
     833         322 :     if (mBroadcasterMap &&
     834         115 :         CanBroadcast(aNameSpaceID, aAttribute)) {
     835             :         auto entry = static_cast<BroadcasterMapEntry*>
     836         112 :                                 (mBroadcasterMap->Search(aElement));
     837             : 
     838         112 :         if (entry) {
     839             :             // We've got listeners: push the value.
     840           0 :             nsAutoString value;
     841          10 :             bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
     842             : 
     843          31 :             for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
     844          22 :                 BroadcastListener* bl = entry->mListeners[i];
     845           0 :                 if ((bl->mAttribute == aAttribute) ||
     846           0 :                     (bl->mAttribute == nsGkAtoms::_asterisk)) {
     847             :                     nsCOMPtr<Element> listenerEl
     848           0 :                         = do_QueryReferent(bl->mListener);
     849           0 :                     if (listenerEl) {
     850          22 :                         nsAutoString currentValue;
     851          11 :                         bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
     852             :                                                            aAttribute,
     853          11 :                                                            currentValue);
     854             :                         // We need to update listener only if we're
     855             :                         // (1) removing an existing attribute,
     856             :                         // (2) adding a new attribute or
     857             :                         // (3) changing the value of an attribute.
     858             :                         bool needsAttrChange =
     859          11 :                             attrSet != hasAttr || !value.Equals(currentValue);
     860             :                         nsDelayedBroadcastUpdate delayedUpdate(aElement,
     861             :                                                                listenerEl,
     862             :                                                                aAttribute,
     863             :                                                                value,
     864             :                                                                attrSet,
     865          33 :                                                                needsAttrChange);
     866             : 
     867             :                         size_t index =
     868           0 :                             mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
     869          11 :                                 0, nsDelayedBroadcastUpdate::Comparator());
     870           0 :                         if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
     871           0 :                             if (mHandlingDelayedAttrChange) {
     872           0 :                                 NS_WARNING("Broadcasting loop!");
     873           0 :                                 continue;
     874             :                             }
     875           0 :                             mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
     876             :                         }
     877             : 
     878           0 :                         mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
     879             :                     }
     880             :                 }
     881             :             }
     882             :         }
     883             :     }
     884             : 
     885             :     // checks for modifications in broadcasters
     886             :     bool listener, resolved;
     887           0 :     CheckBroadcasterHookup(aElement, &listener, &resolved);
     888             : 
     889             :     // See if there is anything we need to persist in the localstore.
     890             :     //
     891             :     // XXX Namespace handling broken :-(
     892         414 :     nsAutoString persist;
     893           0 :     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
     894             :     // Persistence of attributes of xul:window is handled in nsXULWindow.
     895         621 :     if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
     896             :         // XXXldb This should check that it's a token, not just a substring.
     897         219 :         persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
     898           0 :       nsContentUtils::AddScriptRunner(
     899           0 :         NewRunnableMethod<Element*, int32_t, nsAtom*>(
     900             :           "dom::XULDocument::DoPersist",
     901             :           this,
     902             :           &XULDocument::DoPersist,
     903             :           aElement,
     904             :           kNameSpaceID_None,
     905           0 :           aAttribute));
     906             :     }
     907           1 : }
     908             : 
     909             : void
     910          41 : XULDocument::ContentAppended(nsIContent* aFirstNewContent)
     911             : {
     912           0 :     NS_ASSERTION(aFirstNewContent->OwnerDoc() == this, "unexpected doc");
     913             : 
     914             :     // Might not need this, but be safe for now.
     915          82 :     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     916             : 
     917             :     // Update our element map
     918          41 :     nsresult rv = NS_OK;
     919          82 :     for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
     920          41 :          cur = cur->GetNextSibling()) {
     921           0 :         rv = AddSubtreeToDocument(cur);
     922             :     }
     923          41 : }
     924             : 
     925             : void
     926           0 : XULDocument::ContentInserted(nsIContent* aChild)
     927             : {
     928           5 :     NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
     929             : 
     930             :     // Might not need this, but be safe for now.
     931           0 :     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     932             : 
     933           0 :     AddSubtreeToDocument(aChild);
     934           5 : }
     935             : 
     936             : void
     937           5 : XULDocument::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
     938             : {
     939           0 :     NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
     940             : 
     941             :     // Might not need this, but be safe for now.
     942          10 :     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     943             : 
     944           0 :     RemoveSubtreeFromDocument(aChild);
     945           5 : }
     946             : 
     947             : nsresult
     948           6 : XULDocument::AddForwardReference(nsForwardReference* aRef)
     949             : {
     950           6 :     if (mResolutionPhase < aRef->GetPhase()) {
     951           6 :         if (!mForwardReferences.AppendElement(aRef)) {
     952           0 :             delete aRef;
     953             :             return NS_ERROR_OUT_OF_MEMORY;
     954             :         }
     955             :     }
     956             :     else {
     957           0 :         NS_ERROR("forward references have already been resolved");
     958           0 :         delete aRef;
     959             :     }
     960             : 
     961             :     return NS_OK;
     962             : }
     963             : 
     964             : nsresult
     965           0 : XULDocument::ResolveForwardReferences()
     966             : {
     967           0 :     if (mResolutionPhase == nsForwardReference::eDone)
     968             :         return NS_OK;
     969             : 
     970           3 :     NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart,
     971             :                  "nested ResolveForwardReferences()");
     972             : 
     973             :     // Resolve each outstanding 'forward' reference. We iterate
     974             :     // through the list of forward references until no more forward
     975             :     // references can be resolved. This annealing process is
     976             :     // guaranteed to converge because we've "closed the gate" to new
     977             :     // forward references.
     978             : 
     979             :     const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
     980          15 :     while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
     981             :         uint32_t previous = 0;
     982           0 :         while (mForwardReferences.Length() &&
     983           3 :                mForwardReferences.Length() != previous) {
     984             :             previous = mForwardReferences.Length();
     985             : 
     986           1 :             for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) {
     987          36 :                 nsForwardReference* fwdref = mForwardReferences[i];
     988             : 
     989          12 :                 if (fwdref->GetPhase() == *pass) {
     990           6 :                     nsForwardReference::Result result = fwdref->Resolve();
     991             : 
     992           1 :                     switch (result) {
     993             :                     case nsForwardReference::eResolve_Succeeded:
     994             :                     case nsForwardReference::eResolve_Error:
     995           6 :                         mForwardReferences.RemoveElementAt(i);
     996             : 
     997             :                         // fixup because we removed from list
     998           6 :                         --i;
     999           0 :                         break;
    1000             : 
    1001             :                     case nsForwardReference::eResolve_Later:
    1002             :                         // do nothing. we'll try again later
    1003             :                         ;
    1004             :                     }
    1005             : 
    1006           6 :                     if (mResolutionPhase == nsForwardReference::eStart) {
    1007             :                         // Resolve() loaded a dynamic overlay,
    1008             :                         // (see XULDocument::LoadOverlayInternal()).
    1009             :                         // Return for now, we will be called again.
    1010             :                         return NS_OK;
    1011             :                     }
    1012             :                 }
    1013             :             }
    1014             :         }
    1015             : 
    1016           0 :         ++pass;
    1017             :     }
    1018             : 
    1019           3 :     mForwardReferences.Clear();
    1020           0 :     return NS_OK;
    1021             : }
    1022             : 
    1023             : //----------------------------------------------------------------------
    1024             : //
    1025             : // nsIDocument interface
    1026             : //
    1027             : 
    1028             : already_AddRefed<nsINodeList>
    1029           0 : XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
    1030             :                                     const nsAString& aValue)
    1031             : {
    1032           0 :     RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
    1033           0 :     nsAutoPtr<nsString> attrValue(new nsString(aValue));
    1034             :     RefPtr<nsContentList> list = new nsContentList(this,
    1035             :                                             MatchAttribute,
    1036             :                                             nsContentUtils::DestroyMatchString,
    1037           0 :                                             attrValue.forget(),
    1038             :                                             true,
    1039             :                                             attrAtom,
    1040           0 :                                             kNameSpaceID_Unknown);
    1041             : 
    1042           0 :     return list.forget();
    1043             : }
    1044             : 
    1045             : already_AddRefed<nsINodeList>
    1046           0 : XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
    1047             :                                       const nsAString& aAttribute,
    1048             :                                       const nsAString& aValue,
    1049             :                                       ErrorResult& aRv)
    1050             : {
    1051           0 :     RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
    1052           0 :     nsAutoPtr<nsString> attrValue(new nsString(aValue));
    1053             : 
    1054           0 :     int32_t nameSpaceId = kNameSpaceID_Wildcard;
    1055           0 :     if (!aNamespaceURI.EqualsLiteral("*")) {
    1056             :       nsresult rv =
    1057             :         nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
    1058           0 :                                                               nameSpaceId);
    1059           0 :       if (NS_FAILED(rv)) {
    1060           0 :           aRv.Throw(rv);
    1061             :           return nullptr;
    1062             :       }
    1063             :     }
    1064             : 
    1065             :     RefPtr<nsContentList> list = new nsContentList(this,
    1066             :                                             MatchAttribute,
    1067             :                                             nsContentUtils::DestroyMatchString,
    1068           0 :                                             attrValue.forget(),
    1069             :                                             true,
    1070             :                                             attrAtom,
    1071           0 :                                             nameSpaceId);
    1072           0 :     return list.forget();
    1073             : }
    1074             : 
    1075             : void
    1076           0 : XULDocument::Persist(const nsAString& aID,
    1077             :                      const nsAString& aAttr,
    1078             :                      ErrorResult& aRv)
    1079             : {
    1080             :     // If we're currently reading persisted attributes out of the
    1081             :     // localstore, _don't_ re-enter and try to set them again!
    1082           0 :     if (mApplyingPersistedAttrs) {
    1083           0 :         return;
    1084             :     }
    1085             : 
    1086           0 :     Element* element = nsDocument::GetElementById(aID);
    1087           0 :     if (!element) {
    1088             :         return;
    1089             :     }
    1090             : 
    1091           0 :     RefPtr<nsAtom> tag;
    1092             :     int32_t nameSpaceID;
    1093             : 
    1094           0 :     RefPtr<mozilla::dom::NodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
    1095             :     nsresult rv;
    1096           0 :     if (ni) {
    1097           0 :         tag = ni->NameAtom();
    1098           0 :         nameSpaceID = ni->NamespaceID();
    1099             :     }
    1100             :     else {
    1101             :         // Make sure that this QName is going to be valid.
    1102             :         const char16_t *colon;
    1103           0 :         rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon);
    1104             : 
    1105           0 :         if (NS_FAILED(rv)) {
    1106             :             // There was an invalid character or it was malformed.
    1107           0 :             aRv.Throw(NS_ERROR_INVALID_ARG);
    1108           0 :             return;
    1109             :         }
    1110             : 
    1111           0 :         if (colon) {
    1112             :             // We don't really handle namespace qualifiers in attribute names.
    1113           0 :             aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    1114           0 :             return;
    1115             :         }
    1116             : 
    1117           0 :         tag = NS_Atomize(aAttr);
    1118           0 :         if (NS_WARN_IF(!tag)) {
    1119           0 :             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1120           0 :             return;
    1121             :         }
    1122             : 
    1123           0 :         nameSpaceID = kNameSpaceID_None;
    1124             :     }
    1125             : 
    1126           0 :     aRv = Persist(element, nameSpaceID, tag);
    1127             : }
    1128             : 
    1129             : nsresult
    1130           0 : XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
    1131             :                      nsAtom* aAttribute)
    1132             : {
    1133             :     // For non-chrome documents, persistance is simply broken
    1134           0 :     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
    1135             :         return NS_ERROR_NOT_AVAILABLE;
    1136             : 
    1137           0 :     if (!mLocalStore) {
    1138           0 :         mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
    1139           0 :         if (NS_WARN_IF(!mLocalStore)) {
    1140             :             return NS_ERROR_NOT_INITIALIZED;
    1141             :         }
    1142             :     }
    1143             : 
    1144           0 :     nsAutoString id;
    1145             : 
    1146           0 :     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
    1147           0 :     nsAtomString attrstr(aAttribute);
    1148             : 
    1149           0 :     nsAutoString valuestr;
    1150           0 :     aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
    1151             : 
    1152           0 :     nsAutoCString utf8uri;
    1153           0 :     nsresult rv = mDocumentURI->GetSpec(utf8uri);
    1154           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1155             :         return rv;
    1156             :     }
    1157           0 :     NS_ConvertUTF8toUTF16 uri(utf8uri);
    1158             : 
    1159             :     bool hasAttr;
    1160           0 :     rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
    1161           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1162             :         return rv;
    1163             :     }
    1164             : 
    1165           0 :     if (hasAttr && valuestr.IsEmpty()) {
    1166           0 :         return mLocalStore->RemoveValue(uri, id, attrstr);
    1167             :     }
    1168             : 
    1169             :     // Persisting attributes to windows is handled by nsXULWindow.
    1170           0 :     if (aElement->IsXULElement(nsGkAtoms::window)) {
    1171             :         return NS_OK;
    1172             :     }
    1173             : 
    1174           0 :     return mLocalStore->SetValue(uri, id, attrstr, valuestr);
    1175             : }
    1176             : 
    1177             : 
    1178             : nsresult
    1179           0 : XULDocument::GetViewportSize(int32_t* aWidth,
    1180             :                              int32_t* aHeight)
    1181             : {
    1182           0 :     *aWidth = *aHeight = 0;
    1183             : 
    1184           0 :     FlushPendingNotifications(FlushType::Layout);
    1185             : 
    1186           0 :     nsIPresShell *shell = GetShell();
    1187           0 :     NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
    1188             : 
    1189           0 :     nsIFrame* frame = shell->GetRootFrame();
    1190           0 :     NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1191             : 
    1192           0 :     nsSize size = frame->GetSize();
    1193             : 
    1194           0 :     *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
    1195           0 :     *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
    1196             : 
    1197           0 :     return NS_OK;
    1198             : }
    1199             : 
    1200             : int32_t
    1201           0 : XULDocument::GetWidth(ErrorResult& aRv)
    1202             : {
    1203           0 :     int32_t width = 0;
    1204           0 :     int32_t height = 0;
    1205           0 :     aRv = GetViewportSize(&width, &height);
    1206           0 :     return width;
    1207             : }
    1208             : 
    1209             : int32_t
    1210           0 : XULDocument::GetHeight(ErrorResult& aRv)
    1211             : {
    1212           0 :     int32_t width = 0;
    1213           0 :     int32_t height = 0;
    1214           0 :     aRv = GetViewportSize(&width, &height);
    1215           0 :     return height;
    1216             : }
    1217             : 
    1218             : static JSObject*
    1219           0 : GetScopeObjectOfNode(nsINode* node)
    1220             : {
    1221           0 :     MOZ_ASSERT(node, "Must not be called with null.");
    1222             : 
    1223             :     // Window root occasionally keeps alive a node of a document whose
    1224             :     // window is already dead. If in this brief period someone calls
    1225             :     // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
    1226             :     // because it will not know which scope this node belongs to. Returning
    1227             :     // an orphan node like that to JS would be a bug anyway, so to avoid
    1228             :     // this, let's do the same check as nsNodeSH::PreCreate does to
    1229             :     // determine the scope and if it fails let's just return null in
    1230             :     // XULDocument::GetPopupNode.
    1231           0 :     nsIDocument* doc = node->OwnerDoc();
    1232           0 :     MOZ_ASSERT(doc, "This should never happen.");
    1233             : 
    1234           0 :     nsIGlobalObject* global = doc->GetScopeObject();
    1235           0 :     return global ? global->GetGlobalJSObject() : nullptr;
    1236             : }
    1237             : 
    1238             : already_AddRefed<nsINode>
    1239           0 : XULDocument::GetPopupNode()
    1240             : {
    1241           2 :     nsCOMPtr<nsINode> node;
    1242           2 :     nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
    1243           1 :     if (rootWin) {
    1244           0 :         node = rootWin->GetPopupNode(); // addref happens here
    1245             :     }
    1246             : 
    1247           0 :     if (!node) {
    1248           0 :         nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1249           0 :         if (pm) {
    1250           1 :             node = pm->GetLastTriggerPopupNode(this);
    1251             :         }
    1252             :     }
    1253             : 
    1254           2 :     if (node && nsContentUtils::CanCallerAccess(node)
    1255           0 :         && GetScopeObjectOfNode(node)) {
    1256           0 :         return node.forget();
    1257             :     }
    1258             : 
    1259             :     return nullptr;
    1260             : }
    1261             : 
    1262             : void
    1263           0 : XULDocument::SetPopupNode(nsINode* aNode)
    1264             : {
    1265           0 :     nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
    1266           0 :     if (rootWin) {
    1267           0 :         rootWin->SetPopupNode(aNode);
    1268             :     }
    1269           0 : }
    1270             : 
    1271             : // Returns the rangeOffset element from the XUL Popup Manager. This is for
    1272             : // chrome callers only.
    1273             : nsINode*
    1274           0 : XULDocument::GetPopupRangeParent(ErrorResult& aRv)
    1275             : {
    1276           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1277           0 :     if (!pm) {
    1278           0 :         aRv.Throw(NS_ERROR_FAILURE);
    1279           0 :         return nullptr;
    1280             :     }
    1281             : 
    1282           0 :     nsINode* rangeParent = pm->GetMouseLocationParent();
    1283           0 :     if (rangeParent && !nsContentUtils::CanCallerAccess(rangeParent)) {
    1284           0 :         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1285           0 :         return nullptr;
    1286             :     }
    1287             : 
    1288             :     return rangeParent;
    1289             : }
    1290             : 
    1291             : // Returns the rangeOffset element from the XUL Popup Manager. We check the
    1292             : // rangeParent to determine if the caller has rights to access to the data.
    1293             : int32_t
    1294           0 : XULDocument::GetPopupRangeOffset(ErrorResult& aRv)
    1295             : {
    1296           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1297           0 :     if (!pm) {
    1298           0 :         aRv.Throw(NS_ERROR_FAILURE);
    1299           0 :         return 0;
    1300             :     }
    1301             : 
    1302           0 :     nsINode* rangeParent = pm->GetMouseLocationParent();
    1303           0 :     if (rangeParent && !nsContentUtils::CanCallerAccess(rangeParent)) {
    1304           0 :         aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    1305           0 :         return 0;
    1306             :     }
    1307             : 
    1308           0 :     return pm->MouseLocationOffset();
    1309             : }
    1310             : 
    1311             : already_AddRefed<nsINode>
    1312           0 : XULDocument::GetTooltipNode()
    1313             : {
    1314           0 :     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
    1315           0 :     if (pm) {
    1316           0 :         nsCOMPtr<nsINode> node = pm->GetLastTriggerTooltipNode(this);
    1317           0 :         if (node && nsContentUtils::CanCallerAccess(node)) {
    1318           0 :             return node.forget();
    1319             :         }
    1320             :     }
    1321             : 
    1322             :     return nullptr;
    1323             : }
    1324             : 
    1325             : nsresult
    1326        2000 : XULDocument::AddElementToDocumentPre(Element* aElement)
    1327             : {
    1328             :     // Do a bunch of work that's necessary when an element gets added
    1329             :     // to the XUL Document.
    1330             :     nsresult rv;
    1331             : 
    1332             :     // 1. Add the element to the id map, since it seems this can be
    1333             :     // called when creating elements from prototypes.
    1334        2000 :     nsAtom* id = aElement->GetID();
    1335        2000 :     if (id) {
    1336             :         // FIXME: Shouldn't BindToTree take care of this?
    1337           0 :         nsAutoScriptBlocker scriptBlocker;
    1338           1 :         AddToIdTable(aElement, id);
    1339             :     }
    1340             : 
    1341             :     // 2. If the element is a 'command updater' (i.e., has a
    1342             :     // "commandupdater='true'" attribute), then add the element to the
    1343             :     // document's command dispatcher
    1344        2000 :     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
    1345             :                               nsGkAtoms::_true, eCaseMatters)) {
    1346           0 :         rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
    1347           4 :         if (NS_FAILED(rv)) return rv;
    1348             :     }
    1349             : 
    1350             :     // 3. Check for a broadcaster hookup attribute, in which case
    1351             :     // we'll hook the node up as a listener on a broadcaster.
    1352             :     bool listener, resolved;
    1353        2000 :     rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
    1354        2000 :     if (NS_FAILED(rv)) return rv;
    1355             : 
    1356             :     // If it's not there yet, we may be able to defer hookup until
    1357             :     // later.
    1358        2000 :     if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
    1359           6 :         BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
    1360           0 :         rv = AddForwardReference(hookup);
    1361           6 :         if (NS_FAILED(rv)) return rv;
    1362             :     }
    1363             : 
    1364             :     return NS_OK;
    1365             : }
    1366             : 
    1367             : nsresult
    1368           0 : XULDocument::AddElementToDocumentPost(Element* aElement)
    1369             : {
    1370        2003 :     if (aElement == GetRootElement()) {
    1371           0 :         ResetDocumentDirection();
    1372             :     }
    1373             : 
    1374             :     // We need to pay special attention to the keyset tag to set up a listener
    1375        6009 :     if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
    1376             :         // Create our XUL key listener and hook it up.
    1377           2 :         nsXBLService::AttachGlobalKeyHandler(aElement);
    1378             :     }
    1379             : 
    1380           0 :     return NS_OK;
    1381             : }
    1382             : 
    1383             : nsresult
    1384         726 : XULDocument::AddSubtreeToDocument(nsIContent* aContent)
    1385             : {
    1386         726 :     NS_ASSERTION(aContent->GetUncomposedDoc() == this, "Element not in doc!");
    1387             :     // From here on we only care about elements.
    1388           0 :     Element* aElement = Element::FromNode(aContent);
    1389         726 :     if (!aElement) {
    1390             :         return NS_OK;
    1391             :     }
    1392             : 
    1393             :     // Do pre-order addition magic
    1394           0 :     nsresult rv = AddElementToDocumentPre(aElement);
    1395           0 :     if (NS_FAILED(rv)) return rv;
    1396             : 
    1397             :     // Recurse to children
    1398        1087 :     for (nsIContent* child = aElement->GetLastChild();
    1399        1087 :          child;
    1400         385 :          child = child->GetPreviousSibling()) {
    1401             : 
    1402           0 :         rv = AddSubtreeToDocument(child);
    1403         385 :         if (NS_FAILED(rv))
    1404             :             return rv;
    1405             :     }
    1406             : 
    1407             :     // Do post-order addition magic
    1408         702 :     return AddElementToDocumentPost(aElement);
    1409             : }
    1410             : 
    1411             : nsresult
    1412          72 : XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
    1413             : {
    1414             :     // From here on we only care about elements.
    1415          72 :     Element* aElement = Element::FromNode(aContent);
    1416          72 :     if (!aElement) {
    1417             :         return NS_OK;
    1418             :     }
    1419             : 
    1420             :     // Do a bunch of cleanup to remove an element from the XUL
    1421             :     // document.
    1422             :     nsresult rv;
    1423             : 
    1424         204 :     if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
    1425           0 :         nsXBLService::DetachGlobalKeyHandler(aElement);
    1426             :     }
    1427             : 
    1428             :     // 1. Remove any children from the document.
    1429           0 :     for (nsIContent* child = aElement->GetLastChild();
    1430         103 :          child;
    1431          35 :          child = child->GetPreviousSibling()) {
    1432             : 
    1433           0 :         rv = RemoveSubtreeFromDocument(child);
    1434           0 :         if (NS_FAILED(rv))
    1435             :             return rv;
    1436             :     }
    1437             : 
    1438             :     // Remove the element from the id map, since we added it in
    1439             :     // AddElementToDocumentPre().
    1440          68 :     nsAtom* id = aElement->GetID();
    1441          68 :     if (id) {
    1442             :         // FIXME: Shouldn't UnbindFromTree take care of this?
    1443          81 :         nsAutoScriptBlocker scriptBlocker;
    1444          27 :         RemoveFromIdTable(aElement, id);
    1445             :     }
    1446             : 
    1447             :     // 3. If the element is a 'command updater', then remove the
    1448             :     // element from the document's command dispatcher.
    1449           0 :     if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
    1450             :                               nsGkAtoms::_true, eCaseMatters)) {
    1451           0 :         rv = mCommandDispatcher->RemoveCommandUpdater(aElement);
    1452           0 :         if (NS_FAILED(rv)) return rv;
    1453             :     }
    1454             : 
    1455             :     // 4. Remove the element from our broadcaster map, since it is no longer
    1456             :     // in the document.
    1457         204 :     nsCOMPtr<Element> broadcaster, listener;
    1458           0 :     nsAutoString attribute, broadcasterID;
    1459           0 :     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
    1460         204 :                          broadcasterID, attribute, getter_AddRefs(broadcaster));
    1461          68 :     if (rv == NS_FINDBROADCASTER_FOUND) {
    1462           7 :         RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
    1463             :     }
    1464             : 
    1465           0 :     return NS_OK;
    1466             : }
    1467             : 
    1468             : //----------------------------------------------------------------------
    1469             : //
    1470             : // nsINode interface
    1471             : //
    1472             : 
    1473             : nsresult
    1474           0 : XULDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
    1475             :                    bool aPreallocateChildren) const
    1476             : {
    1477             :     // We don't allow cloning of a XUL document
    1478           0 :     *aResult = nullptr;
    1479           0 :     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
    1480             : }
    1481             : 
    1482             : 
    1483             : //----------------------------------------------------------------------
    1484             : //
    1485             : // Implementation methods
    1486             : //
    1487             : 
    1488             : nsresult
    1489           3 : XULDocument::Init()
    1490             : {
    1491           0 :     nsresult rv = XMLDocument::Init();
    1492           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1493             : 
    1494             :     // Create our command dispatcher and hook it up.
    1495           0 :     mCommandDispatcher = new nsXULCommandDispatcher(this);
    1496             : 
    1497           3 :     if (gRefCnt++ == 0) {
    1498             :         // ensure that the XUL prototype cache is instantiated successfully,
    1499             :         // so that we can use nsXULPrototypeCache::GetInstance() without
    1500             :         // null-checks in the rest of the class.
    1501           1 :         nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
    1502           1 :         if (!cache) {
    1503           0 :           NS_ERROR("Could not instantiate nsXULPrototypeCache");
    1504           0 :           return NS_ERROR_FAILURE;
    1505             :         }
    1506             :     }
    1507             : 
    1508             :     Preferences::RegisterCallback(XULDocument::DirectionChanged,
    1509           3 :                                   "intl.uidirection", this);
    1510             : 
    1511           3 :     return NS_OK;
    1512             : }
    1513             : 
    1514             : 
    1515             : nsresult
    1516           3 : XULDocument::StartLayout(void)
    1517             : {
    1518           3 :     mMayStartLayout = true;
    1519           9 :     nsCOMPtr<nsIPresShell> shell = GetShell();
    1520           3 :     if (shell) {
    1521             :         // Resize-reflow this time
    1522           6 :         nsPresContext *cx = shell->GetPresContext();
    1523           0 :         NS_ASSERTION(cx != nullptr, "no pres context");
    1524           3 :         if (! cx)
    1525           0 :             return NS_ERROR_UNEXPECTED;
    1526             : 
    1527           6 :         nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
    1528           6 :         NS_ASSERTION(docShell != nullptr, "container is not a docshell");
    1529           0 :         if (! docShell)
    1530           0 :             return NS_ERROR_UNEXPECTED;
    1531             : 
    1532           3 :         nsresult rv = shell->Initialize();
    1533           3 :         NS_ENSURE_SUCCESS(rv, rv);
    1534             :     }
    1535             : 
    1536             :     return NS_OK;
    1537             : }
    1538             : 
    1539             : /* static */
    1540             : bool
    1541           0 : XULDocument::MatchAttribute(Element* aElement,
    1542             :                             int32_t aNamespaceID,
    1543             :                             nsAtom* aAttrName,
    1544             :                             void* aData)
    1545             : {
    1546           0 :     MOZ_ASSERT(aElement, "Must have content node to work with!");
    1547           0 :     nsString* attrValue = static_cast<nsString*>(aData);
    1548           0 :     if (aNamespaceID != kNameSpaceID_Unknown &&
    1549           0 :         aNamespaceID != kNameSpaceID_Wildcard) {
    1550           0 :         return attrValue->EqualsLiteral("*") ?
    1551             :             aElement->HasAttr(aNamespaceID, aAttrName) :
    1552           0 :             aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
    1553             :                                   eCaseMatters);
    1554             :     }
    1555             : 
    1556             :     // Qualified name match. This takes more work.
    1557             : 
    1558           0 :     uint32_t count = aElement->GetAttrCount();
    1559           0 :     for (uint32_t i = 0; i < count; ++i) {
    1560           0 :         const nsAttrName* name = aElement->GetAttrNameAt(i);
    1561             :         bool nameMatch;
    1562           0 :         if (name->IsAtom()) {
    1563           0 :             nameMatch = name->Atom() == aAttrName;
    1564           0 :         } else if (aNamespaceID == kNameSpaceID_Wildcard) {
    1565           0 :             nameMatch = name->NodeInfo()->Equals(aAttrName);
    1566             :         } else {
    1567           0 :             nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
    1568             :         }
    1569             : 
    1570           0 :         if (nameMatch) {
    1571           0 :             return attrValue->EqualsLiteral("*") ||
    1572           0 :                 aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
    1573             :                                       *attrValue, eCaseMatters);
    1574             :         }
    1575             :     }
    1576             : 
    1577             :     return false;
    1578             : }
    1579             : 
    1580             : nsresult
    1581           0 : XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
    1582             :                                     nsIPrincipal* aDocumentPrincipal,
    1583             :                                     nsIParser** aResult)
    1584             : {
    1585             :     nsresult rv;
    1586             : 
    1587             :     // Create a new prototype document.
    1588           4 :     rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
    1589           2 :     if (NS_FAILED(rv)) return rv;
    1590             : 
    1591           2 :     rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
    1592           0 :     if (NS_FAILED(rv)) {
    1593           0 :         mCurrentPrototype = nullptr;
    1594           0 :         return rv;
    1595             :     }
    1596             : 
    1597             :     // Bootstrap the master document prototype.
    1598           0 :     if (! mMasterPrototype) {
    1599           0 :         mMasterPrototype = mCurrentPrototype;
    1600             :         // Set our principal based on the master proto.
    1601           0 :         SetPrincipal(aDocumentPrincipal);
    1602             :     }
    1603             : 
    1604             :     // Create a XUL content sink, a parser, and kick off a load for
    1605             :     // the overlay.
    1606           0 :     RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
    1607             : 
    1608           4 :     rv = sink->Init(this, mCurrentPrototype);
    1609           2 :     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
    1610           2 :     if (NS_FAILED(rv)) return rv;
    1611             : 
    1612           4 :     nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
    1613           2 :     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
    1614           2 :     if (NS_FAILED(rv)) return rv;
    1615             : 
    1616           4 :     parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
    1617           4 :                        eViewSource);
    1618             : 
    1619           2 :     parser->SetDocumentCharset(UTF_8_ENCODING,
    1620           2 :                                kCharsetFromDocTypeDefault);
    1621           4 :     parser->SetContentSink(sink); // grabs a reference to the parser
    1622             : 
    1623           0 :     parser.forget(aResult);
    1624           2 :     return NS_OK;
    1625             : }
    1626             : 
    1627             : 
    1628             : nsresult
    1629           3 : XULDocument::ApplyPersistentAttributes()
    1630             : {
    1631             :     // For non-chrome documents, persistance is simply broken
    1632           0 :     if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
    1633             :         return NS_ERROR_NOT_AVAILABLE;
    1634             : 
    1635             :     // Add all of the 'persisted' attributes into the content
    1636             :     // model.
    1637           6 :     if (!mLocalStore) {
    1638           3 :         mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
    1639           6 :         if (NS_WARN_IF(!mLocalStore)) {
    1640             :             return NS_ERROR_NOT_INITIALIZED;
    1641             :         }
    1642             :     }
    1643             : 
    1644           0 :     mApplyingPersistedAttrs = true;
    1645           3 :     ApplyPersistentAttributesInternal();
    1646           0 :     mApplyingPersistedAttrs = false;
    1647             : 
    1648             :     // After we've applied persistence once, we should only reapply
    1649             :     // it to nodes created by overlays
    1650           0 :     mRestrictPersistence = true;
    1651           0 :     mPersistenceIds.Clear();
    1652             : 
    1653           0 :     return NS_OK;
    1654             : }
    1655             : 
    1656             : 
    1657             : nsresult
    1658           0 : XULDocument::ApplyPersistentAttributesInternal()
    1659             : {
    1660           6 :     nsCOMArray<Element> elements;
    1661             : 
    1662           6 :     nsAutoCString utf8uri;
    1663           0 :     nsresult rv = mDocumentURI->GetSpec(utf8uri);
    1664           3 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1665             :         return rv;
    1666             :     }
    1667           3 :     NS_ConvertUTF8toUTF16 uri(utf8uri);
    1668             : 
    1669             :     // Get a list of element IDs for which persisted values are available
    1670           6 :     nsCOMPtr<nsIStringEnumerator> ids;
    1671           0 :     rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
    1672           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1673             :         return rv;
    1674             :     }
    1675             : 
    1676             :     while (1) {
    1677           3 :         bool hasmore = false;
    1678           0 :         ids->HasMore(&hasmore);
    1679           0 :         if (!hasmore) {
    1680             :             break;
    1681             :         }
    1682             : 
    1683           0 :         nsAutoString id;
    1684           0 :         ids->GetNext(id);
    1685             : 
    1686           0 :         if (mRestrictPersistence && !mPersistenceIds.Contains(id)) {
    1687           0 :             continue;
    1688             :         }
    1689             : 
    1690           0 :         nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(id);
    1691           0 :         if (!entry) {
    1692             :             continue;
    1693             :         }
    1694             : 
    1695             :         // We want to hold strong refs to the elements while applying
    1696             :         // persistent attributes, just in case.
    1697           0 :         elements.Clear();
    1698           0 :         elements.SetCapacity(entry->GetIdElements().Length());
    1699           0 :         for (Element* element : entry->GetIdElements()) {
    1700           0 :             elements.AppendObject(element);
    1701             :         }
    1702           0 :         if (elements.IsEmpty()) {
    1703             :             continue;
    1704             :         }
    1705             : 
    1706           0 :         rv = ApplyPersistentAttributesToElements(id, elements);
    1707           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1708           0 :             return rv;
    1709             :         }
    1710             :     }
    1711             : 
    1712           0 :     return NS_OK;
    1713             : }
    1714             : 
    1715             : nsresult
    1716           0 : XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
    1717             :                                                  nsCOMArray<Element>& aElements)
    1718             : {
    1719           0 :     nsAutoCString utf8uri;
    1720           0 :     nsresult rv = mDocumentURI->GetSpec(utf8uri);
    1721           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1722             :         return rv;
    1723             :     }
    1724           0 :     NS_ConvertUTF8toUTF16 uri(utf8uri);
    1725             : 
    1726             :     // Get a list of attributes for which persisted values are available
    1727           0 :     nsCOMPtr<nsIStringEnumerator> attrs;
    1728           0 :     rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
    1729           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1730             :         return rv;
    1731             :     }
    1732             : 
    1733             :     while (1) {
    1734           0 :         bool hasmore = PR_FALSE;
    1735           0 :         attrs->HasMore(&hasmore);
    1736           0 :         if (!hasmore) {
    1737             :             break;
    1738             :         }
    1739             : 
    1740           0 :         nsAutoString attrstr;
    1741           0 :         attrs->GetNext(attrstr);
    1742             : 
    1743           0 :         nsAutoString value;
    1744           0 :         rv = mLocalStore->GetValue(uri, aID, attrstr, value);
    1745           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1746           0 :             return rv;
    1747             :         }
    1748             : 
    1749           0 :         RefPtr<nsAtom> attr = NS_Atomize(attrstr);
    1750           0 :         if (NS_WARN_IF(!attr)) {
    1751           0 :             return NS_ERROR_OUT_OF_MEMORY;
    1752             :         }
    1753             : 
    1754           0 :         uint32_t cnt = aElements.Count();
    1755           0 :         for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
    1756           0 :             RefPtr<Element> element = aElements.SafeObjectAt(i);
    1757           0 :             if (!element) {
    1758           0 :                  continue;
    1759             :             }
    1760             : 
    1761             :             // Applying persistent attributes to windows is handled by nsXULWindow.
    1762           0 :             if (element->IsXULElement(nsGkAtoms::window)) {
    1763             :                 continue;
    1764             :             }
    1765             : 
    1766           0 :             Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);
    1767             :         }
    1768             :     }
    1769             : 
    1770           0 :     return NS_OK;
    1771             : }
    1772             : 
    1773             : void
    1774           0 : XULDocument::TraceProtos(JSTracer* aTrc)
    1775             : {
    1776           0 :     uint32_t i, count = mPrototypes.Length();
    1777           0 :     for (i = 0; i < count; ++i) {
    1778           0 :         mPrototypes[i]->TraceProtos(aTrc);
    1779             :     }
    1780             : 
    1781           0 :     if (mCurrentPrototype) {
    1782           0 :         mCurrentPrototype->TraceProtos(aTrc);
    1783             :     }
    1784           0 : }
    1785             : 
    1786             : //----------------------------------------------------------------------
    1787             : //
    1788             : // XULDocument::ContextStack
    1789             : //
    1790             : 
    1791           0 : XULDocument::ContextStack::ContextStack()
    1792           0 :     : mTop(nullptr), mDepth(0)
    1793             : {
    1794           0 : }
    1795             : 
    1796           0 : XULDocument::ContextStack::~ContextStack()
    1797             : {
    1798           0 :     while (mTop) {
    1799           0 :         Entry* doomed = mTop;
    1800           0 :         mTop = mTop->mNext;
    1801           0 :         NS_IF_RELEASE(doomed->mElement);
    1802             :         delete doomed;
    1803             :     }
    1804           0 : }
    1805             : 
    1806             : nsresult
    1807         392 : XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
    1808             :                                 nsIContent* aElement)
    1809             : {
    1810           0 :     Entry* entry = new Entry;
    1811           0 :     entry->mPrototype = aPrototype;
    1812           0 :     entry->mElement   = aElement;
    1813         392 :     NS_IF_ADDREF(entry->mElement);
    1814         392 :     entry->mIndex     = 0;
    1815             : 
    1816           0 :     entry->mNext = mTop;
    1817         392 :     mTop = entry;
    1818             : 
    1819         392 :     ++mDepth;
    1820         392 :     return NS_OK;
    1821             : }
    1822             : 
    1823             : nsresult
    1824         392 : XULDocument::ContextStack::Pop()
    1825             : {
    1826           0 :     if (mDepth == 0)
    1827             :         return NS_ERROR_UNEXPECTED;
    1828             : 
    1829         392 :     Entry* doomed = mTop;
    1830           0 :     mTop = mTop->mNext;
    1831         392 :     --mDepth;
    1832             : 
    1833           1 :     NS_IF_RELEASE(doomed->mElement);
    1834           1 :     delete doomed;
    1835           1 :     return NS_OK;
    1836             : }
    1837             : 
    1838             : nsresult
    1839        1772 : XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
    1840             :                                 nsIContent** aElement,
    1841             :                                 int32_t* aIndex)
    1842             : {
    1843        1772 :     if (mDepth == 0)
    1844             :         return NS_ERROR_UNEXPECTED;
    1845             : 
    1846           0 :     *aPrototype = mTop->mPrototype;
    1847           0 :     *aElement   = mTop->mElement;
    1848           0 :     NS_IF_ADDREF(*aElement);
    1849        1772 :     *aIndex     = mTop->mIndex;
    1850             : 
    1851           0 :     return NS_OK;
    1852             : }
    1853             : 
    1854             : 
    1855             : nsresult
    1856        1380 : XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
    1857             : {
    1858           0 :     if (mDepth == 0)
    1859             :         return NS_ERROR_UNEXPECTED;
    1860             : 
    1861        1380 :     mTop->mIndex = aIndex;
    1862        1380 :     return NS_OK;
    1863             : }
    1864             : 
    1865             : 
    1866             : //----------------------------------------------------------------------
    1867             : //
    1868             : // Content model walking routines
    1869             : //
    1870             : 
    1871             : nsresult
    1872           3 : XULDocument::PrepareToWalk()
    1873             : {
    1874             :     // Prepare to walk the mCurrentPrototype
    1875             :     nsresult rv;
    1876             : 
    1877             :     // Keep an owning reference to the prototype document so that its
    1878             :     // elements aren't yanked from beneath us.
    1879           3 :     mPrototypes.AppendElement(mCurrentPrototype);
    1880             : 
    1881             :     // Get the prototype's root element and initialize the context
    1882             :     // stack for the prototype walk.
    1883           0 :     nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
    1884             : 
    1885           0 :     if (! proto) {
    1886           0 :         if (MOZ_LOG_TEST(gXULLog, LogLevel::Error)) {
    1887           0 :             nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
    1888             : 
    1889           0 :             nsAutoCString urlspec;
    1890           0 :             rv = url->GetSpec(urlspec);
    1891           0 :             if (NS_FAILED(rv)) return rv;
    1892             : 
    1893           0 :             MOZ_LOG(gXULLog, LogLevel::Error,
    1894             :                    ("xul: error parsing '%s'", urlspec.get()));
    1895             :         }
    1896             : 
    1897             :         return NS_OK;
    1898             :     }
    1899             : 
    1900           3 :     nsINode* nodeToInsertBefore = nsINode::GetFirstChild();
    1901           3 :     if (mState != eState_Master) {
    1902           0 :         nodeToInsertBefore = GetRootElement();
    1903             :     }
    1904             : 
    1905             :     const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
    1906           0 :         mCurrentPrototype->GetProcessingInstructions();
    1907             : 
    1908           6 :     uint32_t total = processingInstructions.Length();
    1909          16 :     for (uint32_t i = 0; i < total; ++i) {
    1910          26 :         rv = CreateAndInsertPI(processingInstructions[i],
    1911          13 :                                this, nodeToInsertBefore);
    1912          13 :         if (NS_FAILED(rv)) return rv;
    1913             :     }
    1914             : 
    1915             :     // Now check the chrome registry for any additional overlays.
    1916           3 :     rv = AddChromeOverlays();
    1917           0 :     if (NS_FAILED(rv)) return rv;
    1918             : 
    1919             :     // Do one-time initialization if we're preparing to walk the
    1920             :     // master document's prototype.
    1921           1 :     RefPtr<Element> root;
    1922             : 
    1923           1 :     if (mState == eState_Master) {
    1924             :         // Add the root element
    1925           1 :         rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
    1926           3 :         if (NS_FAILED(rv)) return rv;
    1927             : 
    1928           3 :         rv = AppendChildTo(root, false);
    1929           3 :         if (NS_FAILED(rv)) return rv;
    1930             : 
    1931             :         // Block onload until we've finished building the complete
    1932             :         // document content model.
    1933           3 :         BlockOnload();
    1934             : 
    1935             :         nsContentUtils::AddScriptRunner(
    1936           1 :             new nsDocElementCreatedNotificationRunner(this));
    1937             :     }
    1938             : 
    1939             :     // There'd better not be anything on the context stack at this
    1940             :     // point! This is the basis case for our "induction" in
    1941             :     // ResumeWalk(), below, which'll assume that there's always a
    1942             :     // content element on the context stack if either 1) we're in the
    1943             :     // "master" document, or 2) we're in an overlay, and we've got
    1944             :     // more than one prototype element (the single, root "overlay"
    1945             :     // element) on the stack.
    1946           0 :     NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
    1947           3 :     if (mContextStack.Depth() != 0)
    1948             :         return NS_ERROR_UNEXPECTED;
    1949             : 
    1950           0 :     rv = mContextStack.Push(proto, root);
    1951           0 :     if (NS_FAILED(rv)) return rv;
    1952             : 
    1953           3 :     return NS_OK;
    1954             : }
    1955             : 
    1956             : nsresult
    1957           0 : XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
    1958             :                                nsINode* aParent, nsINode* aBeforeThis)
    1959             : {
    1960           0 :     MOZ_ASSERT(aProtoPI, "null ptr");
    1961          13 :     MOZ_ASSERT(aParent, "null ptr");
    1962             : 
    1963             :     RefPtr<ProcessingInstruction> node =
    1964          26 :         NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
    1965          26 :                                        aProtoPI->mData);
    1966             : 
    1967             :     nsresult rv;
    1968          13 :     if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
    1969          13 :         rv = InsertXMLStylesheetPI(aProtoPI, aParent, aBeforeThis, node);
    1970           0 :     } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) {
    1971           0 :         rv = InsertXULOverlayPI(aProtoPI, aParent, aBeforeThis, node);
    1972             :     } else {
    1973             :         // No special processing, just add the PI to the document.
    1974           0 :         rv = aParent->InsertChildBefore(node->AsContent(),
    1975             :                                         aBeforeThis
    1976             :                                           ? aBeforeThis->AsContent() : nullptr,
    1977           0 :                                         false);
    1978             :     }
    1979             : 
    1980           0 :     return rv;
    1981             : }
    1982             : 
    1983             : nsresult
    1984           0 : XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
    1985             :                                    nsINode* aParent,
    1986             :                                    nsINode* aBeforeThis,
    1987             :                                    nsIContent* aPINode)
    1988             : {
    1989          26 :     nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
    1990          13 :     NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
    1991             :                        "implement nsIStyleSheetLinkingElement!");
    1992             : 
    1993             :     nsresult rv;
    1994             : 
    1995           0 :     ssle->InitStyleLinkElement(false);
    1996             :     // We want to be notified when the style sheet finishes loading, so
    1997             :     // disable style sheet loading for now.
    1998           0 :     ssle->SetEnableUpdates(false);
    1999           0 :     ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
    2000             : 
    2001          13 :     rv = aParent->InsertChildBefore(aPINode->AsContent(),
    2002             :                                     aBeforeThis
    2003             :                                       ? aBeforeThis->AsContent() : nullptr,
    2004           1 :                                     false);
    2005           1 :     if (NS_FAILED(rv)) return rv;
    2006             : 
    2007          13 :     ssle->SetEnableUpdates(true);
    2008             : 
    2009             :     // load the stylesheet if necessary, passing ourselves as
    2010             :     // nsICSSObserver
    2011           1 :     auto result = ssle->UpdateStyleSheet(this);
    2012          13 :     if (result.isErr()) {
    2013             :         // Ignore errors from UpdateStyleSheet; we don't want failure to
    2014             :         // do that to break the XUL document load.  But do propagate out
    2015             :         // NS_ERROR_OUT_OF_MEMORY.
    2016           0 :         if (result.unwrapErr() == NS_ERROR_OUT_OF_MEMORY) {
    2017           0 :             return result.unwrapErr();
    2018             :         }
    2019             :         return NS_OK;
    2020             :     }
    2021             : 
    2022          13 :     auto update = result.unwrap();
    2023           0 :     if (update.ShouldBlock()) {
    2024           0 :         ++mPendingSheets;
    2025             :     }
    2026             : 
    2027             :     return NS_OK;
    2028             : }
    2029             : 
    2030             : nsresult
    2031           0 : XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
    2032             :                                 nsINode* aParent,
    2033             :                                 nsINode* aBeforeThis,
    2034             :                                 nsIContent* aPINode)
    2035             : {
    2036             :     nsresult rv;
    2037             : 
    2038           0 :     rv = aParent->InsertChildBefore(aPINode->AsContent(),
    2039             :                                     aBeforeThis
    2040             :                                       ? aBeforeThis->AsContent() : nullptr,
    2041           0 :                                     false);
    2042           0 :     if (NS_FAILED(rv)) return rv;
    2043             : 
    2044             :     // xul-overlay PI is special only in prolog
    2045           0 :     if (!nsContentUtils::InProlog(aPINode)) {
    2046             :         return NS_OK;
    2047             :     }
    2048             : 
    2049           0 :     nsAutoString href;
    2050           0 :     nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData,
    2051             :                                             nsGkAtoms::href,
    2052           0 :                                             href);
    2053             : 
    2054             :     // If there was no href, we can't do anything with this PI
    2055           0 :     if (href.IsEmpty()) {
    2056             :         return NS_OK;
    2057             :     }
    2058             : 
    2059             :     // Add the overlay to our list of overlays that need to be processed.
    2060           0 :     nsCOMPtr<nsIURI> uri;
    2061             : 
    2062           0 :     rv = NS_NewURI(getter_AddRefs(uri), href, nullptr,
    2063           0 :                    mCurrentPrototype->GetURI());
    2064           0 :     if (NS_SUCCEEDED(rv)) {
    2065             :         // We insert overlays into mUnloadedOverlays at the same index in
    2066             :         // document order, so they end up in the reverse of the document
    2067             :         // order in mUnloadedOverlays.
    2068             :         // This is needed because the code in ResumeWalk loads the overlays
    2069             :         // by processing the last item of mUnloadedOverlays and removing it
    2070             :         // from the array.
    2071           0 :         mUnloadedOverlays.InsertElementAt(0, uri);
    2072           0 :         rv = NS_OK;
    2073           0 :     } else if (rv == NS_ERROR_MALFORMED_URI) {
    2074             :         // The URL is bad, move along. Don't propagate for now.
    2075             :         // XXX report this to the Error Console (bug 359846)
    2076           0 :         rv = NS_OK;
    2077             :     }
    2078             : 
    2079             :     return rv;
    2080             : }
    2081             : 
    2082             : nsresult
    2083           1 : XULDocument::AddChromeOverlays()
    2084             : {
    2085             :     nsresult rv;
    2086             : 
    2087           6 :     nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI();
    2088             : 
    2089             :     /* overlays only apply to chrome or about URIs */
    2090           3 :     if (!IsOverlayAllowed(docUri)) return NS_OK;
    2091             : 
    2092             :     nsCOMPtr<nsIXULOverlayProvider> chromeReg =
    2093           6 :         mozilla::services::GetXULOverlayProviderService();
    2094             :     // In embedding situations, the chrome registry may not provide overlays,
    2095             :     // or even exist at all; that's OK.
    2096           1 :     NS_ENSURE_TRUE(chromeReg, NS_OK);
    2097             : 
    2098           1 :     nsCOMPtr<nsISimpleEnumerator> overlays;
    2099           9 :     rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
    2100           3 :     NS_ENSURE_SUCCESS(rv, rv);
    2101             : 
    2102             :     bool moreOverlays;
    2103           3 :     nsCOMPtr<nsISupports> next;
    2104           3 :     nsCOMPtr<nsIURI> uri;
    2105             : 
    2106           1 :     while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
    2107             :            moreOverlays) {
    2108             : 
    2109           0 :         rv = overlays->GetNext(getter_AddRefs(next));
    2110           0 :         if (NS_FAILED(rv) || !next) break;
    2111             : 
    2112           0 :         uri = do_QueryInterface(next);
    2113           0 :         if (!uri) {
    2114           0 :             NS_ERROR("Chrome registry handed me a non-nsIURI object!");
    2115             :             continue;
    2116             :         }
    2117             : 
    2118             :         // Same comment as in XULDocument::InsertXULOverlayPI
    2119           0 :         mUnloadedOverlays.InsertElementAt(0, uri);
    2120             :     }
    2121             : 
    2122             :     return rv;
    2123             : }
    2124             : 
    2125             : void
    2126           0 : XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver,
    2127             :                          ErrorResult& aRv)
    2128             : {
    2129             :     nsresult rv;
    2130             : 
    2131           0 :     nsCOMPtr<nsIURI> uri;
    2132           0 :     rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr);
    2133           0 :     if (NS_FAILED(rv)) {
    2134           0 :         aRv.Throw(rv);
    2135           0 :         return;
    2136             :     }
    2137             : 
    2138           0 :     if (aObserver) {
    2139           0 :         nsIObserver* obs = nullptr;
    2140           0 :         if (!mOverlayLoadObservers) {
    2141           0 :           mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
    2142             :         }
    2143           0 :         obs = mOverlayLoadObservers->GetWeak(uri);
    2144             : 
    2145           0 :         if (obs) {
    2146             :             // We don't support loading the same overlay twice into the same
    2147             :             // document - that doesn't make sense anyway.
    2148           0 :             aRv.Throw(NS_ERROR_FAILURE);
    2149           0 :             return;
    2150             :         }
    2151           0 :         mOverlayLoadObservers->Put(uri, aObserver);
    2152             :     }
    2153             :     bool shouldReturn, failureFromContent;
    2154           0 :     rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent);
    2155           0 :     if (NS_FAILED(rv)) {
    2156             :         // remove the observer if LoadOverlayInternal generated an error
    2157           0 :         if (mOverlayLoadObservers) {
    2158           0 :             mOverlayLoadObservers->Remove(uri);
    2159             :         }
    2160           0 :         aRv.Throw(rv);
    2161           0 :         return;
    2162             :     }
    2163             : }
    2164             : 
    2165             : nsresult
    2166           0 : XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
    2167             :                                  bool* aShouldReturn,
    2168             :                                  bool* aFailureFromContent)
    2169             : {
    2170             :     nsresult rv;
    2171             : 
    2172             :     // XUL overlays are in the process of being removed. In a Firefox build,
    2173             :     // loading an overlay will no longer work and display an error in the
    2174             :     // console. In automation, doing so will cause a crash.
    2175             :     // However, overlays are allowed in other applications (e.g. Thunderbird)
    2176             :     // while they work on removing them. See bug 1448162.
    2177             : #ifdef MOZ_BREAK_XUL_OVERLAYS
    2178           0 :     nsCString docSpec;
    2179           0 :     mCurrentPrototype->GetURI()->GetSpec(docSpec);
    2180           0 :     nsCString overlaySpec;
    2181           0 :     aURI->GetSpec(overlaySpec);
    2182             :     nsPrintfCString msg("Attempt to load overlay %s into %s\n",
    2183             :                         overlaySpec.get(),
    2184           0 :                         docSpec.get());
    2185             :     nsCOMPtr<nsIConsoleService> consoleSvc =
    2186           0 :         do_GetService("@mozilla.org/consoleservice;1");
    2187           0 :     if (consoleSvc) {
    2188           0 :         consoleSvc->LogStringMessage(NS_ConvertASCIItoUTF16(msg).get());
    2189             :     }
    2190           0 :     printf("%s", msg.get());
    2191           0 :     if (xpc::IsInAutomation()) {
    2192           0 :         MOZ_CRASH("Attempt to load overlay.");
    2193             :     }
    2194             :     return NS_ERROR_NOT_AVAILABLE;
    2195             : #endif
    2196             : 
    2197             :     *aShouldReturn = false;
    2198             :     *aFailureFromContent = false;
    2199             : 
    2200             :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
    2201             :         nsCOMPtr<nsIURI> uri;
    2202             :         mChannel->GetOriginalURI(getter_AddRefs(uri));
    2203             : 
    2204             :         MOZ_LOG(gXULLog, LogLevel::Debug,
    2205             :                 ("xul: %s loading overlay %s",
    2206             :                  uri ? uri->GetSpecOrDefault().get() : "",
    2207             :                  aURI->GetSpecOrDefault().get()));
    2208             :     }
    2209             : 
    2210             :     if (aIsDynamic)
    2211             :         mResolutionPhase = nsForwardReference::eStart;
    2212             : 
    2213             :     // Look in the prototype cache for the prototype document with
    2214             :     // the specified overlay URI. Only use the cache if the containing
    2215             :     // document is chrome otherwise it may not have a system principal and
    2216             :     // the cached document will, see bug 565610.
    2217             :     bool overlayIsChrome = IsChromeURI(aURI);
    2218             :     bool documentIsChrome = mDocumentURI ?
    2219             :         IsChromeURI(mDocumentURI) : false;
    2220             :     mCurrentPrototype = overlayIsChrome && documentIsChrome ?
    2221             :         nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr;
    2222             : 
    2223             :     // Same comment as nsChromeProtocolHandler::NewChannel and
    2224             :     // XULDocument::StartDocumentLoad
    2225             :     // - Ben Goodger
    2226             :     //
    2227             :     // We don't abort on failure here because there are too many valid
    2228             :     // cases that can return failure, and the null-ness of |proto| is
    2229             :     // enough to trigger the fail-safe parse-from-disk solution.
    2230             :     // Example failure cases (for reference) include:
    2231             :     //
    2232             :     // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
    2233             :     //                         parse from disk
    2234             :     // other: the FastLoad file, XUL.mfl, could not be found, probably
    2235             :     //        due to being accessed before a profile has been selected
    2236             :     //        (e.g. loading chrome for the profile manager itself).
    2237             :     //        The .xul file must be parsed from disk.
    2238             : 
    2239             :     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
    2240             :     if (useXULCache && mCurrentPrototype) {
    2241             :         bool loaded;
    2242             :         rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
    2243             :         if (NS_FAILED(rv)) return rv;
    2244             : 
    2245             :         if (! loaded) {
    2246             :             // Return to the main event loop and eagerly await the
    2247             :             // prototype overlay load's completion. When the content
    2248             :             // sink completes, it will trigger an EndLoad(), which'll
    2249             :             // wind us back up here, in ResumeWalk().
    2250             :             *aShouldReturn = true;
    2251             :             return NS_OK;
    2252             :         }
    2253             : 
    2254             :         MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was cached"));
    2255             : 
    2256             :         // Found the overlay's prototype in the cache, fully loaded. If
    2257             :         // this is a dynamic overlay, this will call ResumeWalk.
    2258             :         // Otherwise, we'll return to ResumeWalk, which called us.
    2259             :         return OnPrototypeLoadDone(aIsDynamic);
    2260             :     }
    2261             :     else {
    2262             :         // Not there. Initiate a load.
    2263             :         MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was not cached"));
    2264             : 
    2265             :         if (mIsGoingAway) {
    2266             :             MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: ...and document already destroyed"));
    2267             :             return NS_ERROR_NOT_AVAILABLE;
    2268             :         }
    2269             : 
    2270             :         // We'll set the right principal on the proto doc when we get
    2271             :         // OnStartRequest from the parser, so just pass in a null principal for
    2272             :         // now.
    2273             :         nsCOMPtr<nsIParser> parser;
    2274             :         rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser));
    2275             :         if (NS_FAILED(rv)) return rv;
    2276             : 
    2277             :         // Predicate mIsWritingFastLoad on the XUL cache being enabled,
    2278             :         // so we don't have to re-check whether the cache is enabled all
    2279             :         // the time.
    2280             :         mIsWritingFastLoad = useXULCache;
    2281             : 
    2282             :         nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
    2283             :         if (! listener)
    2284             :             return NS_ERROR_UNEXPECTED;
    2285             : 
    2286             :         // Add an observer to the parser; this'll get called when
    2287             :         // Necko fires its On[Start|Stop]Request() notifications,
    2288             :         // and will let us recover from a missing overlay.
    2289             :         RefPtr<ParserObserver> parserObserver =
    2290             :             new ParserObserver(this, mCurrentPrototype);
    2291             :         parser->Parse(aURI, parserObserver);
    2292             :         parserObserver = nullptr;
    2293             : 
    2294             :         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
    2295             :         nsCOMPtr<nsIChannel> channel;
    2296             :         // Set the owner of the channel to be our principal so
    2297             :         // that the overlay's JSObjects etc end up being created
    2298             :         // with the right principal and in the correct
    2299             :         // compartment.
    2300             :         rv = NS_NewChannel(getter_AddRefs(channel),
    2301             :                            aURI,
    2302             :                            NodePrincipal(),
    2303             :                            nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
    2304             :                            nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
    2305             :                            nsIContentPolicy::TYPE_OTHER,
    2306             :                            nullptr, // PerformanceStorage
    2307             :                            group);
    2308             : 
    2309             :         if (NS_SUCCEEDED(rv)) {
    2310             :             rv = channel->AsyncOpen2(listener);
    2311             :         }
    2312             : 
    2313             :         if (NS_FAILED(rv)) {
    2314             :             // Abandon this prototype
    2315             :             mCurrentPrototype = nullptr;
    2316             : 
    2317             :             // The parser won't get an OnStartRequest and
    2318             :             // OnStopRequest, so it needs a Terminate.
    2319             :             parser->Terminate();
    2320             : 
    2321             :             // Just move on to the next overlay.
    2322             :             ReportMissingOverlay(aURI);
    2323             : 
    2324             :             // XXX the error could indicate an internal error as well...
    2325             :             *aFailureFromContent = true;
    2326             :             return rv;
    2327             :         }
    2328             : 
    2329             :         // If it's a 'chrome:' prototype document, then put it into
    2330             :         // the prototype cache; other XUL documents will be reloaded
    2331             :         // each time.  We must do this after AsyncOpen,
    2332             :         // or chrome code will wrongly create a cached chrome channel
    2333             :         // instead of a real one. Prototypes are only cached when the
    2334             :         // document to be overlayed is chrome to avoid caching overlay
    2335             :         // scripts with incorrect principals, see bug 565610.
    2336             :         if (useXULCache && overlayIsChrome && documentIsChrome) {
    2337             :             nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
    2338             :         }
    2339             : 
    2340             :         // Return to the main event loop and eagerly await the
    2341             :         // overlay load's completion. When the content sink
    2342             :         // completes, it will trigger an EndLoad(), which'll wind
    2343             :         // us back in ResumeWalk().
    2344             :         if (!aIsDynamic)
    2345             :             *aShouldReturn = true;
    2346             :     }
    2347             :     return NS_OK;
    2348             : }
    2349             : 
    2350             : nsresult
    2351           4 : XULDocument::ResumeWalk()
    2352             : {
    2353             :     // Walk the prototype and build the delegate content model. The
    2354             :     // walk is performed in a top-down, left-to-right fashion. That
    2355             :     // is, a parent is built before any of its children; a node is
    2356             :     // only built after all of its siblings to the left are fully
    2357             :     // constructed.
    2358             :     //
    2359             :     // It is interruptable so that transcluded documents (e.g.,
    2360             :     // <html:script src="..." />) can be properly re-loaded if the
    2361             :     // cached copy of the document becomes stale.
    2362             :     nsresult rv;
    2363             :     nsCOMPtr<nsIURI> overlayURI =
    2364          12 :         mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
    2365             : 
    2366             :     while (1) {
    2367             :         // Begin (or resume) walking the current prototype.
    2368             : 
    2369        1775 :         while (mContextStack.Depth() > 0) {
    2370             :             // Look at the top of the stack to determine what we're
    2371             :             // currently working on.
    2372             :             // This will always be a node already constructed and
    2373             :             // inserted to the actual document.
    2374             :             nsXULPrototypeElement* proto;
    2375        3151 :             nsCOMPtr<nsIContent> element;
    2376             :             int32_t indx; // all children of proto before indx (not
    2377             :                           // inclusive) have already been constructed
    2378        1772 :             rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
    2379        1773 :             if (NS_FAILED(rv)) return rv;
    2380             : 
    2381        3544 :             if (indx >= (int32_t)proto->mChildren.Length()) {
    2382         392 :                 if (element) {
    2383             :                     // We've processed all of the prototype's children. If
    2384             :                     // we're in the master prototype, do post-order
    2385             :                     // document-level hookup. (An overlay will get its
    2386             :                     // document hookup done when it's successfully
    2387             :                     // resolved.)
    2388         392 :                     if (mState == eState_Master) {
    2389         392 :                         AddElementToDocumentPost(element->AsElement());
    2390             : 
    2391        1568 :                         if (element->NodeInfo()->Equals(nsGkAtoms::style,
    2392         784 :                                                         kNameSpaceID_XHTML) ||
    2393        1176 :                             element->NodeInfo()->Equals(nsGkAtoms::style,
    2394             :                                                         kNameSpaceID_SVG)) {
    2395             :                             // XXX sucks that we have to do this -
    2396             :                             // see bug 370111
    2397             :                             nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
    2398           0 :                                 do_QueryInterface(element);
    2399           0 :                             NS_ASSERTION(ssle, "<html:style> doesn't implement "
    2400             :                                                "nsIStyleSheetLinkingElement?");
    2401           0 :                             Unused << ssle->UpdateStyleSheet(nullptr);
    2402             :                         }
    2403             :                     }
    2404             :                 }
    2405             :                 // Now pop the context stack back up to the parent
    2406             :                 // element and continue the prototype walk.
    2407         392 :                 mContextStack.Pop();
    2408         392 :                 continue;
    2409             :             }
    2410             : 
    2411             :             // Grab the next child, and advance the current context stack
    2412             :             // to the next sibling to our right.
    2413           0 :             nsXULPrototypeNode* childproto = proto->mChildren[indx];
    2414        1380 :             mContextStack.SetTopIndex(++indx);
    2415             : 
    2416             :             // Whether we're in the "first ply" of an overlay:
    2417             :             // the "hookup" nodes. In the case !processingOverlayHookupNodes,
    2418             :             // we're in the master document -or- we're in an overlay, and far
    2419             :             // enough down into the overlay's content that we can simply build
    2420             :             // the delegates and attach them to the parent node.
    2421        1380 :             bool processingOverlayHookupNodes = (mState == eState_Overlay) &&
    2422           0 :                                                   (mContextStack.Depth() == 1);
    2423             : 
    2424        1380 :             NS_ASSERTION(element || processingOverlayHookupNodes,
    2425             :                          "no element on context stack");
    2426             : 
    2427           0 :             switch (childproto->mType) {
    2428             :             case nsXULPrototypeNode::eType_Element: {
    2429             :                 // An 'element', which may contain more content.
    2430             :                 nsXULPrototypeElement* protoele =
    2431        1298 :                     static_cast<nsXULPrototypeElement*>(childproto);
    2432             : 
    2433           0 :                 RefPtr<Element> child;
    2434             : 
    2435           0 :                 if (!processingOverlayHookupNodes) {
    2436        1298 :                     rv = CreateElementFromPrototype(protoele,
    2437        2596 :                                                     getter_AddRefs(child),
    2438        1298 :                                                     false);
    2439        1298 :                     if (NS_FAILED(rv)) return rv;
    2440             : 
    2441             :                     // ...and append it to the content model.
    2442           0 :                     rv = element->AppendChildTo(child, false);
    2443        1298 :                     if (NS_FAILED(rv)) return rv;
    2444             : 
    2445             :                     // If we're only restoring persisted things on
    2446             :                     // some elements, store the ID here to do that.
    2447           0 :                     if (mRestrictPersistence) {
    2448           0 :                         nsAtom* id = child->GetID();
    2449           0 :                         if (id) {
    2450           0 :                             mPersistenceIds.PutEntry(nsDependentAtomString(id));
    2451             :                         }
    2452             :                     }
    2453             : 
    2454             :                     // do pre-order document-level hookup, but only if
    2455             :                     // we're in the master document. For an overlay,
    2456             :                     // this will happen when the overlay is
    2457             :                     // successfully resolved.
    2458           0 :                     if (mState == eState_Master)
    2459        1298 :                         AddElementToDocumentPre(child);
    2460             :                 }
    2461             :                 else {
    2462             :                     // We're in the "first ply" of an overlay: the
    2463             :                     // "hookup" nodes. Create an 'overlay' element so
    2464             :                     // that we can continue to build content, and
    2465             :                     // enter a forward reference so we can hook it up
    2466             :                     // later.
    2467           0 :                     rv = CreateOverlayElement(protoele, getter_AddRefs(child));
    2468           0 :                     if (NS_FAILED(rv)) return rv;
    2469             :                 }
    2470             : 
    2471             :                 // If it has children, push the element onto the context
    2472             :                 // stack and begin to process them.
    2473           0 :                 if (protoele->mChildren.Length() > 0) {
    2474         389 :                     rv = mContextStack.Push(protoele, child);
    2475         389 :                     if (NS_FAILED(rv)) return rv;
    2476             :                 }
    2477             :                 else {
    2478         909 :                     if (mState == eState_Master) {
    2479             :                         // If there are no children, and we're in the
    2480             :                         // master document, do post-order document hookup
    2481             :                         // immediately.
    2482           1 :                         AddElementToDocumentPost(child);
    2483             :                     }
    2484             :                 }
    2485             :             }
    2486             :             break;
    2487             : 
    2488             :             case nsXULPrototypeNode::eType_Script: {
    2489             :                 // A script reference. Execute the script immediately;
    2490             :                 // this may have side effects in the content model.
    2491             :                 nsXULPrototypeScript* scriptproto =
    2492           0 :                     static_cast<nsXULPrototypeScript*>(childproto);
    2493             : 
    2494           6 :                 if (scriptproto->mSrcURI) {
    2495             :                     // A transcluded script reference; this may
    2496             :                     // "block" our prototype walk if the script isn't
    2497             :                     // cached, or the cached copy of the script is
    2498             :                     // stale and must be reloaded.
    2499             :                     bool blocked;
    2500           1 :                     rv = LoadScript(scriptproto, &blocked);
    2501             :                     // If the script cannot be loaded, just keep going!
    2502             : 
    2503           1 :                     if (NS_SUCCEEDED(rv) && blocked)
    2504           1 :                         return NS_OK;
    2505             :                 }
    2506           2 :                 else if (scriptproto->HasScriptObject()) {
    2507             :                     // An inline script
    2508           0 :                     rv = ExecuteScript(scriptproto);
    2509           0 :                     if (NS_FAILED(rv)) return rv;
    2510             :                 }
    2511             :             }
    2512             :             break;
    2513             : 
    2514             :             case nsXULPrototypeNode::eType_Text: {
    2515             :                 // A simple text node.
    2516             : 
    2517          79 :                 if (!processingOverlayHookupNodes) {
    2518             :                     // This does mean that text nodes that are direct children
    2519             :                     // of <overlay> get ignored.
    2520             : 
    2521             :                     RefPtr<nsTextNode> text =
    2522         158 :                         new nsTextNode(mNodeInfoManager);
    2523             : 
    2524             :                     nsXULPrototypeText* textproto =
    2525          79 :                         static_cast<nsXULPrototypeText*>(childproto);
    2526           0 :                     text->SetText(textproto->mValue, false);
    2527             : 
    2528           0 :                     rv = element->AppendChildTo(text, false);
    2529          79 :                     NS_ENSURE_SUCCESS(rv, rv);
    2530             :                 }
    2531             :             }
    2532             :             break;
    2533             : 
    2534             :             case nsXULPrototypeNode::eType_PI: {
    2535             :                 nsXULPrototypePI* piProto =
    2536           0 :                     static_cast<nsXULPrototypePI*>(childproto);
    2537             : 
    2538             :                 // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
    2539             :                 // outside the prolog, like they used to. Issue a warning.
    2540             : 
    2541           0 :                 if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
    2542           0 :                     piProto->mTarget.EqualsLiteral("xul-overlay")) {
    2543             : 
    2544           0 :                     const char16_t* params[] = { piProto->mTarget.get() };
    2545             : 
    2546           0 :                     nsContentUtils::ReportToConsole(
    2547             :                                         nsIScriptError::warningFlag,
    2548           0 :                                         NS_LITERAL_CSTRING("XUL Document"), nullptr,
    2549             :                                         nsContentUtils::eXUL_PROPERTIES,
    2550             :                                         "PINotInProlog",
    2551             :                                         params, ArrayLength(params),
    2552           0 :                                         overlayURI);
    2553             :                 }
    2554             : 
    2555           0 :                 nsIContent* parent = processingOverlayHookupNodes ?
    2556           0 :                     GetRootElement() : element.get();
    2557             : 
    2558           0 :                 if (parent) {
    2559             :                     // an inline script could have removed the root element
    2560           0 :                     rv = CreateAndInsertPI(piProto, parent, nullptr);
    2561           0 :                     NS_ENSURE_SUCCESS(rv, rv);
    2562             :                 }
    2563             :             }
    2564             :             break;
    2565             : 
    2566             :             default:
    2567           0 :                 NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
    2568             :             }
    2569             :         }
    2570             : 
    2571             :         // Once we get here, the context stack will have been
    2572             :         // depleted. That means that the entire prototype has been
    2573             :         // walked and content has been constructed.
    2574             : 
    2575             :         // If we're not already, mark us as now processing overlays.
    2576           1 :         mState = eState_Overlay;
    2577             : 
    2578             :         // If there are no overlay URIs, then we're done.
    2579           6 :         uint32_t count = mUnloadedOverlays.Length();
    2580           0 :         if (! count)
    2581             :             break;
    2582             : 
    2583           0 :         nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1];
    2584           0 :         mUnloadedOverlays.RemoveElementAt(count - 1);
    2585             : 
    2586             :         bool shouldReturn, failureFromContent;
    2587           0 :         rv = LoadOverlayInternal(uri, false, &shouldReturn,
    2588           0 :                                  &failureFromContent);
    2589           0 :         if (failureFromContent)
    2590             :             // The failure |rv| was the result of a problem in the content
    2591             :             // rather than an unexpected problem in our implementation, so
    2592             :             // just continue with the next overlay.
    2593           0 :             continue;
    2594           0 :         if (NS_FAILED(rv))
    2595           0 :             return rv;
    2596           0 :         if (mOverlayLoadObservers) {
    2597           0 :             nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI);
    2598           0 :             if (obs) {
    2599             :                 // This overlay has an unloaded overlay, so it will never
    2600             :                 // notify. The best we can do is to notify for the unloaded
    2601             :                 // overlay instead, assuming nobody is already notifiable
    2602             :                 // for it. Note that this will confuse the observer.
    2603           0 :                 if (!mOverlayLoadObservers->GetWeak(uri))
    2604           0 :                     mOverlayLoadObservers->Put(uri, obs);
    2605           0 :                 mOverlayLoadObservers->Remove(overlayURI);
    2606             :             }
    2607             :         }
    2608           0 :         if (shouldReturn)
    2609             :             return NS_OK;
    2610           0 :         overlayURI.swap(uri);
    2611             :     }
    2612             : 
    2613             :     // If we get here, there is nothing left for us to walk. The content
    2614             :     // model is built and ready for layout.
    2615           3 :     rv = ResolveForwardReferences();
    2616           3 :     if (NS_FAILED(rv)) return rv;
    2617             : 
    2618           1 :     ApplyPersistentAttributes();
    2619             : 
    2620           3 :     mStillWalking = false;
    2621           1 :     if (mPendingSheets == 0) {
    2622           1 :         rv = DoneWalking();
    2623             :     }
    2624             :     return rv;
    2625             : }
    2626             : 
    2627             : nsresult
    2628           1 : XULDocument::DoneWalking()
    2629             : {
    2630           1 :     MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
    2631           1 :     MOZ_ASSERT(!mStillWalking, "walk not done");
    2632             : 
    2633             :     // XXXldb This is where we should really be setting the chromehidden
    2634             :     // attribute.
    2635             : 
    2636           3 :     if (!mDocumentLoaded) {
    2637             :         // Make sure we don't reenter here from StartLayout().  Note that
    2638             :         // setting mDocumentLoaded to true here means that if StartLayout()
    2639             :         // causes ResumeWalk() to be reentered, we'll take the other branch of
    2640             :         // the |if (!mDocumentLoaded)| check above and since
    2641             :         // mInitialLayoutComplete will be false will follow the else branch
    2642             :         // there too.  See the big comment there for how such reentry can
    2643             :         // happen.
    2644           1 :         mDocumentLoaded = true;
    2645             : 
    2646           3 :         NotifyPossibleTitleChange(false);
    2647             : 
    2648           3 :         nsContentUtils::DispatchTrustedEvent(
    2649             :             this,
    2650             :             static_cast<nsIDocument*>(this),
    2651           3 :             NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
    2652             :             true,
    2653           9 :             false);
    2654             : 
    2655             :         // Before starting layout, check whether we're a toplevel chrome
    2656             :         // window.  If we are, setup some state so that we don't have to restyle
    2657             :         // the whole tree after StartLayout.
    2658           9 :         if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
    2659             :             // We're the chrome document!
    2660           1 :             win->BeforeStartLayout();
    2661             :         }
    2662             : 
    2663           3 :         StartLayout();
    2664             : 
    2665           0 :         if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
    2666           4 :             nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype);
    2667             : 
    2668           3 :         NS_ASSERTION(mDelayFrameLoaderInitialization,
    2669             :                      "mDelayFrameLoaderInitialization should be true!");
    2670           3 :         mDelayFrameLoaderInitialization = false;
    2671           0 :         NS_WARNING_ASSERTION(
    2672             :           mUpdateNestLevel == 0,
    2673             :           "Constructing XUL document in middle of an update?");
    2674           3 :         if (mUpdateNestLevel == 0) {
    2675           3 :             MaybeInitializeFinalizeFrameLoaders();
    2676             :         }
    2677             : 
    2678          12 :         NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
    2679             : 
    2680             :         // DispatchContentLoadedEvents undoes the onload-blocking we
    2681             :         // did in PrepareToWalk().
    2682           3 :         DispatchContentLoadedEvents();
    2683             : 
    2684           3 :         mInitialLayoutComplete = true;
    2685             : 
    2686             :         // Walk the set of pending load notifications and notify any observers.
    2687             :         // See below for detail.
    2688           6 :         if (mPendingOverlayLoadNotifications) {
    2689             :             nsInterfaceHashtable<nsURIHashKey,nsIObserver>* observers =
    2690           0 :                 mOverlayLoadObservers.get();
    2691           0 :             for (auto iter = mPendingOverlayLoadNotifications->Iter();
    2692           0 :                  !iter.Done();
    2693           0 :                  iter.Next()) {
    2694           0 :                 nsIURI* aKey = iter.Key();
    2695           0 :                 iter.Data()->Observe(aKey, "xul-overlay-merged",
    2696           0 :                                      EmptyString().get());
    2697             : 
    2698           0 :                 if (observers) {
    2699           0 :                   observers->Remove(aKey);
    2700             :                 }
    2701             : 
    2702           0 :                 iter.Remove();
    2703             :             }
    2704             :         }
    2705             :     }
    2706             :     else {
    2707           0 :         if (mOverlayLoadObservers) {
    2708           0 :             nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI();
    2709           0 :             nsCOMPtr<nsIObserver> obs;
    2710           0 :             if (mInitialLayoutComplete) {
    2711             :                 // We have completed initial layout, so just send the notification.
    2712           0 :                 mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
    2713           0 :                 if (obs)
    2714           0 :                     obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
    2715           0 :                 mOverlayLoadObservers->Remove(overlayURI);
    2716             :             }
    2717             :             else {
    2718             :                 // If we have not yet displayed the document for the first time
    2719             :                 // (i.e. we came in here as the result of a dynamic overlay load
    2720             :                 // which was spawned by a binding-attached event caused by
    2721             :                 // StartLayout() on the master prototype - we must remember that
    2722             :                 // this overlay has been merged and tell the listeners after
    2723             :                 // StartLayout() is completely finished rather than doing so
    2724             :                 // immediately - otherwise we may be executing code that needs to
    2725             :                 // access XBL Binding implementations on nodes for which frames
    2726             :                 // have not yet been constructed because their bindings have not
    2727             :                 // yet been attached. This can be a race condition because dynamic
    2728             :                 // overlay loading can take varying amounts of time depending on
    2729             :                 // whether or not the overlay prototype is in the XUL cache. The
    2730             :                 // most likely effect of this bug is odd UI initialization due to
    2731             :                 // methods and properties that do not work.
    2732             :                 // XXXbz really, we shouldn't be firing binding constructors
    2733             :                 // until after StartLayout returns!
    2734             : 
    2735           0 :                 if (!mPendingOverlayLoadNotifications) {
    2736             :                     mPendingOverlayLoadNotifications =
    2737           0 :                         new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
    2738             :                 }
    2739             : 
    2740           0 :                 mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs));
    2741           0 :                 if (!obs) {
    2742           0 :                     mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
    2743           0 :                     NS_ASSERTION(obs, "null overlay load observer?");
    2744           0 :                     mPendingOverlayLoadNotifications->Put(overlayURI, obs);
    2745             :                 }
    2746             :             }
    2747             :         }
    2748             :     }
    2749             : 
    2750           1 :     return NS_OK;
    2751             : }
    2752             : 
    2753             : NS_IMETHODIMP
    2754          13 : XULDocument::StyleSheetLoaded(StyleSheet* aSheet,
    2755             :                               bool aWasDeferred,
    2756             :                               nsresult aStatus)
    2757             : {
    2758           1 :     if (!aWasDeferred) {
    2759             :         // Don't care about when alternate sheets finish loading
    2760          12 :         MOZ_ASSERT(mPendingSheets > 0,
    2761             :                    "Unexpected StyleSheetLoaded notification");
    2762             : 
    2763          12 :         --mPendingSheets;
    2764             : 
    2765          12 :         if (!mStillWalking && mPendingSheets == 0) {
    2766           1 :             return DoneWalking();
    2767             :         }
    2768             :     }
    2769             : 
    2770             :     return NS_OK;
    2771             : }
    2772             : 
    2773             : void
    2774         315 : XULDocument::MaybeBroadcast()
    2775             : {
    2776             :     // Only broadcast when not in an update and when safe to run scripts.
    2777         805 :     if (mUpdateNestLevel == 0 &&
    2778           1 :         (mDelayedAttrChangeBroadcasts.Length() ||
    2779         308 :          mDelayedBroadcasters.Length())) {
    2780           1 :         if (!nsContentUtils::IsSafeToRunScript()) {
    2781           1 :             if (!mInDestructor) {
    2782           2 :               nsContentUtils::AddScriptRunner(
    2783           1 :                 NewRunnableMethod("dom::XULDocument::MaybeBroadcast",
    2784             :                                   this,
    2785           1 :                                   &XULDocument::MaybeBroadcast));
    2786             :             }
    2787             :             return;
    2788             :         }
    2789          24 :         if (!mHandlingDelayedAttrChange) {
    2790          13 :             mHandlingDelayedAttrChange = true;
    2791          48 :             for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
    2792          33 :                 nsAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
    2793           0 :                 if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
    2794             :                     nsCOMPtr<Element> listener =
    2795          44 :                         do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
    2796          22 :                     const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
    2797           0 :                     if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
    2798           8 :                         listener->SetAttr(kNameSpaceID_None, attrName, value,
    2799           8 :                                           true);
    2800             :                     } else {
    2801           0 :                         listener->UnsetAttr(kNameSpaceID_None, attrName,
    2802           3 :                                             true);
    2803             :                     }
    2804             :                 }
    2805          33 :                 ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
    2806           0 :                                              mDelayedAttrChangeBroadcasts[i].mListener,
    2807          11 :                                              attrName);
    2808             :             }
    2809           0 :             mDelayedAttrChangeBroadcasts.Clear();
    2810          13 :             mHandlingDelayedAttrChange = false;
    2811             :         }
    2812             : 
    2813          48 :         uint32_t length = mDelayedBroadcasters.Length();
    2814          24 :         if (length) {
    2815           3 :             bool oldValue = mHandlingDelayedBroadcasters;
    2816           3 :             mHandlingDelayedBroadcasters = true;
    2817           0 :             nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
    2818           3 :             mDelayedBroadcasters.SwapElements(delayedBroadcasters);
    2819           8 :             for (uint32_t i = 0; i < length; ++i) {
    2820           0 :                 SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
    2821           0 :                                              delayedBroadcasters[i].mListener,
    2822           0 :                                              delayedBroadcasters[i].mAttr);
    2823             :             }
    2824           0 :             mHandlingDelayedBroadcasters = oldValue;
    2825             :         }
    2826             :     }
    2827             : }
    2828             : 
    2829             : void
    2830         309 : XULDocument::EndUpdate()
    2831             : {
    2832           0 :     XMLDocument::EndUpdate();
    2833           0 :     MaybeBroadcast();
    2834           0 : }
    2835             : 
    2836             : void
    2837           0 : XULDocument::ReportMissingOverlay(nsIURI* aURI)
    2838             : {
    2839           0 :     MOZ_ASSERT(aURI, "Must have a URI");
    2840             : 
    2841           0 :     NS_ConvertUTF8toUTF16 utfSpec(aURI->GetSpecOrDefault());
    2842           0 :     const char16_t* params[] = { utfSpec.get() };
    2843           0 :     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    2844           0 :                                     NS_LITERAL_CSTRING("XUL Document"), this,
    2845             :                                     nsContentUtils::eXUL_PROPERTIES,
    2846             :                                     "MissingOverlay",
    2847           0 :                                     params, ArrayLength(params));
    2848           0 : }
    2849             : 
    2850             : nsresult
    2851           1 : XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
    2852             : {
    2853             :     // Load a transcluded script
    2854             :     nsresult rv;
    2855             : 
    2856           0 :     bool isChromeDoc = IsChromeURI(mDocumentURI);
    2857             : 
    2858           0 :     if (isChromeDoc && aScriptProto->HasScriptObject()) {
    2859           0 :         rv = ExecuteScript(aScriptProto);
    2860             : 
    2861             :         // Ignore return value from execution, and don't block
    2862           0 :         *aBlock = false;
    2863           0 :         return NS_OK;
    2864             :     }
    2865             : 
    2866             :     // Try the XUL script cache, in case two XUL documents source the same
    2867             :     // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
    2868             :     // XXXbe the cache relies on aScriptProto's GC root!
    2869           2 :     bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
    2870             : 
    2871           1 :     if (isChromeDoc && useXULCache) {
    2872             :         JSScript* newScriptObject =
    2873           0 :             nsXULPrototypeCache::GetInstance()->GetScript(
    2874           1 :                                    aScriptProto->mSrcURI);
    2875           0 :         if (newScriptObject) {
    2876             :             // The script language for a proto must remain constant - we
    2877             :             // can't just change it for this unexpected language.
    2878           0 :             aScriptProto->Set(newScriptObject);
    2879             :         }
    2880             : 
    2881           1 :         if (aScriptProto->HasScriptObject()) {
    2882           0 :             rv = ExecuteScript(aScriptProto);
    2883             : 
    2884             :             // Ignore return value from execution, and don't block
    2885           0 :             *aBlock = false;
    2886           0 :             return NS_OK;
    2887             :         }
    2888             :     }
    2889             : 
    2890             :     // Release script objects from FastLoad since we decided against using them
    2891           1 :     aScriptProto->UnlinkJSObjects();
    2892             : 
    2893             :     // Set the current script prototype so that OnStreamComplete can report
    2894             :     // the right file if there are errors in the script.
    2895           1 :     NS_ASSERTION(!mCurrentScriptProto,
    2896             :                  "still loading a script when starting another load?");
    2897           1 :     mCurrentScriptProto = aScriptProto;
    2898             : 
    2899           0 :     if (isChromeDoc && aScriptProto->mSrcLoading) {
    2900             :         // Another XULDocument load has started, which is still in progress.
    2901             :         // Remember to ResumeWalk this document when the load completes.
    2902           0 :         mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
    2903           0 :         aScriptProto->mSrcLoadWaiters = this;
    2904           0 :         NS_ADDREF_THIS();
    2905             :     }
    2906             :     else {
    2907           4 :         nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
    2908             : 
    2909             :         // Note: the loader will keep itself alive while it's loading.
    2910           2 :         nsCOMPtr<nsIStreamLoader> loader;
    2911           4 :         rv = NS_NewStreamLoader(getter_AddRefs(loader),
    2912             :                                 aScriptProto->mSrcURI,
    2913             :                                 this, // aObserver
    2914             :                                 this, // aRequestingContext
    2915             :                                 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
    2916             :                                 nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
    2917           0 :                                 group);
    2918             : 
    2919           1 :         if (NS_FAILED(rv)) {
    2920           0 :             mCurrentScriptProto = nullptr;
    2921           0 :             return rv;
    2922             :         }
    2923             : 
    2924           0 :         aScriptProto->mSrcLoading = true;
    2925             :     }
    2926             : 
    2927             :     // Block until OnStreamComplete resumes us.
    2928           1 :     *aBlock = true;
    2929           1 :     return NS_OK;
    2930             : }
    2931             : 
    2932             : NS_IMETHODIMP
    2933           1 : XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
    2934             :                               nsISupports* context,
    2935             :                               nsresult aStatus,
    2936             :                               uint32_t stringLen,
    2937             :                               const uint8_t* string)
    2938             : {
    2939           2 :     nsCOMPtr<nsIRequest> request;
    2940           0 :     aLoader->GetRequest(getter_AddRefs(request));
    2941           3 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
    2942             : 
    2943             : #ifdef DEBUG
    2944             :     // print a load error on bad status
    2945           1 :     if (NS_FAILED(aStatus)) {
    2946           0 :         if (channel) {
    2947           0 :             nsCOMPtr<nsIURI> uri;
    2948           0 :             channel->GetURI(getter_AddRefs(uri));
    2949           0 :             if (uri) {
    2950           0 :                 printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
    2951             :             }
    2952             :         }
    2953             :     }
    2954             : #endif
    2955             : 
    2956             :     // This is the completion routine that will be called when a
    2957             :     // transcluded script completes. Compile and execute the script
    2958             :     // if the load was successful, then continue building content
    2959             :     // from the prototype.
    2960           0 :     nsresult rv = aStatus;
    2961             : 
    2962           0 :     NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
    2963             :                  "script source not loading on unichar stream complete?");
    2964           1 :     if (!mCurrentScriptProto) {
    2965             :         // XXX Wallpaper for bug 270042
    2966             :         return NS_OK;
    2967             :     }
    2968             : 
    2969           1 :     if (NS_SUCCEEDED(aStatus)) {
    2970             :         // If the including XUL document is a FastLoad document, and we're
    2971             :         // compiling an out-of-line script (one with src=...), then we must
    2972             :         // be writing a new FastLoad file.  If we were reading this script
    2973             :         // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
    2974             :         // nsXULContentSink.cpp) would have already deserialized a non-null
    2975             :         // script->mScriptObject, causing control flow at the top of LoadScript
    2976             :         // not to reach here.
    2977           2 :         nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
    2978             : 
    2979             :         // XXX should also check nsIHttpChannel::requestSucceeded
    2980             : 
    2981           1 :         MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
    2982             :                                             !mOffThreadCompileStringBuf),
    2983             :                    "XULDocument can't load multiple scripts at once");
    2984             : 
    2985           3 :         rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen,
    2986           1 :                                           EmptyString(), this,
    2987             :                                           mOffThreadCompileStringBuf,
    2988           0 :                                           mOffThreadCompileStringLength);
    2989           0 :         if (NS_SUCCEEDED(rv)) {
    2990             :             // Attempt to give ownership of the buffer to the JS engine.  If
    2991             :             // we hit offthread compilation, however, we will have to take it
    2992             :             // back below in order to keep the memory alive until compilation
    2993             :             // completes.
    2994           1 :             JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
    2995             :                                           mOffThreadCompileStringLength,
    2996           3 :                                           JS::SourceBufferHolder::GiveOwnership);
    2997           1 :             mOffThreadCompileStringBuf = nullptr;
    2998           1 :             mOffThreadCompileStringLength = 0;
    2999             : 
    3000           2 :             rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
    3001           1 :             if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
    3002             :                 // We will be notified via OnOffThreadCompileComplete when the
    3003             :                 // compile finishes. Keep the contents of the compiled script
    3004             :                 // alive until the compilation finishes.
    3005           0 :                 mOffThreadCompiling = true;
    3006             :                 // If the JS engine did not take the source buffer, then take
    3007             :                 // it back here to ensure it remains alive.
    3008           0 :                 mOffThreadCompileStringBuf = srcBuf.take();
    3009           0 :                 if (mOffThreadCompileStringBuf) {
    3010           0 :                   mOffThreadCompileStringLength = srcBuf.length();
    3011             :                 }
    3012           0 :                 BlockOnload();
    3013           0 :                 return NS_OK;
    3014             :             }
    3015             :         }
    3016             :     }
    3017             : 
    3018           2 :     return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
    3019             : }
    3020             : 
    3021             : NS_IMETHODIMP
    3022           1 : XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
    3023             : {
    3024             :     // When compiling off thread the script will not have been attached to the
    3025             :     // script proto yet.
    3026           1 :     if (aScript && !mCurrentScriptProto->HasScriptObject())
    3027           0 :         mCurrentScriptProto->Set(aScript);
    3028             : 
    3029             :     // Allow load events to be fired once off thread compilation finishes.
    3030           1 :     if (mOffThreadCompiling) {
    3031           0 :         mOffThreadCompiling = false;
    3032           0 :         UnblockOnload(false);
    3033             :     }
    3034             : 
    3035             :     // After compilation finishes the script's characters are no longer needed.
    3036           1 :     if (mOffThreadCompileStringBuf) {
    3037           0 :       js_free(mOffThreadCompileStringBuf);
    3038           0 :       mOffThreadCompileStringBuf = nullptr;
    3039           0 :       mOffThreadCompileStringLength = 0;
    3040             :     }
    3041             : 
    3042             :     // Clear mCurrentScriptProto now, but save it first for use below in
    3043             :     // the execute code, and in the while loop that resumes walks of other
    3044             :     // documents that raced to load this script.
    3045           1 :     nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
    3046           1 :     mCurrentScriptProto = nullptr;
    3047             : 
    3048             :     // Clear the prototype's loading flag before executing the script or
    3049             :     // resuming document walks, in case any of those control flows starts a
    3050             :     // new script load.
    3051           0 :     scriptProto->mSrcLoading = false;
    3052             : 
    3053           0 :     nsresult rv = aStatus;
    3054           1 :     if (NS_SUCCEEDED(rv)) {
    3055           0 :         rv = ExecuteScript(scriptProto);
    3056             : 
    3057             :         // If the XUL cache is enabled, save the script object there in
    3058             :         // case different XUL documents source the same script.
    3059             :         //
    3060             :         // But don't save the script in the cache unless the master XUL
    3061             :         // document URL is a chrome: URL.  It is valid for a URL such as
    3062             :         // about:config to translate into a master document URL, whose
    3063             :         // prototype document nodes -- including prototype scripts that
    3064             :         // hold GC roots protecting their mJSObject pointers -- are not
    3065             :         // cached in the XUL prototype cache.  See StartDocumentLoad,
    3066             :         // the fillXULCache logic.
    3067             :         //
    3068             :         // A document such as about:config is free to load a script via
    3069             :         // a URL such as chrome://global/content/config.js, and we must
    3070             :         // not cache that script object without a prototype cache entry
    3071             :         // containing a companion nsXULPrototypeScript node that owns a
    3072             :         // GC root protecting the script object.  Otherwise, the script
    3073             :         // cache entry will dangle once the uncached prototype document
    3074             :         // is released when its owning XULDocument is unloaded.
    3075             :         //
    3076             :         // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
    3077             :         // the true crime story.)
    3078           2 :         bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
    3079             : 
    3080           0 :         if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasScriptObject()) {
    3081           0 :             JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
    3082           0 :             nsXULPrototypeCache::GetInstance()->PutScript(
    3083           1 :                                scriptProto->mSrcURI, script);
    3084             :         }
    3085             : 
    3086           2 :         if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
    3087             :             // If we are loading an overlay script, try to serialize
    3088             :             // it to the FastLoad file here.  Master scripts will be
    3089             :             // serialized when the master prototype document gets
    3090             :             // written, at the bottom of ResumeWalk.  That way, master
    3091             :             // out-of-line scripts are serialized in the same order that
    3092             :             // they'll be read, in the FastLoad file, which reduces the
    3093             :             // number of seeks that dump the underlying stream's buffer.
    3094             :             //
    3095             :             // Ignore the return value, as we don't need to propagate
    3096             :             // a failure to write to the FastLoad file, because this
    3097             :             // method aborts that whole process on error.
    3098           0 :             scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
    3099             :         }
    3100             :         // ignore any evaluation errors
    3101             :     }
    3102             : 
    3103           1 :     rv = ResumeWalk();
    3104             : 
    3105             :     // Load a pointer to the prototype-script's list of XULDocuments who
    3106             :     // raced to load the same script
    3107           1 :     XULDocument** docp = &scriptProto->mSrcLoadWaiters;
    3108             : 
    3109             :     // Resume walking other documents that waited for this one's load, first
    3110             :     // executing the script we just compiled, in each doc's script context
    3111             :     XULDocument* doc;
    3112           1 :     while ((doc = *docp) != nullptr) {
    3113           0 :         NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
    3114             :                      "waiting for wrong script to load?");
    3115           0 :         doc->mCurrentScriptProto = nullptr;
    3116             : 
    3117             :         // Unlink doc from scriptProto's list before executing and resuming
    3118           0 :         *docp = doc->mNextSrcLoadWaiter;
    3119           0 :         doc->mNextSrcLoadWaiter = nullptr;
    3120             : 
    3121           0 :         if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasScriptObject()) {
    3122             :             // If the previous doc load was aborted, we want to try loading
    3123             :             // again for the next doc. Otherwise, one abort would lead to all
    3124             :             // subsequent waiting docs to abort as well.
    3125           0 :             bool block = false;
    3126           0 :             doc->LoadScript(scriptProto, &block);
    3127           0 :             NS_RELEASE(doc);
    3128             :             return rv;
    3129             :         }
    3130             : 
    3131             :         // Execute only if we loaded and compiled successfully, then resume
    3132           0 :         if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
    3133           0 :             doc->ExecuteScript(scriptProto);
    3134             :         }
    3135           0 :         doc->ResumeWalk();
    3136           0 :         NS_RELEASE(doc);
    3137             :     }
    3138             : 
    3139             :     return rv;
    3140             : }
    3141             : 
    3142             : nsresult
    3143           3 : XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
    3144             : {
    3145           3 :     MOZ_ASSERT(aScript != nullptr, "null ptr");
    3146             :     NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
    3147           6 :     NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
    3148             : 
    3149             :     nsresult rv;
    3150           0 :     rv = mScriptGlobalObject->EnsureScriptEnvironment();
    3151           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3152             : 
    3153             :     // Execute the precompiled script with the given version
    3154           6 :     nsAutoMicroTask mt;
    3155             : 
    3156             :     // We're about to run script via JS::CloneAndExecuteScript, so we need an
    3157             :     // AutoEntryScript. This is Gecko specific and not in any spec.
    3158           1 :     AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
    3159           3 :     JSContext* cx = aes.cx();
    3160             : 
    3161           1 :     JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
    3162           1 :     NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
    3163             : 
    3164           1 :     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
    3165           3 :     NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
    3166             : 
    3167           3 :     JS::ExposeObjectToActiveJS(global);
    3168           1 :     JSAutoRealm ar(cx, global);
    3169             : 
    3170             :     // The script is in the compilation scope. Clone it into the target scope
    3171             :     // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
    3172             :     // there is no need to manually check the return value.
    3173           6 :     JS::RootedValue rval(cx);
    3174           6 :     JS::CloneAndExecuteScript(cx, scriptObject, &rval);
    3175             : 
    3176             :     return NS_OK;
    3177             : }
    3178             : 
    3179             : 
    3180             : nsresult
    3181        1301 : XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
    3182             :                                         Element** aResult,
    3183             :                                         bool aIsRoot)
    3184             : {
    3185             :     // Create a content model element from a prototype element.
    3186           0 :     MOZ_ASSERT(aPrototype != nullptr, "null ptr");
    3187        1301 :     if (! aPrototype)
    3188             :         return NS_ERROR_NULL_POINTER;
    3189             : 
    3190           0 :     *aResult = nullptr;
    3191        1301 :     nsresult rv = NS_OK;
    3192             : 
    3193           0 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
    3194           0 :         MOZ_LOG(gXULLog, LogLevel::Debug,
    3195             :                ("xul: creating <%s> from prototype",
    3196             :                 NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
    3197             :     }
    3198             : 
    3199        2602 :     RefPtr<Element> result;
    3200             : 
    3201           0 :     if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
    3202             :         // If it's a XUL element, it'll be lightweight until somebody
    3203             :         // monkeys with it.
    3204           0 :         rv = nsXULElement::CreateFromPrototype(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
    3205           0 :         if (NS_FAILED(rv)) return rv;
    3206             :     }
    3207             :     else {
    3208             :         // If it's not a XUL element, it's gonna be heavyweight no matter
    3209             :         // what. So we need to copy everything out of the prototype
    3210             :         // into the element.  Get a nodeinfo from our nodeinfo manager
    3211             :         // for this node.
    3212          34 :         RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
    3213          34 :         newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
    3214             :                                                     aPrototype->mNodeInfo->GetPrefixAtom(),
    3215             :                                                     aPrototype->mNodeInfo->NamespaceID(),
    3216           0 :                                                     ELEMENT_NODE);
    3217           0 :         if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
    3218          34 :         RefPtr<mozilla::dom::NodeInfo> xtfNi = newNodeInfo;
    3219          51 :         rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
    3220          17 :                            NOT_FROM_PARSER);
    3221          17 :         if (NS_FAILED(rv))
    3222           0 :             return rv;
    3223             : 
    3224           0 :         rv = AddAttributes(aPrototype, result);
    3225          17 :         if (NS_FAILED(rv)) return rv;
    3226             :     }
    3227             : 
    3228        1301 :     result.forget(aResult);
    3229             : 
    3230           0 :     return NS_OK;
    3231             : }
    3232             : 
    3233             : nsresult
    3234           0 : XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
    3235             :                                   Element** aResult)
    3236             : {
    3237             :     nsresult rv;
    3238             : 
    3239           0 :     RefPtr<Element> element;
    3240           0 :     rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false);
    3241           0 :     if (NS_FAILED(rv)) return rv;
    3242             : 
    3243             :     OverlayForwardReference* fwdref =
    3244           0 :         new OverlayForwardReference(this, element);
    3245             : 
    3246             :     // transferring ownership to ya...
    3247           0 :     rv = AddForwardReference(fwdref);
    3248           0 :     if (NS_FAILED(rv)) return rv;
    3249             : 
    3250           0 :     element.forget(aResult);
    3251           0 :     return NS_OK;
    3252             : }
    3253             : 
    3254             : nsresult
    3255           0 : XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
    3256             :                            Element* aElement)
    3257             : {
    3258             :     nsresult rv;
    3259             : 
    3260           0 :     for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
    3261           0 :         nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
    3262           0 :         nsAutoString  valueStr;
    3263           0 :         protoattr->mValue.ToString(valueStr);
    3264             : 
    3265           1 :         rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
    3266             :                                protoattr->mName.LocalName(),
    3267             :                                protoattr->mName.GetPrefix(),
    3268             :                                valueStr,
    3269          19 :                                false);
    3270          19 :         if (NS_FAILED(rv)) return rv;
    3271             :     }
    3272             : 
    3273             :     return NS_OK;
    3274             : }
    3275             : 
    3276             : 
    3277             : //----------------------------------------------------------------------
    3278             : //
    3279             : // XULDocument::OverlayForwardReference
    3280             : //
    3281             : 
    3282             : nsForwardReference::Result
    3283           0 : XULDocument::OverlayForwardReference::Resolve()
    3284             : {
    3285             :     // Resolve a forward reference from an overlay element; attempt to
    3286             :     // hook it up into the main document.
    3287             :     nsresult rv;
    3288           0 :     RefPtr<Element> target;
    3289             : 
    3290           0 :     nsIPresShell *shell = mDocument->GetShell();
    3291           0 :     bool notify = shell && shell->DidInitialize();
    3292             : 
    3293           0 :     nsAutoString id;
    3294           0 :     mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
    3295           0 :     if (id.IsEmpty()) {
    3296             :         // mOverlay is a direct child of <overlay> and has no id.
    3297             :         // Insert it under the root element in the base document.
    3298           0 :         Element* root = mDocument->GetRootElement();
    3299           0 :         if (!root) {
    3300             :             return eResolve_Error;
    3301             :         }
    3302             : 
    3303           0 :         rv = XULDocument::InsertElement(root, mOverlay, notify);
    3304           0 :         if (NS_FAILED(rv)) return eResolve_Error;
    3305             : 
    3306           0 :         target = mOverlay;
    3307             :     }
    3308             :     else {
    3309             :         // The hook-up element has an id, try to match it with an element
    3310             :         // with the same id in the base document.
    3311           0 :         target = mDocument->GetElementById(id);
    3312             : 
    3313             :         // If we can't find the element in the document, defer the hookup
    3314             :         // until later.
    3315           0 :         if (!target)
    3316             :             return eResolve_Later;
    3317             : 
    3318           0 :         rv = Merge(target, mOverlay, notify);
    3319           0 :         if (NS_FAILED(rv)) return eResolve_Error;
    3320             :     }
    3321             : 
    3322             :     // Check if 'target' is still in our document --- it might not be!
    3323           0 :     if (!notify && target->GetUncomposedDoc() == mDocument) {
    3324             :         // Add child and any descendants to the element map
    3325             :         // XXX this is bogus, the content in 'target' might already be
    3326             :         // in the document
    3327           0 :         rv = mDocument->AddSubtreeToDocument(target);
    3328           0 :         if (NS_FAILED(rv)) return eResolve_Error;
    3329             :     }
    3330             : 
    3331           0 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
    3332           0 :         nsAutoCString idC;
    3333           0 :         LossyCopyUTF16toASCII(id, idC);
    3334           0 :         MOZ_LOG(gXULLog, LogLevel::Debug,
    3335             :                ("xul: overlay resolved '%s'",
    3336             :                 idC.get()));
    3337             :     }
    3338             : 
    3339           0 :     mResolved = true;
    3340           0 :     return eResolve_Succeeded;
    3341             : }
    3342             : 
    3343             : 
    3344             : 
    3345             : nsresult
    3346           0 : XULDocument::OverlayForwardReference::Merge(Element* aTargetElement,
    3347             :                                             Element* aOverlayElement,
    3348             :                                             bool aNotify)
    3349             : {
    3350             :     // This function is given:
    3351             :     // aTargetElement:  the element in the document whose 'id' attribute
    3352             :     //                  matches a toplevel node in our overlay.
    3353             :     // aOverlayElement: the element in the overlay document that matches
    3354             :     //                  an element in the actual document.
    3355             :     // aNotify:         whether or not content manipulation methods should
    3356             :     //                  use the aNotify parameter. After the initial
    3357             :     //                  reflow (i.e. in the dynamic overlay merge case),
    3358             :     //                  we want all the content manipulation methods we
    3359             :     //                  call to notify so that frames are constructed
    3360             :     //                  etc. Otherwise do not, since that's during initial
    3361             :     //                  document construction before StartLayout has been
    3362             :     //                  called which will do everything for us.
    3363             :     //
    3364             :     // This function merges the tree from the overlay into the tree in
    3365             :     // the document, overwriting attributes and appending child content
    3366             :     // nodes appropriately. (See XUL overlay reference for details)
    3367             : 
    3368             :     nsresult rv;
    3369             : 
    3370             :     // Merge attributes from the overlay content node to that of the
    3371             :     // actual document.
    3372             :     uint32_t i;
    3373             :     const nsAttrName* name;
    3374           0 :     for (i = 0; (name = aOverlayElement->GetAttrNameAt(i)); ++i) {
    3375             :         // We don't want to swap IDs, they should be the same.
    3376           0 :         if (name->Equals(nsGkAtoms::id))
    3377           0 :             continue;
    3378             : 
    3379             :         // In certain cases merging command or observes is unsafe, so don't.
    3380           0 :         if (!aNotify) {
    3381           0 :             if (aTargetElement->NodeInfo()->Equals(nsGkAtoms::observes,
    3382             :                                                    kNameSpaceID_XUL))
    3383             :                 continue;
    3384             : 
    3385           0 :             if (name->Equals(nsGkAtoms::observes) &&
    3386           0 :                 aTargetElement->HasAttr(kNameSpaceID_None, nsGkAtoms::observes))
    3387             :                 continue;
    3388             : 
    3389           0 :             if (name->Equals(nsGkAtoms::command) &&
    3390           0 :                 aTargetElement->HasAttr(kNameSpaceID_None, nsGkAtoms::command) &&
    3391           0 :                 !aTargetElement->NodeInfo()->Equals(nsGkAtoms::key,
    3392           0 :                                                     kNameSpaceID_XUL) &&
    3393           0 :                 !aTargetElement->NodeInfo()->Equals(nsGkAtoms::menuitem,
    3394             :                                                    kNameSpaceID_XUL))
    3395             :                 continue;
    3396             :         }
    3397             : 
    3398           0 :         int32_t nameSpaceID = name->NamespaceID();
    3399           0 :         nsAtom* attr = name->LocalName();
    3400           0 :         nsAtom* prefix = name->GetPrefix();
    3401             : 
    3402           0 :         nsAutoString value;
    3403           0 :         aOverlayElement->GetAttr(nameSpaceID, attr, value);
    3404             : 
    3405             :         // Element in the overlay has the 'removeelement' attribute set
    3406             :         // so remove it from the actual document.
    3407           0 :         if (attr == nsGkAtoms::removeelement && value.EqualsLiteral("true")) {
    3408           0 :             nsCOMPtr<nsINode> parent = aTargetElement->GetParentNode();
    3409           0 :             if (!parent) return NS_ERROR_FAILURE;
    3410           0 :             parent->RemoveChildNode(aTargetElement, true);
    3411           0 :             return NS_OK;
    3412             :         }
    3413             : 
    3414           0 :         rv = aTargetElement->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
    3415           0 :         if (!NS_FAILED(rv) && !aNotify) {
    3416           0 :             rv = mDocument->BroadcastAttributeChangeFromOverlay(
    3417           0 :                     aTargetElement, nameSpaceID, attr, prefix, value);
    3418             :         }
    3419           0 :         if (NS_FAILED(rv)) return rv;
    3420             :     }
    3421             : 
    3422             : 
    3423             :     // Walk our child nodes, looking for elements that have the 'id'
    3424             :     // attribute set. If we find any, we must do a parent check in the
    3425             :     // actual document to ensure that the structure matches that of
    3426             :     // the actual document. If it does, we can call ourselves and attempt
    3427             :     // to merge inside that subtree. If not, we just append the tree to
    3428             :     // the parent like any other.
    3429             : 
    3430           0 :     uint32_t childCount = aOverlayElement->GetChildCount();
    3431             : 
    3432             :     // This must be a strong reference since it will be the only
    3433             :     // reference to a content object during part of this loop.
    3434           0 :     nsCOMPtr<nsIContent> currContent;
    3435             : 
    3436           0 :     for (i = 0; i < childCount; ++i) {
    3437           0 :         currContent = aOverlayElement->GetFirstChild();
    3438             : 
    3439           0 :         nsAtom *idAtom = currContent->GetID();
    3440             : 
    3441           0 :         Element* elementInDocument = nullptr;
    3442           0 :         if (idAtom) {
    3443           0 :             nsDependentAtomString id(idAtom);
    3444             : 
    3445           0 :             if (!id.IsEmpty()) {
    3446           0 :                 nsIDocument *doc = aTargetElement->GetUncomposedDoc();
    3447             :                 //XXXsmaug should we use ShadowRoot::GetElementById()
    3448             :                 //         if doc is null?
    3449           0 :                 if (!doc) return NS_ERROR_FAILURE;
    3450             : 
    3451           0 :                 elementInDocument = doc->GetElementById(id);
    3452             :             }
    3453             :         }
    3454             : 
    3455             :         // The item has an 'id' attribute set, and we need to check with
    3456             :         // the actual document to see if an item with this id exists at
    3457             :         // this locale. If so, we want to merge the subtree under that
    3458             :         // node. Otherwise, we just do an append as if the element had
    3459             :         // no id attribute.
    3460           0 :         if (elementInDocument) {
    3461             :             // Given two parents, aTargetElement and aOverlayElement, we want
    3462             :             // to call merge on currContent if we find an associated
    3463             :             // node in the document with the same id as currContent that
    3464             :             // also has aTargetNode as its parent.
    3465             : 
    3466           0 :             nsIContent* elementParent = elementInDocument->GetParent();
    3467             : 
    3468           0 :             nsAtom *parentID = elementParent->GetID();
    3469           0 :             if (parentID && aTargetElement->GetID() == parentID) {
    3470             :                 // The element matches. "Go Deep!"
    3471             :                 //
    3472             :                 // Note that currContent is necessarily an element, because
    3473             :                 // elementInDocument can only be non-null when currContent has a
    3474             :                 // non-null ID.
    3475           0 :                 rv = Merge(elementInDocument, currContent->AsElement(), aNotify);
    3476           0 :                 if (NS_FAILED(rv)) return rv;
    3477           0 :                 nsIContent* firstChild = aOverlayElement->GetFirstChild();
    3478           0 :                 if (firstChild) {
    3479           0 :                   aOverlayElement->RemoveChildNode(firstChild, false);
    3480             :                 }
    3481             : 
    3482             :                 continue;
    3483             :             }
    3484             :         }
    3485             : 
    3486           0 :         nsIContent* firstChild = aOverlayElement->GetFirstChild();
    3487           0 :         if (firstChild) {
    3488           0 :           aOverlayElement->RemoveChildNode(firstChild, false);
    3489             :         }
    3490             : 
    3491           0 :         rv = InsertElement(aTargetElement, currContent, aNotify);
    3492           0 :         if (NS_FAILED(rv)) return rv;
    3493             :     }
    3494             : 
    3495             :     return NS_OK;
    3496             : }
    3497             : 
    3498             : 
    3499             : 
    3500           0 : XULDocument::OverlayForwardReference::~OverlayForwardReference()
    3501             : {
    3502           0 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
    3503           0 :         nsAutoString id;
    3504           0 :         mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
    3505             : 
    3506           0 :         nsAutoCString idC;
    3507           0 :         LossyCopyUTF16toASCII(id, idC);
    3508             : 
    3509           0 :         nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI();
    3510             : 
    3511           0 :         nsCOMPtr<nsIURI> docURI;
    3512           0 :         mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI));
    3513             : 
    3514           0 :         MOZ_LOG(gXULLog, LogLevel::Warning,
    3515             :                ("xul: %s overlay failed to resolve '%s' in %s",
    3516             :                 protoURI->GetSpecOrDefault().get(), idC.get(),
    3517             :                 docURI ? docURI->GetSpecOrDefault().get() : ""));
    3518             :     }
    3519           0 : }
    3520             : 
    3521             : 
    3522             : //----------------------------------------------------------------------
    3523             : //
    3524             : // XULDocument::BroadcasterHookup
    3525             : //
    3526             : 
    3527             : nsForwardReference::Result
    3528           6 : XULDocument::BroadcasterHookup::Resolve()
    3529             : {
    3530             :     nsresult rv;
    3531             : 
    3532             :     bool listener;
    3533          12 :     rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
    3534           6 :     if (NS_FAILED(rv)) return eResolve_Error;
    3535             : 
    3536           6 :     return mResolved ? eResolve_Succeeded : eResolve_Later;
    3537             : }
    3538             : 
    3539             : 
    3540           1 : XULDocument::BroadcasterHookup::~BroadcasterHookup()
    3541             : {
    3542           6 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
    3543             :         // Tell the world we failed
    3544             : 
    3545           0 :         nsAutoString broadcasterID;
    3546           0 :         nsAutoString attribute;
    3547             : 
    3548           0 :         if (mObservesElement->IsXULElement(nsGkAtoms::observes)) {
    3549           0 :             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID);
    3550           0 :             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute);
    3551             :         }
    3552             :         else {
    3553           0 :             mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID);
    3554           0 :             attribute.Assign('*');
    3555             :         }
    3556             : 
    3557           0 :         nsAutoCString attributeC,broadcasteridC;
    3558           0 :         LossyCopyUTF16toASCII(attribute, attributeC);
    3559           0 :         LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
    3560           0 :         MOZ_LOG(gXULLog, LogLevel::Warning,
    3561             :                ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
    3562             :                 nsAtomCString(mObservesElement->NodeInfo()->NameAtom()).get(),
    3563             :                 attributeC.get(),
    3564             :                 broadcasteridC.get()));
    3565             :     }
    3566          18 : }
    3567             : 
    3568             : //----------------------------------------------------------------------
    3569             : 
    3570             : nsresult
    3571           0 : XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
    3572             :                                                  int32_t aNameSpaceID,
    3573             :                                                  nsAtom* aAttribute,
    3574             :                                                  nsAtom* aPrefix,
    3575             :                                                  const nsAString& aValue)
    3576             : {
    3577           0 :     nsresult rv = NS_OK;
    3578             : 
    3579           0 :     if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
    3580             :         return rv;
    3581             : 
    3582           0 :     if (!aNode->IsElement())
    3583             :         return rv;
    3584             : 
    3585             :     auto entry = static_cast<BroadcasterMapEntry*>
    3586           0 :                             (mBroadcasterMap->Search(aNode->AsElement()));
    3587           0 :     if (!entry)
    3588             :         return rv;
    3589             : 
    3590             :     // We've got listeners: push the value.
    3591           0 :     for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
    3592           0 :         BroadcastListener* bl = entry->mListeners[i];
    3593             : 
    3594           0 :         if ((bl->mAttribute != aAttribute) &&
    3595           0 :             (bl->mAttribute != nsGkAtoms::_asterisk))
    3596           0 :             continue;
    3597             : 
    3598           0 :         nsCOMPtr<Element> l = do_QueryReferent(bl->mListener);
    3599           0 :         if (l) {
    3600           0 :             rv = l->SetAttr(aNameSpaceID, aAttribute,
    3601           0 :                             aPrefix, aValue, false);
    3602           0 :             if (NS_FAILED(rv)) return rv;
    3603             :         }
    3604             :     }
    3605             :     return rv;
    3606             : }
    3607             : 
    3608             : nsresult
    3609        2281 : XULDocument::FindBroadcaster(Element* aElement,
    3610             :                              Element** aListener,
    3611             :                              nsString& aBroadcasterID,
    3612             :                              nsString& aAttribute,
    3613             :                              Element** aBroadcaster)
    3614             : {
    3615        4562 :     mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
    3616        2281 :     *aListener = nullptr;
    3617           1 :     *aBroadcaster = nullptr;
    3618             : 
    3619        4562 :     if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
    3620             :         // It's an <observes> element, which means that the actual
    3621             :         // listener is the _parent_ node. This element should have an
    3622             :         // 'element' attribute that specifies the ID of the
    3623             :         // broadcaster element, and an 'attribute' element, which
    3624             :         // specifies the name of the attribute to observe.
    3625           1 :         nsIContent* parent = aElement->GetParent();
    3626           1 :         if (!parent) {
    3627             :              // <observes> is the root element
    3628             :             return NS_FINDBROADCASTER_NOT_FOUND;
    3629             :         }
    3630             : 
    3631             :         // If we're still parented by an 'overlay' tag, then we haven't
    3632             :         // made it into the real document yet. Defer hookup.
    3633          66 :         if (parent->NodeInfo()->Equals(nsGkAtoms::overlay,
    3634             :                                        kNameSpaceID_XUL)) {
    3635             :             return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
    3636             :         }
    3637             : 
    3638           0 :         *aListener = Element::FromNode(parent);
    3639          22 :         NS_IF_ADDREF(*aListener);
    3640             : 
    3641          22 :         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
    3642          22 :         if (aBroadcasterID.IsEmpty()) {
    3643             :             return NS_FINDBROADCASTER_NOT_FOUND;
    3644             :         }
    3645          22 :         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
    3646             :     }
    3647             :     else {
    3648             :         // It's a generic element, which means that we'll use the
    3649             :         // value of the 'observes' attribute to determine the ID of
    3650             :         // the broadcaster element, and we'll watch _all_ of its
    3651             :         // values.
    3652        2259 :         aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
    3653             : 
    3654             :         // Bail if there's no aBroadcasterID
    3655        2259 :         if (aBroadcasterID.IsEmpty()) {
    3656             :             // Try the command attribute next.
    3657        2188 :             aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
    3658           1 :             if (!aBroadcasterID.IsEmpty()) {
    3659             :                 // We've got something in the command attribute.  We
    3660             :                 // only treat this as a normal broadcaster if we are
    3661             :                 // not a menuitem or a key.
    3662             : 
    3663           1 :                 if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
    3664           1 :                     ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
    3665             :                 return NS_FINDBROADCASTER_NOT_FOUND;
    3666             :               }
    3667             :             }
    3668             :             else {
    3669             :               return NS_FINDBROADCASTER_NOT_FOUND;
    3670             :             }
    3671             :         }
    3672             : 
    3673           1 :         *aListener = aElement;
    3674           1 :         NS_ADDREF(*aListener);
    3675             : 
    3676         114 :         aAttribute.Assign('*');
    3677             :     }
    3678             : 
    3679             :     // Make sure we got a valid listener.
    3680         136 :     NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
    3681             : 
    3682             :     // Try to find the broadcaster element in the document.
    3683         136 :     *aBroadcaster = GetElementById(aBroadcasterID);
    3684             : 
    3685             :     // If we can't find the broadcaster, then we'll need to defer the
    3686             :     // hookup. We may need to resolve some of the other overlays
    3687             :     // first.
    3688           0 :     if (! *aBroadcaster) {
    3689             :         return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
    3690             :     }
    3691             : 
    3692         130 :     NS_ADDREF(*aBroadcaster);
    3693             : 
    3694         130 :     return NS_FINDBROADCASTER_FOUND;
    3695             : }
    3696             : 
    3697             : nsresult
    3698           0 : XULDocument::CheckBroadcasterHookup(Element* aElement,
    3699             :                                     bool* aNeedsHookup,
    3700             :                                     bool* aDidResolve)
    3701             : {
    3702             :     // Resolve a broadcaster hookup. Look at the element that we're
    3703             :     // trying to resolve: it could be an '<observes>' element, or just
    3704             :     // a vanilla element with an 'observes' attribute on it.
    3705             :     nsresult rv;
    3706             : 
    3707        2213 :     *aDidResolve = false;
    3708             : 
    3709        4426 :     nsCOMPtr<Element> listener;
    3710           0 :     nsAutoString broadcasterID;
    3711           0 :     nsAutoString attribute;
    3712        4426 :     nsCOMPtr<Element> broadcaster;
    3713             : 
    3714           0 :     rv = FindBroadcaster(aElement, getter_AddRefs(listener),
    3715        6639 :                          broadcasterID, attribute, getter_AddRefs(broadcaster));
    3716        2213 :     switch (rv) {
    3717             :         case NS_FINDBROADCASTER_NOT_FOUND:
    3718        2084 :             *aNeedsHookup = false;
    3719        2084 :             return NS_OK;
    3720             :         case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
    3721           6 :             *aNeedsHookup = true;
    3722           6 :             return NS_OK;
    3723             :         case NS_FINDBROADCASTER_FOUND:
    3724             :             break;
    3725             :         default:
    3726             :             return rv;
    3727             :     }
    3728             : 
    3729           0 :     NS_ENSURE_ARG(broadcaster && listener);
    3730           0 :     ErrorResult domRv;
    3731         123 :     AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
    3732         246 :     if (domRv.Failed()) {
    3733           0 :         return domRv.StealNSResult();
    3734             :     }
    3735             : 
    3736             :     // Tell the world we succeeded
    3737         123 :     if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
    3738             :         nsCOMPtr<nsIContent> content =
    3739           0 :             do_QueryInterface(listener);
    3740             : 
    3741           0 :         NS_ASSERTION(content != nullptr, "not an nsIContent");
    3742           0 :         if (! content)
    3743           0 :             return rv;
    3744             : 
    3745           0 :         nsAutoCString attributeC,broadcasteridC;
    3746           0 :         LossyCopyUTF16toASCII(attribute, attributeC);
    3747           0 :         LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
    3748           0 :         MOZ_LOG(gXULLog, LogLevel::Debug,
    3749             :                ("xul: broadcaster hookup <%s attribute='%s'> to %s",
    3750             :                 nsAtomCString(content->NodeInfo()->NameAtom()).get(),
    3751             :                 attributeC.get(),
    3752             :                 broadcasteridC.get()));
    3753             :     }
    3754             : 
    3755           0 :     *aNeedsHookup = false;
    3756         123 :     *aDidResolve = true;
    3757         123 :     return NS_OK;
    3758             : }
    3759             : 
    3760             : nsresult
    3761           0 : XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, bool aNotify)
    3762             : {
    3763             :     // Insert aChild appropriately into aParent, accounting for a
    3764             :     // 'pos' attribute set on aChild.
    3765             : 
    3766           0 :     nsAutoString posStr;
    3767           0 :     bool wasInserted = false;
    3768             : 
    3769             :     // insert after an element of a given id
    3770           0 :     if (Element* element = Element::FromNode(aChild)) {
    3771           0 :         element->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr);
    3772             :     }
    3773             : 
    3774           0 :     bool isInsertAfter = true;
    3775           0 :     if (posStr.IsEmpty()) {
    3776           0 :         if (Element* element = Element::FromNode(aChild)) {
    3777           0 :             element->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr);
    3778             :         }
    3779             :         isInsertAfter = false;
    3780             :     }
    3781             : 
    3782           0 :     if (!posStr.IsEmpty()) {
    3783           0 :         nsIDocument *document = aParent->OwnerDoc();
    3784             : 
    3785           0 :         nsIContent *content = nullptr;
    3786             : 
    3787           0 :         char* str = ToNewCString(posStr);
    3788             :         char* rest;
    3789           0 :         char* token = nsCRT::strtok(str, ", ", &rest);
    3790             : 
    3791           0 :         while (token) {
    3792           0 :             content = document->GetElementById(NS_ConvertASCIItoUTF16(token));
    3793           0 :             if (content)
    3794             :                 break;
    3795             : 
    3796           0 :             token = nsCRT::strtok(rest, ", ", &rest);
    3797             :         }
    3798           0 :         free(str);
    3799             : 
    3800           0 :         if (content) {
    3801           0 :             if (content->GetParent() == aParent) {
    3802             :                 nsIContent* nodeToInsertBefore =
    3803           0 :                   isInsertAfter ? content->GetNextSibling() : content;
    3804           0 :                 nsresult rv = aParent->InsertChildBefore(aChild,
    3805             :                                                          nodeToInsertBefore,
    3806           0 :                                                          aNotify);
    3807           0 :                 if (NS_FAILED(rv)) {
    3808           0 :                     return rv;
    3809             :                 }
    3810             : 
    3811             :                 wasInserted = true;
    3812             :             }
    3813             :         }
    3814             :     }
    3815             : 
    3816           0 :     if (!wasInserted) {
    3817           0 :         if (aChild->IsElement() &&
    3818           0 :             aChild->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr) &&
    3819           0 :             !posStr.IsEmpty()) {
    3820             :             nsresult rv;
    3821             :             // Positions are one-indexed.
    3822           0 :             int32_t pos = posStr.ToInteger(&rv);
    3823             :             // Note: if the insertion index (which is |pos - 1|) would be less
    3824             :             // than 0 or greater than the number of children aParent has, then
    3825             :             // don't insert, since the position is bogus.  Just skip on to
    3826             :             // appending.
    3827           0 :             if (NS_SUCCEEDED(rv) && pos > 0 &&
    3828           0 :                 uint32_t(pos - 1) <= aParent->GetChildCount()) {
    3829             :                 nsIContent* nodeToInsertBefore =
    3830           0 :                     aParent->GetChildAt_Deprecated(pos - 1);
    3831           0 :                 rv = aParent->InsertChildBefore(aChild, nodeToInsertBefore,
    3832           0 :                                                 aNotify);
    3833           0 :                 if (NS_SUCCEEDED(rv))
    3834           0 :                     wasInserted = true;
    3835             :                 // If the insertion fails, then we should still
    3836             :                 // attempt an append.  Thus, rather than returning rv
    3837             :                 // immediately, we fall through to the final
    3838             :                 // "catch-all" case that just does an AppendChildTo.
    3839             :             }
    3840             :         }
    3841             :     }
    3842             : 
    3843           0 :     if (!wasInserted) {
    3844           0 :         return aParent->AppendChildTo(aChild, aNotify);
    3845             :     }
    3846             :     return NS_OK;
    3847             : }
    3848             : 
    3849             : //----------------------------------------------------------------------
    3850             : //
    3851             : // CachedChromeStreamListener
    3852             : //
    3853             : 
    3854           1 : XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
    3855             :     : mDocument(aDocument),
    3856           3 :       mProtoLoaded(aProtoLoaded)
    3857             : {
    3858           1 : }
    3859             : 
    3860             : 
    3861           1 : XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
    3862             : {
    3863           1 : }
    3864             : 
    3865             : 
    3866          26 : NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
    3867             :                   nsIRequestObserver, nsIStreamListener)
    3868             : 
    3869             : NS_IMETHODIMP
    3870           1 : XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
    3871             :                                                         nsISupports* acontext)
    3872             : {
    3873           1 :     return NS_ERROR_PARSED_DATA_CACHED;
    3874             : }
    3875             : 
    3876             : 
    3877             : NS_IMETHODIMP
    3878           1 : XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
    3879             :                                                        nsISupports* aContext,
    3880             :                                                        nsresult aStatus)
    3881             : {
    3882           1 :     if (! mProtoLoaded)
    3883             :         return NS_OK;
    3884             : 
    3885           1 :     return mDocument->OnPrototypeLoadDone(true);
    3886             : }
    3887             : 
    3888             : 
    3889             : NS_IMETHODIMP
    3890           0 : XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
    3891             :                                                          nsISupports* aContext,
    3892             :                                                          nsIInputStream* aInStr,
    3893             :                                                          uint64_t aSourceOffset,
    3894             :                                                          uint32_t aCount)
    3895             : {
    3896           0 :     NS_NOTREACHED("CachedChromeStream doesn't receive data");
    3897           0 :     return NS_ERROR_UNEXPECTED;
    3898             : }
    3899             : 
    3900             : //----------------------------------------------------------------------
    3901             : //
    3902             : // ParserObserver
    3903             : //
    3904             : 
    3905           0 : XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument,
    3906           0 :                                             nsXULPrototypeDocument* aPrototype)
    3907           0 :     : mDocument(aDocument), mPrototype(aPrototype)
    3908             : {
    3909           0 : }
    3910             : 
    3911           0 : XULDocument::ParserObserver::~ParserObserver()
    3912             : {
    3913           0 : }
    3914             : 
    3915           0 : NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver)
    3916             : 
    3917             : NS_IMETHODIMP
    3918           0 : XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
    3919             :                                             nsISupports* aContext)
    3920             : {
    3921             :     // Guard against buggy channels calling OnStartRequest multiple times.
    3922           0 :     if (mPrototype) {
    3923           0 :         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
    3924           0 :         nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    3925           0 :         if (channel && secMan) {
    3926           0 :             nsCOMPtr<nsIPrincipal> principal;
    3927           0 :             secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
    3928             : 
    3929           0 :             principal = mDocument->MaybeDowngradePrincipal(principal);
    3930             :             // Failure there is ok -- it'll just set a (safe) null principal
    3931           0 :             mPrototype->SetDocumentPrincipal(principal);
    3932             :         }
    3933             : 
    3934             :         // Make sure to avoid cycles
    3935           0 :         mPrototype = nullptr;
    3936             :     }
    3937             : 
    3938           0 :     return NS_OK;
    3939             : }
    3940             : 
    3941             : NS_IMETHODIMP
    3942           0 : XULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
    3943             :                                            nsISupports* aContext,
    3944             :                                            nsresult aStatus)
    3945             : {
    3946           0 :     nsresult rv = NS_OK;
    3947             : 
    3948           0 :     if (NS_FAILED(aStatus)) {
    3949             :         // If an overlay load fails, we need to nudge the prototype
    3950             :         // walk along.
    3951           0 :         nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
    3952           0 :         if (aChannel) {
    3953           0 :             nsCOMPtr<nsIURI> uri;
    3954           0 :             aChannel->GetOriginalURI(getter_AddRefs(uri));
    3955           0 :             if (uri) {
    3956           0 :                 mDocument->ReportMissingOverlay(uri);
    3957             :             }
    3958             :         }
    3959             : 
    3960           0 :         rv = mDocument->ResumeWalk();
    3961             :     }
    3962             : 
    3963             :     // Drop the reference to the document to break cycle between the
    3964             :     // document, the parser, the content sink, and the parser
    3965             :     // observer.
    3966           0 :     mDocument = nullptr;
    3967             : 
    3968           0 :     return rv;
    3969             : }
    3970             : 
    3971             : already_AddRefed<nsPIWindowRoot>
    3972           1 : XULDocument::GetWindowRoot()
    3973             : {
    3974           1 :   if (!mDocumentContainer) {
    3975             :     return nullptr;
    3976             :   }
    3977             : 
    3978           1 :     nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
    3979           1 :     return piWin ? piWin->GetTopWindowRoot() : nullptr;
    3980             : }
    3981             : 
    3982             : bool
    3983           1 : XULDocument::IsDocumentRightToLeft()
    3984             : {
    3985             :     // setting the localedir attribute on the root element forces a
    3986             :     // specific direction for the document.
    3987           1 :     Element* element = GetRootElement();
    3988           3 :     if (element) {
    3989             :         static Element::AttrValuesArray strings[] =
    3990             :             {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
    3991           3 :         switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
    3992             :                                          strings, eCaseMatters)) {
    3993             :             case 0: return false;
    3994           0 :             case 1: return true;
    3995             :             default: break; // otherwise, not a valid value, so fall through
    3996             :         }
    3997             :     }
    3998             : 
    3999             :     // otherwise, get the locale from the chrome registry and
    4000             :     // look up the intl.uidirection.<locale> preference
    4001             :     nsCOMPtr<nsIXULChromeRegistry> reg =
    4002           6 :         mozilla::services::GetXULChromeRegistryService();
    4003           1 :     if (!reg)
    4004             :         return false;
    4005             : 
    4006           3 :     nsAutoCString package;
    4007             :     bool isChrome;
    4008           3 :     if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
    4009             :         isChrome) {
    4010           1 :         mDocumentURI->GetHostPort(package);
    4011             :     }
    4012             :     else {
    4013             :         // use the 'global' package for about and resource uris.
    4014             :         // otherwise, just default to left-to-right.
    4015             :         bool isAbout, isResource;
    4016           0 :         if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
    4017             :             isAbout) {
    4018           0 :             package.AssignLiteral("global");
    4019             :         }
    4020           0 :         else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
    4021             :             isResource) {
    4022           0 :             package.AssignLiteral("global");
    4023             :         }
    4024             :         else {
    4025           0 :             return false;
    4026             :         }
    4027             :     }
    4028             : 
    4029           3 :     bool isRTL = false;
    4030           3 :     reg->IsLocaleRTL(package, &isRTL);
    4031           3 :     return isRTL;
    4032             : }
    4033             : 
    4034             : void
    4035           3 : XULDocument::ResetDocumentDirection()
    4036             : {
    4037           3 :     DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
    4038           1 : }
    4039             : 
    4040             : void
    4041           0 : XULDocument::DirectionChanged(const char* aPrefName, void* aData)
    4042             : {
    4043             :   // Reset the direction and restyle the document if necessary.
    4044           0 :   XULDocument* doc = (XULDocument *)aData;
    4045           0 :   if (doc) {
    4046           0 :       doc->ResetDocumentDirection();
    4047             :   }
    4048           0 : }
    4049             : 
    4050             : nsIDocument::DocumentTheme
    4051           0 : XULDocument::GetDocumentLWTheme()
    4052             : {
    4053           2 :     if (mDocLWTheme == Doc_Theme_Uninitialized) {
    4054           2 :         mDocLWTheme = ThreadSafeGetDocumentLWTheme();
    4055             :     }
    4056           2 :     return mDocLWTheme;
    4057             : }
    4058             : 
    4059             : nsIDocument::DocumentTheme
    4060           0 : XULDocument::ThreadSafeGetDocumentLWTheme() const
    4061             : {
    4062         777 :     if (mDocLWTheme != Doc_Theme_Uninitialized) {
    4063             :         return mDocLWTheme;
    4064             :     }
    4065             : 
    4066           0 :     DocumentTheme theme = Doc_Theme_None; // No lightweight theme by default
    4067         516 :     Element* element = GetRootElement();
    4068         516 :     nsAutoString hasLWTheme;
    4069        1032 :     if (element &&
    4070         516 :         element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) &&
    4071         516 :         !(hasLWTheme.IsEmpty()) &&
    4072           0 :         hasLWTheme.EqualsLiteral("true")) {
    4073           0 :         theme = Doc_Theme_Neutral;
    4074           0 :         nsAutoString lwTheme;
    4075           0 :         element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
    4076           0 :         if (!(lwTheme.IsEmpty())) {
    4077           0 :             if (lwTheme.EqualsLiteral("dark"))
    4078             :                 theme = Doc_Theme_Dark;
    4079           0 :             else if (lwTheme.EqualsLiteral("bright"))
    4080           0 :                 theme = Doc_Theme_Bright;
    4081             :         }
    4082             :     }
    4083         516 :     return theme;
    4084             : }
    4085             : 
    4086             : JSObject*
    4087           3 : XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
    4088             : {
    4089           3 :   return XULDocumentBinding::Wrap(aCx, this, aGivenProto);
    4090             : }
    4091             : 
    4092             : } // namespace dom
    4093             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952