content/xul/document/src/XULDocument.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/xul/document/src/XULDocument.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4819 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=4 sw=4 et tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 +
    1.12 +  An implementation for the XUL document. This implementation serves
    1.13 +  as the basis for generating an NGLayout content model.
    1.14 +
    1.15 +  Notes
    1.16 +  -----
    1.17 +
    1.18 +  1. We do some monkey business in the document observer methods to`
    1.19 +     keep the element map in sync for HTML elements. Why don't we just
    1.20 +     do it for _all_ elements? Well, in the case of XUL elements,
    1.21 +     which may be lazily created during frame construction, the
    1.22 +     document observer methods will never be called because we'll be
    1.23 +     adding the XUL nodes into the content model "quietly".
    1.24 +
    1.25 +*/
    1.26 +
    1.27 +#include "mozilla/ArrayUtils.h"
    1.28 +
    1.29 +// Note the ALPHABETICAL ORDERING
    1.30 +#include "XULDocument.h"
    1.31 +
    1.32 +#include "nsError.h"
    1.33 +#include "nsIBoxObject.h"
    1.34 +#include "nsIChromeRegistry.h"
    1.35 +#include "nsView.h"
    1.36 +#include "nsViewManager.h"
    1.37 +#include "nsIContentViewer.h"
    1.38 +#include "nsIDOMXULElement.h"
    1.39 +#include "nsIRDFNode.h"
    1.40 +#include "nsIRDFRemoteDataSource.h"
    1.41 +#include "nsIRDFService.h"
    1.42 +#include "nsIStreamListener.h"
    1.43 +#include "nsITimer.h"
    1.44 +#include "nsDocShell.h"
    1.45 +#include "nsGkAtoms.h"
    1.46 +#include "nsXMLContentSink.h"
    1.47 +#include "nsXULContentSink.h"
    1.48 +#include "nsXULContentUtils.h"
    1.49 +#include "nsIXULOverlayProvider.h"
    1.50 +#include "nsNetUtil.h"
    1.51 +#include "nsParserCIID.h"
    1.52 +#include "nsPIBoxObject.h"
    1.53 +#include "nsRDFCID.h"
    1.54 +#include "nsILocalStore.h"
    1.55 +#include "nsXPIDLString.h"
    1.56 +#include "nsPIDOMWindow.h"
    1.57 +#include "nsPIWindowRoot.h"
    1.58 +#include "nsXULCommandDispatcher.h"
    1.59 +#include "nsXULElement.h"
    1.60 +#include "prlog.h"
    1.61 +#include "rdf.h"
    1.62 +#include "nsIFrame.h"
    1.63 +#include "nsXBLService.h"
    1.64 +#include "nsCExternalHandlerService.h"
    1.65 +#include "nsMimeTypes.h"
    1.66 +#include "nsIObjectInputStream.h"
    1.67 +#include "nsIObjectOutputStream.h"
    1.68 +#include "nsContentList.h"
    1.69 +#include "nsIScriptGlobalObject.h"
    1.70 +#include "nsIScriptSecurityManager.h"
    1.71 +#include "nsNodeInfoManager.h"
    1.72 +#include "nsContentCreatorFunctions.h"
    1.73 +#include "nsContentUtils.h"
    1.74 +#include "nsIParser.h"
    1.75 +#include "nsCharsetSource.h"
    1.76 +#include "nsIParserService.h"
    1.77 +#include "nsCSSStyleSheet.h"
    1.78 +#include "mozilla/css/Loader.h"
    1.79 +#include "nsIScriptError.h"
    1.80 +#include "nsIStyleSheetLinkingElement.h"
    1.81 +#include "nsIObserverService.h"
    1.82 +#include "nsNodeUtils.h"
    1.83 +#include "nsIDocShellTreeOwner.h"
    1.84 +#include "nsIXULWindow.h"
    1.85 +#include "nsXULPopupManager.h"
    1.86 +#include "nsCCUncollectableMarker.h"
    1.87 +#include "nsURILoader.h"
    1.88 +#include "mozilla/BasicEvents.h"
    1.89 +#include "mozilla/dom/Element.h"
    1.90 +#include "mozilla/dom/ProcessingInstruction.h"
    1.91 +#include "mozilla/dom/XULDocumentBinding.h"
    1.92 +#include "mozilla/EventDispatcher.h"
    1.93 +#include "mozilla/Preferences.h"
    1.94 +#include "nsTextNode.h"
    1.95 +#include "nsJSUtils.h"
    1.96 +#include "mozilla/dom/URL.h"
    1.97 +
    1.98 +using namespace mozilla;
    1.99 +using namespace mozilla::dom;
   1.100 +
   1.101 +//----------------------------------------------------------------------
   1.102 +//
   1.103 +// CIDs
   1.104 +//
   1.105 +
   1.106 +static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
   1.107 +
   1.108 +static bool IsOverlayAllowed(nsIURI* aURI)
   1.109 +{
   1.110 +    bool canOverlay = false;
   1.111 +    if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay)
   1.112 +        return true;
   1.113 +    if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay)
   1.114 +        return true;
   1.115 +    return false;
   1.116 +}
   1.117 +
   1.118 +//----------------------------------------------------------------------
   1.119 +//
   1.120 +// Miscellaneous Constants
   1.121 +//
   1.122 +
   1.123 +const nsForwardReference::Phase nsForwardReference::kPasses[] = {
   1.124 +    nsForwardReference::eConstruction,
   1.125 +    nsForwardReference::eHookup,
   1.126 +    nsForwardReference::eDone
   1.127 +};
   1.128 +
   1.129 +const uint32_t kMaxAttrNameLength = 512;
   1.130 +const uint32_t kMaxAttributeLength = 4096;
   1.131 +
   1.132 +//----------------------------------------------------------------------
   1.133 +//
   1.134 +// Statics
   1.135 +//
   1.136 +
   1.137 +int32_t XULDocument::gRefCnt = 0;
   1.138 +
   1.139 +nsIRDFService* XULDocument::gRDFService;
   1.140 +nsIRDFResource* XULDocument::kNC_persist;
   1.141 +nsIRDFResource* XULDocument::kNC_attribute;
   1.142 +nsIRDFResource* XULDocument::kNC_value;
   1.143 +
   1.144 +PRLogModuleInfo* XULDocument::gXULLog;
   1.145 +
   1.146 +//----------------------------------------------------------------------
   1.147 +
   1.148 +struct BroadcasterMapEntry : public PLDHashEntryHdr {
   1.149 +    Element*         mBroadcaster; // [WEAK]
   1.150 +    nsSmallVoidArray mListeners;   // [OWNING] of BroadcastListener objects
   1.151 +};
   1.152 +
   1.153 +struct BroadcastListener {
   1.154 +    nsWeakPtr mListener;
   1.155 +    nsCOMPtr<nsIAtom> mAttribute;
   1.156 +};
   1.157 +
   1.158 +Element*
   1.159 +nsRefMapEntry::GetFirstElement()
   1.160 +{
   1.161 +    return static_cast<Element*>(mRefContentList.SafeElementAt(0));
   1.162 +}
   1.163 +
   1.164 +void
   1.165 +nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements)
   1.166 +{
   1.167 +    for (int32_t i = 0; i < mRefContentList.Count(); ++i) {
   1.168 +        aElements->AppendObject(static_cast<nsIContent*>(mRefContentList[i]));
   1.169 +    }
   1.170 +}
   1.171 +
   1.172 +bool
   1.173 +nsRefMapEntry::AddElement(Element* aElement)
   1.174 +{
   1.175 +    if (mRefContentList.IndexOf(aElement) >= 0)
   1.176 +        return true;
   1.177 +    return mRefContentList.AppendElement(aElement);
   1.178 +}
   1.179 +
   1.180 +bool
   1.181 +nsRefMapEntry::RemoveElement(Element* aElement)
   1.182 +{
   1.183 +    mRefContentList.RemoveElement(aElement);
   1.184 +    return mRefContentList.Count() == 0;
   1.185 +}
   1.186 +
   1.187 +//----------------------------------------------------------------------
   1.188 +//
   1.189 +// ctors & dtors
   1.190 +//
   1.191 +
   1.192 +namespace mozilla {
   1.193 +namespace dom {
   1.194 +
   1.195 +XULDocument::XULDocument(void)
   1.196 +    : XMLDocument("application/vnd.mozilla.xul+xml"),
   1.197 +      mDocLWTheme(Doc_Theme_Uninitialized),
   1.198 +      mState(eState_Master),
   1.199 +      mResolutionPhase(nsForwardReference::eStart)
   1.200 +{
   1.201 +    // NOTE! nsDocument::operator new() zeroes out all members, so don't
   1.202 +    // bother initializing members to 0.
   1.203 +
   1.204 +    // Override the default in nsDocument
   1.205 +    mCharacterSet.AssignLiteral("UTF-8");
   1.206 +
   1.207 +    mDefaultElementType = kNameSpaceID_XUL;
   1.208 +    mIsXUL = true;
   1.209 +
   1.210 +    mDelayFrameLoaderInitialization = true;
   1.211 +
   1.212 +    mAllowXULXBL = eTriTrue;
   1.213 +}
   1.214 +
   1.215 +XULDocument::~XULDocument()
   1.216 +{
   1.217 +    NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
   1.218 +        "unreferenced document still waiting for script source to load?");
   1.219 +
   1.220 +    // In case we failed somewhere early on and the forward observer
   1.221 +    // decls never got resolved.
   1.222 +    mForwardReferences.Clear();
   1.223 +    // Likewise for any references we have to IDs where we might
   1.224 +    // look for persisted data:
   1.225 +    mPersistenceIds.Clear();
   1.226 +
   1.227 +    // Destroy our broadcaster map.
   1.228 +    if (mBroadcasterMap) {
   1.229 +        PL_DHashTableDestroy(mBroadcasterMap);
   1.230 +    }
   1.231 +
   1.232 +    if (mLocalStore) {
   1.233 +        nsCOMPtr<nsIRDFRemoteDataSource> remote =
   1.234 +            do_QueryInterface(mLocalStore);
   1.235 +        if (remote)
   1.236 +            remote->Flush();
   1.237 +    }
   1.238 +
   1.239 +    delete mTemplateBuilderTable;
   1.240 +
   1.241 +    Preferences::UnregisterCallback(XULDocument::DirectionChanged,
   1.242 +                                    "intl.uidirection.", this);
   1.243 +
   1.244 +    if (--gRefCnt == 0) {
   1.245 +        NS_IF_RELEASE(gRDFService);
   1.246 +
   1.247 +        NS_IF_RELEASE(kNC_persist);
   1.248 +        NS_IF_RELEASE(kNC_attribute);
   1.249 +        NS_IF_RELEASE(kNC_value);
   1.250 +    }
   1.251 +
   1.252 +    if (mOffThreadCompileStringBuf) {
   1.253 +      js_free(mOffThreadCompileStringBuf);
   1.254 +    }
   1.255 +}
   1.256 +
   1.257 +} // namespace dom
   1.258 +} // namespace mozilla
   1.259 +
   1.260 +nsresult
   1.261 +NS_NewXULDocument(nsIXULDocument** result)
   1.262 +{
   1.263 +    NS_PRECONDITION(result != nullptr, "null ptr");
   1.264 +    if (! result)
   1.265 +        return NS_ERROR_NULL_POINTER;
   1.266 +
   1.267 +    XULDocument* doc = new XULDocument();
   1.268 +    if (! doc)
   1.269 +        return NS_ERROR_OUT_OF_MEMORY;
   1.270 +
   1.271 +    NS_ADDREF(doc);
   1.272 +
   1.273 +    nsresult rv;
   1.274 +    if (NS_FAILED(rv = doc->Init())) {
   1.275 +        NS_RELEASE(doc);
   1.276 +        return rv;
   1.277 +    }
   1.278 +
   1.279 +    *result = doc;
   1.280 +    return NS_OK;
   1.281 +}
   1.282 +
   1.283 +
   1.284 +namespace mozilla {
   1.285 +namespace dom {
   1.286 +
   1.287 +//----------------------------------------------------------------------
   1.288 +//
   1.289 +// nsISupports interface
   1.290 +//
   1.291 +
   1.292 +static PLDHashOperator
   1.293 +TraverseTemplateBuilders(nsISupports* aKey, nsIXULTemplateBuilder* aData,
   1.294 +                         void* aContext)
   1.295 +{
   1.296 +    nsCycleCollectionTraversalCallback *cb =
   1.297 +        static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   1.298 +
   1.299 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable key");
   1.300 +    cb->NoteXPCOMChild(aKey);
   1.301 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable value");
   1.302 +    cb->NoteXPCOMChild(aData);
   1.303 +
   1.304 +    return PL_DHASH_NEXT;
   1.305 +}
   1.306 +
   1.307 +static PLDHashOperator
   1.308 +TraverseObservers(nsIURI* aKey, nsIObserver* aData, void* aContext)
   1.309 +{
   1.310 +    nsCycleCollectionTraversalCallback *cb =
   1.311 +        static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   1.312 +
   1.313 +    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value");
   1.314 +    cb->NoteXPCOMChild(aData);
   1.315 +
   1.316 +    return PL_DHASH_NEXT;
   1.317 +}
   1.318 +
   1.319 +NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
   1.320 +
   1.321 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
   1.322 +    NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
   1.323 +                 "Shouldn't traverse XULDocument!");
   1.324 +    // XXX tmp->mForwardReferences?
   1.325 +    // XXX tmp->mContextStack?
   1.326 +
   1.327 +    // An element will only have a template builder as long as it's in the
   1.328 +    // document, so we'll traverse the table here instead of from the element.
   1.329 +    if (tmp->mTemplateBuilderTable)
   1.330 +        tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb);
   1.331 +
   1.332 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
   1.333 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
   1.334 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
   1.335 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes);
   1.336 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
   1.337 +
   1.338 +    if (tmp->mOverlayLoadObservers) {
   1.339 +        tmp->mOverlayLoadObservers->EnumerateRead(TraverseObservers, &cb);
   1.340 +    }
   1.341 +    if (tmp->mPendingOverlayLoadNotifications) {
   1.342 +        tmp->mPendingOverlayLoadNotifications->EnumerateRead(TraverseObservers, &cb);
   1.343 +    }
   1.344 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.345 +
   1.346 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
   1.347 +    delete tmp->mTemplateBuilderTable;
   1.348 +    tmp->mTemplateBuilderTable = nullptr;
   1.349 +
   1.350 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
   1.351 +    //XXX We should probably unlink all the objects we traverse.
   1.352 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.353 +
   1.354 +NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument)
   1.355 +NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
   1.356 +
   1.357 +
   1.358 +// QueryInterface implementation for XULDocument
   1.359 +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument)
   1.360 +    NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument,
   1.361 +                                 nsIDOMXULDocument, nsIStreamLoaderObserver,
   1.362 +                                 nsICSSLoaderObserver, nsIOffThreadScriptReceiver)
   1.363 +NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument)
   1.364 +
   1.365 +
   1.366 +//----------------------------------------------------------------------
   1.367 +//
   1.368 +// nsIDocument interface
   1.369 +//
   1.370 +
   1.371 +void
   1.372 +XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
   1.373 +{
   1.374 +    NS_NOTREACHED("Reset");
   1.375 +}
   1.376 +
   1.377 +void
   1.378 +XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
   1.379 +                        nsIPrincipal* aPrincipal)
   1.380 +{
   1.381 +    NS_NOTREACHED("ResetToURI");
   1.382 +}
   1.383 +
   1.384 +void
   1.385 +XULDocument::SetContentType(const nsAString& aContentType)
   1.386 +{
   1.387 +    NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
   1.388 +                 "xul-documents always has content-type application/vnd.mozilla.xul+xml");
   1.389 +    // Don't do anything, xul always has the mimetype
   1.390 +    // application/vnd.mozilla.xul+xml
   1.391 +}
   1.392 +
   1.393 +// This is called when the master document begins loading, whether it's
   1.394 +// being cached or not.
   1.395 +nsresult
   1.396 +XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
   1.397 +                               nsILoadGroup* aLoadGroup,
   1.398 +                               nsISupports* aContainer,
   1.399 +                               nsIStreamListener **aDocListener,
   1.400 +                               bool aReset, nsIContentSink* aSink)
   1.401 +{
   1.402 +#ifdef PR_LOGGING
   1.403 +    if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
   1.404 +
   1.405 +        nsCOMPtr<nsIURI> uri;
   1.406 +        nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
   1.407 +        if (NS_SUCCEEDED(rv)) {
   1.408 +            nsAutoCString urlspec;
   1.409 +            rv = uri->GetSpec(urlspec);
   1.410 +            if (NS_SUCCEEDED(rv)) {
   1.411 +                PR_LOG(gXULLog, PR_LOG_WARNING,
   1.412 +                       ("xul: load document '%s'", urlspec.get()));
   1.413 +            }
   1.414 +        }
   1.415 +    }
   1.416 +#endif
   1.417 +    // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
   1.418 +    // we'll possibly need to reset our content type afterwards.
   1.419 +    mStillWalking = true;
   1.420 +    mMayStartLayout = false;
   1.421 +    mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
   1.422 +
   1.423 +    mChannel = aChannel;
   1.424 +
   1.425 +    mHaveInputEncoding = true;
   1.426 +
   1.427 +    // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
   1.428 +    nsresult rv =
   1.429 +        NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
   1.430 +    NS_ENSURE_SUCCESS(rv, rv);
   1.431 +    
   1.432 +    ResetStylesheetsToURI(mDocumentURI);
   1.433 +
   1.434 +    RetrieveRelevantHeaders(aChannel);
   1.435 +
   1.436 +    // Look in the chrome cache: we've got this puppy loaded
   1.437 +    // already.
   1.438 +    nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
   1.439 +            nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
   1.440 +            nullptr;
   1.441 +
   1.442 +    // Same comment as nsChromeProtocolHandler::NewChannel and
   1.443 +    // XULDocument::ResumeWalk
   1.444 +    // - Ben Goodger
   1.445 +    //
   1.446 +    // We don't abort on failure here because there are too many valid
   1.447 +    // cases that can return failure, and the null-ness of |proto| is enough
   1.448 +    // to trigger the fail-safe parse-from-disk solution. Example failure cases
   1.449 +    // (for reference) include:
   1.450 +    //
   1.451 +    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
   1.452 +    //                         parse from disk
   1.453 +    // other: the startup cache file could not be found, probably
   1.454 +    //        due to being accessed before a profile has been selected (e.g.
   1.455 +    //        loading chrome for the profile manager itself). This must be
   1.456 +    //        parsed from disk.
   1.457 +
   1.458 +    if (proto) {
   1.459 +        // If we're racing with another document to load proto, wait till the
   1.460 +        // load has finished loading before trying to add cloned style sheets.
   1.461 +        // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
   1.462 +        // find all racing documents and notify them via OnPrototypeLoadDone,
   1.463 +        // which will add style sheet clones to each document.
   1.464 +        bool loaded;
   1.465 +        rv = proto->AwaitLoadDone(this, &loaded);
   1.466 +        if (NS_FAILED(rv)) return rv;
   1.467 +
   1.468 +        mMasterPrototype = mCurrentPrototype = proto;
   1.469 +
   1.470 +        // Set up the right principal on ourselves.
   1.471 +        SetPrincipal(proto->DocumentPrincipal());
   1.472 +
   1.473 +        // We need a listener, even if proto is not yet loaded, in which
   1.474 +        // event the listener's OnStopRequest method does nothing, and all
   1.475 +        // the interesting work happens below XULDocument::EndLoad, from
   1.476 +        // the call there to mCurrentPrototype->NotifyLoadDone().
   1.477 +        *aDocListener = new CachedChromeStreamListener(this, loaded);
   1.478 +        if (! *aDocListener)
   1.479 +            return NS_ERROR_OUT_OF_MEMORY;
   1.480 +    }
   1.481 +    else {
   1.482 +        bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
   1.483 +        bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
   1.484 +
   1.485 +
   1.486 +        // It's just a vanilla document load. Create a parser to deal
   1.487 +        // with the stream n' stuff.
   1.488 +
   1.489 +        nsCOMPtr<nsIParser> parser;
   1.490 +        rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
   1.491 +                           getter_AddRefs(parser));
   1.492 +        if (NS_FAILED(rv)) return rv;
   1.493 +
   1.494 +        // Predicate mIsWritingFastLoad on the XUL cache being enabled,
   1.495 +        // so we don't have to re-check whether the cache is enabled all
   1.496 +        // the time.
   1.497 +        mIsWritingFastLoad = useXULCache;
   1.498 +
   1.499 +        nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
   1.500 +        NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
   1.501 +        if (NS_FAILED(rv)) return rv;
   1.502 +
   1.503 +        *aDocListener = listener;
   1.504 +
   1.505 +        parser->Parse(mDocumentURI);
   1.506 +
   1.507 +        // Put the current prototype, created under PrepareToLoad, into the
   1.508 +        // XUL prototype cache now.  We can't do this under PrepareToLoad or
   1.509 +        // overlay loading will break; search for PutPrototype in ResumeWalk
   1.510 +        // and see the comment there.
   1.511 +        if (fillXULCache) {
   1.512 +            nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
   1.513 +        }
   1.514 +    }
   1.515 +
   1.516 +    NS_IF_ADDREF(*aDocListener);
   1.517 +    return NS_OK;
   1.518 +}
   1.519 +
   1.520 +// This gets invoked after a prototype for this document or one of
   1.521 +// its overlays is fully built in the content sink.
   1.522 +void
   1.523 +XULDocument::EndLoad()
   1.524 +{
   1.525 +    // This can happen if an overlay fails to load
   1.526 +    if (!mCurrentPrototype)
   1.527 +        return;
   1.528 +
   1.529 +    nsresult rv;
   1.530 +
   1.531 +    // Whack the prototype document into the cache so that the next
   1.532 +    // time somebody asks for it, they don't need to load it by hand.
   1.533 +
   1.534 +    nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
   1.535 +    bool isChrome = IsChromeURI(uri);
   1.536 +
   1.537 +    // Remember if the XUL cache is on
   1.538 +    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
   1.539 +
   1.540 +    // If the current prototype is an overlay document (non-master prototype)
   1.541 +    // and we're filling the FastLoad disk cache, tell the cache we're done
   1.542 +    // loading it, and write the prototype. The master prototype is put into
   1.543 +    // the cache earlier in XULDocument::StartDocumentLoad.
   1.544 +    if (useXULCache && mIsWritingFastLoad && isChrome &&
   1.545 +        mMasterPrototype != mCurrentPrototype) {
   1.546 +        nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
   1.547 +    }
   1.548 +
   1.549 +    if (IsOverlayAllowed(uri)) {
   1.550 +        nsCOMPtr<nsIXULOverlayProvider> reg =
   1.551 +            mozilla::services::GetXULOverlayProviderService();
   1.552 +
   1.553 +        if (reg) {
   1.554 +            nsCOMPtr<nsISimpleEnumerator> overlays;
   1.555 +            rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
   1.556 +            if (NS_FAILED(rv)) return;
   1.557 +
   1.558 +            bool moreSheets;
   1.559 +            nsCOMPtr<nsISupports> next;
   1.560 +            nsCOMPtr<nsIURI> sheetURI;
   1.561 +
   1.562 +            while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
   1.563 +                   moreSheets) {
   1.564 +                overlays->GetNext(getter_AddRefs(next));
   1.565 +
   1.566 +                sheetURI = do_QueryInterface(next);
   1.567 +                if (!sheetURI) {
   1.568 +                    NS_ERROR("Chrome registry handed me a non-nsIURI object!");
   1.569 +                    continue;
   1.570 +                }
   1.571 +
   1.572 +                if (IsChromeURI(sheetURI)) {
   1.573 +                    mCurrentPrototype->AddStyleSheetReference(sheetURI);
   1.574 +                }
   1.575 +            }
   1.576 +        }
   1.577 +
   1.578 +        if (isChrome && useXULCache) {
   1.579 +            // If it's a chrome prototype document, then notify any
   1.580 +            // documents that raced to load the prototype, and awaited
   1.581 +            // its load completion via proto->AwaitLoadDone().
   1.582 +            rv = mCurrentPrototype->NotifyLoadDone();
   1.583 +            if (NS_FAILED(rv)) return;
   1.584 +        }
   1.585 +    }
   1.586 +
   1.587 +    OnPrototypeLoadDone(true);
   1.588 +#ifdef PR_LOGGING
   1.589 +    if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) {
   1.590 +        nsAutoCString urlspec;
   1.591 +        rv = uri->GetSpec(urlspec);
   1.592 +        if (NS_SUCCEEDED(rv)) {
   1.593 +            PR_LOG(gXULLog, PR_LOG_WARNING,
   1.594 +                   ("xul: Finished loading document '%s'", urlspec.get()));
   1.595 +        }
   1.596 +    }
   1.597 +#endif
   1.598 +}
   1.599 +
   1.600 +NS_IMETHODIMP
   1.601 +XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
   1.602 +{
   1.603 +    nsresult rv;
   1.604 +
   1.605 +    // Add the style overlays from chrome registry, if any.
   1.606 +    rv = AddPrototypeSheets();
   1.607 +    if (NS_FAILED(rv)) return rv;
   1.608 +
   1.609 +    rv = PrepareToWalk();
   1.610 +    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
   1.611 +    if (NS_FAILED(rv)) return rv;
   1.612 +
   1.613 +    if (aResumeWalk) {
   1.614 +        rv = ResumeWalk();
   1.615 +    }
   1.616 +    return rv;
   1.617 +}
   1.618 +
   1.619 +// called when an error occurs parsing a document
   1.620 +bool
   1.621 +XULDocument::OnDocumentParserError()
   1.622 +{
   1.623 +  // don't report errors that are from overlays
   1.624 +  if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) {
   1.625 +    nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
   1.626 +    if (IsChromeURI(uri)) {
   1.627 +      nsCOMPtr<nsIObserverService> os =
   1.628 +        mozilla::services::GetObserverService();
   1.629 +      if (os)
   1.630 +        os->NotifyObservers(uri, "xul-overlay-parsererror",
   1.631 +                            EmptyString().get());
   1.632 +    }
   1.633 +
   1.634 +    return false;
   1.635 +  }
   1.636 +
   1.637 +  return true;
   1.638 +}
   1.639 +
   1.640 +static void
   1.641 +ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
   1.642 +{
   1.643 +    BroadcasterMapEntry* entry =
   1.644 +        static_cast<BroadcasterMapEntry*>(aEntry);
   1.645 +    for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   1.646 +        delete (BroadcastListener*)entry->mListeners[i];
   1.647 +    }
   1.648 +
   1.649 +    // N.B. that we need to manually run the dtor because we
   1.650 +    // constructed the nsSmallVoidArray object in-place.
   1.651 +    entry->mListeners.~nsSmallVoidArray();
   1.652 +}
   1.653 +
   1.654 +static bool
   1.655 +CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute)
   1.656 +{
   1.657 +    // Don't push changes to the |id|, |ref|, |persist|, |command| or
   1.658 +    // |observes| attribute.
   1.659 +    if (aNameSpaceID == kNameSpaceID_None) {
   1.660 +        if ((aAttribute == nsGkAtoms::id) ||
   1.661 +            (aAttribute == nsGkAtoms::ref) ||
   1.662 +            (aAttribute == nsGkAtoms::persist) ||
   1.663 +            (aAttribute == nsGkAtoms::command) ||
   1.664 +            (aAttribute == nsGkAtoms::observes)) {
   1.665 +            return false;
   1.666 +        }
   1.667 +    }
   1.668 +    return true;
   1.669 +}
   1.670 +
   1.671 +struct nsAttrNameInfo
   1.672 +{
   1.673 +  nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) :
   1.674 +    mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
   1.675 +  nsAttrNameInfo(const nsAttrNameInfo& aOther) :
   1.676 +    mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
   1.677 +    mPrefix(aOther.mPrefix) {}
   1.678 +  int32_t           mNamespaceID;
   1.679 +  nsCOMPtr<nsIAtom> mName;
   1.680 +  nsCOMPtr<nsIAtom> mPrefix;
   1.681 +};
   1.682 +
   1.683 +void
   1.684 +XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
   1.685 +                                          Element *aListener,
   1.686 +                                          const nsAString &aAttr)
   1.687 +{
   1.688 +    if (!nsContentUtils::IsSafeToRunScript()) {
   1.689 +        nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
   1.690 +                                               aAttr);
   1.691 +        mDelayedBroadcasters.AppendElement(delayedUpdate);
   1.692 +        MaybeBroadcast();
   1.693 +        return;
   1.694 +    }
   1.695 +    bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
   1.696 +
   1.697 +    if (aAttr.EqualsLiteral("*")) {
   1.698 +        uint32_t count = aBroadcaster->GetAttrCount();
   1.699 +        nsTArray<nsAttrNameInfo> attributes(count);
   1.700 +        for (uint32_t i = 0; i < count; ++i) {
   1.701 +            const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
   1.702 +            int32_t nameSpaceID = attrName->NamespaceID();
   1.703 +            nsIAtom* name = attrName->LocalName();
   1.704 +
   1.705 +            // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
   1.706 +            if (! CanBroadcast(nameSpaceID, name))
   1.707 +                continue;
   1.708 +
   1.709 +            attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
   1.710 +                                                    attrName->GetPrefix()));
   1.711 +        }
   1.712 +
   1.713 +        count = attributes.Length();
   1.714 +        while (count-- > 0) {
   1.715 +            int32_t nameSpaceID = attributes[count].mNamespaceID;
   1.716 +            nsIAtom* name = attributes[count].mName;
   1.717 +            nsAutoString value;
   1.718 +            if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
   1.719 +              aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
   1.720 +                                 value, notify);
   1.721 +            }
   1.722 +
   1.723 +#if 0
   1.724 +            // XXX we don't fire the |onbroadcast| handler during
   1.725 +            // initial hookup: doing so would potentially run the
   1.726 +            // |onbroadcast| handler before the |onload| handler,
   1.727 +            // which could define JS properties that mask XBL
   1.728 +            // properties, etc.
   1.729 +            ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
   1.730 +#endif
   1.731 +        }
   1.732 +    }
   1.733 +    else {
   1.734 +        // Find out if the attribute is even present at all.
   1.735 +        nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr);
   1.736 +
   1.737 +        nsAutoString value;
   1.738 +        if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
   1.739 +            aListener->SetAttr(kNameSpaceID_None, name, value, notify);
   1.740 +        } else {
   1.741 +            aListener->UnsetAttr(kNameSpaceID_None, name, notify);
   1.742 +        }
   1.743 +
   1.744 +#if 0
   1.745 +        // XXX we don't fire the |onbroadcast| handler during initial
   1.746 +        // hookup: doing so would potentially run the |onbroadcast|
   1.747 +        // handler before the |onload| handler, which could define JS
   1.748 +        // properties that mask XBL properties, etc.
   1.749 +        ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
   1.750 +#endif
   1.751 +    }
   1.752 +}
   1.753 +
   1.754 +NS_IMETHODIMP
   1.755 +XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
   1.756 +                                     nsIDOMElement* aListener,
   1.757 +                                     const nsAString& aAttr)
   1.758 +{
   1.759 +    ErrorResult rv;
   1.760 +    nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
   1.761 +    nsCOMPtr<Element> listener = do_QueryInterface(aListener);
   1.762 +    NS_ENSURE_ARG(broadcaster && listener);
   1.763 +    AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv);
   1.764 +    return rv.ErrorCode();
   1.765 +}
   1.766 +
   1.767 +void
   1.768 +XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
   1.769 +                                     const nsAString& aAttr, ErrorResult& aRv)
   1.770 +{
   1.771 +    nsresult rv =
   1.772 +        nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
   1.773 +
   1.774 +    if (NS_FAILED(rv)) {
   1.775 +        aRv.Throw(rv);
   1.776 +        return;
   1.777 +    }
   1.778 +
   1.779 +    rv = nsContentUtils::CheckSameOrigin(this, &aListener);
   1.780 +
   1.781 +    if (NS_FAILED(rv)) {
   1.782 +        aRv.Throw(rv);
   1.783 +        return;
   1.784 +    }
   1.785 +
   1.786 +    static const PLDHashTableOps gOps = {
   1.787 +        PL_DHashAllocTable,
   1.788 +        PL_DHashFreeTable,
   1.789 +        PL_DHashVoidPtrKeyStub,
   1.790 +        PL_DHashMatchEntryStub,
   1.791 +        PL_DHashMoveEntryStub,
   1.792 +        ClearBroadcasterMapEntry,
   1.793 +        PL_DHashFinalizeStub,
   1.794 +        nullptr
   1.795 +    };
   1.796 +
   1.797 +    if (! mBroadcasterMap) {
   1.798 +        mBroadcasterMap =
   1.799 +            PL_NewDHashTable(&gOps, nullptr, sizeof(BroadcasterMapEntry),
   1.800 +                             PL_DHASH_MIN_SIZE);
   1.801 +
   1.802 +        if (! mBroadcasterMap) {
   1.803 +            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.804 +            return;
   1.805 +        }
   1.806 +    }
   1.807 +
   1.808 +    BroadcasterMapEntry* entry =
   1.809 +        static_cast<BroadcasterMapEntry*>
   1.810 +                   (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   1.811 +                                            PL_DHASH_LOOKUP));
   1.812 +
   1.813 +    if (PL_DHASH_ENTRY_IS_FREE(entry)) {
   1.814 +        entry =
   1.815 +            static_cast<BroadcasterMapEntry*>
   1.816 +                       (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   1.817 +                                                PL_DHASH_ADD));
   1.818 +
   1.819 +        if (! entry) {
   1.820 +            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.821 +            return;
   1.822 +        }
   1.823 +
   1.824 +        entry->mBroadcaster = &aBroadcaster;
   1.825 +
   1.826 +        // N.B. placement new to construct the nsSmallVoidArray object
   1.827 +        // in-place
   1.828 +        new (&entry->mListeners) nsSmallVoidArray();
   1.829 +    }
   1.830 +
   1.831 +    // Only add the listener if it's not there already!
   1.832 +    nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
   1.833 +
   1.834 +    BroadcastListener* bl;
   1.835 +    for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   1.836 +        bl = static_cast<BroadcastListener*>(entry->mListeners[i]);
   1.837 +
   1.838 +        nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
   1.839 +
   1.840 +        if (blListener == &aListener && bl->mAttribute == attr)
   1.841 +            return;
   1.842 +    }
   1.843 +
   1.844 +    bl = new BroadcastListener;
   1.845 +
   1.846 +    bl->mListener  = do_GetWeakReference(&aListener);
   1.847 +    bl->mAttribute = attr;
   1.848 +
   1.849 +    entry->mListeners.AppendElement(bl);
   1.850 +
   1.851 +    SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
   1.852 +}
   1.853 +
   1.854 +NS_IMETHODIMP
   1.855 +XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
   1.856 +                                        nsIDOMElement* aListener,
   1.857 +                                        const nsAString& aAttr)
   1.858 +{
   1.859 +    nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
   1.860 +    nsCOMPtr<Element> listener = do_QueryInterface(aListener);
   1.861 +    NS_ENSURE_ARG(broadcaster && listener);
   1.862 +    RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr);
   1.863 +    return NS_OK;
   1.864 +}
   1.865 +
   1.866 +void
   1.867 +XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
   1.868 +                                        Element& aListener,
   1.869 +                                        const nsAString& aAttr)
   1.870 +{
   1.871 +    // If we haven't added any broadcast listeners, then there sure
   1.872 +    // aren't any to remove.
   1.873 +    if (! mBroadcasterMap)
   1.874 +        return;
   1.875 +
   1.876 +    BroadcasterMapEntry* entry =
   1.877 +        static_cast<BroadcasterMapEntry*>
   1.878 +                   (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   1.879 +                                            PL_DHASH_LOOKUP));
   1.880 +
   1.881 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.882 +        nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
   1.883 +        for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
   1.884 +            BroadcastListener* bl =
   1.885 +                static_cast<BroadcastListener*>(entry->mListeners[i]);
   1.886 +
   1.887 +            nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
   1.888 +
   1.889 +            if (blListener == &aListener && bl->mAttribute == attr) {
   1.890 +                entry->mListeners.RemoveElementAt(i);
   1.891 +                delete bl;
   1.892 +
   1.893 +                if (entry->mListeners.Count() == 0)
   1.894 +                    PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster,
   1.895 +                                         PL_DHASH_REMOVE);
   1.896 +
   1.897 +                break;
   1.898 +            }
   1.899 +        }
   1.900 +    }
   1.901 +}
   1.902 +
   1.903 +nsresult
   1.904 +XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
   1.905 +                                          Element* aListener,
   1.906 +                                          nsIAtom* aAttr)
   1.907 +{
   1.908 +    // Now we execute the onchange handler in the context of the
   1.909 +    // observer. We need to find the observer in order to
   1.910 +    // execute the handler.
   1.911 +
   1.912 +    for (nsIContent* child = aListener->GetFirstChild();
   1.913 +         child;
   1.914 +         child = child->GetNextSibling()) {
   1.915 +
   1.916 +        // Look for an <observes> element beneath the listener. This
   1.917 +        // ought to have an |element| attribute that refers to
   1.918 +        // aBroadcaster, and an |attribute| element that tells us what
   1.919 +        // attriubtes we're listening for.
   1.920 +        if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL))
   1.921 +            continue;
   1.922 +
   1.923 +        // Is this the element that was listening to us?
   1.924 +        nsAutoString listeningToID;
   1.925 +        child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
   1.926 +
   1.927 +        nsAutoString broadcasterID;
   1.928 +        aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
   1.929 +
   1.930 +        if (listeningToID != broadcasterID)
   1.931 +            continue;
   1.932 +
   1.933 +        // We are observing the broadcaster, but is this the right
   1.934 +        // attribute?
   1.935 +        nsAutoString listeningToAttribute;
   1.936 +        child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
   1.937 +                       listeningToAttribute);
   1.938 +
   1.939 +        if (!aAttr->Equals(listeningToAttribute) &&
   1.940 +            !listeningToAttribute.EqualsLiteral("*")) {
   1.941 +            continue;
   1.942 +        }
   1.943 +
   1.944 +        // This is the right <observes> element. Execute the
   1.945 +        // |onbroadcast| event handler
   1.946 +        WidgetEvent event(true, NS_XUL_BROADCAST);
   1.947 +
   1.948 +        nsCOMPtr<nsIPresShell> shell = GetShell();
   1.949 +        if (shell) {
   1.950 +            nsRefPtr<nsPresContext> aPresContext = shell->GetPresContext();
   1.951 +
   1.952 +            // Handle the DOM event
   1.953 +            nsEventStatus status = nsEventStatus_eIgnore;
   1.954 +            EventDispatcher::Dispatch(child, aPresContext, &event, nullptr,
   1.955 +                                      &status);
   1.956 +        }
   1.957 +    }
   1.958 +
   1.959 +    return NS_OK;
   1.960 +}
   1.961 +
   1.962 +void
   1.963 +XULDocument::AttributeWillChange(nsIDocument* aDocument,
   1.964 +                                 Element* aElement, int32_t aNameSpaceID,
   1.965 +                                 nsIAtom* aAttribute, int32_t aModType)
   1.966 +{
   1.967 +    NS_ABORT_IF_FALSE(aElement, "Null content!");
   1.968 +    NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
   1.969 +
   1.970 +    // XXXbz check aNameSpaceID, dammit!
   1.971 +    // See if we need to update our ref map.
   1.972 +    if (aAttribute == nsGkAtoms::ref ||
   1.973 +        (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) {
   1.974 +        // Might not need this, but be safe for now.
   1.975 +        nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   1.976 +        RemoveElementFromRefMap(aElement);
   1.977 +    }
   1.978 +}
   1.979 +
   1.980 +void
   1.981 +XULDocument::AttributeChanged(nsIDocument* aDocument,
   1.982 +                              Element* aElement, int32_t aNameSpaceID,
   1.983 +                              nsIAtom* aAttribute, int32_t aModType)
   1.984 +{
   1.985 +    NS_ASSERTION(aDocument == this, "unexpected doc");
   1.986 +
   1.987 +    // Might not need this, but be safe for now.
   1.988 +    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
   1.989 +
   1.990 +    // XXXbz check aNameSpaceID, dammit!
   1.991 +    // See if we need to update our ref map.
   1.992 +    if (aAttribute == nsGkAtoms::ref ||
   1.993 +        (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) {
   1.994 +        AddElementToRefMap(aElement);
   1.995 +    }
   1.996 +    
   1.997 +    nsresult rv;
   1.998 +
   1.999 +    // Synchronize broadcast listeners
  1.1000 +    if (mBroadcasterMap &&
  1.1001 +        CanBroadcast(aNameSpaceID, aAttribute)) {
  1.1002 +        BroadcasterMapEntry* entry =
  1.1003 +            static_cast<BroadcasterMapEntry*>
  1.1004 +                       (PL_DHashTableOperate(mBroadcasterMap, aElement,
  1.1005 +                                                PL_DHASH_LOOKUP));
  1.1006 +
  1.1007 +        if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
  1.1008 +            // We've got listeners: push the value.
  1.1009 +            nsAutoString value;
  1.1010 +            bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
  1.1011 +
  1.1012 +            int32_t i;
  1.1013 +            for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
  1.1014 +                BroadcastListener* bl =
  1.1015 +                    static_cast<BroadcastListener*>(entry->mListeners[i]);
  1.1016 +
  1.1017 +                if ((bl->mAttribute == aAttribute) ||
  1.1018 +                    (bl->mAttribute == nsGkAtoms::_asterix)) {
  1.1019 +                    nsCOMPtr<Element> listenerEl
  1.1020 +                        = do_QueryReferent(bl->mListener);
  1.1021 +                    if (listenerEl) {
  1.1022 +                        nsAutoString currentValue;
  1.1023 +                        bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
  1.1024 +                                                           aAttribute,
  1.1025 +                                                           currentValue);
  1.1026 +                        // We need to update listener only if we're
  1.1027 +                        // (1) removing an existing attribute,
  1.1028 +                        // (2) adding a new attribute or
  1.1029 +                        // (3) changing the value of an attribute.
  1.1030 +                        bool needsAttrChange =
  1.1031 +                            attrSet != hasAttr || !value.Equals(currentValue);
  1.1032 +                        nsDelayedBroadcastUpdate delayedUpdate(aElement,
  1.1033 +                                                               listenerEl,
  1.1034 +                                                               aAttribute,
  1.1035 +                                                               value,
  1.1036 +                                                               attrSet,
  1.1037 +                                                               needsAttrChange);
  1.1038 +
  1.1039 +                        uint32_t index =
  1.1040 +                            mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
  1.1041 +                                0, nsDelayedBroadcastUpdate::Comparator());
  1.1042 +                        if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
  1.1043 +                            if (mHandlingDelayedAttrChange) {
  1.1044 +                                NS_WARNING("Broadcasting loop!");
  1.1045 +                                continue;
  1.1046 +                            }
  1.1047 +                            mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
  1.1048 +                        }
  1.1049 +
  1.1050 +                        mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
  1.1051 +                    }
  1.1052 +                }
  1.1053 +            }
  1.1054 +        }
  1.1055 +    }
  1.1056 +
  1.1057 +    // checks for modifications in broadcasters
  1.1058 +    bool listener, resolved;
  1.1059 +    CheckBroadcasterHookup(aElement, &listener, &resolved);
  1.1060 +
  1.1061 +    // See if there is anything we need to persist in the localstore.
  1.1062 +    //
  1.1063 +    // XXX Namespace handling broken :-(
  1.1064 +    nsAutoString persist;
  1.1065 +    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
  1.1066 +    if (!persist.IsEmpty()) {
  1.1067 +        // XXXldb This should check that it's a token, not just a substring.
  1.1068 +        if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
  1.1069 +            rv = Persist(aElement, kNameSpaceID_None, aAttribute);
  1.1070 +            if (NS_FAILED(rv)) return;
  1.1071 +        }
  1.1072 +    }
  1.1073 +}
  1.1074 +
  1.1075 +void
  1.1076 +XULDocument::ContentAppended(nsIDocument* aDocument,
  1.1077 +                             nsIContent* aContainer,
  1.1078 +                             nsIContent* aFirstNewContent,
  1.1079 +                             int32_t aNewIndexInContainer)
  1.1080 +{
  1.1081 +    NS_ASSERTION(aDocument == this, "unexpected doc");
  1.1082 +    
  1.1083 +    // Might not need this, but be safe for now.
  1.1084 +    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1.1085 +
  1.1086 +    // Update our element map
  1.1087 +    nsresult rv = NS_OK;
  1.1088 +    for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
  1.1089 +         cur = cur->GetNextSibling()) {
  1.1090 +        rv = AddSubtreeToDocument(cur);
  1.1091 +    }
  1.1092 +}
  1.1093 +
  1.1094 +void
  1.1095 +XULDocument::ContentInserted(nsIDocument* aDocument,
  1.1096 +                             nsIContent* aContainer,
  1.1097 +                             nsIContent* aChild,
  1.1098 +                             int32_t aIndexInContainer)
  1.1099 +{
  1.1100 +    NS_ASSERTION(aDocument == this, "unexpected doc");
  1.1101 +
  1.1102 +    // Might not need this, but be safe for now.
  1.1103 +    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1.1104 +
  1.1105 +    AddSubtreeToDocument(aChild);
  1.1106 +}
  1.1107 +
  1.1108 +void
  1.1109 +XULDocument::ContentRemoved(nsIDocument* aDocument,
  1.1110 +                            nsIContent* aContainer,
  1.1111 +                            nsIContent* aChild,
  1.1112 +                            int32_t aIndexInContainer,
  1.1113 +                            nsIContent* aPreviousSibling)
  1.1114 +{
  1.1115 +    NS_ASSERTION(aDocument == this, "unexpected doc");
  1.1116 +
  1.1117 +    // Might not need this, but be safe for now.
  1.1118 +    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1.1119 +
  1.1120 +    RemoveSubtreeFromDocument(aChild);
  1.1121 +}
  1.1122 +
  1.1123 +//----------------------------------------------------------------------
  1.1124 +//
  1.1125 +// nsIXULDocument interface
  1.1126 +//
  1.1127 +
  1.1128 +void
  1.1129 +XULDocument::GetElementsForID(const nsAString& aID,
  1.1130 +                              nsCOMArray<nsIContent>& aElements)
  1.1131 +{
  1.1132 +    aElements.Clear();
  1.1133 +
  1.1134 +    nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
  1.1135 +    if (entry) {
  1.1136 +        entry->AppendAllIdContent(&aElements);
  1.1137 +    }
  1.1138 +    nsRefMapEntry *refEntry = mRefMap.GetEntry(aID);
  1.1139 +    if (refEntry) {
  1.1140 +        refEntry->AppendAll(&aElements);
  1.1141 +    }
  1.1142 +}
  1.1143 +
  1.1144 +nsresult
  1.1145 +XULDocument::AddForwardReference(nsForwardReference* aRef)
  1.1146 +{
  1.1147 +    if (mResolutionPhase < aRef->GetPhase()) {
  1.1148 +        if (!mForwardReferences.AppendElement(aRef)) {
  1.1149 +            delete aRef;
  1.1150 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1151 +        }
  1.1152 +    }
  1.1153 +    else {
  1.1154 +        NS_ERROR("forward references have already been resolved");
  1.1155 +        delete aRef;
  1.1156 +    }
  1.1157 +
  1.1158 +    return NS_OK;
  1.1159 +}
  1.1160 +
  1.1161 +nsresult
  1.1162 +XULDocument::ResolveForwardReferences()
  1.1163 +{
  1.1164 +    if (mResolutionPhase == nsForwardReference::eDone)
  1.1165 +        return NS_OK;
  1.1166 +
  1.1167 +    NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart,
  1.1168 +                 "nested ResolveForwardReferences()");
  1.1169 +        
  1.1170 +    // Resolve each outstanding 'forward' reference. We iterate
  1.1171 +    // through the list of forward references until no more forward
  1.1172 +    // references can be resolved. This annealing process is
  1.1173 +    // guaranteed to converge because we've "closed the gate" to new
  1.1174 +    // forward references.
  1.1175 +
  1.1176 +    const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
  1.1177 +    while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
  1.1178 +        uint32_t previous = 0;
  1.1179 +        while (mForwardReferences.Length() &&
  1.1180 +               mForwardReferences.Length() != previous) {
  1.1181 +            previous = mForwardReferences.Length();
  1.1182 +
  1.1183 +            for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) {
  1.1184 +                nsForwardReference* fwdref = mForwardReferences[i];
  1.1185 +
  1.1186 +                if (fwdref->GetPhase() == *pass) {
  1.1187 +                    nsForwardReference::Result result = fwdref->Resolve();
  1.1188 +
  1.1189 +                    switch (result) {
  1.1190 +                    case nsForwardReference::eResolve_Succeeded:
  1.1191 +                    case nsForwardReference::eResolve_Error:
  1.1192 +                        mForwardReferences.RemoveElementAt(i);
  1.1193 +
  1.1194 +                        // fixup because we removed from list
  1.1195 +                        --i;
  1.1196 +                        break;
  1.1197 +
  1.1198 +                    case nsForwardReference::eResolve_Later:
  1.1199 +                        // do nothing. we'll try again later
  1.1200 +                        ;
  1.1201 +                    }
  1.1202 +
  1.1203 +                    if (mResolutionPhase == nsForwardReference::eStart) {
  1.1204 +                        // Resolve() loaded a dynamic overlay,
  1.1205 +                        // (see XULDocument::LoadOverlayInternal()).
  1.1206 +                        // Return for now, we will be called again.
  1.1207 +                        return NS_OK;
  1.1208 +                    }
  1.1209 +                }
  1.1210 +            }
  1.1211 +        }
  1.1212 +
  1.1213 +        ++pass;
  1.1214 +    }
  1.1215 +
  1.1216 +    mForwardReferences.Clear();
  1.1217 +    return NS_OK;
  1.1218 +}
  1.1219 +
  1.1220 +//----------------------------------------------------------------------
  1.1221 +//
  1.1222 +// nsIDOMDocument interface
  1.1223 +//
  1.1224 +
  1.1225 +NS_IMETHODIMP
  1.1226 +XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  1.1227 +                                    const nsAString& aValue,
  1.1228 +                                    nsIDOMNodeList** aReturn)
  1.1229 +{
  1.1230 +    *aReturn = GetElementsByAttribute(aAttribute, aValue).take();
  1.1231 +    return NS_OK;
  1.1232 +}
  1.1233 +
  1.1234 +already_AddRefed<nsINodeList>
  1.1235 +XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  1.1236 +                                    const nsAString& aValue)
  1.1237 +{
  1.1238 +    nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
  1.1239 +    void* attrValue = new nsString(aValue);
  1.1240 +    nsRefPtr<nsContentList> list = new nsContentList(this,
  1.1241 +                                            MatchAttribute,
  1.1242 +                                            nsContentUtils::DestroyMatchString,
  1.1243 +                                            attrValue,
  1.1244 +                                            true,
  1.1245 +                                            attrAtom,
  1.1246 +                                            kNameSpaceID_Unknown);
  1.1247 +    
  1.1248 +    return list.forget();
  1.1249 +}
  1.1250 +
  1.1251 +NS_IMETHODIMP
  1.1252 +XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1.1253 +                                      const nsAString& aAttribute,
  1.1254 +                                      const nsAString& aValue,
  1.1255 +                                      nsIDOMNodeList** aReturn)
  1.1256 +{
  1.1257 +    ErrorResult rv;
  1.1258 +    *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute,
  1.1259 +                                        aValue, rv).take();
  1.1260 +    return rv.ErrorCode();
  1.1261 +}
  1.1262 +
  1.1263 +already_AddRefed<nsINodeList>
  1.1264 +XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1.1265 +                                      const nsAString& aAttribute,
  1.1266 +                                      const nsAString& aValue,
  1.1267 +                                      ErrorResult& aRv)
  1.1268 +{
  1.1269 +    nsCOMPtr<nsIAtom> attrAtom(do_GetAtom(aAttribute));
  1.1270 +    void* attrValue = new nsString(aValue);
  1.1271 +
  1.1272 +    int32_t nameSpaceId = kNameSpaceID_Wildcard;
  1.1273 +    if (!aNamespaceURI.EqualsLiteral("*")) {
  1.1274 +      nsresult rv =
  1.1275 +        nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
  1.1276 +                                                              nameSpaceId);
  1.1277 +      if (NS_FAILED(rv)) {
  1.1278 +          aRv.Throw(rv);
  1.1279 +          return nullptr;
  1.1280 +      }
  1.1281 +    }
  1.1282 +
  1.1283 +    nsRefPtr<nsContentList> list = new nsContentList(this,
  1.1284 +                                            MatchAttribute,
  1.1285 +                                            nsContentUtils::DestroyMatchString,
  1.1286 +                                            attrValue,
  1.1287 +                                            true,
  1.1288 +                                            attrAtom,
  1.1289 +                                            nameSpaceId);
  1.1290 +    return list.forget();
  1.1291 +}
  1.1292 +
  1.1293 +NS_IMETHODIMP
  1.1294 +XULDocument::Persist(const nsAString& aID,
  1.1295 +                     const nsAString& aAttr)
  1.1296 +{
  1.1297 +    // If we're currently reading persisted attributes out of the
  1.1298 +    // localstore, _don't_ re-enter and try to set them again!
  1.1299 +    if (mApplyingPersistedAttrs)
  1.1300 +        return NS_OK;
  1.1301 +
  1.1302 +    Element* element = nsDocument::GetElementById(aID);
  1.1303 +    if (!element)
  1.1304 +        return NS_OK;
  1.1305 +
  1.1306 +    nsCOMPtr<nsIAtom> tag;
  1.1307 +    int32_t nameSpaceID;
  1.1308 +
  1.1309 +    nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
  1.1310 +    nsresult rv;
  1.1311 +    if (ni) {
  1.1312 +        tag = ni->NameAtom();
  1.1313 +        nameSpaceID = ni->NamespaceID();
  1.1314 +    }
  1.1315 +    else {
  1.1316 +        // Make sure that this QName is going to be valid.
  1.1317 +        const char16_t *colon;
  1.1318 +        rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon);
  1.1319 +
  1.1320 +        if (NS_FAILED(rv)) {
  1.1321 +            // There was an invalid character or it was malformed.
  1.1322 +            return NS_ERROR_INVALID_ARG;
  1.1323 +        }
  1.1324 +
  1.1325 +        if (colon) {
  1.1326 +            // We don't really handle namespace qualifiers in attribute names.
  1.1327 +            return NS_ERROR_NOT_IMPLEMENTED;
  1.1328 +        }
  1.1329 +
  1.1330 +        tag = do_GetAtom(aAttr);
  1.1331 +        NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
  1.1332 +
  1.1333 +        nameSpaceID = kNameSpaceID_None;
  1.1334 +    }
  1.1335 +
  1.1336 +    rv = Persist(element, nameSpaceID, tag);
  1.1337 +    if (NS_FAILED(rv)) return rv;
  1.1338 +
  1.1339 +    return NS_OK;
  1.1340 +}
  1.1341 +
  1.1342 +nsresult
  1.1343 +XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
  1.1344 +                     nsIAtom* aAttribute)
  1.1345 +{
  1.1346 +    // For non-chrome documents, persistance is simply broken
  1.1347 +    if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  1.1348 +        return NS_ERROR_NOT_AVAILABLE;
  1.1349 +
  1.1350 +    // First make sure we _have_ a local store to stuff the persisted
  1.1351 +    // information into. (We might not have one if profile information
  1.1352 +    // hasn't been loaded yet...)
  1.1353 +    if (!mLocalStore)
  1.1354 +        return NS_OK;
  1.1355 +
  1.1356 +    nsresult rv;
  1.1357 +
  1.1358 +    nsCOMPtr<nsIRDFResource> element;
  1.1359 +    rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
  1.1360 +    if (NS_FAILED(rv)) return rv;
  1.1361 +
  1.1362 +    // No ID, so nothing to persist.
  1.1363 +    if (! element)
  1.1364 +        return NS_OK;
  1.1365 +
  1.1366 +    // Ick. Construct a property from the attribute. Punt on
  1.1367 +    // namespaces for now.
  1.1368 +    // Don't bother with unreasonable attributes. We clamp long values,
  1.1369 +    // but truncating attribute names turns it into a different attribute
  1.1370 +    // so there's no point in persisting anything at all
  1.1371 +    nsAtomCString attrstr(aAttribute);
  1.1372 +    if (attrstr.Length() > kMaxAttrNameLength) {
  1.1373 +        NS_WARNING("Can't persist, Attribute name too long");
  1.1374 +        return NS_ERROR_ILLEGAL_VALUE;
  1.1375 +    }
  1.1376 +
  1.1377 +    nsCOMPtr<nsIRDFResource> attr;
  1.1378 +    rv = gRDFService->GetResource(attrstr,
  1.1379 +                                  getter_AddRefs(attr));
  1.1380 +    if (NS_FAILED(rv)) return rv;
  1.1381 +
  1.1382 +    // Turn the value into a literal
  1.1383 +    nsAutoString valuestr;
  1.1384 +    aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
  1.1385 +
  1.1386 +    // prevent over-long attributes that choke the parser (bug 319846)
  1.1387 +    // (can't simply Truncate without testing, it's implemented
  1.1388 +    // using SetLength and will grow a short string)
  1.1389 +    if (valuestr.Length() > kMaxAttributeLength) {
  1.1390 +        NS_WARNING("Truncating persisted attribute value");
  1.1391 +        valuestr.Truncate(kMaxAttributeLength);
  1.1392 +    }
  1.1393 +
  1.1394 +    // See if there was an old value...
  1.1395 +    nsCOMPtr<nsIRDFNode> oldvalue;
  1.1396 +    rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue));
  1.1397 +    if (NS_FAILED(rv)) return rv;
  1.1398 +
  1.1399 +    if (oldvalue && valuestr.IsEmpty()) {
  1.1400 +        // ...there was an oldvalue, and they've removed it. XXXThis
  1.1401 +        // handling isn't quite right...
  1.1402 +        rv = mLocalStore->Unassert(element, attr, oldvalue);
  1.1403 +    }
  1.1404 +    else {
  1.1405 +        // Now either 'change' or 'assert' based on whether there was
  1.1406 +        // an old value.
  1.1407 +        nsCOMPtr<nsIRDFLiteral> newvalue;
  1.1408 +        rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
  1.1409 +        if (NS_FAILED(rv)) return rv;
  1.1410 +
  1.1411 +        if (oldvalue) {
  1.1412 +            if (oldvalue != newvalue)
  1.1413 +                rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
  1.1414 +            else
  1.1415 +                rv = NS_OK;
  1.1416 +        }
  1.1417 +        else {
  1.1418 +            rv = mLocalStore->Assert(element, attr, newvalue, true);
  1.1419 +        }
  1.1420 +    }
  1.1421 +
  1.1422 +    if (NS_FAILED(rv)) return rv;
  1.1423 +
  1.1424 +    // Add it to the persisted set for this document (if it's not
  1.1425 +    // there already).
  1.1426 +    {
  1.1427 +        nsAutoCString docurl;
  1.1428 +        rv = mDocumentURI->GetSpec(docurl);
  1.1429 +        if (NS_FAILED(rv)) return rv;
  1.1430 +
  1.1431 +        nsCOMPtr<nsIRDFResource> doc;
  1.1432 +        rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
  1.1433 +        if (NS_FAILED(rv)) return rv;
  1.1434 +
  1.1435 +        bool hasAssertion;
  1.1436 +        rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion);
  1.1437 +        if (NS_FAILED(rv)) return rv;
  1.1438 +
  1.1439 +        if (! hasAssertion) {
  1.1440 +            rv = mLocalStore->Assert(doc, kNC_persist, element, true);
  1.1441 +            if (NS_FAILED(rv)) return rv;
  1.1442 +        }
  1.1443 +    }
  1.1444 +
  1.1445 +    return NS_OK;
  1.1446 +}
  1.1447 +
  1.1448 +
  1.1449 +nsresult
  1.1450 +XULDocument::GetViewportSize(int32_t* aWidth,
  1.1451 +                             int32_t* aHeight)
  1.1452 +{
  1.1453 +    *aWidth = *aHeight = 0;
  1.1454 +
  1.1455 +    FlushPendingNotifications(Flush_Layout);
  1.1456 +
  1.1457 +    nsIPresShell *shell = GetShell();
  1.1458 +    NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
  1.1459 +
  1.1460 +    nsIFrame* frame = shell->GetRootFrame();
  1.1461 +    NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
  1.1462 +
  1.1463 +    nsSize size = frame->GetSize();
  1.1464 +
  1.1465 +    *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
  1.1466 +    *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
  1.1467 +
  1.1468 +    return NS_OK;
  1.1469 +}
  1.1470 +
  1.1471 +NS_IMETHODIMP
  1.1472 +XULDocument::GetWidth(int32_t* aWidth)
  1.1473 +{
  1.1474 +    NS_ENSURE_ARG_POINTER(aWidth);
  1.1475 +
  1.1476 +    int32_t height;
  1.1477 +    return GetViewportSize(aWidth, &height);
  1.1478 +}
  1.1479 +
  1.1480 +int32_t
  1.1481 +XULDocument::GetWidth(ErrorResult& aRv)
  1.1482 +{
  1.1483 +    int32_t width;
  1.1484 +    aRv = GetWidth(&width);
  1.1485 +    return width;
  1.1486 +}
  1.1487 +
  1.1488 +NS_IMETHODIMP
  1.1489 +XULDocument::GetHeight(int32_t* aHeight)
  1.1490 +{
  1.1491 +    NS_ENSURE_ARG_POINTER(aHeight);
  1.1492 +
  1.1493 +    int32_t width;
  1.1494 +    return GetViewportSize(&width, aHeight);
  1.1495 +}
  1.1496 +
  1.1497 +int32_t
  1.1498 +XULDocument::GetHeight(ErrorResult& aRv)
  1.1499 +{
  1.1500 +    int32_t height;
  1.1501 +    aRv = GetHeight(&height);
  1.1502 +    return height;
  1.1503 +}
  1.1504 +
  1.1505 +JSObject*
  1.1506 +GetScopeObjectOfNode(nsIDOMNode* node)
  1.1507 +{
  1.1508 +    MOZ_ASSERT(node, "Must not be called with null.");
  1.1509 +
  1.1510 +    // Window root occasionally keeps alive a node of a document whose
  1.1511 +    // window is already dead. If in this brief period someone calls
  1.1512 +    // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
  1.1513 +    // because it will not know which scope this node belongs to. Returning
  1.1514 +    // an orphan node like that to JS would be a bug anyway, so to avoid
  1.1515 +    // this, let's do the same check as nsNodeSH::PreCreate does to
  1.1516 +    // determine the scope and if it fails let's just return null in
  1.1517 +    // XULDocument::GetPopupNode.
  1.1518 +    nsCOMPtr<nsINode> inode = do_QueryInterface(node);
  1.1519 +    MOZ_ASSERT(inode, "How can this happen?");
  1.1520 +
  1.1521 +    nsIDocument* doc = inode->OwnerDoc();
  1.1522 +    MOZ_ASSERT(inode, "This should never happen.");
  1.1523 +
  1.1524 +    nsIGlobalObject* global = doc->GetScopeObject();
  1.1525 +    return global ? global->GetGlobalJSObject() : nullptr;
  1.1526 +}
  1.1527 +
  1.1528 +//----------------------------------------------------------------------
  1.1529 +//
  1.1530 +// nsIDOMXULDocument interface
  1.1531 +//
  1.1532 +
  1.1533 +NS_IMETHODIMP
  1.1534 +XULDocument::GetPopupNode(nsIDOMNode** aNode)
  1.1535 +{
  1.1536 +    *aNode = nullptr;
  1.1537 +
  1.1538 +    nsCOMPtr<nsIDOMNode> node;
  1.1539 +    nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1.1540 +    if (rootWin)
  1.1541 +        node = rootWin->GetPopupNode(); // addref happens here
  1.1542 +
  1.1543 +    if (!node) {
  1.1544 +        nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.1545 +        if (pm) {
  1.1546 +            node = pm->GetLastTriggerPopupNode(this);
  1.1547 +        }
  1.1548 +    }
  1.1549 +
  1.1550 +    if (node && nsContentUtils::CanCallerAccess(node)
  1.1551 +        && GetScopeObjectOfNode(node)) {
  1.1552 +        node.swap(*aNode);
  1.1553 +    }
  1.1554 +
  1.1555 +    return NS_OK;
  1.1556 +}
  1.1557 +
  1.1558 +already_AddRefed<nsINode>
  1.1559 +XULDocument::GetPopupNode()
  1.1560 +{
  1.1561 +    nsCOMPtr<nsIDOMNode> node;
  1.1562 +    DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node));
  1.1563 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1564 +    nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1.1565 +    return retval.forget();
  1.1566 +}
  1.1567 +
  1.1568 +NS_IMETHODIMP
  1.1569 +XULDocument::SetPopupNode(nsIDOMNode* aNode)
  1.1570 +{
  1.1571 +    if (aNode) {
  1.1572 +        // only allow real node objects
  1.1573 +        nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1.1574 +        NS_ENSURE_ARG(node);
  1.1575 +    }
  1.1576 +
  1.1577 +    nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1.1578 +    if (rootWin)
  1.1579 +        rootWin->SetPopupNode(aNode); // addref happens here
  1.1580 +
  1.1581 +    return NS_OK;
  1.1582 +}
  1.1583 +
  1.1584 +void
  1.1585 +XULDocument::SetPopupNode(nsINode* aNode)
  1.1586 +{
  1.1587 +    nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode));
  1.1588 +    DebugOnly<nsresult> rv = SetPopupNode(node);
  1.1589 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1590 +}
  1.1591 +
  1.1592 +// Returns the rangeOffset element from the XUL Popup Manager. This is for
  1.1593 +// chrome callers only.
  1.1594 +NS_IMETHODIMP
  1.1595 +XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
  1.1596 +{
  1.1597 +    NS_ENSURE_ARG_POINTER(aRangeParent);
  1.1598 +    *aRangeParent = nullptr;
  1.1599 +
  1.1600 +    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.1601 +    if (!pm)
  1.1602 +        return NS_ERROR_FAILURE;
  1.1603 +
  1.1604 +    int32_t offset;
  1.1605 +    pm->GetMouseLocation(aRangeParent, &offset);
  1.1606 +
  1.1607 +    if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) {
  1.1608 +        NS_RELEASE(*aRangeParent);
  1.1609 +        return NS_ERROR_DOM_SECURITY_ERR;
  1.1610 +    }
  1.1611 +
  1.1612 +    return NS_OK;
  1.1613 +}
  1.1614 +
  1.1615 +already_AddRefed<nsINode>
  1.1616 +XULDocument::GetPopupRangeParent(ErrorResult& aRv)
  1.1617 +{
  1.1618 +    nsCOMPtr<nsIDOMNode> node;
  1.1619 +    aRv = GetPopupRangeParent(getter_AddRefs(node));
  1.1620 +    nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1.1621 +    return retval.forget();
  1.1622 +}
  1.1623 +
  1.1624 +
  1.1625 +// Returns the rangeOffset element from the XUL Popup Manager. We check the
  1.1626 +// rangeParent to determine if the caller has rights to access to the data.
  1.1627 +NS_IMETHODIMP
  1.1628 +XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset)
  1.1629 +{
  1.1630 +    ErrorResult rv;
  1.1631 +    *aRangeOffset = GetPopupRangeOffset(rv);
  1.1632 +    return rv.ErrorCode();
  1.1633 +}
  1.1634 +
  1.1635 +int32_t
  1.1636 +XULDocument::GetPopupRangeOffset(ErrorResult& aRv)
  1.1637 +{
  1.1638 +    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.1639 +    if (!pm) {
  1.1640 +        aRv.Throw(NS_ERROR_FAILURE);
  1.1641 +        return 0;
  1.1642 +    }
  1.1643 +
  1.1644 +    int32_t offset;
  1.1645 +    nsCOMPtr<nsIDOMNode> parent;
  1.1646 +    pm->GetMouseLocation(getter_AddRefs(parent), &offset);
  1.1647 +
  1.1648 +    if (parent && !nsContentUtils::CanCallerAccess(parent)) {
  1.1649 +        aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1.1650 +        return 0;
  1.1651 +    }
  1.1652 +    return offset;
  1.1653 +}
  1.1654 +
  1.1655 +NS_IMETHODIMP
  1.1656 +XULDocument::GetTooltipNode(nsIDOMNode** aNode)
  1.1657 +{
  1.1658 +    *aNode = nullptr;
  1.1659 +
  1.1660 +    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1.1661 +    if (pm) {
  1.1662 +        nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
  1.1663 +        if (node && nsContentUtils::CanCallerAccess(node))
  1.1664 +            node.swap(*aNode);
  1.1665 +    }
  1.1666 +
  1.1667 +    return NS_OK;
  1.1668 +}
  1.1669 +
  1.1670 +already_AddRefed<nsINode>
  1.1671 +XULDocument::GetTooltipNode()
  1.1672 +{
  1.1673 +    nsCOMPtr<nsIDOMNode> node;
  1.1674 +    DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node));
  1.1675 +    MOZ_ASSERT(NS_SUCCEEDED(rv));
  1.1676 +    nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1.1677 +    return retval.forget();
  1.1678 +}
  1.1679 +
  1.1680 +NS_IMETHODIMP
  1.1681 +XULDocument::SetTooltipNode(nsIDOMNode* aNode)
  1.1682 +{
  1.1683 +    // do nothing
  1.1684 +    return NS_OK;
  1.1685 +}
  1.1686 +
  1.1687 +
  1.1688 +NS_IMETHODIMP
  1.1689 +XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
  1.1690 +{
  1.1691 +    *aTracker = mCommandDispatcher;
  1.1692 +    NS_IF_ADDREF(*aTracker);
  1.1693 +    return NS_OK;
  1.1694 +}
  1.1695 +
  1.1696 +Element*
  1.1697 +XULDocument::GetElementById(const nsAString& aId)
  1.1698 +{
  1.1699 +    if (!CheckGetElementByIdArg(aId))
  1.1700 +        return nullptr;
  1.1701 +
  1.1702 +    nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId);
  1.1703 +    if (entry) {
  1.1704 +        Element* element = entry->GetIdElement();
  1.1705 +        if (element)
  1.1706 +            return element;
  1.1707 +    }
  1.1708 +
  1.1709 +    nsRefMapEntry* refEntry = mRefMap.GetEntry(aId);
  1.1710 +    if (refEntry) {
  1.1711 +        NS_ASSERTION(refEntry->GetFirstElement(),
  1.1712 +                     "nsRefMapEntries should have nonempty content lists");
  1.1713 +        return refEntry->GetFirstElement();
  1.1714 +    }
  1.1715 +    return nullptr;
  1.1716 +}
  1.1717 +
  1.1718 +nsresult
  1.1719 +XULDocument::AddElementToDocumentPre(Element* aElement)
  1.1720 +{
  1.1721 +    // Do a bunch of work that's necessary when an element gets added
  1.1722 +    // to the XUL Document.
  1.1723 +    nsresult rv;
  1.1724 +
  1.1725 +    // 1. Add the element to the resource-to-element map. Also add it to
  1.1726 +    // the id map, since it seems this can be called when creating
  1.1727 +    // elements from prototypes.
  1.1728 +    nsIAtom* id = aElement->GetID();
  1.1729 +    if (id) {
  1.1730 +        // FIXME: Shouldn't BindToTree take care of this?
  1.1731 +        nsAutoScriptBlocker scriptBlocker;
  1.1732 +        AddToIdTable(aElement, id);
  1.1733 +    }
  1.1734 +    rv = AddElementToRefMap(aElement);
  1.1735 +    if (NS_FAILED(rv)) return rv;
  1.1736 +
  1.1737 +    // 2. If the element is a 'command updater' (i.e., has a
  1.1738 +    // "commandupdater='true'" attribute), then add the element to the
  1.1739 +    // document's command dispatcher
  1.1740 +    if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1.1741 +                              nsGkAtoms::_true, eCaseMatters)) {
  1.1742 +        rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
  1.1743 +        if (NS_FAILED(rv)) return rv;
  1.1744 +    }
  1.1745 +
  1.1746 +    // 3. Check for a broadcaster hookup attribute, in which case
  1.1747 +    // we'll hook the node up as a listener on a broadcaster.
  1.1748 +    bool listener, resolved;
  1.1749 +    rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
  1.1750 +    if (NS_FAILED(rv)) return rv;
  1.1751 +
  1.1752 +    // If it's not there yet, we may be able to defer hookup until
  1.1753 +    // later.
  1.1754 +    if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
  1.1755 +        BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
  1.1756 +        if (! hookup)
  1.1757 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1758 +
  1.1759 +        rv = AddForwardReference(hookup);
  1.1760 +        if (NS_FAILED(rv)) return rv;
  1.1761 +    }
  1.1762 +
  1.1763 +    return NS_OK;
  1.1764 +}
  1.1765 +
  1.1766 +nsresult
  1.1767 +XULDocument::AddElementToDocumentPost(Element* aElement)
  1.1768 +{
  1.1769 +    // We need to pay special attention to the keyset tag to set up a listener
  1.1770 +    if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1.1771 +        // Create our XUL key listener and hook it up.
  1.1772 +        nsXBLService::AttachGlobalKeyHandler(aElement);
  1.1773 +    }
  1.1774 +
  1.1775 +    // See if we need to attach a XUL template to this node
  1.1776 +    bool needsHookup;
  1.1777 +    nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
  1.1778 +    if (NS_FAILED(rv))
  1.1779 +        return rv;
  1.1780 +
  1.1781 +    if (needsHookup) {
  1.1782 +        if (mResolutionPhase == nsForwardReference::eDone) {
  1.1783 +            rv = CreateTemplateBuilder(aElement);
  1.1784 +            if (NS_FAILED(rv))
  1.1785 +                return rv;
  1.1786 +        }
  1.1787 +        else {
  1.1788 +            TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
  1.1789 +            if (! hookup)
  1.1790 +                return NS_ERROR_OUT_OF_MEMORY;
  1.1791 +
  1.1792 +            rv = AddForwardReference(hookup);
  1.1793 +            if (NS_FAILED(rv))
  1.1794 +                return rv;
  1.1795 +        }
  1.1796 +    }
  1.1797 +
  1.1798 +    return NS_OK;
  1.1799 +}
  1.1800 +
  1.1801 +NS_IMETHODIMP
  1.1802 +XULDocument::AddSubtreeToDocument(nsIContent* aContent)
  1.1803 +{
  1.1804 +    NS_ASSERTION(aContent->GetCurrentDoc() == this, "Element not in doc!");
  1.1805 +    // From here on we only care about elements.
  1.1806 +    if (!aContent->IsElement()) {
  1.1807 +        return NS_OK;
  1.1808 +    }
  1.1809 +
  1.1810 +    Element* aElement = aContent->AsElement();
  1.1811 +
  1.1812 +    // Do pre-order addition magic
  1.1813 +    nsresult rv = AddElementToDocumentPre(aElement);
  1.1814 +    if (NS_FAILED(rv)) return rv;
  1.1815 +
  1.1816 +    // Recurse to children
  1.1817 +    for (nsIContent* child = aElement->GetLastChild();
  1.1818 +         child;
  1.1819 +         child = child->GetPreviousSibling()) {
  1.1820 +
  1.1821 +        rv = AddSubtreeToDocument(child);
  1.1822 +        if (NS_FAILED(rv))
  1.1823 +            return rv;
  1.1824 +    }
  1.1825 +
  1.1826 +    // Do post-order addition magic
  1.1827 +    return AddElementToDocumentPost(aElement);
  1.1828 +}
  1.1829 +
  1.1830 +NS_IMETHODIMP
  1.1831 +XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
  1.1832 +{
  1.1833 +    // From here on we only care about elements.
  1.1834 +    if (!aContent->IsElement()) {
  1.1835 +        return NS_OK;
  1.1836 +    }
  1.1837 +
  1.1838 +    Element* aElement = aContent->AsElement();
  1.1839 +
  1.1840 +    // Do a bunch of cleanup to remove an element from the XUL
  1.1841 +    // document.
  1.1842 +    nsresult rv;
  1.1843 +
  1.1844 +    if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1.1845 +        nsXBLService::DetachGlobalKeyHandler(aElement);
  1.1846 +    }
  1.1847 +
  1.1848 +    // 1. Remove any children from the document.
  1.1849 +    for (nsIContent* child = aElement->GetLastChild();
  1.1850 +         child;
  1.1851 +         child = child->GetPreviousSibling()) {
  1.1852 +
  1.1853 +        rv = RemoveSubtreeFromDocument(child);
  1.1854 +        if (NS_FAILED(rv))
  1.1855 +            return rv;
  1.1856 +    }
  1.1857 +
  1.1858 +    // 2. Remove the element from the resource-to-element map.
  1.1859 +    // Also remove it from the id map, since we added it in
  1.1860 +    // AddElementToDocumentPre().
  1.1861 +    RemoveElementFromRefMap(aElement);
  1.1862 +    nsIAtom* id = aElement->GetID();
  1.1863 +    if (id) {
  1.1864 +        // FIXME: Shouldn't UnbindFromTree take care of this?
  1.1865 +        nsAutoScriptBlocker scriptBlocker;
  1.1866 +        RemoveFromIdTable(aElement, id);
  1.1867 +    }
  1.1868 +
  1.1869 +    // 3. If the element is a 'command updater', then remove the
  1.1870 +    // element from the document's command dispatcher.
  1.1871 +    if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1.1872 +                              nsGkAtoms::_true, eCaseMatters)) {
  1.1873 +        nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
  1.1874 +        NS_ASSERTION(domelement != nullptr, "not a DOM element");
  1.1875 +        if (! domelement)
  1.1876 +            return NS_ERROR_UNEXPECTED;
  1.1877 +
  1.1878 +        rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
  1.1879 +        if (NS_FAILED(rv)) return rv;
  1.1880 +    }
  1.1881 +
  1.1882 +    // 4. Remove the element from our broadcaster map, since it is no longer
  1.1883 +    // in the document.
  1.1884 +    nsCOMPtr<Element> broadcaster, listener;
  1.1885 +    nsAutoString attribute, broadcasterID;
  1.1886 +    rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  1.1887 +                         broadcasterID, attribute, getter_AddRefs(broadcaster));
  1.1888 +    if (rv == NS_FINDBROADCASTER_FOUND) {
  1.1889 +        RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
  1.1890 +    }
  1.1891 +
  1.1892 +    return NS_OK;
  1.1893 +}
  1.1894 +
  1.1895 +NS_IMETHODIMP
  1.1896 +XULDocument::SetTemplateBuilderFor(nsIContent* aContent,
  1.1897 +                                   nsIXULTemplateBuilder* aBuilder)
  1.1898 +{
  1.1899 +    if (! mTemplateBuilderTable) {
  1.1900 +        if (!aBuilder) {
  1.1901 +            return NS_OK;
  1.1902 +        }
  1.1903 +        mTemplateBuilderTable = new BuilderTable;
  1.1904 +    }
  1.1905 +
  1.1906 +    if (aBuilder) {
  1.1907 +        mTemplateBuilderTable->Put(aContent, aBuilder);
  1.1908 +    }
  1.1909 +    else {
  1.1910 +        mTemplateBuilderTable->Remove(aContent);
  1.1911 +    }
  1.1912 +
  1.1913 +    return NS_OK;
  1.1914 +}
  1.1915 +
  1.1916 +NS_IMETHODIMP
  1.1917 +XULDocument::GetTemplateBuilderFor(nsIContent* aContent,
  1.1918 +                                   nsIXULTemplateBuilder** aResult)
  1.1919 +{
  1.1920 +    if (mTemplateBuilderTable) {
  1.1921 +        mTemplateBuilderTable->Get(aContent, aResult);
  1.1922 +    }
  1.1923 +    else
  1.1924 +        *aResult = nullptr;
  1.1925 +
  1.1926 +    return NS_OK;
  1.1927 +}
  1.1928 +
  1.1929 +static void
  1.1930 +GetRefMapAttribute(Element* aElement, nsAutoString* aValue)
  1.1931 +{
  1.1932 +    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue);
  1.1933 +    if (aValue->IsEmpty() && !aElement->GetIDAttributeName()) {
  1.1934 +        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, *aValue);
  1.1935 +    }
  1.1936 +}
  1.1937 +
  1.1938 +nsresult
  1.1939 +XULDocument::AddElementToRefMap(Element* aElement)
  1.1940 +{
  1.1941 +    // Look at the element's 'ref' attribute, and if set,
  1.1942 +    // add an entry in the resource-to-element map to the element.
  1.1943 +    nsAutoString value;
  1.1944 +    GetRefMapAttribute(aElement, &value);
  1.1945 +    if (!value.IsEmpty()) {
  1.1946 +        nsRefMapEntry *entry = mRefMap.PutEntry(value);
  1.1947 +        if (!entry)
  1.1948 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1949 +        if (!entry->AddElement(aElement))
  1.1950 +            return NS_ERROR_OUT_OF_MEMORY;
  1.1951 +    }
  1.1952 +
  1.1953 +    return NS_OK;
  1.1954 +}
  1.1955 +
  1.1956 +void
  1.1957 +XULDocument::RemoveElementFromRefMap(Element* aElement)
  1.1958 +{
  1.1959 +    // Remove the element from the resource-to-element map.
  1.1960 +    nsAutoString value;
  1.1961 +    GetRefMapAttribute(aElement, &value);
  1.1962 +    if (!value.IsEmpty()) {
  1.1963 +        nsRefMapEntry *entry = mRefMap.GetEntry(value);
  1.1964 +        if (!entry)
  1.1965 +            return;
  1.1966 +        if (entry->RemoveElement(aElement)) {
  1.1967 +            mRefMap.RawRemoveEntry(entry);
  1.1968 +        }
  1.1969 +    }
  1.1970 +}
  1.1971 +
  1.1972 +//----------------------------------------------------------------------
  1.1973 +//
  1.1974 +// nsIDOMNode interface
  1.1975 +//
  1.1976 +
  1.1977 +nsresult
  1.1978 +XULDocument::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
  1.1979 +{
  1.1980 +    // We don't allow cloning of a XUL document
  1.1981 +    *aResult = nullptr;
  1.1982 +    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
  1.1983 +}
  1.1984 +
  1.1985 +
  1.1986 +//----------------------------------------------------------------------
  1.1987 +//
  1.1988 +// Implementation methods
  1.1989 +//
  1.1990 +
  1.1991 +nsresult
  1.1992 +XULDocument::Init()
  1.1993 +{
  1.1994 +    nsresult rv = XMLDocument::Init();
  1.1995 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1996 +
  1.1997 +    // Create our command dispatcher and hook it up.
  1.1998 +    mCommandDispatcher = new nsXULCommandDispatcher(this);
  1.1999 +    NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
  1.2000 +
  1.2001 +    // this _could_ fail; e.g., if we've tried to grab the local store
  1.2002 +    // before profiles have initialized. If so, no big deal; nothing
  1.2003 +    // will persist.
  1.2004 +    mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID);
  1.2005 +
  1.2006 +    if (gRefCnt++ == 0) {
  1.2007 +        // Keep the RDF service cached in a member variable to make using
  1.2008 +        // it a bit less painful
  1.2009 +        rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
  1.2010 +        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
  1.2011 +        if (NS_FAILED(rv)) return rv;
  1.2012 +
  1.2013 +        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
  1.2014 +                                 &kNC_persist);
  1.2015 +        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
  1.2016 +                                 &kNC_attribute);
  1.2017 +        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
  1.2018 +                                 &kNC_value);
  1.2019 +
  1.2020 +        // ensure that the XUL prototype cache is instantiated successfully,
  1.2021 +        // so that we can use nsXULPrototypeCache::GetInstance() without
  1.2022 +        // null-checks in the rest of the class.
  1.2023 +        nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1.2024 +        if (!cache) {
  1.2025 +          NS_ERROR("Could not instantiate nsXULPrototypeCache");
  1.2026 +          return NS_ERROR_FAILURE;
  1.2027 +        }
  1.2028 +    }
  1.2029 +
  1.2030 +    Preferences::RegisterCallback(XULDocument::DirectionChanged,
  1.2031 +                                  "intl.uidirection.", this);
  1.2032 +
  1.2033 +#ifdef PR_LOGGING
  1.2034 +    if (! gXULLog)
  1.2035 +        gXULLog = PR_NewLogModule("XULDocument");
  1.2036 +#endif
  1.2037 +
  1.2038 +    return NS_OK;
  1.2039 +}
  1.2040 +
  1.2041 +
  1.2042 +nsresult
  1.2043 +XULDocument::StartLayout(void)
  1.2044 +{
  1.2045 +    mMayStartLayout = true;
  1.2046 +    nsCOMPtr<nsIPresShell> shell = GetShell();
  1.2047 +    if (shell) {
  1.2048 +        // Resize-reflow this time
  1.2049 +        nsPresContext *cx = shell->GetPresContext();
  1.2050 +        NS_ASSERTION(cx != nullptr, "no pres context");
  1.2051 +        if (! cx)
  1.2052 +            return NS_ERROR_UNEXPECTED;
  1.2053 +
  1.2054 +        nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
  1.2055 +        NS_ASSERTION(docShell != nullptr, "container is not a docshell");
  1.2056 +        if (! docShell)
  1.2057 +            return NS_ERROR_UNEXPECTED;
  1.2058 +
  1.2059 +        nsresult rv = NS_OK;
  1.2060 +        nsRect r = cx->GetVisibleArea();
  1.2061 +        rv = shell->Initialize(r.width, r.height);
  1.2062 +        NS_ENSURE_SUCCESS(rv, rv);
  1.2063 +    }
  1.2064 +
  1.2065 +    return NS_OK;
  1.2066 +}
  1.2067 +
  1.2068 +/* static */
  1.2069 +bool
  1.2070 +XULDocument::MatchAttribute(nsIContent* aContent,
  1.2071 +                            int32_t aNamespaceID,
  1.2072 +                            nsIAtom* aAttrName,
  1.2073 +                            void* aData)
  1.2074 +{
  1.2075 +    NS_PRECONDITION(aContent, "Must have content node to work with!");
  1.2076 +    nsString* attrValue = static_cast<nsString*>(aData);
  1.2077 +    if (aNamespaceID != kNameSpaceID_Unknown &&
  1.2078 +        aNamespaceID != kNameSpaceID_Wildcard) {
  1.2079 +        return attrValue->EqualsLiteral("*") ?
  1.2080 +            aContent->HasAttr(aNamespaceID, aAttrName) :
  1.2081 +            aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
  1.2082 +                                  eCaseMatters);
  1.2083 +    }
  1.2084 +
  1.2085 +    // Qualified name match. This takes more work.
  1.2086 +
  1.2087 +    uint32_t count = aContent->GetAttrCount();
  1.2088 +    for (uint32_t i = 0; i < count; ++i) {
  1.2089 +        const nsAttrName* name = aContent->GetAttrNameAt(i);
  1.2090 +        bool nameMatch;
  1.2091 +        if (name->IsAtom()) {
  1.2092 +            nameMatch = name->Atom() == aAttrName;
  1.2093 +        } else if (aNamespaceID == kNameSpaceID_Wildcard) {
  1.2094 +            nameMatch = name->NodeInfo()->Equals(aAttrName);
  1.2095 +        } else {
  1.2096 +            nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
  1.2097 +        }
  1.2098 +
  1.2099 +        if (nameMatch) {
  1.2100 +            return attrValue->EqualsLiteral("*") ||
  1.2101 +                aContent->AttrValueIs(name->NamespaceID(), name->LocalName(),
  1.2102 +                                      *attrValue, eCaseMatters);
  1.2103 +        }
  1.2104 +    }
  1.2105 +
  1.2106 +    return false;
  1.2107 +}
  1.2108 +
  1.2109 +nsresult
  1.2110 +XULDocument::PrepareToLoad(nsISupports* aContainer,
  1.2111 +                           const char* aCommand,
  1.2112 +                           nsIChannel* aChannel,
  1.2113 +                           nsILoadGroup* aLoadGroup,
  1.2114 +                           nsIParser** aResult)
  1.2115 +{
  1.2116 +    // Get the document's principal
  1.2117 +    nsCOMPtr<nsIPrincipal> principal;
  1.2118 +    nsContentUtils::GetSecurityManager()->
  1.2119 +        GetChannelPrincipal(aChannel, getter_AddRefs(principal));
  1.2120 +    return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
  1.2121 +}
  1.2122 +
  1.2123 +
  1.2124 +nsresult
  1.2125 +XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
  1.2126 +                                    nsIPrincipal* aDocumentPrincipal,
  1.2127 +                                    nsIParser** aResult)
  1.2128 +{
  1.2129 +    nsresult rv;
  1.2130 +
  1.2131 +    // Create a new prototype document.
  1.2132 +    rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
  1.2133 +    if (NS_FAILED(rv)) return rv;
  1.2134 +
  1.2135 +    rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
  1.2136 +    if (NS_FAILED(rv)) {
  1.2137 +        mCurrentPrototype = nullptr;
  1.2138 +        return rv;
  1.2139 +    }    
  1.2140 +
  1.2141 +    // Bootstrap the master document prototype.
  1.2142 +    if (! mMasterPrototype) {
  1.2143 +        mMasterPrototype = mCurrentPrototype;
  1.2144 +        // Set our principal based on the master proto.
  1.2145 +        SetPrincipal(aDocumentPrincipal);
  1.2146 +    }
  1.2147 +
  1.2148 +    // Create a XUL content sink, a parser, and kick off a load for
  1.2149 +    // the overlay.
  1.2150 +    nsRefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
  1.2151 +    if (!sink) return NS_ERROR_OUT_OF_MEMORY;
  1.2152 +
  1.2153 +    rv = sink->Init(this, mCurrentPrototype);
  1.2154 +    NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
  1.2155 +    if (NS_FAILED(rv)) return rv;
  1.2156 +
  1.2157 +    nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
  1.2158 +    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
  1.2159 +    if (NS_FAILED(rv)) return rv;
  1.2160 +
  1.2161 +    parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
  1.2162 +                       eViewSource);
  1.2163 +
  1.2164 +    parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
  1.2165 +                               kCharsetFromDocTypeDefault);
  1.2166 +    parser->SetContentSink(sink); // grabs a reference to the parser
  1.2167 +
  1.2168 +    *aResult = parser;
  1.2169 +    NS_ADDREF(*aResult);
  1.2170 +    return NS_OK;
  1.2171 +}
  1.2172 +
  1.2173 +
  1.2174 +nsresult
  1.2175 +XULDocument::ApplyPersistentAttributes()
  1.2176 +{
  1.2177 +    // For non-chrome documents, persistance is simply broken
  1.2178 +    if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  1.2179 +        return NS_ERROR_NOT_AVAILABLE;
  1.2180 +
  1.2181 +    // Add all of the 'persisted' attributes into the content
  1.2182 +    // model.
  1.2183 +    if (!mLocalStore)
  1.2184 +        return NS_OK;
  1.2185 +
  1.2186 +    mApplyingPersistedAttrs = true;
  1.2187 +    ApplyPersistentAttributesInternal();
  1.2188 +    mApplyingPersistedAttrs = false;
  1.2189 +
  1.2190 +    // After we've applied persistence once, we should only reapply
  1.2191 +    // it to nodes created by overlays
  1.2192 +    mRestrictPersistence = true;
  1.2193 +    mPersistenceIds.Clear();
  1.2194 +
  1.2195 +    return NS_OK;
  1.2196 +}
  1.2197 +
  1.2198 +
  1.2199 +nsresult 
  1.2200 +XULDocument::ApplyPersistentAttributesInternal()
  1.2201 +{
  1.2202 +    nsCOMArray<nsIContent> elements;
  1.2203 +
  1.2204 +    nsAutoCString docurl;
  1.2205 +    mDocumentURI->GetSpec(docurl);
  1.2206 +
  1.2207 +    nsCOMPtr<nsIRDFResource> doc;
  1.2208 +    gRDFService->GetResource(docurl, getter_AddRefs(doc));
  1.2209 +
  1.2210 +    nsCOMPtr<nsISimpleEnumerator> persisted;
  1.2211 +    mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted));
  1.2212 +
  1.2213 +    while (1) {
  1.2214 +        bool hasmore = false;
  1.2215 +        persisted->HasMoreElements(&hasmore);
  1.2216 +        if (! hasmore)
  1.2217 +            break;
  1.2218 +
  1.2219 +        nsCOMPtr<nsISupports> isupports;
  1.2220 +        persisted->GetNext(getter_AddRefs(isupports));
  1.2221 +
  1.2222 +        nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
  1.2223 +        if (! resource) {
  1.2224 +            NS_WARNING("expected element to be a resource");
  1.2225 +            continue;
  1.2226 +        }
  1.2227 +
  1.2228 +        const char *uri;
  1.2229 +        resource->GetValueConst(&uri);
  1.2230 +        if (! uri)
  1.2231 +            continue;
  1.2232 +
  1.2233 +        nsAutoString id;
  1.2234 +        nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id);
  1.2235 +
  1.2236 +        if (id.IsEmpty())
  1.2237 +            continue;
  1.2238 +
  1.2239 +        if (mRestrictPersistence && !mPersistenceIds.Contains(id))
  1.2240 +            continue;
  1.2241 +
  1.2242 +        // This will clear the array if there are no elements.
  1.2243 +        GetElementsForID(id, elements);
  1.2244 +
  1.2245 +        if (!elements.Count())
  1.2246 +            continue;
  1.2247 +
  1.2248 +        ApplyPersistentAttributesToElements(resource, elements);
  1.2249 +    }
  1.2250 +
  1.2251 +    return NS_OK;
  1.2252 +}
  1.2253 +
  1.2254 +
  1.2255 +nsresult
  1.2256 +XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
  1.2257 +                                                 nsCOMArray<nsIContent>& aElements)
  1.2258 +{
  1.2259 +    nsresult rv;
  1.2260 +
  1.2261 +    nsCOMPtr<nsISimpleEnumerator> attrs;
  1.2262 +    rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
  1.2263 +    if (NS_FAILED(rv)) return rv;
  1.2264 +
  1.2265 +    while (1) {
  1.2266 +        bool hasmore;
  1.2267 +        rv = attrs->HasMoreElements(&hasmore);
  1.2268 +        if (NS_FAILED(rv)) return rv;
  1.2269 +
  1.2270 +        if (! hasmore)
  1.2271 +            break;
  1.2272 +
  1.2273 +        nsCOMPtr<nsISupports> isupports;
  1.2274 +        rv = attrs->GetNext(getter_AddRefs(isupports));
  1.2275 +        if (NS_FAILED(rv)) return rv;
  1.2276 +
  1.2277 +        nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
  1.2278 +        if (! property) {
  1.2279 +            NS_WARNING("expected a resource");
  1.2280 +            continue;
  1.2281 +        }
  1.2282 +
  1.2283 +        const char* attrname;
  1.2284 +        rv = property->GetValueConst(&attrname);
  1.2285 +        if (NS_FAILED(rv)) return rv;
  1.2286 +
  1.2287 +        nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
  1.2288 +        if (! attr)
  1.2289 +            return NS_ERROR_OUT_OF_MEMORY;
  1.2290 +
  1.2291 +        // XXX could hang namespace off here, as well...
  1.2292 +
  1.2293 +        nsCOMPtr<nsIRDFNode> node;
  1.2294 +        rv = mLocalStore->GetTarget(aResource, property, true,
  1.2295 +                                    getter_AddRefs(node));
  1.2296 +        if (NS_FAILED(rv)) return rv;
  1.2297 +
  1.2298 +        nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
  1.2299 +        if (! literal) {
  1.2300 +            NS_WARNING("expected a literal");
  1.2301 +            continue;
  1.2302 +        }
  1.2303 +
  1.2304 +        const char16_t* value;
  1.2305 +        rv = literal->GetValueConst(&value);
  1.2306 +        if (NS_FAILED(rv)) return rv;
  1.2307 +
  1.2308 +        nsDependentString wrapper(value);
  1.2309 +
  1.2310 +        uint32_t cnt = aElements.Count();
  1.2311 +
  1.2312 +        for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
  1.2313 +            nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
  1.2314 +            if (!element)
  1.2315 +                continue;
  1.2316 +
  1.2317 +            rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
  1.2318 +                                  attr,
  1.2319 +                                  wrapper,
  1.2320 +                                  true);
  1.2321 +        }
  1.2322 +    }
  1.2323 +
  1.2324 +    return NS_OK;
  1.2325 +}
  1.2326 +
  1.2327 +void
  1.2328 +XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
  1.2329 +{
  1.2330 +    uint32_t i, count = mPrototypes.Length();
  1.2331 +    for (i = 0; i < count; ++i) {
  1.2332 +        mPrototypes[i]->TraceProtos(aTrc, aGCNumber);
  1.2333 +    }
  1.2334 +}
  1.2335 +
  1.2336 +//----------------------------------------------------------------------
  1.2337 +//
  1.2338 +// XULDocument::ContextStack
  1.2339 +//
  1.2340 +
  1.2341 +XULDocument::ContextStack::ContextStack()
  1.2342 +    : mTop(nullptr), mDepth(0)
  1.2343 +{
  1.2344 +}
  1.2345 +
  1.2346 +XULDocument::ContextStack::~ContextStack()
  1.2347 +{
  1.2348 +    while (mTop) {
  1.2349 +        Entry* doomed = mTop;
  1.2350 +        mTop = mTop->mNext;
  1.2351 +        NS_IF_RELEASE(doomed->mElement);
  1.2352 +        delete doomed;
  1.2353 +    }
  1.2354 +}
  1.2355 +
  1.2356 +nsresult
  1.2357 +XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
  1.2358 +                                nsIContent* aElement)
  1.2359 +{
  1.2360 +    Entry* entry = new Entry;
  1.2361 +    if (! entry)
  1.2362 +        return NS_ERROR_OUT_OF_MEMORY;
  1.2363 +
  1.2364 +    entry->mPrototype = aPrototype;
  1.2365 +    entry->mElement   = aElement;
  1.2366 +    NS_IF_ADDREF(entry->mElement);
  1.2367 +    entry->mIndex     = 0;
  1.2368 +
  1.2369 +    entry->mNext = mTop;
  1.2370 +    mTop = entry;
  1.2371 +
  1.2372 +    ++mDepth;
  1.2373 +    return NS_OK;
  1.2374 +}
  1.2375 +
  1.2376 +nsresult
  1.2377 +XULDocument::ContextStack::Pop()
  1.2378 +{
  1.2379 +    if (mDepth == 0)
  1.2380 +        return NS_ERROR_UNEXPECTED;
  1.2381 +
  1.2382 +    Entry* doomed = mTop;
  1.2383 +    mTop = mTop->mNext;
  1.2384 +    --mDepth;
  1.2385 +
  1.2386 +    NS_IF_RELEASE(doomed->mElement);
  1.2387 +    delete doomed;
  1.2388 +    return NS_OK;
  1.2389 +}
  1.2390 +
  1.2391 +nsresult
  1.2392 +XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
  1.2393 +                                nsIContent** aElement,
  1.2394 +                                int32_t* aIndex)
  1.2395 +{
  1.2396 +    if (mDepth == 0)
  1.2397 +        return NS_ERROR_UNEXPECTED;
  1.2398 +
  1.2399 +    *aPrototype = mTop->mPrototype;
  1.2400 +    *aElement   = mTop->mElement;
  1.2401 +    NS_IF_ADDREF(*aElement);
  1.2402 +    *aIndex     = mTop->mIndex;
  1.2403 +
  1.2404 +    return NS_OK;
  1.2405 +}
  1.2406 +
  1.2407 +
  1.2408 +nsresult
  1.2409 +XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
  1.2410 +{
  1.2411 +    if (mDepth == 0)
  1.2412 +        return NS_ERROR_UNEXPECTED;
  1.2413 +
  1.2414 +    mTop->mIndex = aIndex;
  1.2415 +    return NS_OK;
  1.2416 +}
  1.2417 +
  1.2418 +
  1.2419 +//----------------------------------------------------------------------
  1.2420 +//
  1.2421 +// Content model walking routines
  1.2422 +//
  1.2423 +
  1.2424 +nsresult
  1.2425 +XULDocument::PrepareToWalk()
  1.2426 +{
  1.2427 +    // Prepare to walk the mCurrentPrototype
  1.2428 +    nsresult rv;
  1.2429 +
  1.2430 +    // Keep an owning reference to the prototype document so that its
  1.2431 +    // elements aren't yanked from beneath us.
  1.2432 +    mPrototypes.AppendElement(mCurrentPrototype);
  1.2433 +
  1.2434 +    // Get the prototype's root element and initialize the context
  1.2435 +    // stack for the prototype walk.
  1.2436 +    nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
  1.2437 +
  1.2438 +    if (! proto) {
  1.2439 +#ifdef PR_LOGGING
  1.2440 +        if (PR_LOG_TEST(gXULLog, PR_LOG_ERROR)) {
  1.2441 +            nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
  1.2442 +
  1.2443 +            nsAutoCString urlspec;
  1.2444 +            rv = url->GetSpec(urlspec);
  1.2445 +            if (NS_FAILED(rv)) return rv;
  1.2446 +
  1.2447 +            PR_LOG(gXULLog, PR_LOG_ERROR,
  1.2448 +                   ("xul: error parsing '%s'", urlspec.get()));
  1.2449 +        }
  1.2450 +#endif
  1.2451 +
  1.2452 +        return NS_OK;
  1.2453 +    }
  1.2454 +
  1.2455 +    uint32_t piInsertionPoint = 0;
  1.2456 +    if (mState != eState_Master) {
  1.2457 +        int32_t indexOfRoot = IndexOf(GetRootElement());
  1.2458 +        NS_ASSERTION(indexOfRoot >= 0,
  1.2459 +                     "No root content when preparing to walk overlay!");
  1.2460 +        piInsertionPoint = indexOfRoot;
  1.2461 +    }
  1.2462 +
  1.2463 +    const nsTArray<nsRefPtr<nsXULPrototypePI> >& processingInstructions =
  1.2464 +        mCurrentPrototype->GetProcessingInstructions();
  1.2465 +
  1.2466 +    uint32_t total = processingInstructions.Length();
  1.2467 +    for (uint32_t i = 0; i < total; ++i) {
  1.2468 +        rv = CreateAndInsertPI(processingInstructions[i],
  1.2469 +                               this, piInsertionPoint + i);
  1.2470 +        if (NS_FAILED(rv)) return rv;
  1.2471 +    }
  1.2472 +
  1.2473 +    // Now check the chrome registry for any additional overlays.
  1.2474 +    rv = AddChromeOverlays();
  1.2475 +    if (NS_FAILED(rv)) return rv;
  1.2476 +
  1.2477 +    // Do one-time initialization if we're preparing to walk the
  1.2478 +    // master document's prototype.
  1.2479 +    nsRefPtr<Element> root;
  1.2480 +
  1.2481 +    if (mState == eState_Master) {
  1.2482 +        // Add the root element
  1.2483 +        rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
  1.2484 +        if (NS_FAILED(rv)) return rv;
  1.2485 +
  1.2486 +        rv = AppendChildTo(root, false);
  1.2487 +        if (NS_FAILED(rv)) return rv;
  1.2488 +        
  1.2489 +        rv = AddElementToRefMap(root);
  1.2490 +        if (NS_FAILED(rv)) return rv;
  1.2491 +
  1.2492 +        // Block onload until we've finished building the complete
  1.2493 +        // document content model.
  1.2494 +        BlockOnload();
  1.2495 +    }
  1.2496 +
  1.2497 +    // There'd better not be anything on the context stack at this
  1.2498 +    // point! This is the basis case for our "induction" in
  1.2499 +    // ResumeWalk(), below, which'll assume that there's always a
  1.2500 +    // content element on the context stack if either 1) we're in the
  1.2501 +    // "master" document, or 2) we're in an overlay, and we've got
  1.2502 +    // more than one prototype element (the single, root "overlay"
  1.2503 +    // element) on the stack.
  1.2504 +    NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
  1.2505 +    if (mContextStack.Depth() != 0)
  1.2506 +        return NS_ERROR_UNEXPECTED;
  1.2507 +
  1.2508 +    rv = mContextStack.Push(proto, root);
  1.2509 +    if (NS_FAILED(rv)) return rv;
  1.2510 +
  1.2511 +    return NS_OK;
  1.2512 +}
  1.2513 +
  1.2514 +nsresult
  1.2515 +XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
  1.2516 +                               nsINode* aParent, uint32_t aIndex)
  1.2517 +{
  1.2518 +    NS_PRECONDITION(aProtoPI, "null ptr");
  1.2519 +    NS_PRECONDITION(aParent, "null ptr");
  1.2520 +
  1.2521 +    nsRefPtr<ProcessingInstruction> node =
  1.2522 +        NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
  1.2523 +                                       aProtoPI->mData);
  1.2524 +
  1.2525 +    nsresult rv;
  1.2526 +    if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
  1.2527 +        rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node);
  1.2528 +    } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) {
  1.2529 +        rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node);
  1.2530 +    } else {
  1.2531 +        // No special processing, just add the PI to the document.
  1.2532 +        rv = aParent->InsertChildAt(node, aIndex, false);
  1.2533 +    }
  1.2534 +
  1.2535 +    return rv;
  1.2536 +}
  1.2537 +
  1.2538 +nsresult
  1.2539 +XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
  1.2540 +                                   nsINode* aParent,
  1.2541 +                                   uint32_t aIndex,
  1.2542 +                                   nsIContent* aPINode)
  1.2543 +{
  1.2544 +    nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
  1.2545 +    NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
  1.2546 +                       "implement nsIStyleSheetLinkingElement!");
  1.2547 +
  1.2548 +    nsresult rv;
  1.2549 +
  1.2550 +    ssle->InitStyleLinkElement(false);
  1.2551 +    // We want to be notified when the style sheet finishes loading, so
  1.2552 +    // disable style sheet loading for now.
  1.2553 +    ssle->SetEnableUpdates(false);
  1.2554 +    ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
  1.2555 +
  1.2556 +    rv = aParent->InsertChildAt(aPINode, aIndex, false);
  1.2557 +    if (NS_FAILED(rv)) return rv;
  1.2558 +
  1.2559 +    ssle->SetEnableUpdates(true);
  1.2560 +
  1.2561 +    // load the stylesheet if necessary, passing ourselves as
  1.2562 +    // nsICSSObserver
  1.2563 +    bool willNotify;
  1.2564 +    bool isAlternate;
  1.2565 +    rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
  1.2566 +    if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
  1.2567 +        ++mPendingSheets;
  1.2568 +    }
  1.2569 +
  1.2570 +    // Ignore errors from UpdateStyleSheet; we don't want failure to
  1.2571 +    // do that to break the XUL document load.  But do propagate out
  1.2572 +    // NS_ERROR_OUT_OF_MEMORY.
  1.2573 +    if (rv == NS_ERROR_OUT_OF_MEMORY) {
  1.2574 +        return rv;
  1.2575 +    }
  1.2576 +    
  1.2577 +    return NS_OK;
  1.2578 +}
  1.2579 +
  1.2580 +nsresult
  1.2581 +XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
  1.2582 +                                nsINode* aParent,
  1.2583 +                                uint32_t aIndex,
  1.2584 +                                nsIContent* aPINode)
  1.2585 +{
  1.2586 +    nsresult rv;
  1.2587 +
  1.2588 +    rv = aParent->InsertChildAt(aPINode, aIndex, false);
  1.2589 +    if (NS_FAILED(rv)) return rv;
  1.2590 +
  1.2591 +    // xul-overlay PI is special only in prolog
  1.2592 +    if (!nsContentUtils::InProlog(aPINode)) {
  1.2593 +        return NS_OK;
  1.2594 +    }
  1.2595 +
  1.2596 +    nsAutoString href;
  1.2597 +    nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData,
  1.2598 +                                            nsGkAtoms::href,
  1.2599 +                                            href);
  1.2600 +
  1.2601 +    // If there was no href, we can't do anything with this PI
  1.2602 +    if (href.IsEmpty()) {
  1.2603 +        return NS_OK;
  1.2604 +    }
  1.2605 +
  1.2606 +    // Add the overlay to our list of overlays that need to be processed.
  1.2607 +    nsCOMPtr<nsIURI> uri;
  1.2608 +
  1.2609 +    rv = NS_NewURI(getter_AddRefs(uri), href, nullptr,
  1.2610 +                   mCurrentPrototype->GetURI());
  1.2611 +    if (NS_SUCCEEDED(rv)) {
  1.2612 +        // We insert overlays into mUnloadedOverlays at the same index in
  1.2613 +        // document order, so they end up in the reverse of the document
  1.2614 +        // order in mUnloadedOverlays.
  1.2615 +        // This is needed because the code in ResumeWalk loads the overlays
  1.2616 +        // by processing the last item of mUnloadedOverlays and removing it
  1.2617 +        // from the array.
  1.2618 +        mUnloadedOverlays.InsertElementAt(0, uri);
  1.2619 +        rv = NS_OK;
  1.2620 +    } else if (rv == NS_ERROR_MALFORMED_URI) {
  1.2621 +        // The URL is bad, move along. Don't propagate for now.
  1.2622 +        // XXX report this to the Error Console (bug 359846)
  1.2623 +        rv = NS_OK;
  1.2624 +    }
  1.2625 +
  1.2626 +    return rv;
  1.2627 +}
  1.2628 +
  1.2629 +nsresult
  1.2630 +XULDocument::AddChromeOverlays()
  1.2631 +{
  1.2632 +    nsresult rv;
  1.2633 +
  1.2634 +    nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI();
  1.2635 +
  1.2636 +    /* overlays only apply to chrome or about URIs */
  1.2637 +    if (!IsOverlayAllowed(docUri)) return NS_OK;
  1.2638 +
  1.2639 +    nsCOMPtr<nsIXULOverlayProvider> chromeReg =
  1.2640 +        mozilla::services::GetXULOverlayProviderService();
  1.2641 +    // In embedding situations, the chrome registry may not provide overlays,
  1.2642 +    // or even exist at all; that's OK.
  1.2643 +    NS_ENSURE_TRUE(chromeReg, NS_OK);
  1.2644 +
  1.2645 +    nsCOMPtr<nsISimpleEnumerator> overlays;
  1.2646 +    rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
  1.2647 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2648 +
  1.2649 +    bool moreOverlays;
  1.2650 +    nsCOMPtr<nsISupports> next;
  1.2651 +    nsCOMPtr<nsIURI> uri;
  1.2652 +
  1.2653 +    while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
  1.2654 +           moreOverlays) {
  1.2655 +
  1.2656 +        rv = overlays->GetNext(getter_AddRefs(next));
  1.2657 +        if (NS_FAILED(rv) || !next) break;
  1.2658 +
  1.2659 +        uri = do_QueryInterface(next);
  1.2660 +        if (!uri) {
  1.2661 +            NS_ERROR("Chrome registry handed me a non-nsIURI object!");
  1.2662 +            continue;
  1.2663 +        }
  1.2664 +
  1.2665 +        // Same comment as in XULDocument::InsertXULOverlayPI
  1.2666 +        mUnloadedOverlays.InsertElementAt(0, uri);
  1.2667 +    }
  1.2668 +
  1.2669 +    return rv;
  1.2670 +}
  1.2671 +
  1.2672 +NS_IMETHODIMP
  1.2673 +XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver)
  1.2674 +{
  1.2675 +    nsresult rv;
  1.2676 +
  1.2677 +    nsCOMPtr<nsIURI> uri;
  1.2678 +    rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr);
  1.2679 +    if (NS_FAILED(rv)) return rv;
  1.2680 +
  1.2681 +    if (aObserver) {
  1.2682 +        nsIObserver* obs = nullptr;
  1.2683 +        if (!mOverlayLoadObservers) {
  1.2684 +          mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  1.2685 +        }
  1.2686 +        obs = mOverlayLoadObservers->GetWeak(uri);
  1.2687 +
  1.2688 +        if (obs) {
  1.2689 +            // We don't support loading the same overlay twice into the same
  1.2690 +            // document - that doesn't make sense anyway.
  1.2691 +            return NS_ERROR_FAILURE;
  1.2692 +        }
  1.2693 +        mOverlayLoadObservers->Put(uri, aObserver);
  1.2694 +    }
  1.2695 +    bool shouldReturn, failureFromContent;
  1.2696 +    rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent);
  1.2697 +    if (NS_FAILED(rv) && mOverlayLoadObservers)
  1.2698 +        mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error
  1.2699 +    return rv;
  1.2700 +}
  1.2701 +
  1.2702 +nsresult
  1.2703 +XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
  1.2704 +                                 bool* aShouldReturn,
  1.2705 +                                 bool* aFailureFromContent)
  1.2706 +{
  1.2707 +    nsresult rv;
  1.2708 +
  1.2709 +    *aShouldReturn = false;
  1.2710 +    *aFailureFromContent = false;
  1.2711 +
  1.2712 +#ifdef PR_LOGGING
  1.2713 +    if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
  1.2714 +        nsAutoCString urlspec;
  1.2715 +        aURI->GetSpec(urlspec);
  1.2716 +        nsAutoCString parentDoc;
  1.2717 +        nsCOMPtr<nsIURI> uri;
  1.2718 +        nsresult rv = mChannel->GetOriginalURI(getter_AddRefs(uri));
  1.2719 +        if (NS_SUCCEEDED(rv))
  1.2720 +            rv = uri->GetSpec(parentDoc);
  1.2721 +        if (!(parentDoc.get()))
  1.2722 +            parentDoc = "";
  1.2723 +
  1.2724 +        PR_LOG(gXULLog, PR_LOG_DEBUG,
  1.2725 +                ("xul: %s loading overlay %s", parentDoc.get(), urlspec.get()));
  1.2726 +    }
  1.2727 +#endif
  1.2728 +
  1.2729 +    if (aIsDynamic)
  1.2730 +        mResolutionPhase = nsForwardReference::eStart;
  1.2731 +
  1.2732 +    // Chrome documents are allowed to load overlays from anywhere.
  1.2733 +    // In all other cases, the overlay is only allowed to load if
  1.2734 +    // the master document and prototype document have the same origin.
  1.2735 +
  1.2736 +    bool documentIsChrome = IsChromeURI(mDocumentURI);
  1.2737 +    if (!documentIsChrome) {
  1.2738 +        // Make sure we're allowed to load this overlay.
  1.2739 +        rv = NodePrincipal()->CheckMayLoad(aURI, true, false);
  1.2740 +        if (NS_FAILED(rv)) {
  1.2741 +            *aFailureFromContent = true;
  1.2742 +            return rv;
  1.2743 +        }
  1.2744 +    }
  1.2745 +
  1.2746 +    // Look in the prototype cache for the prototype document with
  1.2747 +    // the specified overlay URI. Only use the cache if the containing
  1.2748 +    // document is chrome otherwise it may not have a system principal and
  1.2749 +    // the cached document will, see bug 565610.
  1.2750 +    bool overlayIsChrome = IsChromeURI(aURI);
  1.2751 +    mCurrentPrototype = overlayIsChrome && documentIsChrome ?
  1.2752 +        nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr;
  1.2753 +
  1.2754 +    // Same comment as nsChromeProtocolHandler::NewChannel and
  1.2755 +    // XULDocument::StartDocumentLoad
  1.2756 +    // - Ben Goodger
  1.2757 +    //
  1.2758 +    // We don't abort on failure here because there are too many valid
  1.2759 +    // cases that can return failure, and the null-ness of |proto| is
  1.2760 +    // enough to trigger the fail-safe parse-from-disk solution.
  1.2761 +    // Example failure cases (for reference) include:
  1.2762 +    //
  1.2763 +    // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
  1.2764 +    //                         parse from disk
  1.2765 +    // other: the FastLoad file, XUL.mfl, could not be found, probably
  1.2766 +    //        due to being accessed before a profile has been selected
  1.2767 +    //        (e.g. loading chrome for the profile manager itself).
  1.2768 +    //        The .xul file must be parsed from disk.
  1.2769 +
  1.2770 +    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  1.2771 +    if (useXULCache && mCurrentPrototype) {
  1.2772 +        bool loaded;
  1.2773 +        rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
  1.2774 +        if (NS_FAILED(rv)) return rv;
  1.2775 +
  1.2776 +        if (! loaded) {
  1.2777 +            // Return to the main event loop and eagerly await the
  1.2778 +            // prototype overlay load's completion. When the content
  1.2779 +            // sink completes, it will trigger an EndLoad(), which'll
  1.2780 +            // wind us back up here, in ResumeWalk().
  1.2781 +            *aShouldReturn = true;
  1.2782 +            return NS_OK;
  1.2783 +        }
  1.2784 +
  1.2785 +        PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
  1.2786 +
  1.2787 +        // Found the overlay's prototype in the cache, fully loaded. If
  1.2788 +        // this is a dynamic overlay, this will call ResumeWalk.
  1.2789 +        // Otherwise, we'll return to ResumeWalk, which called us.
  1.2790 +        return OnPrototypeLoadDone(aIsDynamic);
  1.2791 +    }
  1.2792 +    else {
  1.2793 +        // Not there. Initiate a load.
  1.2794 +        PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
  1.2795 +
  1.2796 +        if (mIsGoingAway) {
  1.2797 +            PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: ...and document already destroyed"));
  1.2798 +            return NS_ERROR_NOT_AVAILABLE;
  1.2799 +        }
  1.2800 +
  1.2801 +        // We'll set the right principal on the proto doc when we get
  1.2802 +        // OnStartRequest from the parser, so just pass in a null principal for
  1.2803 +        // now.
  1.2804 +        nsCOMPtr<nsIParser> parser;
  1.2805 +        rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser));
  1.2806 +        if (NS_FAILED(rv)) return rv;
  1.2807 +
  1.2808 +        // Predicate mIsWritingFastLoad on the XUL cache being enabled,
  1.2809 +        // so we don't have to re-check whether the cache is enabled all
  1.2810 +        // the time.
  1.2811 +        mIsWritingFastLoad = useXULCache;
  1.2812 +
  1.2813 +        nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
  1.2814 +        if (! listener)
  1.2815 +            return NS_ERROR_UNEXPECTED;
  1.2816 +
  1.2817 +        // Add an observer to the parser; this'll get called when
  1.2818 +        // Necko fires its On[Start|Stop]Request() notifications,
  1.2819 +        // and will let us recover from a missing overlay.
  1.2820 +        ParserObserver* parserObserver =
  1.2821 +            new ParserObserver(this, mCurrentPrototype);
  1.2822 +        if (! parserObserver)
  1.2823 +            return NS_ERROR_OUT_OF_MEMORY;
  1.2824 +
  1.2825 +        NS_ADDREF(parserObserver);
  1.2826 +        parser->Parse(aURI, parserObserver);
  1.2827 +        NS_RELEASE(parserObserver);
  1.2828 +
  1.2829 +        nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  1.2830 +        nsCOMPtr<nsIChannel> channel;
  1.2831 +        rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, group);
  1.2832 +
  1.2833 +        if (NS_SUCCEEDED(rv)) {
  1.2834 +            // Set the owner of the channel to be our principal so
  1.2835 +            // that the overlay's JSObjects etc end up being created
  1.2836 +            // with the right principal and in the correct
  1.2837 +            // compartment.
  1.2838 +            channel->SetOwner(NodePrincipal());
  1.2839 +
  1.2840 +            rv = channel->AsyncOpen(listener, nullptr);
  1.2841 +        }
  1.2842 +
  1.2843 +        if (NS_FAILED(rv)) {
  1.2844 +            // Abandon this prototype
  1.2845 +            mCurrentPrototype = nullptr;
  1.2846 +
  1.2847 +            // The parser won't get an OnStartRequest and
  1.2848 +            // OnStopRequest, so it needs a Terminate.
  1.2849 +            parser->Terminate();
  1.2850 +
  1.2851 +            // Just move on to the next overlay.  NS_OpenURI could fail
  1.2852 +            // just because a channel could not be opened, which can happen
  1.2853 +            // if a file or chrome package does not exist.
  1.2854 +            ReportMissingOverlay(aURI);
  1.2855 +            
  1.2856 +            // XXX the error could indicate an internal error as well...
  1.2857 +            *aFailureFromContent = true;
  1.2858 +            return rv;
  1.2859 +        }
  1.2860 +
  1.2861 +        // If it's a 'chrome:' prototype document, then put it into
  1.2862 +        // the prototype cache; other XUL documents will be reloaded
  1.2863 +        // each time.  We must do this after NS_OpenURI and AsyncOpen,
  1.2864 +        // or chrome code will wrongly create a cached chrome channel
  1.2865 +        // instead of a real one. Prototypes are only cached when the
  1.2866 +        // document to be overlayed is chrome to avoid caching overlay
  1.2867 +        // scripts with incorrect principals, see bug 565610.
  1.2868 +        if (useXULCache && overlayIsChrome && documentIsChrome) {
  1.2869 +            nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
  1.2870 +        }
  1.2871 +
  1.2872 +        // Return to the main event loop and eagerly await the
  1.2873 +        // overlay load's completion. When the content sink
  1.2874 +        // completes, it will trigger an EndLoad(), which'll wind
  1.2875 +        // us back in ResumeWalk().
  1.2876 +        if (!aIsDynamic)
  1.2877 +            *aShouldReturn = true;
  1.2878 +    }
  1.2879 +    return NS_OK;
  1.2880 +}
  1.2881 +
  1.2882 +static PLDHashOperator
  1.2883 +FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr<nsIObserver>& aObserver, void* aClosure)
  1.2884 +{
  1.2885 +    aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get());
  1.2886 +
  1.2887 +    typedef nsInterfaceHashtable<nsURIHashKey,nsIObserver> table;
  1.2888 +    table* observers = static_cast<table*>(aClosure);
  1.2889 +    if (observers) {
  1.2890 +      observers->Remove(aKey);
  1.2891 +    }
  1.2892 +
  1.2893 +    return PL_DHASH_REMOVE;
  1.2894 +}
  1.2895 +
  1.2896 +nsresult
  1.2897 +XULDocument::ResumeWalk()
  1.2898 +{
  1.2899 +    // Walk the prototype and build the delegate content model. The
  1.2900 +    // walk is performed in a top-down, left-to-right fashion. That
  1.2901 +    // is, a parent is built before any of its children; a node is
  1.2902 +    // only built after all of its siblings to the left are fully
  1.2903 +    // constructed.
  1.2904 +    //
  1.2905 +    // It is interruptable so that transcluded documents (e.g.,
  1.2906 +    // <html:script src="..." />) can be properly re-loaded if the
  1.2907 +    // cached copy of the document becomes stale.
  1.2908 +    nsresult rv;
  1.2909 +    nsCOMPtr<nsIURI> overlayURI =
  1.2910 +        mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
  1.2911 +
  1.2912 +    while (1) {
  1.2913 +        // Begin (or resume) walking the current prototype.
  1.2914 +
  1.2915 +        while (mContextStack.Depth() > 0) {
  1.2916 +            // Look at the top of the stack to determine what we're
  1.2917 +            // currently working on.
  1.2918 +            // This will always be a node already constructed and
  1.2919 +            // inserted to the actual document.
  1.2920 +            nsXULPrototypeElement* proto;
  1.2921 +            nsCOMPtr<nsIContent> element;
  1.2922 +            int32_t indx; // all children of proto before indx (not
  1.2923 +                          // inclusive) have already been constructed
  1.2924 +            rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
  1.2925 +            if (NS_FAILED(rv)) return rv;
  1.2926 +
  1.2927 +            if (indx >= (int32_t)proto->mChildren.Length()) {
  1.2928 +                if (element) {
  1.2929 +                    // We've processed all of the prototype's children. If
  1.2930 +                    // we're in the master prototype, do post-order
  1.2931 +                    // document-level hookup. (An overlay will get its
  1.2932 +                    // document hookup done when it's successfully
  1.2933 +                    // resolved.)
  1.2934 +                    if (mState == eState_Master) {
  1.2935 +                        AddElementToDocumentPost(element->AsElement());
  1.2936 +
  1.2937 +                        if (element->NodeInfo()->Equals(nsGkAtoms::style,
  1.2938 +                                                        kNameSpaceID_XHTML) ||
  1.2939 +                            element->NodeInfo()->Equals(nsGkAtoms::style,
  1.2940 +                                                        kNameSpaceID_SVG)) {
  1.2941 +                            // XXX sucks that we have to do this -
  1.2942 +                            // see bug 370111
  1.2943 +                            nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
  1.2944 +                                do_QueryInterface(element);
  1.2945 +                            NS_ASSERTION(ssle, "<html:style> doesn't implement "
  1.2946 +                                               "nsIStyleSheetLinkingElement?");
  1.2947 +                            bool willNotify;
  1.2948 +                            bool isAlternate;
  1.2949 +                            ssle->UpdateStyleSheet(nullptr, &willNotify,
  1.2950 +                                                   &isAlternate);
  1.2951 +                        }
  1.2952 +                    }
  1.2953 +                }
  1.2954 +                // Now pop the context stack back up to the parent
  1.2955 +                // element and continue the prototype walk.
  1.2956 +                mContextStack.Pop();
  1.2957 +                continue;
  1.2958 +            }
  1.2959 +
  1.2960 +            // Grab the next child, and advance the current context stack
  1.2961 +            // to the next sibling to our right.
  1.2962 +            nsXULPrototypeNode* childproto = proto->mChildren[indx];
  1.2963 +            mContextStack.SetTopIndex(++indx);
  1.2964 +
  1.2965 +            // Whether we're in the "first ply" of an overlay:
  1.2966 +            // the "hookup" nodes. In the case !processingOverlayHookupNodes,
  1.2967 +            // we're in the master document -or- we're in an overlay, and far
  1.2968 +            // enough down into the overlay's content that we can simply build
  1.2969 +            // the delegates and attach them to the parent node.
  1.2970 +            bool processingOverlayHookupNodes = (mState == eState_Overlay) && 
  1.2971 +                                                  (mContextStack.Depth() == 1);
  1.2972 +
  1.2973 +            NS_ASSERTION(element || processingOverlayHookupNodes,
  1.2974 +                         "no element on context stack");
  1.2975 +
  1.2976 +            switch (childproto->mType) {
  1.2977 +            case nsXULPrototypeNode::eType_Element: {
  1.2978 +                // An 'element', which may contain more content.
  1.2979 +                nsXULPrototypeElement* protoele =
  1.2980 +                    static_cast<nsXULPrototypeElement*>(childproto);
  1.2981 +
  1.2982 +                nsRefPtr<Element> child;
  1.2983 +
  1.2984 +                if (!processingOverlayHookupNodes) {
  1.2985 +                    rv = CreateElementFromPrototype(protoele,
  1.2986 +                                                    getter_AddRefs(child),
  1.2987 +                                                    false);
  1.2988 +                    if (NS_FAILED(rv)) return rv;
  1.2989 +
  1.2990 +                    // ...and append it to the content model.
  1.2991 +                    rv = element->AppendChildTo(child, false);
  1.2992 +                    if (NS_FAILED(rv)) return rv;
  1.2993 +
  1.2994 +                    // If we're only restoring persisted things on
  1.2995 +                    // some elements, store the ID here to do that.
  1.2996 +                    if (mRestrictPersistence) {
  1.2997 +                        nsIAtom* id = child->GetID();
  1.2998 +                        if (id) {
  1.2999 +                            mPersistenceIds.PutEntry(nsDependentAtomString(id));
  1.3000 +                        }
  1.3001 +                    }
  1.3002 +
  1.3003 +                    // do pre-order document-level hookup, but only if
  1.3004 +                    // we're in the master document. For an overlay,
  1.3005 +                    // this will happen when the overlay is
  1.3006 +                    // successfully resolved.
  1.3007 +                    if (mState == eState_Master)
  1.3008 +                        AddElementToDocumentPre(child);
  1.3009 +                }
  1.3010 +                else {
  1.3011 +                    // We're in the "first ply" of an overlay: the
  1.3012 +                    // "hookup" nodes. Create an 'overlay' element so
  1.3013 +                    // that we can continue to build content, and
  1.3014 +                    // enter a forward reference so we can hook it up
  1.3015 +                    // later.
  1.3016 +                    rv = CreateOverlayElement(protoele, getter_AddRefs(child));
  1.3017 +                    if (NS_FAILED(rv)) return rv;
  1.3018 +                }
  1.3019 +
  1.3020 +                // If it has children, push the element onto the context
  1.3021 +                // stack and begin to process them.
  1.3022 +                if (protoele->mChildren.Length() > 0) {
  1.3023 +                    rv = mContextStack.Push(protoele, child);
  1.3024 +                    if (NS_FAILED(rv)) return rv;
  1.3025 +                }
  1.3026 +                else {
  1.3027 +                    if (mState == eState_Master) {
  1.3028 +                        // If there are no children, and we're in the
  1.3029 +                        // master document, do post-order document hookup
  1.3030 +                        // immediately.
  1.3031 +                        AddElementToDocumentPost(child);
  1.3032 +                    }
  1.3033 +                }
  1.3034 +            }
  1.3035 +            break;
  1.3036 +
  1.3037 +            case nsXULPrototypeNode::eType_Script: {
  1.3038 +                // A script reference. Execute the script immediately;
  1.3039 +                // this may have side effects in the content model.
  1.3040 +                nsXULPrototypeScript* scriptproto =
  1.3041 +                    static_cast<nsXULPrototypeScript*>(childproto);
  1.3042 +
  1.3043 +                if (scriptproto->mSrcURI) {
  1.3044 +                    // A transcluded script reference; this may
  1.3045 +                    // "block" our prototype walk if the script isn't
  1.3046 +                    // cached, or the cached copy of the script is
  1.3047 +                    // stale and must be reloaded.
  1.3048 +                    bool blocked;
  1.3049 +                    rv = LoadScript(scriptproto, &blocked);
  1.3050 +                    // If the script cannot be loaded, just keep going!
  1.3051 +
  1.3052 +                    if (NS_SUCCEEDED(rv) && blocked)
  1.3053 +                        return NS_OK;
  1.3054 +                }
  1.3055 +                else if (scriptproto->GetScriptObject()) {
  1.3056 +                    // An inline script
  1.3057 +                    rv = ExecuteScript(scriptproto);
  1.3058 +                    if (NS_FAILED(rv)) return rv;
  1.3059 +                }
  1.3060 +            }
  1.3061 +            break;
  1.3062 +
  1.3063 +            case nsXULPrototypeNode::eType_Text: {
  1.3064 +                // A simple text node.
  1.3065 +
  1.3066 +                if (!processingOverlayHookupNodes) {
  1.3067 +                    // This does mean that text nodes that are direct children
  1.3068 +                    // of <overlay> get ignored.
  1.3069 +
  1.3070 +                    nsRefPtr<nsTextNode> text =
  1.3071 +                        new nsTextNode(mNodeInfoManager);
  1.3072 +
  1.3073 +                    nsXULPrototypeText* textproto =
  1.3074 +                        static_cast<nsXULPrototypeText*>(childproto);
  1.3075 +                    text->SetText(textproto->mValue, false);
  1.3076 +
  1.3077 +                    rv = element->AppendChildTo(text, false);
  1.3078 +                    NS_ENSURE_SUCCESS(rv, rv);
  1.3079 +                }
  1.3080 +            }
  1.3081 +            break;
  1.3082 +
  1.3083 +            case nsXULPrototypeNode::eType_PI: {
  1.3084 +                nsXULPrototypePI* piProto =
  1.3085 +                    static_cast<nsXULPrototypePI*>(childproto);
  1.3086 +
  1.3087 +                // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
  1.3088 +                // outside the prolog, like they used to. Issue a warning.
  1.3089 +
  1.3090 +                if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
  1.3091 +                    piProto->mTarget.EqualsLiteral("xul-overlay")) {
  1.3092 +
  1.3093 +                    const char16_t* params[] = { piProto->mTarget.get() };
  1.3094 +
  1.3095 +                    nsContentUtils::ReportToConsole(
  1.3096 +                                        nsIScriptError::warningFlag,
  1.3097 +                                        NS_LITERAL_CSTRING("XUL Document"), nullptr,
  1.3098 +                                        nsContentUtils::eXUL_PROPERTIES,
  1.3099 +                                        "PINotInProlog",
  1.3100 +                                        params, ArrayLength(params),
  1.3101 +                                        overlayURI);
  1.3102 +                }
  1.3103 +
  1.3104 +                nsIContent* parent = processingOverlayHookupNodes ?
  1.3105 +                    GetRootElement() : element.get();
  1.3106 +
  1.3107 +                if (parent) {
  1.3108 +                    // an inline script could have removed the root element
  1.3109 +                    rv = CreateAndInsertPI(piProto, parent,
  1.3110 +                                           parent->GetChildCount());
  1.3111 +                    NS_ENSURE_SUCCESS(rv, rv);
  1.3112 +                }
  1.3113 +            }
  1.3114 +            break;
  1.3115 +
  1.3116 +            default:
  1.3117 +                NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
  1.3118 +            }
  1.3119 +        }
  1.3120 +
  1.3121 +        // Once we get here, the context stack will have been
  1.3122 +        // depleted. That means that the entire prototype has been
  1.3123 +        // walked and content has been constructed.
  1.3124 +
  1.3125 +        // If we're not already, mark us as now processing overlays.
  1.3126 +        mState = eState_Overlay;
  1.3127 +
  1.3128 +        // If there are no overlay URIs, then we're done.
  1.3129 +        uint32_t count = mUnloadedOverlays.Length();
  1.3130 +        if (! count)
  1.3131 +            break;
  1.3132 +
  1.3133 +        nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1];
  1.3134 +        mUnloadedOverlays.RemoveElementAt(count - 1);
  1.3135 +
  1.3136 +        bool shouldReturn, failureFromContent;
  1.3137 +        rv = LoadOverlayInternal(uri, false, &shouldReturn,
  1.3138 +                                 &failureFromContent);
  1.3139 +        if (failureFromContent)
  1.3140 +            // The failure |rv| was the result of a problem in the content
  1.3141 +            // rather than an unexpected problem in our implementation, so
  1.3142 +            // just continue with the next overlay.
  1.3143 +            continue;
  1.3144 +        if (NS_FAILED(rv))
  1.3145 +            return rv;
  1.3146 +        if (mOverlayLoadObservers) {
  1.3147 +            nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI);
  1.3148 +            if (obs) {
  1.3149 +                // This overlay has an unloaded overlay, so it will never
  1.3150 +                // notify. The best we can do is to notify for the unloaded
  1.3151 +                // overlay instead, assuming nobody is already notifiable
  1.3152 +                // for it. Note that this will confuse the observer.
  1.3153 +                if (!mOverlayLoadObservers->GetWeak(uri))
  1.3154 +                    mOverlayLoadObservers->Put(uri, obs);
  1.3155 +                mOverlayLoadObservers->Remove(overlayURI);
  1.3156 +            }
  1.3157 +        }
  1.3158 +        if (shouldReturn)
  1.3159 +            return NS_OK;
  1.3160 +        overlayURI.swap(uri);
  1.3161 +    }
  1.3162 +
  1.3163 +    // If we get here, there is nothing left for us to walk. The content
  1.3164 +    // model is built and ready for layout.
  1.3165 +    rv = ResolveForwardReferences();
  1.3166 +    if (NS_FAILED(rv)) return rv;
  1.3167 +
  1.3168 +    ApplyPersistentAttributes();
  1.3169 +
  1.3170 +    mStillWalking = false;
  1.3171 +    if (mPendingSheets == 0) {
  1.3172 +        rv = DoneWalking();
  1.3173 +    }
  1.3174 +    return rv;
  1.3175 +}
  1.3176 +
  1.3177 +nsresult
  1.3178 +XULDocument::DoneWalking()
  1.3179 +{
  1.3180 +    NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded");
  1.3181 +    NS_PRECONDITION(!mStillWalking, "walk not done");
  1.3182 +
  1.3183 +    // XXXldb This is where we should really be setting the chromehidden
  1.3184 +    // attribute.
  1.3185 +
  1.3186 +    uint32_t count = mOverlaySheets.Length();
  1.3187 +    for (uint32_t i = 0; i < count; ++i) {
  1.3188 +        AddStyleSheet(mOverlaySheets[i]);
  1.3189 +    }
  1.3190 +    mOverlaySheets.Clear();
  1.3191 +
  1.3192 +    if (!mDocumentLoaded) {
  1.3193 +        // Make sure we don't reenter here from StartLayout().  Note that
  1.3194 +        // setting mDocumentLoaded to true here means that if StartLayout()
  1.3195 +        // causes ResumeWalk() to be reentered, we'll take the other branch of
  1.3196 +        // the |if (!mDocumentLoaded)| check above and since
  1.3197 +        // mInitialLayoutComplete will be false will follow the else branch
  1.3198 +        // there too.  See the big comment there for how such reentry can
  1.3199 +        // happen.
  1.3200 +        mDocumentLoaded = true;
  1.3201 +
  1.3202 +        NotifyPossibleTitleChange(false);
  1.3203 +
  1.3204 +        // Before starting layout, check whether we're a toplevel chrome
  1.3205 +        // window.  If we are, set our chrome flags now, so that we don't have
  1.3206 +        // to restyle the whole frame tree after StartLayout.
  1.3207 +        nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
  1.3208 +        if (item) {
  1.3209 +            nsCOMPtr<nsIDocShellTreeOwner> owner;
  1.3210 +            item->GetTreeOwner(getter_AddRefs(owner));
  1.3211 +            nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
  1.3212 +            if (xulWin) {
  1.3213 +                nsCOMPtr<nsIDocShell> xulWinShell;
  1.3214 +                xulWin->GetDocShell(getter_AddRefs(xulWinShell));
  1.3215 +                if (SameCOMIdentity(xulWinShell, item)) {
  1.3216 +                    // We're the chrome document!  Apply our chrome flags now.
  1.3217 +                    xulWin->ApplyChromeFlags();
  1.3218 +                }
  1.3219 +            }
  1.3220 +        }
  1.3221 +
  1.3222 +        StartLayout();
  1.3223 +
  1.3224 +        if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
  1.3225 +            nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype);
  1.3226 +
  1.3227 +        NS_ASSERTION(mDelayFrameLoaderInitialization,
  1.3228 +                     "mDelayFrameLoaderInitialization should be true!");
  1.3229 +        mDelayFrameLoaderInitialization = false;
  1.3230 +        NS_WARN_IF_FALSE(mUpdateNestLevel == 0,
  1.3231 +                         "Constructing XUL document in middle of an update?");
  1.3232 +        if (mUpdateNestLevel == 0) {
  1.3233 +            MaybeInitializeFinalizeFrameLoaders();
  1.3234 +        }
  1.3235 +
  1.3236 +        NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
  1.3237 +
  1.3238 +        // DispatchContentLoadedEvents undoes the onload-blocking we
  1.3239 +        // did in PrepareToWalk().
  1.3240 +        DispatchContentLoadedEvents();
  1.3241 +
  1.3242 +        mInitialLayoutComplete = true;
  1.3243 +
  1.3244 +        // Walk the set of pending load notifications and notify any observers.
  1.3245 +        // See below for detail.
  1.3246 +        if (mPendingOverlayLoadNotifications)
  1.3247 +            mPendingOverlayLoadNotifications->Enumerate(
  1.3248 +                FirePendingMergeNotification, mOverlayLoadObservers.get());
  1.3249 +    }
  1.3250 +    else {
  1.3251 +        if (mOverlayLoadObservers) {
  1.3252 +            nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI();
  1.3253 +            nsCOMPtr<nsIObserver> obs;
  1.3254 +            if (mInitialLayoutComplete) {
  1.3255 +                // We have completed initial layout, so just send the notification.
  1.3256 +                mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  1.3257 +                if (obs)
  1.3258 +                    obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
  1.3259 +                mOverlayLoadObservers->Remove(overlayURI);
  1.3260 +            }
  1.3261 +            else {
  1.3262 +                // If we have not yet displayed the document for the first time 
  1.3263 +                // (i.e. we came in here as the result of a dynamic overlay load
  1.3264 +                // which was spawned by a binding-attached event caused by 
  1.3265 +                // StartLayout() on the master prototype - we must remember that
  1.3266 +                // this overlay has been merged and tell the listeners after 
  1.3267 +                // StartLayout() is completely finished rather than doing so 
  1.3268 +                // immediately - otherwise we may be executing code that needs to
  1.3269 +                // access XBL Binding implementations on nodes for which frames 
  1.3270 +                // have not yet been constructed because their bindings have not
  1.3271 +                // yet been attached. This can be a race condition because dynamic
  1.3272 +                // overlay loading can take varying amounts of time depending on
  1.3273 +                // whether or not the overlay prototype is in the XUL cache. The
  1.3274 +                // most likely effect of this bug is odd UI initialization due to
  1.3275 +                // methods and properties that do not work.
  1.3276 +                // XXXbz really, we shouldn't be firing binding constructors
  1.3277 +                // until after StartLayout returns!
  1.3278 +
  1.3279 +                if (!mPendingOverlayLoadNotifications) {
  1.3280 +                    mPendingOverlayLoadNotifications =
  1.3281 +                        new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  1.3282 +                }
  1.3283 +                
  1.3284 +                mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs));
  1.3285 +                if (!obs) {
  1.3286 +                    mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  1.3287 +                    NS_ASSERTION(obs, "null overlay load observer?");
  1.3288 +                    mPendingOverlayLoadNotifications->Put(overlayURI, obs);
  1.3289 +                }
  1.3290 +            }
  1.3291 +        }
  1.3292 +    }
  1.3293 +
  1.3294 +    return NS_OK;
  1.3295 +}
  1.3296 +
  1.3297 +NS_IMETHODIMP
  1.3298 +XULDocument::StyleSheetLoaded(nsCSSStyleSheet* aSheet,
  1.3299 +                              bool aWasAlternate,
  1.3300 +                              nsresult aStatus)
  1.3301 +{
  1.3302 +    if (!aWasAlternate) {
  1.3303 +        // Don't care about when alternate sheets finish loading
  1.3304 +
  1.3305 +        NS_ASSERTION(mPendingSheets > 0,
  1.3306 +            "Unexpected StyleSheetLoaded notification");
  1.3307 +
  1.3308 +        --mPendingSheets;
  1.3309 +
  1.3310 +        if (!mStillWalking && mPendingSheets == 0) {
  1.3311 +            return DoneWalking();
  1.3312 +        }
  1.3313 +    }
  1.3314 +
  1.3315 +    return NS_OK;
  1.3316 +}
  1.3317 +
  1.3318 +void
  1.3319 +XULDocument::MaybeBroadcast()
  1.3320 +{
  1.3321 +    // Only broadcast when not in an update and when safe to run scripts.
  1.3322 +    if (mUpdateNestLevel == 0 &&
  1.3323 +        (mDelayedAttrChangeBroadcasts.Length() ||
  1.3324 +         mDelayedBroadcasters.Length())) {
  1.3325 +        if (!nsContentUtils::IsSafeToRunScript()) {
  1.3326 +            if (!mInDestructor) {
  1.3327 +                nsContentUtils::AddScriptRunner(
  1.3328 +                    NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast));
  1.3329 +            }
  1.3330 +            return;
  1.3331 +        }
  1.3332 +        if (!mHandlingDelayedAttrChange) {
  1.3333 +            mHandlingDelayedAttrChange = true;
  1.3334 +            for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
  1.3335 +                nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
  1.3336 +                if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
  1.3337 +                    nsCOMPtr<nsIContent> listener =
  1.3338 +                        do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
  1.3339 +                    nsString value = mDelayedAttrChangeBroadcasts[i].mAttr;
  1.3340 +                    if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
  1.3341 +                        listener->SetAttr(kNameSpaceID_None, attrName, value,
  1.3342 +                                          true);
  1.3343 +                    } else {
  1.3344 +                        listener->UnsetAttr(kNameSpaceID_None, attrName,
  1.3345 +                                            true);
  1.3346 +                    }
  1.3347 +                }
  1.3348 +                ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
  1.3349 +                                             mDelayedAttrChangeBroadcasts[i].mListener,
  1.3350 +                                             attrName);
  1.3351 +            }
  1.3352 +            mDelayedAttrChangeBroadcasts.Clear();
  1.3353 +            mHandlingDelayedAttrChange = false;
  1.3354 +        }
  1.3355 +
  1.3356 +        uint32_t length = mDelayedBroadcasters.Length();
  1.3357 +        if (length) {
  1.3358 +            bool oldValue = mHandlingDelayedBroadcasters;
  1.3359 +            mHandlingDelayedBroadcasters = true;
  1.3360 +            nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
  1.3361 +            mDelayedBroadcasters.SwapElements(delayedBroadcasters);
  1.3362 +            for (uint32_t i = 0; i < length; ++i) {
  1.3363 +                SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
  1.3364 +                                             delayedBroadcasters[i].mListener,
  1.3365 +                                             delayedBroadcasters[i].mAttr);
  1.3366 +            }
  1.3367 +            mHandlingDelayedBroadcasters = oldValue;
  1.3368 +        }
  1.3369 +    }
  1.3370 +}
  1.3371 +
  1.3372 +void
  1.3373 +XULDocument::EndUpdate(nsUpdateType aUpdateType)
  1.3374 +{
  1.3375 +    XMLDocument::EndUpdate(aUpdateType);
  1.3376 +
  1.3377 +    MaybeBroadcast();
  1.3378 +}
  1.3379 +
  1.3380 +void
  1.3381 +XULDocument::ReportMissingOverlay(nsIURI* aURI)
  1.3382 +{
  1.3383 +    NS_PRECONDITION(aURI, "Must have a URI");
  1.3384 +    
  1.3385 +    nsAutoCString spec;
  1.3386 +    aURI->GetSpec(spec);
  1.3387 +
  1.3388 +    NS_ConvertUTF8toUTF16 utfSpec(spec);
  1.3389 +    const char16_t* params[] = { utfSpec.get() };
  1.3390 +    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  1.3391 +                                    NS_LITERAL_CSTRING("XUL Document"), this,
  1.3392 +                                    nsContentUtils::eXUL_PROPERTIES,
  1.3393 +                                    "MissingOverlay",
  1.3394 +                                    params, ArrayLength(params));
  1.3395 +}
  1.3396 +
  1.3397 +nsresult
  1.3398 +XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
  1.3399 +{
  1.3400 +    // Load a transcluded script
  1.3401 +    nsresult rv;
  1.3402 +
  1.3403 +    bool isChromeDoc = IsChromeURI(mDocumentURI);
  1.3404 +
  1.3405 +    if (isChromeDoc && aScriptProto->GetScriptObject()) {
  1.3406 +        rv = ExecuteScript(aScriptProto);
  1.3407 +
  1.3408 +        // Ignore return value from execution, and don't block
  1.3409 +        *aBlock = false;
  1.3410 +        return NS_OK;
  1.3411 +    }
  1.3412 +
  1.3413 +    // Try the XUL script cache, in case two XUL documents source the same
  1.3414 +    // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
  1.3415 +    // XXXbe the cache relies on aScriptProto's GC root!
  1.3416 +    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  1.3417 +
  1.3418 +    if (isChromeDoc && useXULCache) {
  1.3419 +        JSScript* newScriptObject =
  1.3420 +            nsXULPrototypeCache::GetInstance()->GetScript(
  1.3421 +                                   aScriptProto->mSrcURI);
  1.3422 +        if (newScriptObject) {
  1.3423 +            // The script language for a proto must remain constant - we
  1.3424 +            // can't just change it for this unexpected language.
  1.3425 +            aScriptProto->Set(newScriptObject);
  1.3426 +        }
  1.3427 +
  1.3428 +        if (aScriptProto->GetScriptObject()) {
  1.3429 +            rv = ExecuteScript(aScriptProto);
  1.3430 +
  1.3431 +            // Ignore return value from execution, and don't block
  1.3432 +            *aBlock = false;
  1.3433 +            return NS_OK;
  1.3434 +        }
  1.3435 +    }
  1.3436 +
  1.3437 +    // Allow security manager and content policies to veto the load. Note that
  1.3438 +    // at this point we already lost context information of the script.
  1.3439 +    rv = nsScriptLoader::ShouldLoadScript(
  1.3440 +                            this,
  1.3441 +                            static_cast<nsIDocument*>(this),
  1.3442 +                            aScriptProto->mSrcURI,
  1.3443 +                            NS_LITERAL_STRING("application/x-javascript"));
  1.3444 +    if (NS_FAILED(rv)) {
  1.3445 +      *aBlock = false;
  1.3446 +      return rv;
  1.3447 +    }
  1.3448 +
  1.3449 +    // Release script objects from FastLoad since we decided against using them
  1.3450 +    aScriptProto->UnlinkJSObjects();
  1.3451 +
  1.3452 +    // Set the current script prototype so that OnStreamComplete can report
  1.3453 +    // the right file if there are errors in the script.
  1.3454 +    NS_ASSERTION(!mCurrentScriptProto,
  1.3455 +                 "still loading a script when starting another load?");
  1.3456 +    mCurrentScriptProto = aScriptProto;
  1.3457 +
  1.3458 +    if (aScriptProto->mSrcLoading) {
  1.3459 +        // Another XULDocument load has started, which is still in progress.
  1.3460 +        // Remember to ResumeWalk this document when the load completes.
  1.3461 +        mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
  1.3462 +        aScriptProto->mSrcLoadWaiters = this;
  1.3463 +        NS_ADDREF_THIS();
  1.3464 +    }
  1.3465 +    else {
  1.3466 +        nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  1.3467 +
  1.3468 +        // Note: the loader will keep itself alive while it's loading.
  1.3469 +        nsCOMPtr<nsIStreamLoader> loader;
  1.3470 +        rv = NS_NewStreamLoader(getter_AddRefs(loader), aScriptProto->mSrcURI,
  1.3471 +                                this, nullptr, group);
  1.3472 +        if (NS_FAILED(rv)) {
  1.3473 +            mCurrentScriptProto = nullptr;
  1.3474 +            return rv;
  1.3475 +        }
  1.3476 +
  1.3477 +        aScriptProto->mSrcLoading = true;
  1.3478 +    }
  1.3479 +
  1.3480 +    // Block until OnStreamComplete resumes us.
  1.3481 +    *aBlock = true;
  1.3482 +    return NS_OK;
  1.3483 +}
  1.3484 +
  1.3485 +NS_IMETHODIMP
  1.3486 +XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
  1.3487 +                              nsISupports* context,
  1.3488 +                              nsresult aStatus,
  1.3489 +                              uint32_t stringLen,
  1.3490 +                              const uint8_t* string)
  1.3491 +{
  1.3492 +    nsCOMPtr<nsIRequest> request;
  1.3493 +    aLoader->GetRequest(getter_AddRefs(request));
  1.3494 +    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  1.3495 +
  1.3496 +#ifdef DEBUG
  1.3497 +    // print a load error on bad status
  1.3498 +    if (NS_FAILED(aStatus)) {
  1.3499 +        if (channel) {
  1.3500 +            nsCOMPtr<nsIURI> uri;
  1.3501 +            channel->GetURI(getter_AddRefs(uri));
  1.3502 +            if (uri) {
  1.3503 +                nsAutoCString uriSpec;
  1.3504 +                uri->GetSpec(uriSpec);
  1.3505 +                printf("Failed to load %s\n", uriSpec.get());
  1.3506 +            }
  1.3507 +        }
  1.3508 +    }
  1.3509 +#endif
  1.3510 +
  1.3511 +    // This is the completion routine that will be called when a
  1.3512 +    // transcluded script completes. Compile and execute the script
  1.3513 +    // if the load was successful, then continue building content
  1.3514 +    // from the prototype.
  1.3515 +    nsresult rv = aStatus;
  1.3516 +
  1.3517 +    NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
  1.3518 +                 "script source not loading on unichar stream complete?");
  1.3519 +    if (!mCurrentScriptProto) {
  1.3520 +        // XXX Wallpaper for bug 270042
  1.3521 +        return NS_OK;
  1.3522 +    }
  1.3523 +
  1.3524 +    if (NS_SUCCEEDED(aStatus)) {
  1.3525 +        // If the including XUL document is a FastLoad document, and we're
  1.3526 +        // compiling an out-of-line script (one with src=...), then we must
  1.3527 +        // be writing a new FastLoad file.  If we were reading this script
  1.3528 +        // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
  1.3529 +        // nsXULContentSink.cpp) would have already deserialized a non-null
  1.3530 +        // script->mScriptObject, causing control flow at the top of LoadScript
  1.3531 +        // not to reach here.
  1.3532 +        nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
  1.3533 +
  1.3534 +        // XXX should also check nsIHttpChannel::requestSucceeded
  1.3535 +
  1.3536 +        MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
  1.3537 +                                            !mOffThreadCompileStringBuf),
  1.3538 +                   "XULDocument can't load multiple scripts at once");
  1.3539 +
  1.3540 +        rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen,
  1.3541 +                                            EmptyString(), this,
  1.3542 +                                            mOffThreadCompileStringBuf,
  1.3543 +                                            mOffThreadCompileStringLength);
  1.3544 +        if (NS_SUCCEEDED(rv)) {
  1.3545 +            // Attempt to give ownership of the buffer to the JS engine.  If
  1.3546 +            // we hit offthread compilation, however, we will have to take it
  1.3547 +            // back below in order to keep the memory alive until compilation
  1.3548 +            // completes.
  1.3549 +            JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
  1.3550 +                                          mOffThreadCompileStringLength,
  1.3551 +                                          JS::SourceBufferHolder::GiveOwnership);
  1.3552 +            mOffThreadCompileStringBuf = nullptr;
  1.3553 +            mOffThreadCompileStringLength = 0;
  1.3554 +
  1.3555 +            rv = mCurrentScriptProto->Compile(srcBuf,
  1.3556 +                                              uri, 1, this,
  1.3557 +                                              mMasterPrototype,
  1.3558 +                                              this);
  1.3559 +            if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) {
  1.3560 +                // We will be notified via OnOffThreadCompileComplete when the
  1.3561 +                // compile finishes. Keep the contents of the compiled script
  1.3562 +                // alive until the compilation finishes.
  1.3563 +                mOffThreadCompiling = true;
  1.3564 +                // If the JS engine did not take the source buffer, then take
  1.3565 +                // it back here to ensure it remains alive.
  1.3566 +                mOffThreadCompileStringBuf = srcBuf.take();
  1.3567 +                if (mOffThreadCompileStringBuf) {
  1.3568 +                  mOffThreadCompileStringLength = srcBuf.length();
  1.3569 +                }
  1.3570 +                BlockOnload();
  1.3571 +                return NS_OK;
  1.3572 +            }
  1.3573 +        }
  1.3574 +    }
  1.3575 +
  1.3576 +    return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
  1.3577 +}
  1.3578 +
  1.3579 +NS_IMETHODIMP
  1.3580 +XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
  1.3581 +{
  1.3582 +    // When compiling off thread the script will not have been attached to the
  1.3583 +    // script proto yet.
  1.3584 +    if (aScript && !mCurrentScriptProto->GetScriptObject())
  1.3585 +        mCurrentScriptProto->Set(aScript);
  1.3586 +
  1.3587 +    // Allow load events to be fired once off thread compilation finishes.
  1.3588 +    if (mOffThreadCompiling) {
  1.3589 +        mOffThreadCompiling = false;
  1.3590 +        UnblockOnload(false);
  1.3591 +    }
  1.3592 +
  1.3593 +    // After compilation finishes the script's characters are no longer needed.
  1.3594 +    if (mOffThreadCompileStringBuf) {
  1.3595 +      js_free(mOffThreadCompileStringBuf);
  1.3596 +      mOffThreadCompileStringBuf = nullptr;
  1.3597 +      mOffThreadCompileStringLength = 0;
  1.3598 +    }
  1.3599 +
  1.3600 +    // Clear mCurrentScriptProto now, but save it first for use below in
  1.3601 +    // the execute code, and in the while loop that resumes walks of other
  1.3602 +    // documents that raced to load this script.
  1.3603 +    nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
  1.3604 +    mCurrentScriptProto = nullptr;
  1.3605 +
  1.3606 +    // Clear the prototype's loading flag before executing the script or
  1.3607 +    // resuming document walks, in case any of those control flows starts a
  1.3608 +    // new script load.
  1.3609 +    scriptProto->mSrcLoading = false;
  1.3610 +
  1.3611 +    nsresult rv = aStatus;
  1.3612 +    if (NS_SUCCEEDED(rv)) {
  1.3613 +        rv = ExecuteScript(scriptProto);
  1.3614 +
  1.3615 +        // If the XUL cache is enabled, save the script object there in
  1.3616 +        // case different XUL documents source the same script.
  1.3617 +        //
  1.3618 +        // But don't save the script in the cache unless the master XUL
  1.3619 +        // document URL is a chrome: URL.  It is valid for a URL such as
  1.3620 +        // about:config to translate into a master document URL, whose
  1.3621 +        // prototype document nodes -- including prototype scripts that
  1.3622 +        // hold GC roots protecting their mJSObject pointers -- are not
  1.3623 +        // cached in the XUL prototype cache.  See StartDocumentLoad,
  1.3624 +        // the fillXULCache logic.
  1.3625 +        //
  1.3626 +        // A document such as about:config is free to load a script via
  1.3627 +        // a URL such as chrome://global/content/config.js, and we must
  1.3628 +        // not cache that script object without a prototype cache entry
  1.3629 +        // containing a companion nsXULPrototypeScript node that owns a
  1.3630 +        // GC root protecting the script object.  Otherwise, the script
  1.3631 +        // cache entry will dangle once the uncached prototype document
  1.3632 +        // is released when its owning XULDocument is unloaded.
  1.3633 +        //
  1.3634 +        // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
  1.3635 +        // the true crime story.)
  1.3636 +        bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  1.3637 +  
  1.3638 +        if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->GetScriptObject()) {
  1.3639 +            nsXULPrototypeCache::GetInstance()->PutScript(
  1.3640 +                               scriptProto->mSrcURI,
  1.3641 +                               scriptProto->GetScriptObject());
  1.3642 +        }
  1.3643 +
  1.3644 +        if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
  1.3645 +            // If we are loading an overlay script, try to serialize
  1.3646 +            // it to the FastLoad file here.  Master scripts will be
  1.3647 +            // serialized when the master prototype document gets
  1.3648 +            // written, at the bottom of ResumeWalk.  That way, master
  1.3649 +            // out-of-line scripts are serialized in the same order that
  1.3650 +            // they'll be read, in the FastLoad file, which reduces the
  1.3651 +            // number of seeks that dump the underlying stream's buffer.
  1.3652 +            //
  1.3653 +            // Ignore the return value, as we don't need to propagate
  1.3654 +            // a failure to write to the FastLoad file, because this
  1.3655 +            // method aborts that whole process on error.
  1.3656 +            scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
  1.3657 +        }
  1.3658 +        // ignore any evaluation errors
  1.3659 +    }
  1.3660 +
  1.3661 +    rv = ResumeWalk();
  1.3662 +
  1.3663 +    // Load a pointer to the prototype-script's list of XULDocuments who
  1.3664 +    // raced to load the same script
  1.3665 +    XULDocument** docp = &scriptProto->mSrcLoadWaiters;
  1.3666 +
  1.3667 +    // Resume walking other documents that waited for this one's load, first
  1.3668 +    // executing the script we just compiled, in each doc's script context
  1.3669 +    XULDocument* doc;
  1.3670 +    while ((doc = *docp) != nullptr) {
  1.3671 +        NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
  1.3672 +                     "waiting for wrong script to load?");
  1.3673 +        doc->mCurrentScriptProto = nullptr;
  1.3674 +
  1.3675 +        // Unlink doc from scriptProto's list before executing and resuming
  1.3676 +        *docp = doc->mNextSrcLoadWaiter;
  1.3677 +        doc->mNextSrcLoadWaiter = nullptr;
  1.3678 +
  1.3679 +        // Execute only if we loaded and compiled successfully, then resume
  1.3680 +        if (NS_SUCCEEDED(aStatus) && scriptProto->GetScriptObject()) {
  1.3681 +            doc->ExecuteScript(scriptProto);
  1.3682 +        }
  1.3683 +        doc->ResumeWalk();
  1.3684 +        NS_RELEASE(doc);
  1.3685 +    }
  1.3686 +
  1.3687 +    return rv;
  1.3688 +}
  1.3689 +
  1.3690 +nsresult
  1.3691 +XULDocument::ExecuteScript(nsIScriptContext * aContext,
  1.3692 +                           JS::Handle<JSScript*> aScriptObject)
  1.3693 +{
  1.3694 +    NS_PRECONDITION(aScriptObject != nullptr && aContext != nullptr, "null ptr");
  1.3695 +    if (! aScriptObject || ! aContext)
  1.3696 +        return NS_ERROR_NULL_POINTER;
  1.3697 +
  1.3698 +    NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
  1.3699 +
  1.3700 +    // Execute the precompiled script with the given version
  1.3701 +    nsAutoMicroTask mt;
  1.3702 +    JSContext *cx = aContext->GetNativeContext();
  1.3703 +    AutoCxPusher pusher(cx);
  1.3704 +    JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
  1.3705 +    NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
  1.3706 +    NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK);
  1.3707 +    JS::ExposeObjectToActiveJS(global);
  1.3708 +    xpc_UnmarkGrayScript(aScriptObject);
  1.3709 +    JSAutoCompartment ac(cx, global);
  1.3710 +
  1.3711 +    // The script is in the compilation scope. Clone it into the target scope
  1.3712 +    // and execute it.
  1.3713 +    if (!JS::CloneAndExecuteScript(cx, global, aScriptObject))
  1.3714 +        nsJSUtils::ReportPendingException(cx);
  1.3715 +    return NS_OK;
  1.3716 +}
  1.3717 +
  1.3718 +nsresult
  1.3719 +XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
  1.3720 +{
  1.3721 +    NS_PRECONDITION(aScript != nullptr, "null ptr");
  1.3722 +    NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
  1.3723 +    NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
  1.3724 +
  1.3725 +    nsresult rv;
  1.3726 +    rv = mScriptGlobalObject->EnsureScriptEnvironment();
  1.3727 +    NS_ENSURE_SUCCESS(rv, rv);
  1.3728 +
  1.3729 +    nsCOMPtr<nsIScriptContext> context =
  1.3730 +      mScriptGlobalObject->GetScriptContext();
  1.3731 +    // failure getting a script context is fatal.
  1.3732 +    NS_ENSURE_TRUE(context != nullptr, NS_ERROR_UNEXPECTED);
  1.3733 +
  1.3734 +    if (aScript->GetScriptObject())
  1.3735 +        rv = ExecuteScript(context, aScript->GetScriptObject());
  1.3736 +    else
  1.3737 +        rv = NS_ERROR_UNEXPECTED;
  1.3738 +    return rv;
  1.3739 +}
  1.3740 +
  1.3741 +
  1.3742 +nsresult
  1.3743 +XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
  1.3744 +                                        Element** aResult,
  1.3745 +                                        bool aIsRoot)
  1.3746 +{
  1.3747 +    // Create a content model element from a prototype element.
  1.3748 +    NS_PRECONDITION(aPrototype != nullptr, "null ptr");
  1.3749 +    if (! aPrototype)
  1.3750 +        return NS_ERROR_NULL_POINTER;
  1.3751 +
  1.3752 +    *aResult = nullptr;
  1.3753 +    nsresult rv = NS_OK;
  1.3754 +
  1.3755 +#ifdef PR_LOGGING
  1.3756 +    if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  1.3757 +        PR_LOG(gXULLog, PR_LOG_NOTICE,
  1.3758 +               ("xul: creating <%s> from prototype",
  1.3759 +                NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
  1.3760 +    }
  1.3761 +#endif
  1.3762 +
  1.3763 +    nsRefPtr<Element> result;
  1.3764 +
  1.3765 +    if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
  1.3766 +        // If it's a XUL element, it'll be lightweight until somebody
  1.3767 +        // monkeys with it.
  1.3768 +        rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
  1.3769 +        if (NS_FAILED(rv)) return rv;
  1.3770 +    }
  1.3771 +    else {
  1.3772 +        // If it's not a XUL element, it's gonna be heavyweight no matter
  1.3773 +        // what. So we need to copy everything out of the prototype
  1.3774 +        // into the element.  Get a nodeinfo from our nodeinfo manager
  1.3775 +        // for this node.
  1.3776 +        nsCOMPtr<nsINodeInfo> newNodeInfo;
  1.3777 +        newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
  1.3778 +                                                    aPrototype->mNodeInfo->GetPrefixAtom(),
  1.3779 +                                                    aPrototype->mNodeInfo->NamespaceID(),
  1.3780 +                                                    nsIDOMNode::ELEMENT_NODE);
  1.3781 +        if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
  1.3782 +        nsCOMPtr<nsINodeInfo> xtfNi = newNodeInfo;
  1.3783 +        rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
  1.3784 +                           NOT_FROM_PARSER);
  1.3785 +        if (NS_FAILED(rv))
  1.3786 +            return rv;
  1.3787 +
  1.3788 +        rv = AddAttributes(aPrototype, result);
  1.3789 +        if (NS_FAILED(rv)) return rv;
  1.3790 +    }
  1.3791 +
  1.3792 +    result.swap(*aResult);
  1.3793 +
  1.3794 +    return NS_OK;
  1.3795 +}
  1.3796 +
  1.3797 +nsresult
  1.3798 +XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
  1.3799 +                                  Element** aResult)
  1.3800 +{
  1.3801 +    nsresult rv;
  1.3802 +
  1.3803 +    nsRefPtr<Element> element;
  1.3804 +    rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false);
  1.3805 +    if (NS_FAILED(rv)) return rv;
  1.3806 +
  1.3807 +    OverlayForwardReference* fwdref =
  1.3808 +        new OverlayForwardReference(this, element);
  1.3809 +    if (! fwdref)
  1.3810 +        return NS_ERROR_OUT_OF_MEMORY;
  1.3811 +
  1.3812 +    // transferring ownership to ya...
  1.3813 +    rv = AddForwardReference(fwdref);
  1.3814 +    if (NS_FAILED(rv)) return rv;
  1.3815 +
  1.3816 +    NS_ADDREF(*aResult = element);
  1.3817 +    return NS_OK;
  1.3818 +}
  1.3819 +
  1.3820 +nsresult
  1.3821 +XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
  1.3822 +                           nsIContent* aElement)
  1.3823 +{
  1.3824 +    nsresult rv;
  1.3825 +
  1.3826 +    for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
  1.3827 +        nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
  1.3828 +        nsAutoString  valueStr;
  1.3829 +        protoattr->mValue.ToString(valueStr);
  1.3830 +
  1.3831 +        rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
  1.3832 +                               protoattr->mName.LocalName(),
  1.3833 +                               protoattr->mName.GetPrefix(),
  1.3834 +                               valueStr,
  1.3835 +                               false);
  1.3836 +        if (NS_FAILED(rv)) return rv;
  1.3837 +    }
  1.3838 +
  1.3839 +    return NS_OK;
  1.3840 +}
  1.3841 +
  1.3842 +
  1.3843 +nsresult
  1.3844 +XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
  1.3845 +                                        bool* aNeedsHookup)
  1.3846 +{
  1.3847 +    // See if the element already has a `database' attribute. If it
  1.3848 +    // does, then the template builder has already been created.
  1.3849 +    //
  1.3850 +    // XXX This approach will crash and burn (well, maybe not _that_
  1.3851 +    // bad) if aElement is not a XUL element.
  1.3852 +    //
  1.3853 +    // XXXvarga Do we still want to support non XUL content?
  1.3854 +    nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
  1.3855 +    if (xulElement) {
  1.3856 +        nsCOMPtr<nsIRDFCompositeDataSource> ds;
  1.3857 +        xulElement->GetDatabase(getter_AddRefs(ds));
  1.3858 +        if (ds) {
  1.3859 +            *aNeedsHookup = false;
  1.3860 +            return NS_OK;
  1.3861 +        }
  1.3862 +    }
  1.3863 +
  1.3864 +    // Check aElement for a 'datasources' attribute, if it has
  1.3865 +    // one a XUL template builder needs to be hooked up.
  1.3866 +    *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
  1.3867 +                                      nsGkAtoms::datasources);
  1.3868 +    return NS_OK;
  1.3869 +}
  1.3870 +
  1.3871 +/* static */ nsresult
  1.3872 +XULDocument::CreateTemplateBuilder(nsIContent* aElement)
  1.3873 +{
  1.3874 +    // Check if need to construct a tree builder or content builder.
  1.3875 +    bool isTreeBuilder = false;
  1.3876 +
  1.3877 +    // return successful if the element is not is a document, as an inline
  1.3878 +    // script could have removed it
  1.3879 +    nsIDocument *document = aElement->GetCurrentDoc();
  1.3880 +    NS_ENSURE_TRUE(document, NS_OK);
  1.3881 +
  1.3882 +    int32_t nameSpaceID;
  1.3883 +    nsIAtom* baseTag = document->BindingManager()->
  1.3884 +      ResolveTag(aElement, &nameSpaceID);
  1.3885 +
  1.3886 +    if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) {
  1.3887 +        // By default, we build content for a tree and then we attach
  1.3888 +        // the tree content view. However, if the `dont-build-content'
  1.3889 +        // flag is set, then we we'll attach a tree builder which
  1.3890 +        // directly implements the tree view.
  1.3891 +
  1.3892 +        nsAutoString flags;
  1.3893 +        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
  1.3894 +        if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
  1.3895 +            isTreeBuilder = true;
  1.3896 +        }
  1.3897 +    }
  1.3898 +
  1.3899 +    if (isTreeBuilder) {
  1.3900 +        // Create and initialize a tree builder.
  1.3901 +        nsCOMPtr<nsIXULTemplateBuilder> builder =
  1.3902 +            do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
  1.3903 +
  1.3904 +        if (! builder)
  1.3905 +            return NS_ERROR_FAILURE;
  1.3906 +
  1.3907 +        builder->Init(aElement);
  1.3908 +
  1.3909 +        // Create a <treechildren> if one isn't there already.
  1.3910 +        // XXXvarga what about attributes?
  1.3911 +        nsCOMPtr<nsIContent> bodyContent;
  1.3912 +        nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
  1.3913 +                                          nsGkAtoms::treechildren,
  1.3914 +                                          getter_AddRefs(bodyContent));
  1.3915 +
  1.3916 +        if (! bodyContent) {
  1.3917 +            nsresult rv =
  1.3918 +                document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren),
  1.3919 +                                     nullptr, kNameSpaceID_XUL,
  1.3920 +                                     getter_AddRefs(bodyContent));
  1.3921 +            NS_ENSURE_SUCCESS(rv, rv);
  1.3922 +
  1.3923 +            aElement->AppendChildTo(bodyContent, false);
  1.3924 +        }
  1.3925 +    }
  1.3926 +    else {
  1.3927 +        // Create and initialize a content builder.
  1.3928 +        nsCOMPtr<nsIXULTemplateBuilder> builder
  1.3929 +            = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
  1.3930 +
  1.3931 +        if (! builder)
  1.3932 +            return NS_ERROR_FAILURE;
  1.3933 +
  1.3934 +        builder->Init(aElement);
  1.3935 +        builder->CreateContents(aElement, false);
  1.3936 +    }
  1.3937 +
  1.3938 +    return NS_OK;
  1.3939 +}
  1.3940 +
  1.3941 +
  1.3942 +nsresult
  1.3943 +XULDocument::AddPrototypeSheets()
  1.3944 +{
  1.3945 +    nsresult rv;
  1.3946 +
  1.3947 +    const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences();
  1.3948 +
  1.3949 +    for (int32_t i = 0; i < sheets.Count(); i++) {
  1.3950 +        nsCOMPtr<nsIURI> uri = sheets[i];
  1.3951 +
  1.3952 +        nsRefPtr<nsCSSStyleSheet> incompleteSheet;
  1.3953 +        rv = CSSLoader()->LoadSheet(uri,
  1.3954 +                                    mCurrentPrototype->DocumentPrincipal(),
  1.3955 +                                    EmptyCString(), this,
  1.3956 +                                    getter_AddRefs(incompleteSheet));
  1.3957 +
  1.3958 +        // XXXldb We need to prevent bogus sheets from being held in the
  1.3959 +        // prototype's list, but until then, don't propagate the failure
  1.3960 +        // from LoadSheet (and thus exit the loop).
  1.3961 +        if (NS_SUCCEEDED(rv)) {
  1.3962 +            ++mPendingSheets;
  1.3963 +            if (!mOverlaySheets.AppendElement(incompleteSheet)) {
  1.3964 +                return NS_ERROR_OUT_OF_MEMORY;
  1.3965 +            }
  1.3966 +        }
  1.3967 +    }
  1.3968 +
  1.3969 +    return NS_OK;
  1.3970 +}
  1.3971 +
  1.3972 +
  1.3973 +//----------------------------------------------------------------------
  1.3974 +//
  1.3975 +// XULDocument::OverlayForwardReference
  1.3976 +//
  1.3977 +
  1.3978 +nsForwardReference::Result
  1.3979 +XULDocument::OverlayForwardReference::Resolve()
  1.3980 +{
  1.3981 +    // Resolve a forward reference from an overlay element; attempt to
  1.3982 +    // hook it up into the main document.
  1.3983 +    nsresult rv;
  1.3984 +    nsCOMPtr<nsIContent> target;
  1.3985 +
  1.3986 +    nsIPresShell *shell = mDocument->GetShell();
  1.3987 +    bool notify = shell && shell->DidInitialize();
  1.3988 +
  1.3989 +    nsAutoString id;
  1.3990 +    mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  1.3991 +    if (id.IsEmpty()) {
  1.3992 +        // mOverlay is a direct child of <overlay> and has no id.
  1.3993 +        // Insert it under the root element in the base document.
  1.3994 +        Element* root = mDocument->GetRootElement();
  1.3995 +        if (!root) {
  1.3996 +            return eResolve_Error;
  1.3997 +        }
  1.3998 +
  1.3999 +        rv = mDocument->InsertElement(root, mOverlay, notify);
  1.4000 +        if (NS_FAILED(rv)) return eResolve_Error;
  1.4001 +
  1.4002 +        target = mOverlay;
  1.4003 +    }
  1.4004 +    else {
  1.4005 +        // The hook-up element has an id, try to match it with an element
  1.4006 +        // with the same id in the base document.
  1.4007 +        target = mDocument->GetElementById(id);
  1.4008 +
  1.4009 +        // If we can't find the element in the document, defer the hookup
  1.4010 +        // until later.
  1.4011 +        if (!target)
  1.4012 +            return eResolve_Later;
  1.4013 +
  1.4014 +        rv = Merge(target, mOverlay, notify);
  1.4015 +        if (NS_FAILED(rv)) return eResolve_Error;
  1.4016 +    }
  1.4017 +
  1.4018 +    // Check if 'target' is still in our document --- it might not be!
  1.4019 +    if (!notify && target->GetCurrentDoc() == mDocument) {
  1.4020 +        // Add child and any descendants to the element map
  1.4021 +        // XXX this is bogus, the content in 'target' might already be
  1.4022 +        // in the document
  1.4023 +        rv = mDocument->AddSubtreeToDocument(target);
  1.4024 +        if (NS_FAILED(rv)) return eResolve_Error;
  1.4025 +    }
  1.4026 +
  1.4027 +#ifdef PR_LOGGING
  1.4028 +    if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  1.4029 +        nsAutoCString idC;
  1.4030 +        idC.AssignWithConversion(id);
  1.4031 +        PR_LOG(gXULLog, PR_LOG_NOTICE,
  1.4032 +               ("xul: overlay resolved '%s'",
  1.4033 +                idC.get()));
  1.4034 +    }
  1.4035 +#endif
  1.4036 +
  1.4037 +    mResolved = true;
  1.4038 +    return eResolve_Succeeded;
  1.4039 +}
  1.4040 +
  1.4041 +
  1.4042 +
  1.4043 +nsresult
  1.4044 +XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
  1.4045 +                                            nsIContent* aOverlayNode,
  1.4046 +                                            bool aNotify)
  1.4047 +{
  1.4048 +    // This function is given:
  1.4049 +    // aTargetNode:  the node in the document whose 'id' attribute
  1.4050 +    //               matches a toplevel node in our overlay.
  1.4051 +    // aOverlayNode: the node in the overlay document that matches
  1.4052 +    //               a node in the actual document.
  1.4053 +    // aNotify:      whether or not content manipulation methods should
  1.4054 +    //               use the aNotify parameter. After the initial 
  1.4055 +    //               reflow (i.e. in the dynamic overlay merge case),
  1.4056 +    //               we want all the content manipulation methods we
  1.4057 +    //               call to notify so that frames are constructed 
  1.4058 +    //               etc. Otherwise do not, since that's during initial
  1.4059 +    //               document construction before StartLayout has been
  1.4060 +    //               called which will do everything for us.
  1.4061 +    //
  1.4062 +    // This function merges the tree from the overlay into the tree in
  1.4063 +    // the document, overwriting attributes and appending child content
  1.4064 +    // nodes appropriately. (See XUL overlay reference for details)
  1.4065 +
  1.4066 +    nsresult rv;
  1.4067 +
  1.4068 +    // Merge attributes from the overlay content node to that of the
  1.4069 +    // actual document.
  1.4070 +    uint32_t i;
  1.4071 +    const nsAttrName* name;
  1.4072 +    for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) {
  1.4073 +        // We don't want to swap IDs, they should be the same.
  1.4074 +        if (name->Equals(nsGkAtoms::id))
  1.4075 +            continue;
  1.4076 +
  1.4077 +        // In certain cases merging command or observes is unsafe, so don't.
  1.4078 +        if (!aNotify) {
  1.4079 +            if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes,
  1.4080 +                                                kNameSpaceID_XUL))
  1.4081 +                continue;
  1.4082 +
  1.4083 +            if (name->Equals(nsGkAtoms::observes) &&
  1.4084 +                aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes))
  1.4085 +                continue;
  1.4086 +
  1.4087 +            if (name->Equals(nsGkAtoms::command) &&
  1.4088 +                aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) &&
  1.4089 +                !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key,
  1.4090 +                                                 kNameSpaceID_XUL) &&
  1.4091 +                !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem,
  1.4092 +                                                 kNameSpaceID_XUL))
  1.4093 +                continue;
  1.4094 +        }
  1.4095 +
  1.4096 +        int32_t nameSpaceID = name->NamespaceID();
  1.4097 +        nsIAtom* attr = name->LocalName();
  1.4098 +        nsIAtom* prefix = name->GetPrefix();
  1.4099 +
  1.4100 +        nsAutoString value;
  1.4101 +        aOverlayNode->GetAttr(nameSpaceID, attr, value);
  1.4102 +
  1.4103 +        // Element in the overlay has the 'removeelement' attribute set
  1.4104 +        // so remove it from the actual document.
  1.4105 +        if (attr == nsGkAtoms::removeelement &&
  1.4106 +            value.EqualsLiteral("true")) {
  1.4107 +
  1.4108 +            nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode();
  1.4109 +            if (!parent) return NS_ERROR_FAILURE;
  1.4110 +            rv = RemoveElement(parent, aTargetNode);
  1.4111 +            if (NS_FAILED(rv)) return rv;
  1.4112 +
  1.4113 +            return NS_OK;
  1.4114 +        }
  1.4115 +
  1.4116 +        rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
  1.4117 +        if (!NS_FAILED(rv) && !aNotify)
  1.4118 +            rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode,
  1.4119 +                                                                nameSpaceID,
  1.4120 +                                                                attr, prefix,
  1.4121 +                                                                value);
  1.4122 +        if (NS_FAILED(rv)) return rv;
  1.4123 +    }
  1.4124 +
  1.4125 +
  1.4126 +    // Walk our child nodes, looking for elements that have the 'id'
  1.4127 +    // attribute set. If we find any, we must do a parent check in the
  1.4128 +    // actual document to ensure that the structure matches that of
  1.4129 +    // the actual document. If it does, we can call ourselves and attempt
  1.4130 +    // to merge inside that subtree. If not, we just append the tree to
  1.4131 +    // the parent like any other.
  1.4132 +
  1.4133 +    uint32_t childCount = aOverlayNode->GetChildCount();
  1.4134 +
  1.4135 +    // This must be a strong reference since it will be the only
  1.4136 +    // reference to a content object during part of this loop.
  1.4137 +    nsCOMPtr<nsIContent> currContent;
  1.4138 +
  1.4139 +    for (i = 0; i < childCount; ++i) {
  1.4140 +        currContent = aOverlayNode->GetFirstChild();
  1.4141 +
  1.4142 +        nsIAtom *idAtom = currContent->GetID();
  1.4143 +
  1.4144 +        nsIContent *elementInDocument = nullptr;
  1.4145 +        if (idAtom) {
  1.4146 +            nsDependentAtomString id(idAtom);
  1.4147 +
  1.4148 +            if (!id.IsEmpty()) {
  1.4149 +                nsIDocument *doc = aTargetNode->GetDocument();
  1.4150 +                if (!doc) return NS_ERROR_FAILURE;
  1.4151 +
  1.4152 +                elementInDocument = doc->GetElementById(id);
  1.4153 +            }
  1.4154 +        }
  1.4155 +
  1.4156 +        // The item has an 'id' attribute set, and we need to check with
  1.4157 +        // the actual document to see if an item with this id exists at
  1.4158 +        // this locale. If so, we want to merge the subtree under that
  1.4159 +        // node. Otherwise, we just do an append as if the element had
  1.4160 +        // no id attribute.
  1.4161 +        if (elementInDocument) {
  1.4162 +            // Given two parents, aTargetNode and aOverlayNode, we want
  1.4163 +            // to call merge on currContent if we find an associated
  1.4164 +            // node in the document with the same id as currContent that
  1.4165 +            // also has aTargetNode as its parent.
  1.4166 +
  1.4167 +            nsIContent *elementParent = elementInDocument->GetParent();
  1.4168 +
  1.4169 +            nsIAtom *parentID = elementParent->GetID();
  1.4170 +            if (parentID &&
  1.4171 +                aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
  1.4172 +                                         nsDependentAtomString(parentID),
  1.4173 +                                         eCaseMatters)) {
  1.4174 +                // The element matches. "Go Deep!"
  1.4175 +                rv = Merge(elementInDocument, currContent, aNotify);
  1.4176 +                if (NS_FAILED(rv)) return rv;
  1.4177 +                aOverlayNode->RemoveChildAt(0, false);
  1.4178 +
  1.4179 +                continue;
  1.4180 +            }
  1.4181 +        }
  1.4182 +
  1.4183 +        aOverlayNode->RemoveChildAt(0, false);
  1.4184 +
  1.4185 +        rv = InsertElement(aTargetNode, currContent, aNotify);
  1.4186 +        if (NS_FAILED(rv)) return rv;
  1.4187 +    }
  1.4188 +
  1.4189 +    return NS_OK;
  1.4190 +}
  1.4191 +
  1.4192 +
  1.4193 +
  1.4194 +XULDocument::OverlayForwardReference::~OverlayForwardReference()
  1.4195 +{
  1.4196 +#ifdef PR_LOGGING
  1.4197 +    if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
  1.4198 +        nsAutoString id;
  1.4199 +        mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  1.4200 +
  1.4201 +        nsAutoCString idC;
  1.4202 +        idC.AssignWithConversion(id);
  1.4203 +
  1.4204 +        nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI();
  1.4205 +        nsAutoCString urlspec;
  1.4206 +        protoURI->GetSpec(urlspec);
  1.4207 +
  1.4208 +        nsCOMPtr<nsIURI> docURI;
  1.4209 +        nsAutoCString parentDoc;
  1.4210 +        nsresult rv = mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI));
  1.4211 +        if (NS_SUCCEEDED(rv))
  1.4212 +            docURI->GetSpec(parentDoc);
  1.4213 +        PR_LOG(gXULLog, PR_LOG_WARNING,
  1.4214 +               ("xul: %s overlay failed to resolve '%s' in %s",
  1.4215 +                urlspec.get(), idC.get(), parentDoc.get()));
  1.4216 +    }
  1.4217 +#endif
  1.4218 +}
  1.4219 +
  1.4220 +
  1.4221 +//----------------------------------------------------------------------
  1.4222 +//
  1.4223 +// XULDocument::BroadcasterHookup
  1.4224 +//
  1.4225 +
  1.4226 +nsForwardReference::Result
  1.4227 +XULDocument::BroadcasterHookup::Resolve()
  1.4228 +{
  1.4229 +    nsresult rv;
  1.4230 +
  1.4231 +    bool listener;
  1.4232 +    rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
  1.4233 +    if (NS_FAILED(rv)) return eResolve_Error;
  1.4234 +
  1.4235 +    return mResolved ? eResolve_Succeeded : eResolve_Later;
  1.4236 +}
  1.4237 +
  1.4238 +
  1.4239 +XULDocument::BroadcasterHookup::~BroadcasterHookup()
  1.4240 +{
  1.4241 +#ifdef PR_LOGGING
  1.4242 +    if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) {
  1.4243 +        // Tell the world we failed
  1.4244 +        nsIAtom *tag = mObservesElement->Tag();
  1.4245 +
  1.4246 +        nsAutoString broadcasterID;
  1.4247 +        nsAutoString attribute;
  1.4248 +
  1.4249 +        if (tag == nsGkAtoms::observes) {
  1.4250 +            mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID);
  1.4251 +            mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute);
  1.4252 +        }
  1.4253 +        else {
  1.4254 +            mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID);
  1.4255 +            attribute.AssignLiteral("*");
  1.4256 +        }
  1.4257 +
  1.4258 +        nsAutoCString attributeC,broadcasteridC;
  1.4259 +        attributeC.AssignWithConversion(attribute);
  1.4260 +        broadcasteridC.AssignWithConversion(broadcasterID);
  1.4261 +        PR_LOG(gXULLog, PR_LOG_WARNING,
  1.4262 +               ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
  1.4263 +                nsAtomCString(tag).get(),
  1.4264 +                attributeC.get(),
  1.4265 +                broadcasteridC.get()));
  1.4266 +    }
  1.4267 +#endif
  1.4268 +}
  1.4269 +
  1.4270 +
  1.4271 +//----------------------------------------------------------------------
  1.4272 +//
  1.4273 +// XULDocument::TemplateBuilderHookup
  1.4274 +//
  1.4275 +
  1.4276 +nsForwardReference::Result
  1.4277 +XULDocument::TemplateBuilderHookup::Resolve()
  1.4278 +{
  1.4279 +    bool needsHookup;
  1.4280 +    nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
  1.4281 +    if (NS_FAILED(rv))
  1.4282 +        return eResolve_Error;
  1.4283 +
  1.4284 +    if (needsHookup) {
  1.4285 +        rv = CreateTemplateBuilder(mElement);
  1.4286 +        if (NS_FAILED(rv))
  1.4287 +            return eResolve_Error;
  1.4288 +    }
  1.4289 +
  1.4290 +    return eResolve_Succeeded;
  1.4291 +}
  1.4292 +
  1.4293 +
  1.4294 +//----------------------------------------------------------------------
  1.4295 +
  1.4296 +nsresult
  1.4297 +XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
  1.4298 +                                                 int32_t aNameSpaceID,
  1.4299 +                                                 nsIAtom* aAttribute,
  1.4300 +                                                 nsIAtom* aPrefix,
  1.4301 +                                                 const nsAString& aValue)
  1.4302 +{
  1.4303 +    nsresult rv = NS_OK;
  1.4304 +
  1.4305 +    if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
  1.4306 +        return rv;
  1.4307 +
  1.4308 +    if (!aNode->IsElement())
  1.4309 +        return rv;
  1.4310 +
  1.4311 +    BroadcasterMapEntry* entry = static_cast<BroadcasterMapEntry*>
  1.4312 +        (PL_DHashTableOperate(mBroadcasterMap, aNode->AsElement(), PL_DHASH_LOOKUP));
  1.4313 +    if (!PL_DHASH_ENTRY_IS_BUSY(entry))
  1.4314 +        return rv;
  1.4315 +
  1.4316 +    // We've got listeners: push the value.
  1.4317 +    int32_t i;
  1.4318 +    for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
  1.4319 +        BroadcastListener* bl = static_cast<BroadcastListener*>
  1.4320 +            (entry->mListeners[i]);
  1.4321 +
  1.4322 +        if ((bl->mAttribute != aAttribute) &&
  1.4323 +            (bl->mAttribute != nsGkAtoms::_asterix))
  1.4324 +            continue;
  1.4325 +
  1.4326 +        nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener);
  1.4327 +        if (l) {
  1.4328 +            rv = l->SetAttr(aNameSpaceID, aAttribute,
  1.4329 +                            aPrefix, aValue, false);
  1.4330 +            if (NS_FAILED(rv)) return rv;
  1.4331 +        }
  1.4332 +    }
  1.4333 +    return rv;
  1.4334 +}
  1.4335 +
  1.4336 +nsresult
  1.4337 +XULDocument::FindBroadcaster(Element* aElement,
  1.4338 +                             Element** aListener,
  1.4339 +                             nsString& aBroadcasterID,
  1.4340 +                             nsString& aAttribute,
  1.4341 +                             Element** aBroadcaster)
  1.4342 +{
  1.4343 +    nsINodeInfo *ni = aElement->NodeInfo();
  1.4344 +    *aListener = nullptr;
  1.4345 +    *aBroadcaster = nullptr;
  1.4346 +
  1.4347 +    if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
  1.4348 +        // It's an <observes> element, which means that the actual
  1.4349 +        // listener is the _parent_ node. This element should have an
  1.4350 +        // 'element' attribute that specifies the ID of the
  1.4351 +        // broadcaster element, and an 'attribute' element, which
  1.4352 +        // specifies the name of the attribute to observe.
  1.4353 +        nsIContent* parent = aElement->GetParent();
  1.4354 +        if (!parent) {
  1.4355 +             // <observes> is the root element
  1.4356 +            return NS_FINDBROADCASTER_NOT_FOUND;
  1.4357 +        }
  1.4358 +
  1.4359 +        // If we're still parented by an 'overlay' tag, then we haven't
  1.4360 +        // made it into the real document yet. Defer hookup.
  1.4361 +        if (parent->NodeInfo()->Equals(nsGkAtoms::overlay,
  1.4362 +                                       kNameSpaceID_XUL)) {
  1.4363 +            return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  1.4364 +        }
  1.4365 +
  1.4366 +        *aListener = parent->IsElement() ? parent->AsElement() : nullptr;
  1.4367 +        NS_IF_ADDREF(*aListener);
  1.4368 +
  1.4369 +        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
  1.4370 +        if (aBroadcasterID.IsEmpty()) {
  1.4371 +            return NS_FINDBROADCASTER_NOT_FOUND;
  1.4372 +        }
  1.4373 +        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
  1.4374 +    }
  1.4375 +    else {
  1.4376 +        // It's a generic element, which means that we'll use the
  1.4377 +        // value of the 'observes' attribute to determine the ID of
  1.4378 +        // the broadcaster element, and we'll watch _all_ of its
  1.4379 +        // values.
  1.4380 +        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
  1.4381 +
  1.4382 +        // Bail if there's no aBroadcasterID
  1.4383 +        if (aBroadcasterID.IsEmpty()) {
  1.4384 +            // Try the command attribute next.
  1.4385 +            aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
  1.4386 +            if (!aBroadcasterID.IsEmpty()) {
  1.4387 +                // We've got something in the command attribute.  We
  1.4388 +                // only treat this as a normal broadcaster if we are
  1.4389 +                // not a menuitem or a key.
  1.4390 +
  1.4391 +                if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
  1.4392 +                    ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
  1.4393 +                return NS_FINDBROADCASTER_NOT_FOUND;
  1.4394 +              }
  1.4395 +            }
  1.4396 +            else {
  1.4397 +              return NS_FINDBROADCASTER_NOT_FOUND;
  1.4398 +            }
  1.4399 +        }
  1.4400 +
  1.4401 +        *aListener = aElement;
  1.4402 +        NS_ADDREF(*aListener);
  1.4403 +
  1.4404 +        aAttribute.AssignLiteral("*");
  1.4405 +    }
  1.4406 +
  1.4407 +    // Make sure we got a valid listener.
  1.4408 +    NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
  1.4409 +
  1.4410 +    // Try to find the broadcaster element in the document.
  1.4411 +    *aBroadcaster = GetElementById(aBroadcasterID);
  1.4412 +
  1.4413 +    // If we can't find the broadcaster, then we'll need to defer the
  1.4414 +    // hookup. We may need to resolve some of the other overlays
  1.4415 +    // first.
  1.4416 +    if (! *aBroadcaster) {
  1.4417 +        return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  1.4418 +    }
  1.4419 +
  1.4420 +    NS_ADDREF(*aBroadcaster);
  1.4421 +
  1.4422 +    return NS_FINDBROADCASTER_FOUND;
  1.4423 +}
  1.4424 +
  1.4425 +nsresult
  1.4426 +XULDocument::CheckBroadcasterHookup(Element* aElement,
  1.4427 +                                    bool* aNeedsHookup,
  1.4428 +                                    bool* aDidResolve)
  1.4429 +{
  1.4430 +    // Resolve a broadcaster hookup. Look at the element that we're
  1.4431 +    // trying to resolve: it could be an '<observes>' element, or just
  1.4432 +    // a vanilla element with an 'observes' attribute on it.
  1.4433 +    nsresult rv;
  1.4434 +
  1.4435 +    *aDidResolve = false;
  1.4436 +
  1.4437 +    nsCOMPtr<Element> listener;
  1.4438 +    nsAutoString broadcasterID;
  1.4439 +    nsAutoString attribute;
  1.4440 +    nsCOMPtr<Element> broadcaster;
  1.4441 +
  1.4442 +    rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  1.4443 +                         broadcasterID, attribute, getter_AddRefs(broadcaster));
  1.4444 +    switch (rv) {
  1.4445 +        case NS_FINDBROADCASTER_NOT_FOUND:
  1.4446 +            *aNeedsHookup = false;
  1.4447 +            return NS_OK;
  1.4448 +        case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
  1.4449 +            *aNeedsHookup = true;
  1.4450 +            return NS_OK;
  1.4451 +        case NS_FINDBROADCASTER_FOUND:
  1.4452 +            break;
  1.4453 +        default:
  1.4454 +            return rv;
  1.4455 +    }
  1.4456 +
  1.4457 +    NS_ENSURE_ARG(broadcaster && listener);
  1.4458 +    ErrorResult domRv;
  1.4459 +    AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
  1.4460 +    if (domRv.Failed()) {
  1.4461 +        return domRv.ErrorCode();
  1.4462 +    }
  1.4463 +
  1.4464 +#ifdef PR_LOGGING
  1.4465 +    // Tell the world we succeeded
  1.4466 +    if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) {
  1.4467 +        nsCOMPtr<nsIContent> content =
  1.4468 +            do_QueryInterface(listener);
  1.4469 +
  1.4470 +        NS_ASSERTION(content != nullptr, "not an nsIContent");
  1.4471 +        if (! content)
  1.4472 +            return rv;
  1.4473 +
  1.4474 +        nsAutoCString attributeC,broadcasteridC;
  1.4475 +        attributeC.AssignWithConversion(attribute);
  1.4476 +        broadcasteridC.AssignWithConversion(broadcasterID);
  1.4477 +        PR_LOG(gXULLog, PR_LOG_NOTICE,
  1.4478 +               ("xul: broadcaster hookup <%s attribute='%s'> to %s",
  1.4479 +                nsAtomCString(content->Tag()).get(),
  1.4480 +                attributeC.get(),
  1.4481 +                broadcasteridC.get()));
  1.4482 +    }
  1.4483 +#endif
  1.4484 +
  1.4485 +    *aNeedsHookup = false;
  1.4486 +    *aDidResolve = true;
  1.4487 +    return NS_OK;
  1.4488 +}
  1.4489 +
  1.4490 +nsresult
  1.4491 +XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild,
  1.4492 +                           bool aNotify)
  1.4493 +{
  1.4494 +    // Insert aChild appropriately into aParent, accounting for a
  1.4495 +    // 'pos' attribute set on aChild.
  1.4496 +
  1.4497 +    nsAutoString posStr;
  1.4498 +    bool wasInserted = false;
  1.4499 +
  1.4500 +    // insert after an element of a given id
  1.4501 +    aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr);
  1.4502 +    bool isInsertAfter = true;
  1.4503 +
  1.4504 +    if (posStr.IsEmpty()) {
  1.4505 +        aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr);
  1.4506 +        isInsertAfter = false;
  1.4507 +    }
  1.4508 +
  1.4509 +    if (!posStr.IsEmpty()) {
  1.4510 +        nsIDocument *document = aParent->OwnerDoc();
  1.4511 +
  1.4512 +        nsIContent *content = nullptr;
  1.4513 +
  1.4514 +        char* str = ToNewCString(posStr);
  1.4515 +        char* rest;
  1.4516 +        char* token = nsCRT::strtok(str, ", ", &rest);
  1.4517 +
  1.4518 +        while (token) {
  1.4519 +            content = document->GetElementById(NS_ConvertASCIItoUTF16(token));
  1.4520 +            if (content)
  1.4521 +                break;
  1.4522 +
  1.4523 +            token = nsCRT::strtok(rest, ", ", &rest);
  1.4524 +        }
  1.4525 +        nsMemory::Free(str);
  1.4526 +
  1.4527 +        if (content) {
  1.4528 +            int32_t pos = aParent->IndexOf(content);
  1.4529 +
  1.4530 +            if (pos != -1) {
  1.4531 +                pos = isInsertAfter ? pos + 1 : pos;
  1.4532 +                nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify);
  1.4533 +                if (NS_FAILED(rv))
  1.4534 +                    return rv;
  1.4535 +
  1.4536 +                wasInserted = true;
  1.4537 +            }
  1.4538 +        }
  1.4539 +    }
  1.4540 +
  1.4541 +    if (!wasInserted) {
  1.4542 +
  1.4543 +        aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr);
  1.4544 +        if (!posStr.IsEmpty()) {
  1.4545 +            nsresult rv;
  1.4546 +            // Positions are one-indexed.
  1.4547 +            int32_t pos = posStr.ToInteger(&rv);
  1.4548 +            // Note: if the insertion index (which is |pos - 1|) would be less
  1.4549 +            // than 0 or greater than the number of children aParent has, then
  1.4550 +            // don't insert, since the position is bogus.  Just skip on to
  1.4551 +            // appending.
  1.4552 +            if (NS_SUCCEEDED(rv) && pos > 0 &&
  1.4553 +                uint32_t(pos - 1) <= aParent->GetChildCount()) {
  1.4554 +                rv = aParent->InsertChildAt(aChild, pos - 1, aNotify);
  1.4555 +                if (NS_SUCCEEDED(rv))
  1.4556 +                    wasInserted = true;
  1.4557 +                // If the insertion fails, then we should still
  1.4558 +                // attempt an append.  Thus, rather than returning rv
  1.4559 +                // immediately, we fall through to the final
  1.4560 +                // "catch-all" case that just does an AppendChildTo.
  1.4561 +            }
  1.4562 +        }
  1.4563 +    }
  1.4564 +
  1.4565 +    if (!wasInserted) {
  1.4566 +        return aParent->AppendChildTo(aChild, aNotify);
  1.4567 +    }
  1.4568 +    return NS_OK;
  1.4569 +}
  1.4570 +
  1.4571 +nsresult
  1.4572 +XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild)
  1.4573 +{
  1.4574 +    int32_t nodeOffset = aParent->IndexOf(aChild);
  1.4575 +
  1.4576 +    aParent->RemoveChildAt(nodeOffset, true);
  1.4577 +    return NS_OK;
  1.4578 +}
  1.4579 +
  1.4580 +//----------------------------------------------------------------------
  1.4581 +//
  1.4582 +// CachedChromeStreamListener
  1.4583 +//
  1.4584 +
  1.4585 +XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
  1.4586 +    : mDocument(aDocument),
  1.4587 +      mProtoLoaded(aProtoLoaded)
  1.4588 +{
  1.4589 +    NS_ADDREF(mDocument);
  1.4590 +}
  1.4591 +
  1.4592 +
  1.4593 +XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
  1.4594 +{
  1.4595 +    NS_RELEASE(mDocument);
  1.4596 +}
  1.4597 +
  1.4598 +
  1.4599 +NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
  1.4600 +                  nsIRequestObserver, nsIStreamListener)
  1.4601 +
  1.4602 +NS_IMETHODIMP
  1.4603 +XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
  1.4604 +                                                        nsISupports* acontext)
  1.4605 +{
  1.4606 +    return NS_ERROR_PARSED_DATA_CACHED;
  1.4607 +}
  1.4608 +
  1.4609 +
  1.4610 +NS_IMETHODIMP
  1.4611 +XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
  1.4612 +                                                       nsISupports* aContext,
  1.4613 +                                                       nsresult aStatus)
  1.4614 +{
  1.4615 +    if (! mProtoLoaded)
  1.4616 +        return NS_OK;
  1.4617 +
  1.4618 +    return mDocument->OnPrototypeLoadDone(true);
  1.4619 +}
  1.4620 +
  1.4621 +
  1.4622 +NS_IMETHODIMP
  1.4623 +XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
  1.4624 +                                                         nsISupports* aContext,
  1.4625 +                                                         nsIInputStream* aInStr,
  1.4626 +                                                         uint64_t aSourceOffset,
  1.4627 +                                                         uint32_t aCount)
  1.4628 +{
  1.4629 +    NS_NOTREACHED("CachedChromeStream doesn't receive data");
  1.4630 +    return NS_ERROR_UNEXPECTED;
  1.4631 +}
  1.4632 +
  1.4633 +//----------------------------------------------------------------------
  1.4634 +//
  1.4635 +// ParserObserver
  1.4636 +//
  1.4637 +
  1.4638 +XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument,
  1.4639 +                                            nsXULPrototypeDocument* aPrototype)
  1.4640 +    : mDocument(aDocument), mPrototype(aPrototype)
  1.4641 +{
  1.4642 +}
  1.4643 +
  1.4644 +XULDocument::ParserObserver::~ParserObserver()
  1.4645 +{
  1.4646 +}
  1.4647 +
  1.4648 +NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver)
  1.4649 +
  1.4650 +NS_IMETHODIMP
  1.4651 +XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
  1.4652 +                                            nsISupports* aContext)
  1.4653 +{
  1.4654 +    // Guard against buggy channels calling OnStartRequest multiple times.
  1.4655 +    if (mPrototype) {
  1.4656 +        nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  1.4657 +        nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  1.4658 +        if (channel && secMan) {
  1.4659 +            nsCOMPtr<nsIPrincipal> principal;
  1.4660 +            secMan->GetChannelPrincipal(channel, getter_AddRefs(principal));
  1.4661 +
  1.4662 +            // Failure there is ok -- it'll just set a (safe) null principal
  1.4663 +            mPrototype->SetDocumentPrincipal(principal);
  1.4664 +        }
  1.4665 +
  1.4666 +        // Make sure to avoid cycles
  1.4667 +        mPrototype = nullptr;
  1.4668 +    }
  1.4669 +        
  1.4670 +    return NS_OK;
  1.4671 +}
  1.4672 +
  1.4673 +NS_IMETHODIMP
  1.4674 +XULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
  1.4675 +                                           nsISupports* aContext,
  1.4676 +                                           nsresult aStatus)
  1.4677 +{
  1.4678 +    nsresult rv = NS_OK;
  1.4679 +
  1.4680 +    if (NS_FAILED(aStatus)) {
  1.4681 +        // If an overlay load fails, we need to nudge the prototype
  1.4682 +        // walk along.
  1.4683 +        nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
  1.4684 +        if (aChannel) {
  1.4685 +            nsCOMPtr<nsIURI> uri;
  1.4686 +            aChannel->GetOriginalURI(getter_AddRefs(uri));
  1.4687 +            if (uri) {
  1.4688 +                mDocument->ReportMissingOverlay(uri);
  1.4689 +            }
  1.4690 +        }
  1.4691 +
  1.4692 +        rv = mDocument->ResumeWalk();
  1.4693 +    }
  1.4694 +
  1.4695 +    // Drop the reference to the document to break cycle between the
  1.4696 +    // document, the parser, the content sink, and the parser
  1.4697 +    // observer.
  1.4698 +    mDocument = nullptr;
  1.4699 +
  1.4700 +    return rv;
  1.4701 +}
  1.4702 +
  1.4703 +already_AddRefed<nsPIWindowRoot>
  1.4704 +XULDocument::GetWindowRoot()
  1.4705 +{
  1.4706 +    nsCOMPtr<nsIInterfaceRequestor> ir(mDocumentContainer);
  1.4707 +    nsCOMPtr<nsIDOMWindow> window(do_GetInterface(ir));
  1.4708 +    nsCOMPtr<nsPIDOMWindow> piWin(do_QueryInterface(window));
  1.4709 +    return piWin ? piWin->GetTopWindowRoot() : nullptr;
  1.4710 +}
  1.4711 +
  1.4712 +bool
  1.4713 +XULDocument::IsDocumentRightToLeft()
  1.4714 +{
  1.4715 +    // setting the localedir attribute on the root element forces a
  1.4716 +    // specific direction for the document.
  1.4717 +    Element* element = GetRootElement();
  1.4718 +    if (element) {
  1.4719 +        static nsIContent::AttrValuesArray strings[] =
  1.4720 +            {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
  1.4721 +        switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
  1.4722 +                                         strings, eCaseMatters)) {
  1.4723 +            case 0: return false;
  1.4724 +            case 1: return true;
  1.4725 +            default: break; // otherwise, not a valid value, so fall through
  1.4726 +        }
  1.4727 +    }
  1.4728 +
  1.4729 +    // otherwise, get the locale from the chrome registry and
  1.4730 +    // look up the intl.uidirection.<locale> preference
  1.4731 +    nsCOMPtr<nsIXULChromeRegistry> reg =
  1.4732 +        mozilla::services::GetXULChromeRegistryService();
  1.4733 +    if (!reg)
  1.4734 +        return false;
  1.4735 +
  1.4736 +    nsAutoCString package;
  1.4737 +    bool isChrome;
  1.4738 +    if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
  1.4739 +        isChrome) {
  1.4740 +        mDocumentURI->GetHostPort(package);
  1.4741 +    }
  1.4742 +    else {
  1.4743 +        // use the 'global' package for about and resource uris.
  1.4744 +        // otherwise, just default to left-to-right.
  1.4745 +        bool isAbout, isResource;
  1.4746 +        if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
  1.4747 +            isAbout) {
  1.4748 +            package.AssignLiteral("global");
  1.4749 +        }
  1.4750 +        else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
  1.4751 +            isResource) {
  1.4752 +            package.AssignLiteral("global");
  1.4753 +        }
  1.4754 +        else {
  1.4755 +            return false;
  1.4756 +        }
  1.4757 +    }
  1.4758 +
  1.4759 +    bool isRTL = false;
  1.4760 +    reg->IsLocaleRTL(package, &isRTL);
  1.4761 +    return isRTL;
  1.4762 +}
  1.4763 +
  1.4764 +void
  1.4765 +XULDocument::ResetDocumentDirection()
  1.4766 +{
  1.4767 +    DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
  1.4768 +}
  1.4769 +
  1.4770 +void
  1.4771 +XULDocument::DirectionChanged(const char* aPrefName, void* aData)
  1.4772 +{
  1.4773 +  // Reset the direction and restyle the document if necessary.
  1.4774 +  XULDocument* doc = (XULDocument *)aData;
  1.4775 +  if (doc) {
  1.4776 +      doc->ResetDocumentDirection();
  1.4777 +  }
  1.4778 +}
  1.4779 +
  1.4780 +int
  1.4781 +XULDocument::GetDocumentLWTheme()
  1.4782 +{
  1.4783 +    if (mDocLWTheme == Doc_Theme_Uninitialized) {
  1.4784 +        mDocLWTheme = Doc_Theme_None; // No lightweight theme by default
  1.4785 +
  1.4786 +        Element* element = GetRootElement();
  1.4787 +        nsAutoString hasLWTheme;
  1.4788 +        if (element &&
  1.4789 +            element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) &&
  1.4790 +            !(hasLWTheme.IsEmpty()) &&
  1.4791 +            hasLWTheme.EqualsLiteral("true")) {
  1.4792 +            mDocLWTheme = Doc_Theme_Neutral;
  1.4793 +            nsAutoString lwTheme;
  1.4794 +            element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
  1.4795 +            if (!(lwTheme.IsEmpty())) {
  1.4796 +                if (lwTheme.EqualsLiteral("dark"))
  1.4797 +                    mDocLWTheme = Doc_Theme_Dark;
  1.4798 +                else if (lwTheme.EqualsLiteral("bright"))
  1.4799 +                    mDocLWTheme = Doc_Theme_Bright;
  1.4800 +            }
  1.4801 +        }
  1.4802 +    }
  1.4803 +    return mDocLWTheme;
  1.4804 +}
  1.4805 +
  1.4806 +NS_IMETHODIMP
  1.4807 +XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult)
  1.4808 +{
  1.4809 +    ErrorResult rv;
  1.4810 +    nsCOMPtr<Element> el = do_QueryInterface(aElement);
  1.4811 +    *aResult = GetBoxObjectFor(el, rv).take();
  1.4812 +    return rv.ErrorCode();
  1.4813 +}
  1.4814 +
  1.4815 +JSObject*
  1.4816 +XULDocument::WrapNode(JSContext *aCx)
  1.4817 +{
  1.4818 +  return XULDocumentBinding::Wrap(aCx, this);
  1.4819 +}
  1.4820 +
  1.4821 +} // namespace dom
  1.4822 +} // namespace mozilla

mercurial