michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=4 sw=4 et tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: michael@0: An implementation for the XUL document. This implementation serves michael@0: as the basis for generating an NGLayout content model. michael@0: michael@0: Notes michael@0: ----- michael@0: michael@0: 1. We do some monkey business in the document observer methods to` michael@0: keep the element map in sync for HTML elements. Why don't we just michael@0: do it for _all_ elements? Well, in the case of XUL elements, michael@0: which may be lazily created during frame construction, the michael@0: document observer methods will never be called because we'll be michael@0: adding the XUL nodes into the content model "quietly". michael@0: michael@0: */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: // Note the ALPHABETICAL ORDERING michael@0: #include "XULDocument.h" michael@0: michael@0: #include "nsError.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "nsIChromeRegistry.h" michael@0: #include "nsView.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIDOMXULElement.h" michael@0: #include "nsIRDFNode.h" michael@0: #include "nsIRDFRemoteDataSource.h" michael@0: #include "nsIRDFService.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsITimer.h" michael@0: #include "nsDocShell.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsXMLContentSink.h" michael@0: #include "nsXULContentSink.h" michael@0: #include "nsXULContentUtils.h" michael@0: #include "nsIXULOverlayProvider.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsParserCIID.h" michael@0: #include "nsPIBoxObject.h" michael@0: #include "nsRDFCID.h" michael@0: #include "nsILocalStore.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsPIWindowRoot.h" michael@0: #include "nsXULCommandDispatcher.h" michael@0: #include "nsXULElement.h" michael@0: #include "prlog.h" michael@0: #include "rdf.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsXBLService.h" michael@0: #include "nsCExternalHandlerService.h" michael@0: #include "nsMimeTypes.h" michael@0: #include "nsIObjectInputStream.h" michael@0: #include "nsIObjectOutputStream.h" michael@0: #include "nsContentList.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIParser.h" michael@0: #include "nsCharsetSource.h" michael@0: #include "nsIParserService.h" michael@0: #include "nsCSSStyleSheet.h" michael@0: #include "mozilla/css/Loader.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIStyleSheetLinkingElement.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsIXULWindow.h" michael@0: #include "nsXULPopupManager.h" michael@0: #include "nsCCUncollectableMarker.h" michael@0: #include "nsURILoader.h" michael@0: #include "mozilla/BasicEvents.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/ProcessingInstruction.h" michael@0: #include "mozilla/dom/XULDocumentBinding.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsTextNode.h" michael@0: #include "nsJSUtils.h" michael@0: #include "mozilla/dom/URL.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // CIDs michael@0: // michael@0: michael@0: static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); michael@0: michael@0: static bool IsOverlayAllowed(nsIURI* aURI) michael@0: { michael@0: bool canOverlay = false; michael@0: if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay) michael@0: return true; michael@0: if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay) michael@0: return true; michael@0: return false; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // Miscellaneous Constants michael@0: // michael@0: michael@0: const nsForwardReference::Phase nsForwardReference::kPasses[] = { michael@0: nsForwardReference::eConstruction, michael@0: nsForwardReference::eHookup, michael@0: nsForwardReference::eDone michael@0: }; michael@0: michael@0: const uint32_t kMaxAttrNameLength = 512; michael@0: const uint32_t kMaxAttributeLength = 4096; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // Statics michael@0: // michael@0: michael@0: int32_t XULDocument::gRefCnt = 0; michael@0: michael@0: nsIRDFService* XULDocument::gRDFService; michael@0: nsIRDFResource* XULDocument::kNC_persist; michael@0: nsIRDFResource* XULDocument::kNC_attribute; michael@0: nsIRDFResource* XULDocument::kNC_value; michael@0: michael@0: PRLogModuleInfo* XULDocument::gXULLog; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: struct BroadcasterMapEntry : public PLDHashEntryHdr { michael@0: Element* mBroadcaster; // [WEAK] michael@0: nsSmallVoidArray mListeners; // [OWNING] of BroadcastListener objects michael@0: }; michael@0: michael@0: struct BroadcastListener { michael@0: nsWeakPtr mListener; michael@0: nsCOMPtr mAttribute; michael@0: }; michael@0: michael@0: Element* michael@0: nsRefMapEntry::GetFirstElement() michael@0: { michael@0: return static_cast(mRefContentList.SafeElementAt(0)); michael@0: } michael@0: michael@0: void michael@0: nsRefMapEntry::AppendAll(nsCOMArray* aElements) michael@0: { michael@0: for (int32_t i = 0; i < mRefContentList.Count(); ++i) { michael@0: aElements->AppendObject(static_cast(mRefContentList[i])); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsRefMapEntry::AddElement(Element* aElement) michael@0: { michael@0: if (mRefContentList.IndexOf(aElement) >= 0) michael@0: return true; michael@0: return mRefContentList.AppendElement(aElement); michael@0: } michael@0: michael@0: bool michael@0: nsRefMapEntry::RemoveElement(Element* aElement) michael@0: { michael@0: mRefContentList.RemoveElement(aElement); michael@0: return mRefContentList.Count() == 0; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // ctors & dtors michael@0: // michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: XULDocument::XULDocument(void) michael@0: : XMLDocument("application/vnd.mozilla.xul+xml"), michael@0: mDocLWTheme(Doc_Theme_Uninitialized), michael@0: mState(eState_Master), michael@0: mResolutionPhase(nsForwardReference::eStart) michael@0: { michael@0: // NOTE! nsDocument::operator new() zeroes out all members, so don't michael@0: // bother initializing members to 0. michael@0: michael@0: // Override the default in nsDocument michael@0: mCharacterSet.AssignLiteral("UTF-8"); michael@0: michael@0: mDefaultElementType = kNameSpaceID_XUL; michael@0: mIsXUL = true; michael@0: michael@0: mDelayFrameLoaderInitialization = true; michael@0: michael@0: mAllowXULXBL = eTriTrue; michael@0: } michael@0: michael@0: XULDocument::~XULDocument() michael@0: { michael@0: NS_ASSERTION(mNextSrcLoadWaiter == nullptr, michael@0: "unreferenced document still waiting for script source to load?"); michael@0: michael@0: // In case we failed somewhere early on and the forward observer michael@0: // decls never got resolved. michael@0: mForwardReferences.Clear(); michael@0: // Likewise for any references we have to IDs where we might michael@0: // look for persisted data: michael@0: mPersistenceIds.Clear(); michael@0: michael@0: // Destroy our broadcaster map. michael@0: if (mBroadcasterMap) { michael@0: PL_DHashTableDestroy(mBroadcasterMap); michael@0: } michael@0: michael@0: if (mLocalStore) { michael@0: nsCOMPtr remote = michael@0: do_QueryInterface(mLocalStore); michael@0: if (remote) michael@0: remote->Flush(); michael@0: } michael@0: michael@0: delete mTemplateBuilderTable; michael@0: michael@0: Preferences::UnregisterCallback(XULDocument::DirectionChanged, michael@0: "intl.uidirection.", this); michael@0: michael@0: if (--gRefCnt == 0) { michael@0: NS_IF_RELEASE(gRDFService); michael@0: michael@0: NS_IF_RELEASE(kNC_persist); michael@0: NS_IF_RELEASE(kNC_attribute); michael@0: NS_IF_RELEASE(kNC_value); michael@0: } michael@0: michael@0: if (mOffThreadCompileStringBuf) { michael@0: js_free(mOffThreadCompileStringBuf); michael@0: } michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: nsresult michael@0: NS_NewXULDocument(nsIXULDocument** result) michael@0: { michael@0: NS_PRECONDITION(result != nullptr, "null ptr"); michael@0: if (! result) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: XULDocument* doc = new XULDocument(); michael@0: if (! doc) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(doc); michael@0: michael@0: nsresult rv; michael@0: if (NS_FAILED(rv = doc->Init())) { michael@0: NS_RELEASE(doc); michael@0: return rv; michael@0: } michael@0: michael@0: *result = doc; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsISupports interface michael@0: // michael@0: michael@0: static PLDHashOperator michael@0: TraverseTemplateBuilders(nsISupports* aKey, nsIXULTemplateBuilder* aData, michael@0: void* aContext) michael@0: { michael@0: nsCycleCollectionTraversalCallback *cb = michael@0: static_cast(aContext); michael@0: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable key"); michael@0: cb->NoteXPCOMChild(aKey); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mTemplateBuilderTable value"); michael@0: cb->NoteXPCOMChild(aData); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: TraverseObservers(nsIURI* aKey, nsIObserver* aData, void* aContext) michael@0: { michael@0: nsCycleCollectionTraversalCallback *cb = michael@0: static_cast(aContext); michael@0: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value"); michael@0: cb->NoteXPCOMChild(aData); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument) michael@0: NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()), michael@0: "Shouldn't traverse XULDocument!"); michael@0: // XXX tmp->mForwardReferences? michael@0: // XXX tmp->mContextStack? michael@0: michael@0: // An element will only have a template builder as long as it's in the michael@0: // document, so we'll traverse the table here instead of from the element. michael@0: if (tmp->mTemplateBuilderTable) michael@0: tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb); michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore) michael@0: michael@0: if (tmp->mOverlayLoadObservers) { michael@0: tmp->mOverlayLoadObservers->EnumerateRead(TraverseObservers, &cb); michael@0: } michael@0: if (tmp->mPendingOverlayLoadNotifications) { michael@0: tmp->mPendingOverlayLoadNotifications->EnumerateRead(TraverseObservers, &cb); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument) michael@0: delete tmp->mTemplateBuilderTable; michael@0: tmp->mTemplateBuilderTable = nullptr; michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher) michael@0: //XXX We should probably unlink all the objects we traverse. michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument) michael@0: NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument) michael@0: michael@0: michael@0: // QueryInterface implementation for XULDocument michael@0: NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument) michael@0: NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument, michael@0: nsIDOMXULDocument, nsIStreamLoaderObserver, michael@0: nsICSSLoaderObserver, nsIOffThreadScriptReceiver) michael@0: NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument) michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIDocument interface michael@0: // michael@0: michael@0: void michael@0: XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) michael@0: { michael@0: NS_NOTREACHED("Reset"); michael@0: } michael@0: michael@0: void michael@0: XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: NS_NOTREACHED("ResetToURI"); michael@0: } michael@0: michael@0: void michael@0: XULDocument::SetContentType(const nsAString& aContentType) michael@0: { michael@0: NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"), michael@0: "xul-documents always has content-type application/vnd.mozilla.xul+xml"); michael@0: // Don't do anything, xul always has the mimetype michael@0: // application/vnd.mozilla.xul+xml michael@0: } michael@0: michael@0: // This is called when the master document begins loading, whether it's michael@0: // being cached or not. michael@0: nsresult michael@0: XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: nsISupports* aContainer, michael@0: nsIStreamListener **aDocListener, michael@0: bool aReset, nsIContentSink* aSink) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { michael@0: michael@0: nsCOMPtr uri; michael@0: nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoCString urlspec; michael@0: rv = uri->GetSpec(urlspec); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: PR_LOG(gXULLog, PR_LOG_WARNING, michael@0: ("xul: load document '%s'", urlspec.get())); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: // NOTE: If this ever starts calling nsDocument::StartDocumentLoad michael@0: // we'll possibly need to reset our content type afterwards. michael@0: mStillWalking = true; michael@0: mMayStartLayout = false; michael@0: mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); michael@0: michael@0: mChannel = aChannel; michael@0: michael@0: mHaveInputEncoding = true; michael@0: michael@0: // Get the URI. Note that this should match nsDocShell::OnLoadingSite michael@0: nsresult rv = michael@0: NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: ResetStylesheetsToURI(mDocumentURI); michael@0: michael@0: RetrieveRelevantHeaders(aChannel); michael@0: michael@0: // Look in the chrome cache: we've got this puppy loaded michael@0: // already. michael@0: nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ? michael@0: nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) : michael@0: nullptr; michael@0: michael@0: // Same comment as nsChromeProtocolHandler::NewChannel and michael@0: // XULDocument::ResumeWalk michael@0: // - Ben Goodger michael@0: // michael@0: // We don't abort on failure here because there are too many valid michael@0: // cases that can return failure, and the null-ness of |proto| is enough michael@0: // to trigger the fail-safe parse-from-disk solution. Example failure cases michael@0: // (for reference) include: michael@0: // michael@0: // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache, michael@0: // parse from disk michael@0: // other: the startup cache file could not be found, probably michael@0: // due to being accessed before a profile has been selected (e.g. michael@0: // loading chrome for the profile manager itself). This must be michael@0: // parsed from disk. michael@0: michael@0: if (proto) { michael@0: // If we're racing with another document to load proto, wait till the michael@0: // load has finished loading before trying to add cloned style sheets. michael@0: // XULDocument::EndLoad will call proto->NotifyLoadDone, which will michael@0: // find all racing documents and notify them via OnPrototypeLoadDone, michael@0: // which will add style sheet clones to each document. michael@0: bool loaded; michael@0: rv = proto->AwaitLoadDone(this, &loaded); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mMasterPrototype = mCurrentPrototype = proto; michael@0: michael@0: // Set up the right principal on ourselves. michael@0: SetPrincipal(proto->DocumentPrincipal()); michael@0: michael@0: // We need a listener, even if proto is not yet loaded, in which michael@0: // event the listener's OnStopRequest method does nothing, and all michael@0: // the interesting work happens below XULDocument::EndLoad, from michael@0: // the call there to mCurrentPrototype->NotifyLoadDone(). michael@0: *aDocListener = new CachedChromeStreamListener(this, loaded); michael@0: if (! *aDocListener) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: else { michael@0: bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); michael@0: bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI)); michael@0: michael@0: michael@0: // It's just a vanilla document load. Create a parser to deal michael@0: // with the stream n' stuff. michael@0: michael@0: nsCOMPtr parser; michael@0: rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup, michael@0: getter_AddRefs(parser)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Predicate mIsWritingFastLoad on the XUL cache being enabled, michael@0: // so we don't have to re-check whether the cache is enabled all michael@0: // the time. michael@0: mIsWritingFastLoad = useXULCache; michael@0: michael@0: nsCOMPtr listener = do_QueryInterface(parser, &rv); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *aDocListener = listener; michael@0: michael@0: parser->Parse(mDocumentURI); michael@0: michael@0: // Put the current prototype, created under PrepareToLoad, into the michael@0: // XUL prototype cache now. We can't do this under PrepareToLoad or michael@0: // overlay loading will break; search for PutPrototype in ResumeWalk michael@0: // and see the comment there. michael@0: if (fillXULCache) { michael@0: nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); michael@0: } michael@0: } michael@0: michael@0: NS_IF_ADDREF(*aDocListener); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // This gets invoked after a prototype for this document or one of michael@0: // its overlays is fully built in the content sink. michael@0: void michael@0: XULDocument::EndLoad() michael@0: { michael@0: // This can happen if an overlay fails to load michael@0: if (!mCurrentPrototype) michael@0: return; michael@0: michael@0: nsresult rv; michael@0: michael@0: // Whack the prototype document into the cache so that the next michael@0: // time somebody asks for it, they don't need to load it by hand. michael@0: michael@0: nsCOMPtr uri = mCurrentPrototype->GetURI(); michael@0: bool isChrome = IsChromeURI(uri); michael@0: michael@0: // Remember if the XUL cache is on michael@0: bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); michael@0: michael@0: // If the current prototype is an overlay document (non-master prototype) michael@0: // and we're filling the FastLoad disk cache, tell the cache we're done michael@0: // loading it, and write the prototype. The master prototype is put into michael@0: // the cache earlier in XULDocument::StartDocumentLoad. michael@0: if (useXULCache && mIsWritingFastLoad && isChrome && michael@0: mMasterPrototype != mCurrentPrototype) { michael@0: nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype); michael@0: } michael@0: michael@0: if (IsOverlayAllowed(uri)) { michael@0: nsCOMPtr reg = michael@0: mozilla::services::GetXULOverlayProviderService(); michael@0: michael@0: if (reg) { michael@0: nsCOMPtr overlays; michael@0: rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays)); michael@0: if (NS_FAILED(rv)) return; michael@0: michael@0: bool moreSheets; michael@0: nsCOMPtr next; michael@0: nsCOMPtr sheetURI; michael@0: michael@0: while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) && michael@0: moreSheets) { michael@0: overlays->GetNext(getter_AddRefs(next)); michael@0: michael@0: sheetURI = do_QueryInterface(next); michael@0: if (!sheetURI) { michael@0: NS_ERROR("Chrome registry handed me a non-nsIURI object!"); michael@0: continue; michael@0: } michael@0: michael@0: if (IsChromeURI(sheetURI)) { michael@0: mCurrentPrototype->AddStyleSheetReference(sheetURI); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (isChrome && useXULCache) { michael@0: // If it's a chrome prototype document, then notify any michael@0: // documents that raced to load the prototype, and awaited michael@0: // its load completion via proto->AwaitLoadDone(). michael@0: rv = mCurrentPrototype->NotifyLoadDone(); michael@0: if (NS_FAILED(rv)) return; michael@0: } michael@0: } michael@0: michael@0: OnPrototypeLoadDone(true); michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { michael@0: nsAutoCString urlspec; michael@0: rv = uri->GetSpec(urlspec); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: PR_LOG(gXULLog, PR_LOG_WARNING, michael@0: ("xul: Finished loading document '%s'", urlspec.get())); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::OnPrototypeLoadDone(bool aResumeWalk) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Add the style overlays from chrome registry, if any. michael@0: rv = AddPrototypeSheets(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = PrepareToWalk(); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aResumeWalk) { michael@0: rv = ResumeWalk(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: // called when an error occurs parsing a document michael@0: bool michael@0: XULDocument::OnDocumentParserError() michael@0: { michael@0: // don't report errors that are from overlays michael@0: if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) { michael@0: nsCOMPtr uri = mCurrentPrototype->GetURI(); michael@0: if (IsChromeURI(uri)) { michael@0: nsCOMPtr os = michael@0: mozilla::services::GetObserverService(); michael@0: if (os) michael@0: os->NotifyObservers(uri, "xul-overlay-parsererror", michael@0: EmptyString().get()); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static void michael@0: ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry) michael@0: { michael@0: BroadcasterMapEntry* entry = michael@0: static_cast(aEntry); michael@0: for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { michael@0: delete (BroadcastListener*)entry->mListeners[i]; michael@0: } michael@0: michael@0: // N.B. that we need to manually run the dtor because we michael@0: // constructed the nsSmallVoidArray object in-place. michael@0: entry->mListeners.~nsSmallVoidArray(); michael@0: } michael@0: michael@0: static bool michael@0: CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute) michael@0: { michael@0: // Don't push changes to the |id|, |ref|, |persist|, |command| or michael@0: // |observes| attribute. michael@0: if (aNameSpaceID == kNameSpaceID_None) { michael@0: if ((aAttribute == nsGkAtoms::id) || michael@0: (aAttribute == nsGkAtoms::ref) || michael@0: (aAttribute == nsGkAtoms::persist) || michael@0: (aAttribute == nsGkAtoms::command) || michael@0: (aAttribute == nsGkAtoms::observes)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: struct nsAttrNameInfo michael@0: { michael@0: nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) : michael@0: mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {} michael@0: nsAttrNameInfo(const nsAttrNameInfo& aOther) : michael@0: mNamespaceID(aOther.mNamespaceID), mName(aOther.mName), michael@0: mPrefix(aOther.mPrefix) {} michael@0: int32_t mNamespaceID; michael@0: nsCOMPtr mName; michael@0: nsCOMPtr mPrefix; michael@0: }; michael@0: michael@0: void michael@0: XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster, michael@0: Element *aListener, michael@0: const nsAString &aAttr) michael@0: { michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener, michael@0: aAttr); michael@0: mDelayedBroadcasters.AppendElement(delayedUpdate); michael@0: MaybeBroadcast(); michael@0: return; michael@0: } michael@0: bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters; michael@0: michael@0: if (aAttr.EqualsLiteral("*")) { michael@0: uint32_t count = aBroadcaster->GetAttrCount(); michael@0: nsTArray attributes(count); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i); michael@0: int32_t nameSpaceID = attrName->NamespaceID(); michael@0: nsIAtom* name = attrName->LocalName(); michael@0: michael@0: // _Don't_ push the |id|, |ref|, or |persist| attribute's value! michael@0: if (! CanBroadcast(nameSpaceID, name)) michael@0: continue; michael@0: michael@0: attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name, michael@0: attrName->GetPrefix())); michael@0: } michael@0: michael@0: count = attributes.Length(); michael@0: while (count-- > 0) { michael@0: int32_t nameSpaceID = attributes[count].mNamespaceID; michael@0: nsIAtom* name = attributes[count].mName; michael@0: nsAutoString value; michael@0: if (aBroadcaster->GetAttr(nameSpaceID, name, value)) { michael@0: aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix, michael@0: value, notify); michael@0: } michael@0: michael@0: #if 0 michael@0: // XXX we don't fire the |onbroadcast| handler during michael@0: // initial hookup: doing so would potentially run the michael@0: // |onbroadcast| handler before the |onload| handler, michael@0: // which could define JS properties that mask XBL michael@0: // properties, etc. michael@0: ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); michael@0: #endif michael@0: } michael@0: } michael@0: else { michael@0: // Find out if the attribute is even present at all. michael@0: nsCOMPtr name = do_GetAtom(aAttr); michael@0: michael@0: nsAutoString value; michael@0: if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) { michael@0: aListener->SetAttr(kNameSpaceID_None, name, value, notify); michael@0: } else { michael@0: aListener->UnsetAttr(kNameSpaceID_None, name, notify); michael@0: } michael@0: michael@0: #if 0 michael@0: // XXX we don't fire the |onbroadcast| handler during initial michael@0: // hookup: doing so would potentially run the |onbroadcast| michael@0: // handler before the |onload| handler, which could define JS michael@0: // properties that mask XBL properties, etc. michael@0: ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster, michael@0: nsIDOMElement* aListener, michael@0: const nsAString& aAttr) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr broadcaster = do_QueryInterface(aBroadcaster); michael@0: nsCOMPtr listener = do_QueryInterface(aListener); michael@0: NS_ENSURE_ARG(broadcaster && listener); michael@0: AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: void michael@0: XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, michael@0: const nsAString& aAttr, ErrorResult& aRv) michael@0: { michael@0: nsresult rv = michael@0: nsContentUtils::CheckSameOrigin(this, &aBroadcaster); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: rv = nsContentUtils::CheckSameOrigin(this, &aListener); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: static const PLDHashTableOps gOps = { michael@0: PL_DHashAllocTable, michael@0: PL_DHashFreeTable, michael@0: PL_DHashVoidPtrKeyStub, michael@0: PL_DHashMatchEntryStub, michael@0: PL_DHashMoveEntryStub, michael@0: ClearBroadcasterMapEntry, michael@0: PL_DHashFinalizeStub, michael@0: nullptr michael@0: }; michael@0: michael@0: if (! mBroadcasterMap) { michael@0: mBroadcasterMap = michael@0: PL_NewDHashTable(&gOps, nullptr, sizeof(BroadcasterMapEntry), michael@0: PL_DHASH_MIN_SIZE); michael@0: michael@0: if (! mBroadcasterMap) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: BroadcasterMapEntry* entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_FREE(entry)) { michael@0: entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, michael@0: PL_DHASH_ADD)); michael@0: michael@0: if (! entry) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return; michael@0: } michael@0: michael@0: entry->mBroadcaster = &aBroadcaster; michael@0: michael@0: // N.B. placement new to construct the nsSmallVoidArray object michael@0: // in-place michael@0: new (&entry->mListeners) nsSmallVoidArray(); michael@0: } michael@0: michael@0: // Only add the listener if it's not there already! michael@0: nsCOMPtr attr = do_GetAtom(aAttr); michael@0: michael@0: BroadcastListener* bl; michael@0: for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { michael@0: bl = static_cast(entry->mListeners[i]); michael@0: michael@0: nsCOMPtr blListener = do_QueryReferent(bl->mListener); michael@0: michael@0: if (blListener == &aListener && bl->mAttribute == attr) michael@0: return; michael@0: } michael@0: michael@0: bl = new BroadcastListener; michael@0: michael@0: bl->mListener = do_GetWeakReference(&aListener); michael@0: bl->mAttribute = attr; michael@0: michael@0: entry->mListeners.AppendElement(bl); michael@0: michael@0: SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster, michael@0: nsIDOMElement* aListener, michael@0: const nsAString& aAttr) michael@0: { michael@0: nsCOMPtr broadcaster = do_QueryInterface(aBroadcaster); michael@0: nsCOMPtr listener = do_QueryInterface(aListener); michael@0: NS_ENSURE_ARG(broadcaster && listener); michael@0: RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster, michael@0: Element& aListener, michael@0: const nsAString& aAttr) michael@0: { michael@0: // If we haven't added any broadcast listeners, then there sure michael@0: // aren't any to remove. michael@0: if (! mBroadcasterMap) michael@0: return; michael@0: michael@0: BroadcasterMapEntry* entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: nsCOMPtr attr = do_GetAtom(aAttr); michael@0: for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) { michael@0: BroadcastListener* bl = michael@0: static_cast(entry->mListeners[i]); michael@0: michael@0: nsCOMPtr blListener = do_QueryReferent(bl->mListener); michael@0: michael@0: if (blListener == &aListener && bl->mAttribute == attr) { michael@0: entry->mListeners.RemoveElementAt(i); michael@0: delete bl; michael@0: michael@0: if (entry->mListeners.Count() == 0) michael@0: PL_DHashTableOperate(mBroadcasterMap, &aBroadcaster, michael@0: PL_DHASH_REMOVE); michael@0: michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, michael@0: Element* aListener, michael@0: nsIAtom* aAttr) michael@0: { michael@0: // Now we execute the onchange handler in the context of the michael@0: // observer. We need to find the observer in order to michael@0: // execute the handler. michael@0: michael@0: for (nsIContent* child = aListener->GetFirstChild(); michael@0: child; michael@0: child = child->GetNextSibling()) { michael@0: michael@0: // Look for an element beneath the listener. This michael@0: // ought to have an |element| attribute that refers to michael@0: // aBroadcaster, and an |attribute| element that tells us what michael@0: // attriubtes we're listening for. michael@0: if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) michael@0: continue; michael@0: michael@0: // Is this the element that was listening to us? michael@0: nsAutoString listeningToID; michael@0: child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID); michael@0: michael@0: nsAutoString broadcasterID; michael@0: aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID); michael@0: michael@0: if (listeningToID != broadcasterID) michael@0: continue; michael@0: michael@0: // We are observing the broadcaster, but is this the right michael@0: // attribute? michael@0: nsAutoString listeningToAttribute; michael@0: child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, michael@0: listeningToAttribute); michael@0: michael@0: if (!aAttr->Equals(listeningToAttribute) && michael@0: !listeningToAttribute.EqualsLiteral("*")) { michael@0: continue; michael@0: } michael@0: michael@0: // This is the right element. Execute the michael@0: // |onbroadcast| event handler michael@0: WidgetEvent event(true, NS_XUL_BROADCAST); michael@0: michael@0: nsCOMPtr shell = GetShell(); michael@0: if (shell) { michael@0: nsRefPtr aPresContext = shell->GetPresContext(); michael@0: michael@0: // Handle the DOM event michael@0: nsEventStatus status = nsEventStatus_eIgnore; michael@0: EventDispatcher::Dispatch(child, aPresContext, &event, nullptr, michael@0: &status); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::AttributeWillChange(nsIDocument* aDocument, michael@0: Element* aElement, int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, int32_t aModType) michael@0: { michael@0: NS_ABORT_IF_FALSE(aElement, "Null content!"); michael@0: NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!"); michael@0: michael@0: // XXXbz check aNameSpaceID, dammit! michael@0: // See if we need to update our ref map. michael@0: if (aAttribute == nsGkAtoms::ref || michael@0: (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) { michael@0: // Might not need this, but be safe for now. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: RemoveElementFromRefMap(aElement); michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULDocument::AttributeChanged(nsIDocument* aDocument, michael@0: Element* aElement, int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, int32_t aModType) michael@0: { michael@0: NS_ASSERTION(aDocument == this, "unexpected doc"); michael@0: michael@0: // Might not need this, but be safe for now. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: // XXXbz check aNameSpaceID, dammit! michael@0: // See if we need to update our ref map. michael@0: if (aAttribute == nsGkAtoms::ref || michael@0: (aAttribute == nsGkAtoms::id && !aElement->GetIDAttributeName())) { michael@0: AddElementToRefMap(aElement); michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: // Synchronize broadcast listeners michael@0: if (mBroadcasterMap && michael@0: CanBroadcast(aNameSpaceID, aAttribute)) { michael@0: BroadcasterMapEntry* entry = michael@0: static_cast michael@0: (PL_DHashTableOperate(mBroadcasterMap, aElement, michael@0: PL_DHASH_LOOKUP)); michael@0: michael@0: if (PL_DHASH_ENTRY_IS_BUSY(entry)) { michael@0: // We've got listeners: push the value. michael@0: nsAutoString value; michael@0: bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value); michael@0: michael@0: int32_t i; michael@0: for (i = entry->mListeners.Count() - 1; i >= 0; --i) { michael@0: BroadcastListener* bl = michael@0: static_cast(entry->mListeners[i]); michael@0: michael@0: if ((bl->mAttribute == aAttribute) || michael@0: (bl->mAttribute == nsGkAtoms::_asterix)) { michael@0: nsCOMPtr listenerEl michael@0: = do_QueryReferent(bl->mListener); michael@0: if (listenerEl) { michael@0: nsAutoString currentValue; michael@0: bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None, michael@0: aAttribute, michael@0: currentValue); michael@0: // We need to update listener only if we're michael@0: // (1) removing an existing attribute, michael@0: // (2) adding a new attribute or michael@0: // (3) changing the value of an attribute. michael@0: bool needsAttrChange = michael@0: attrSet != hasAttr || !value.Equals(currentValue); michael@0: nsDelayedBroadcastUpdate delayedUpdate(aElement, michael@0: listenerEl, michael@0: aAttribute, michael@0: value, michael@0: attrSet, michael@0: needsAttrChange); michael@0: michael@0: uint32_t index = michael@0: mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate, michael@0: 0, nsDelayedBroadcastUpdate::Comparator()); michael@0: if (index != mDelayedAttrChangeBroadcasts.NoIndex) { michael@0: if (mHandlingDelayedAttrChange) { michael@0: NS_WARNING("Broadcasting loop!"); michael@0: continue; michael@0: } michael@0: mDelayedAttrChangeBroadcasts.RemoveElementAt(index); michael@0: } michael@0: michael@0: mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // checks for modifications in broadcasters michael@0: bool listener, resolved; michael@0: CheckBroadcasterHookup(aElement, &listener, &resolved); michael@0: michael@0: // See if there is anything we need to persist in the localstore. michael@0: // michael@0: // XXX Namespace handling broken :-( michael@0: nsAutoString persist; michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); michael@0: if (!persist.IsEmpty()) { michael@0: // XXXldb This should check that it's a token, not just a substring. michael@0: if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) { michael@0: rv = Persist(aElement, kNameSpaceID_None, aAttribute); michael@0: if (NS_FAILED(rv)) return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULDocument::ContentAppended(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aFirstNewContent, michael@0: int32_t aNewIndexInContainer) michael@0: { michael@0: NS_ASSERTION(aDocument == this, "unexpected doc"); michael@0: michael@0: // Might not need this, but be safe for now. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: // Update our element map michael@0: nsresult rv = NS_OK; michael@0: for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv); michael@0: cur = cur->GetNextSibling()) { michael@0: rv = AddSubtreeToDocument(cur); michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULDocument::ContentInserted(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer) michael@0: { michael@0: NS_ASSERTION(aDocument == this, "unexpected doc"); michael@0: michael@0: // Might not need this, but be safe for now. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: AddSubtreeToDocument(aChild); michael@0: } michael@0: michael@0: void michael@0: XULDocument::ContentRemoved(nsIDocument* aDocument, michael@0: nsIContent* aContainer, michael@0: nsIContent* aChild, michael@0: int32_t aIndexInContainer, michael@0: nsIContent* aPreviousSibling) michael@0: { michael@0: NS_ASSERTION(aDocument == this, "unexpected doc"); michael@0: michael@0: // Might not need this, but be safe for now. michael@0: nsCOMPtr kungFuDeathGrip(this); michael@0: michael@0: RemoveSubtreeFromDocument(aChild); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIXULDocument interface michael@0: // michael@0: michael@0: void michael@0: XULDocument::GetElementsForID(const nsAString& aID, michael@0: nsCOMArray& aElements) michael@0: { michael@0: aElements.Clear(); michael@0: michael@0: nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID); michael@0: if (entry) { michael@0: entry->AppendAllIdContent(&aElements); michael@0: } michael@0: nsRefMapEntry *refEntry = mRefMap.GetEntry(aID); michael@0: if (refEntry) { michael@0: refEntry->AppendAll(&aElements); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddForwardReference(nsForwardReference* aRef) michael@0: { michael@0: if (mResolutionPhase < aRef->GetPhase()) { michael@0: if (!mForwardReferences.AppendElement(aRef)) { michael@0: delete aRef; michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: else { michael@0: NS_ERROR("forward references have already been resolved"); michael@0: delete aRef; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ResolveForwardReferences() michael@0: { michael@0: if (mResolutionPhase == nsForwardReference::eDone) michael@0: return NS_OK; michael@0: michael@0: NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart, michael@0: "nested ResolveForwardReferences()"); michael@0: michael@0: // Resolve each outstanding 'forward' reference. We iterate michael@0: // through the list of forward references until no more forward michael@0: // references can be resolved. This annealing process is michael@0: // guaranteed to converge because we've "closed the gate" to new michael@0: // forward references. michael@0: michael@0: const nsForwardReference::Phase* pass = nsForwardReference::kPasses; michael@0: while ((mResolutionPhase = *pass) != nsForwardReference::eDone) { michael@0: uint32_t previous = 0; michael@0: while (mForwardReferences.Length() && michael@0: mForwardReferences.Length() != previous) { michael@0: previous = mForwardReferences.Length(); michael@0: michael@0: for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) { michael@0: nsForwardReference* fwdref = mForwardReferences[i]; michael@0: michael@0: if (fwdref->GetPhase() == *pass) { michael@0: nsForwardReference::Result result = fwdref->Resolve(); michael@0: michael@0: switch (result) { michael@0: case nsForwardReference::eResolve_Succeeded: michael@0: case nsForwardReference::eResolve_Error: michael@0: mForwardReferences.RemoveElementAt(i); michael@0: michael@0: // fixup because we removed from list michael@0: --i; michael@0: break; michael@0: michael@0: case nsForwardReference::eResolve_Later: michael@0: // do nothing. we'll try again later michael@0: ; michael@0: } michael@0: michael@0: if (mResolutionPhase == nsForwardReference::eStart) { michael@0: // Resolve() loaded a dynamic overlay, michael@0: // (see XULDocument::LoadOverlayInternal()). michael@0: // Return for now, we will be called again. michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: ++pass; michael@0: } michael@0: michael@0: mForwardReferences.Clear(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIDOMDocument interface michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetElementsByAttribute(const nsAString& aAttribute, michael@0: const nsAString& aValue, michael@0: nsIDOMNodeList** aReturn) michael@0: { michael@0: *aReturn = GetElementsByAttribute(aAttribute, aValue).take(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetElementsByAttribute(const nsAString& aAttribute, michael@0: const nsAString& aValue) michael@0: { michael@0: nsCOMPtr attrAtom(do_GetAtom(aAttribute)); michael@0: void* attrValue = new nsString(aValue); michael@0: nsRefPtr list = new nsContentList(this, michael@0: MatchAttribute, michael@0: nsContentUtils::DestroyMatchString, michael@0: attrValue, michael@0: true, michael@0: attrAtom, michael@0: kNameSpaceID_Unknown); michael@0: michael@0: return list.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aAttribute, michael@0: const nsAString& aValue, michael@0: nsIDOMNodeList** aReturn) michael@0: { michael@0: ErrorResult rv; michael@0: *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute, michael@0: aValue, rv).take(); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aAttribute, michael@0: const nsAString& aValue, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr attrAtom(do_GetAtom(aAttribute)); michael@0: void* attrValue = new nsString(aValue); michael@0: michael@0: int32_t nameSpaceId = kNameSpaceID_Wildcard; michael@0: if (!aNamespaceURI.EqualsLiteral("*")) { michael@0: nsresult rv = michael@0: nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, michael@0: nameSpaceId); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: nsRefPtr list = new nsContentList(this, michael@0: MatchAttribute, michael@0: nsContentUtils::DestroyMatchString, michael@0: attrValue, michael@0: true, michael@0: attrAtom, michael@0: nameSpaceId); michael@0: return list.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::Persist(const nsAString& aID, michael@0: const nsAString& aAttr) michael@0: { michael@0: // If we're currently reading persisted attributes out of the michael@0: // localstore, _don't_ re-enter and try to set them again! michael@0: if (mApplyingPersistedAttrs) michael@0: return NS_OK; michael@0: michael@0: Element* element = nsDocument::GetElementById(aID); michael@0: if (!element) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr tag; michael@0: int32_t nameSpaceID; michael@0: michael@0: nsCOMPtr ni = element->GetExistingAttrNameFromQName(aAttr); michael@0: nsresult rv; michael@0: if (ni) { michael@0: tag = ni->NameAtom(); michael@0: nameSpaceID = ni->NamespaceID(); michael@0: } michael@0: else { michael@0: // Make sure that this QName is going to be valid. michael@0: const char16_t *colon; michael@0: rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // There was an invalid character or it was malformed. michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (colon) { michael@0: // We don't really handle namespace qualifiers in attribute names. michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: tag = do_GetAtom(aAttr); michael@0: NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nameSpaceID = kNameSpaceID_None; michael@0: } michael@0: michael@0: rv = Persist(element, nameSpaceID, tag); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute) michael@0: { michael@0: // For non-chrome documents, persistance is simply broken michael@0: if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // First make sure we _have_ a local store to stuff the persisted michael@0: // information into. (We might not have one if profile information michael@0: // hasn't been loaded yet...) michael@0: if (!mLocalStore) michael@0: return NS_OK; michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr element; michael@0: rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // No ID, so nothing to persist. michael@0: if (! element) michael@0: return NS_OK; michael@0: michael@0: // Ick. Construct a property from the attribute. Punt on michael@0: // namespaces for now. michael@0: // Don't bother with unreasonable attributes. We clamp long values, michael@0: // but truncating attribute names turns it into a different attribute michael@0: // so there's no point in persisting anything at all michael@0: nsAtomCString attrstr(aAttribute); michael@0: if (attrstr.Length() > kMaxAttrNameLength) { michael@0: NS_WARNING("Can't persist, Attribute name too long"); michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: nsCOMPtr attr; michael@0: rv = gRDFService->GetResource(attrstr, michael@0: getter_AddRefs(attr)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Turn the value into a literal michael@0: nsAutoString valuestr; michael@0: aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr); michael@0: michael@0: // prevent over-long attributes that choke the parser (bug 319846) michael@0: // (can't simply Truncate without testing, it's implemented michael@0: // using SetLength and will grow a short string) michael@0: if (valuestr.Length() > kMaxAttributeLength) { michael@0: NS_WARNING("Truncating persisted attribute value"); michael@0: valuestr.Truncate(kMaxAttributeLength); michael@0: } michael@0: michael@0: // See if there was an old value... michael@0: nsCOMPtr oldvalue; michael@0: rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (oldvalue && valuestr.IsEmpty()) { michael@0: // ...there was an oldvalue, and they've removed it. XXXThis michael@0: // handling isn't quite right... michael@0: rv = mLocalStore->Unassert(element, attr, oldvalue); michael@0: } michael@0: else { michael@0: // Now either 'change' or 'assert' based on whether there was michael@0: // an old value. michael@0: nsCOMPtr newvalue; michael@0: rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (oldvalue) { michael@0: if (oldvalue != newvalue) michael@0: rv = mLocalStore->Change(element, attr, oldvalue, newvalue); michael@0: else michael@0: rv = NS_OK; michael@0: } michael@0: else { michael@0: rv = mLocalStore->Assert(element, attr, newvalue, true); michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Add it to the persisted set for this document (if it's not michael@0: // there already). michael@0: { michael@0: nsAutoCString docurl; michael@0: rv = mDocumentURI->GetSpec(docurl); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr doc; michael@0: rv = gRDFService->GetResource(docurl, getter_AddRefs(doc)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: bool hasAssertion; michael@0: rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (! hasAssertion) { michael@0: rv = mLocalStore->Assert(doc, kNC_persist, element, true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::GetViewportSize(int32_t* aWidth, michael@0: int32_t* aHeight) michael@0: { michael@0: *aWidth = *aHeight = 0; michael@0: michael@0: FlushPendingNotifications(Flush_Layout); michael@0: michael@0: nsIPresShell *shell = GetShell(); michael@0: NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); michael@0: michael@0: nsIFrame* frame = shell->GetRootFrame(); michael@0: NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); michael@0: michael@0: nsSize size = frame->GetSize(); michael@0: michael@0: *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width); michael@0: *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetWidth(int32_t* aWidth) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aWidth); michael@0: michael@0: int32_t height; michael@0: return GetViewportSize(aWidth, &height); michael@0: } michael@0: michael@0: int32_t michael@0: XULDocument::GetWidth(ErrorResult& aRv) michael@0: { michael@0: int32_t width; michael@0: aRv = GetWidth(&width); michael@0: return width; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetHeight(int32_t* aHeight) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aHeight); michael@0: michael@0: int32_t width; michael@0: return GetViewportSize(&width, aHeight); michael@0: } michael@0: michael@0: int32_t michael@0: XULDocument::GetHeight(ErrorResult& aRv) michael@0: { michael@0: int32_t height; michael@0: aRv = GetHeight(&height); michael@0: return height; michael@0: } michael@0: michael@0: JSObject* michael@0: GetScopeObjectOfNode(nsIDOMNode* node) michael@0: { michael@0: MOZ_ASSERT(node, "Must not be called with null."); michael@0: michael@0: // Window root occasionally keeps alive a node of a document whose michael@0: // window is already dead. If in this brief period someone calls michael@0: // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw, michael@0: // because it will not know which scope this node belongs to. Returning michael@0: // an orphan node like that to JS would be a bug anyway, so to avoid michael@0: // this, let's do the same check as nsNodeSH::PreCreate does to michael@0: // determine the scope and if it fails let's just return null in michael@0: // XULDocument::GetPopupNode. michael@0: nsCOMPtr inode = do_QueryInterface(node); michael@0: MOZ_ASSERT(inode, "How can this happen?"); michael@0: michael@0: nsIDocument* doc = inode->OwnerDoc(); michael@0: MOZ_ASSERT(inode, "This should never happen."); michael@0: michael@0: nsIGlobalObject* global = doc->GetScopeObject(); michael@0: return global ? global->GetGlobalJSObject() : nullptr; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIDOMXULDocument interface michael@0: // michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetPopupNode(nsIDOMNode** aNode) michael@0: { michael@0: *aNode = nullptr; michael@0: michael@0: nsCOMPtr node; michael@0: nsCOMPtr rootWin = GetWindowRoot(); michael@0: if (rootWin) michael@0: node = rootWin->GetPopupNode(); // addref happens here michael@0: michael@0: if (!node) { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: node = pm->GetLastTriggerPopupNode(this); michael@0: } michael@0: } michael@0: michael@0: if (node && nsContentUtils::CanCallerAccess(node) michael@0: && GetScopeObjectOfNode(node)) { michael@0: node.swap(*aNode); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetPopupNode() michael@0: { michael@0: nsCOMPtr node; michael@0: DebugOnly rv = GetPopupNode(getter_AddRefs(node)); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: nsCOMPtr retval(do_QueryInterface(node)); michael@0: return retval.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::SetPopupNode(nsIDOMNode* aNode) michael@0: { michael@0: if (aNode) { michael@0: // only allow real node objects michael@0: nsCOMPtr node = do_QueryInterface(aNode); michael@0: NS_ENSURE_ARG(node); michael@0: } michael@0: michael@0: nsCOMPtr rootWin = GetWindowRoot(); michael@0: if (rootWin) michael@0: rootWin->SetPopupNode(aNode); // addref happens here michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::SetPopupNode(nsINode* aNode) michael@0: { michael@0: nsCOMPtr node(do_QueryInterface(aNode)); michael@0: DebugOnly rv = SetPopupNode(node); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: } michael@0: michael@0: // Returns the rangeOffset element from the XUL Popup Manager. This is for michael@0: // chrome callers only. michael@0: NS_IMETHODIMP michael@0: XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRangeParent); michael@0: *aRangeParent = nullptr; michael@0: michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (!pm) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: int32_t offset; michael@0: pm->GetMouseLocation(aRangeParent, &offset); michael@0: michael@0: if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) { michael@0: NS_RELEASE(*aRangeParent); michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetPopupRangeParent(ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr node; michael@0: aRv = GetPopupRangeParent(getter_AddRefs(node)); michael@0: nsCOMPtr retval(do_QueryInterface(node)); michael@0: return retval.forget(); michael@0: } michael@0: michael@0: michael@0: // Returns the rangeOffset element from the XUL Popup Manager. We check the michael@0: // rangeParent to determine if the caller has rights to access to the data. michael@0: NS_IMETHODIMP michael@0: XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset) michael@0: { michael@0: ErrorResult rv; michael@0: *aRangeOffset = GetPopupRangeOffset(rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: int32_t michael@0: XULDocument::GetPopupRangeOffset(ErrorResult& aRv) michael@0: { michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (!pm) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return 0; michael@0: } michael@0: michael@0: int32_t offset; michael@0: nsCOMPtr parent; michael@0: pm->GetMouseLocation(getter_AddRefs(parent), &offset); michael@0: michael@0: if (parent && !nsContentUtils::CanCallerAccess(parent)) { michael@0: aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); michael@0: return 0; michael@0: } michael@0: return offset; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetTooltipNode(nsIDOMNode** aNode) michael@0: { michael@0: *aNode = nullptr; michael@0: michael@0: nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); michael@0: if (pm) { michael@0: nsCOMPtr node = pm->GetLastTriggerTooltipNode(this); michael@0: if (node && nsContentUtils::CanCallerAccess(node)) michael@0: node.swap(*aNode); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetTooltipNode() michael@0: { michael@0: nsCOMPtr node; michael@0: DebugOnly rv = GetTooltipNode(getter_AddRefs(node)); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: nsCOMPtr retval(do_QueryInterface(node)); michael@0: return retval.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::SetTooltipNode(nsIDOMNode* aNode) michael@0: { michael@0: // do nothing michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker) michael@0: { michael@0: *aTracker = mCommandDispatcher; michael@0: NS_IF_ADDREF(*aTracker); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Element* michael@0: XULDocument::GetElementById(const nsAString& aId) michael@0: { michael@0: if (!CheckGetElementByIdArg(aId)) michael@0: return nullptr; michael@0: michael@0: nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); michael@0: if (entry) { michael@0: Element* element = entry->GetIdElement(); michael@0: if (element) michael@0: return element; michael@0: } michael@0: michael@0: nsRefMapEntry* refEntry = mRefMap.GetEntry(aId); michael@0: if (refEntry) { michael@0: NS_ASSERTION(refEntry->GetFirstElement(), michael@0: "nsRefMapEntries should have nonempty content lists"); michael@0: return refEntry->GetFirstElement(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddElementToDocumentPre(Element* aElement) michael@0: { michael@0: // Do a bunch of work that's necessary when an element gets added michael@0: // to the XUL Document. michael@0: nsresult rv; michael@0: michael@0: // 1. Add the element to the resource-to-element map. Also add it to michael@0: // the id map, since it seems this can be called when creating michael@0: // elements from prototypes. michael@0: nsIAtom* id = aElement->GetID(); michael@0: if (id) { michael@0: // FIXME: Shouldn't BindToTree take care of this? michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: AddToIdTable(aElement, id); michael@0: } michael@0: rv = AddElementToRefMap(aElement); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // 2. If the element is a 'command updater' (i.e., has a michael@0: // "commandupdater='true'" attribute), then add the element to the michael@0: // document's command dispatcher michael@0: if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater, michael@0: nsGkAtoms::_true, eCaseMatters)) { michael@0: rv = nsXULContentUtils::SetCommandUpdater(this, aElement); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // 3. Check for a broadcaster hookup attribute, in which case michael@0: // we'll hook the node up as a listener on a broadcaster. michael@0: bool listener, resolved; michael@0: rv = CheckBroadcasterHookup(aElement, &listener, &resolved); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // If it's not there yet, we may be able to defer hookup until michael@0: // later. michael@0: if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) { michael@0: BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement); michael@0: if (! hookup) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = AddForwardReference(hookup); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddElementToDocumentPost(Element* aElement) michael@0: { michael@0: // We need to pay special attention to the keyset tag to set up a listener michael@0: if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { michael@0: // Create our XUL key listener and hook it up. michael@0: nsXBLService::AttachGlobalKeyHandler(aElement); michael@0: } michael@0: michael@0: // See if we need to attach a XUL template to this node michael@0: bool needsHookup; michael@0: nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (needsHookup) { michael@0: if (mResolutionPhase == nsForwardReference::eDone) { michael@0: rv = CreateTemplateBuilder(aElement); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: else { michael@0: TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement); michael@0: if (! hookup) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = AddForwardReference(hookup); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::AddSubtreeToDocument(nsIContent* aContent) michael@0: { michael@0: NS_ASSERTION(aContent->GetCurrentDoc() == this, "Element not in doc!"); michael@0: // From here on we only care about elements. michael@0: if (!aContent->IsElement()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: Element* aElement = aContent->AsElement(); michael@0: michael@0: // Do pre-order addition magic michael@0: nsresult rv = AddElementToDocumentPre(aElement); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Recurse to children michael@0: for (nsIContent* child = aElement->GetLastChild(); michael@0: child; michael@0: child = child->GetPreviousSibling()) { michael@0: michael@0: rv = AddSubtreeToDocument(child); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: // Do post-order addition magic michael@0: return AddElementToDocumentPost(aElement); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent) michael@0: { michael@0: // From here on we only care about elements. michael@0: if (!aContent->IsElement()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: Element* aElement = aContent->AsElement(); michael@0: michael@0: // Do a bunch of cleanup to remove an element from the XUL michael@0: // document. michael@0: nsresult rv; michael@0: michael@0: if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { michael@0: nsXBLService::DetachGlobalKeyHandler(aElement); michael@0: } michael@0: michael@0: // 1. Remove any children from the document. michael@0: for (nsIContent* child = aElement->GetLastChild(); michael@0: child; michael@0: child = child->GetPreviousSibling()) { michael@0: michael@0: rv = RemoveSubtreeFromDocument(child); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: // 2. Remove the element from the resource-to-element map. michael@0: // Also remove it from the id map, since we added it in michael@0: // AddElementToDocumentPre(). michael@0: RemoveElementFromRefMap(aElement); michael@0: nsIAtom* id = aElement->GetID(); michael@0: if (id) { michael@0: // FIXME: Shouldn't UnbindFromTree take care of this? michael@0: nsAutoScriptBlocker scriptBlocker; michael@0: RemoveFromIdTable(aElement, id); michael@0: } michael@0: michael@0: // 3. If the element is a 'command updater', then remove the michael@0: // element from the document's command dispatcher. michael@0: if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater, michael@0: nsGkAtoms::_true, eCaseMatters)) { michael@0: nsCOMPtr domelement = do_QueryInterface(aElement); michael@0: NS_ASSERTION(domelement != nullptr, "not a DOM element"); michael@0: if (! domelement) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = mCommandDispatcher->RemoveCommandUpdater(domelement); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // 4. Remove the element from our broadcaster map, since it is no longer michael@0: // in the document. michael@0: nsCOMPtr broadcaster, listener; michael@0: nsAutoString attribute, broadcasterID; michael@0: rv = FindBroadcaster(aElement, getter_AddRefs(listener), michael@0: broadcasterID, attribute, getter_AddRefs(broadcaster)); michael@0: if (rv == NS_FINDBROADCASTER_FOUND) { michael@0: RemoveBroadcastListenerFor(*broadcaster, *listener, attribute); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::SetTemplateBuilderFor(nsIContent* aContent, michael@0: nsIXULTemplateBuilder* aBuilder) michael@0: { michael@0: if (! mTemplateBuilderTable) { michael@0: if (!aBuilder) { michael@0: return NS_OK; michael@0: } michael@0: mTemplateBuilderTable = new BuilderTable; michael@0: } michael@0: michael@0: if (aBuilder) { michael@0: mTemplateBuilderTable->Put(aContent, aBuilder); michael@0: } michael@0: else { michael@0: mTemplateBuilderTable->Remove(aContent); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetTemplateBuilderFor(nsIContent* aContent, michael@0: nsIXULTemplateBuilder** aResult) michael@0: { michael@0: if (mTemplateBuilderTable) { michael@0: mTemplateBuilderTable->Get(aContent, aResult); michael@0: } michael@0: else michael@0: *aResult = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: GetRefMapAttribute(Element* aElement, nsAutoString* aValue) michael@0: { michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue); michael@0: if (aValue->IsEmpty() && !aElement->GetIDAttributeName()) { michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, *aValue); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddElementToRefMap(Element* aElement) michael@0: { michael@0: // Look at the element's 'ref' attribute, and if set, michael@0: // add an entry in the resource-to-element map to the element. michael@0: nsAutoString value; michael@0: GetRefMapAttribute(aElement, &value); michael@0: if (!value.IsEmpty()) { michael@0: nsRefMapEntry *entry = mRefMap.PutEntry(value); michael@0: if (!entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: if (!entry->AddElement(aElement)) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::RemoveElementFromRefMap(Element* aElement) michael@0: { michael@0: // Remove the element from the resource-to-element map. michael@0: nsAutoString value; michael@0: GetRefMapAttribute(aElement, &value); michael@0: if (!value.IsEmpty()) { michael@0: nsRefMapEntry *entry = mRefMap.GetEntry(value); michael@0: if (!entry) michael@0: return; michael@0: if (entry->RemoveElement(aElement)) { michael@0: mRefMap.RawRemoveEntry(entry); michael@0: } michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsIDOMNode interface michael@0: // michael@0: michael@0: nsresult michael@0: XULDocument::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const michael@0: { michael@0: // We don't allow cloning of a XUL document michael@0: *aResult = nullptr; michael@0: return NS_ERROR_DOM_NOT_SUPPORTED_ERR; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // Implementation methods michael@0: // michael@0: michael@0: nsresult michael@0: XULDocument::Init() michael@0: { michael@0: nsresult rv = XMLDocument::Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Create our command dispatcher and hook it up. michael@0: mCommandDispatcher = new nsXULCommandDispatcher(this); michael@0: NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: // this _could_ fail; e.g., if we've tried to grab the local store michael@0: // before profiles have initialized. If so, no big deal; nothing michael@0: // will persist. michael@0: mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID); michael@0: michael@0: if (gRefCnt++ == 0) { michael@0: // Keep the RDF service cached in a member variable to make using michael@0: // it a bit less painful michael@0: rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"), michael@0: &kNC_persist); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"), michael@0: &kNC_attribute); michael@0: gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"), michael@0: &kNC_value); michael@0: michael@0: // ensure that the XUL prototype cache is instantiated successfully, michael@0: // so that we can use nsXULPrototypeCache::GetInstance() without michael@0: // null-checks in the rest of the class. michael@0: nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); michael@0: if (!cache) { michael@0: NS_ERROR("Could not instantiate nsXULPrototypeCache"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: Preferences::RegisterCallback(XULDocument::DirectionChanged, michael@0: "intl.uidirection.", this); michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (! gXULLog) michael@0: gXULLog = PR_NewLogModule("XULDocument"); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::StartLayout(void) michael@0: { michael@0: mMayStartLayout = true; michael@0: nsCOMPtr shell = GetShell(); michael@0: if (shell) { michael@0: // Resize-reflow this time michael@0: nsPresContext *cx = shell->GetPresContext(); michael@0: NS_ASSERTION(cx != nullptr, "no pres context"); michael@0: if (! cx) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr docShell = cx->GetDocShell(); michael@0: NS_ASSERTION(docShell != nullptr, "container is not a docshell"); michael@0: if (! docShell) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsRect r = cx->GetVisibleArea(); michael@0: rv = shell->Initialize(r.width, r.height); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: XULDocument::MatchAttribute(nsIContent* aContent, michael@0: int32_t aNamespaceID, michael@0: nsIAtom* aAttrName, michael@0: void* aData) michael@0: { michael@0: NS_PRECONDITION(aContent, "Must have content node to work with!"); michael@0: nsString* attrValue = static_cast(aData); michael@0: if (aNamespaceID != kNameSpaceID_Unknown && michael@0: aNamespaceID != kNameSpaceID_Wildcard) { michael@0: return attrValue->EqualsLiteral("*") ? michael@0: aContent->HasAttr(aNamespaceID, aAttrName) : michael@0: aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue, michael@0: eCaseMatters); michael@0: } michael@0: michael@0: // Qualified name match. This takes more work. michael@0: michael@0: uint32_t count = aContent->GetAttrCount(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: const nsAttrName* name = aContent->GetAttrNameAt(i); michael@0: bool nameMatch; michael@0: if (name->IsAtom()) { michael@0: nameMatch = name->Atom() == aAttrName; michael@0: } else if (aNamespaceID == kNameSpaceID_Wildcard) { michael@0: nameMatch = name->NodeInfo()->Equals(aAttrName); michael@0: } else { michael@0: nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName); michael@0: } michael@0: michael@0: if (nameMatch) { michael@0: return attrValue->EqualsLiteral("*") || michael@0: aContent->AttrValueIs(name->NamespaceID(), name->LocalName(), michael@0: *attrValue, eCaseMatters); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::PrepareToLoad(nsISupports* aContainer, michael@0: const char* aCommand, michael@0: nsIChannel* aChannel, michael@0: nsILoadGroup* aLoadGroup, michael@0: nsIParser** aResult) michael@0: { michael@0: // Get the document's principal michael@0: nsCOMPtr principal; michael@0: nsContentUtils::GetSecurityManager()-> michael@0: GetChannelPrincipal(aChannel, getter_AddRefs(principal)); michael@0: return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult); michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand, michael@0: nsIPrincipal* aDocumentPrincipal, michael@0: nsIParser** aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Create a new prototype document. michael@0: rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal); michael@0: if (NS_FAILED(rv)) { michael@0: mCurrentPrototype = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: // Bootstrap the master document prototype. michael@0: if (! mMasterPrototype) { michael@0: mMasterPrototype = mCurrentPrototype; michael@0: // Set our principal based on the master proto. michael@0: SetPrincipal(aDocumentPrincipal); michael@0: } michael@0: michael@0: // Create a XUL content sink, a parser, and kick off a load for michael@0: // the overlay. michael@0: nsRefPtr sink = new XULContentSinkImpl(); michael@0: if (!sink) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = sink->Init(this, mCurrentPrototype); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr parser = do_CreateInstance(kParserCID, &rv); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser"); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal : michael@0: eViewSource); michael@0: michael@0: parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"), michael@0: kCharsetFromDocTypeDefault); michael@0: parser->SetContentSink(sink); // grabs a reference to the parser michael@0: michael@0: *aResult = parser; michael@0: NS_ADDREF(*aResult); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::ApplyPersistentAttributes() michael@0: { michael@0: // For non-chrome documents, persistance is simply broken michael@0: if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: // Add all of the 'persisted' attributes into the content michael@0: // model. michael@0: if (!mLocalStore) michael@0: return NS_OK; michael@0: michael@0: mApplyingPersistedAttrs = true; michael@0: ApplyPersistentAttributesInternal(); michael@0: mApplyingPersistedAttrs = false; michael@0: michael@0: // After we've applied persistence once, we should only reapply michael@0: // it to nodes created by overlays michael@0: mRestrictPersistence = true; michael@0: mPersistenceIds.Clear(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::ApplyPersistentAttributesInternal() michael@0: { michael@0: nsCOMArray elements; michael@0: michael@0: nsAutoCString docurl; michael@0: mDocumentURI->GetSpec(docurl); michael@0: michael@0: nsCOMPtr doc; michael@0: gRDFService->GetResource(docurl, getter_AddRefs(doc)); michael@0: michael@0: nsCOMPtr persisted; michael@0: mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted)); michael@0: michael@0: while (1) { michael@0: bool hasmore = false; michael@0: persisted->HasMoreElements(&hasmore); michael@0: if (! hasmore) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: persisted->GetNext(getter_AddRefs(isupports)); michael@0: michael@0: nsCOMPtr resource = do_QueryInterface(isupports); michael@0: if (! resource) { michael@0: NS_WARNING("expected element to be a resource"); michael@0: continue; michael@0: } michael@0: michael@0: const char *uri; michael@0: resource->GetValueConst(&uri); michael@0: if (! uri) michael@0: continue; michael@0: michael@0: nsAutoString id; michael@0: nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id); michael@0: michael@0: if (id.IsEmpty()) michael@0: continue; michael@0: michael@0: if (mRestrictPersistence && !mPersistenceIds.Contains(id)) michael@0: continue; michael@0: michael@0: // This will clear the array if there are no elements. michael@0: GetElementsForID(id, elements); michael@0: michael@0: if (!elements.Count()) michael@0: continue; michael@0: michael@0: ApplyPersistentAttributesToElements(resource, elements); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource, michael@0: nsCOMArray& aElements) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr attrs; michael@0: rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: while (1) { michael@0: bool hasmore; michael@0: rv = attrs->HasMoreElements(&hasmore); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (! hasmore) michael@0: break; michael@0: michael@0: nsCOMPtr isupports; michael@0: rv = attrs->GetNext(getter_AddRefs(isupports)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr property = do_QueryInterface(isupports); michael@0: if (! property) { michael@0: NS_WARNING("expected a resource"); michael@0: continue; michael@0: } michael@0: michael@0: const char* attrname; michael@0: rv = property->GetValueConst(&attrname); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr attr = do_GetAtom(attrname); michael@0: if (! attr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // XXX could hang namespace off here, as well... michael@0: michael@0: nsCOMPtr node; michael@0: rv = mLocalStore->GetTarget(aResource, property, true, michael@0: getter_AddRefs(node)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsCOMPtr literal = do_QueryInterface(node); michael@0: if (! literal) { michael@0: NS_WARNING("expected a literal"); michael@0: continue; michael@0: } michael@0: michael@0: const char16_t* value; michael@0: rv = literal->GetValueConst(&value); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsDependentString wrapper(value); michael@0: michael@0: uint32_t cnt = aElements.Count(); michael@0: michael@0: for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { michael@0: nsCOMPtr element = aElements.SafeObjectAt(i); michael@0: if (!element) michael@0: continue; michael@0: michael@0: rv = element->SetAttr(/* XXX */ kNameSpaceID_None, michael@0: attr, michael@0: wrapper, michael@0: true); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber) michael@0: { michael@0: uint32_t i, count = mPrototypes.Length(); michael@0: for (i = 0; i < count; ++i) { michael@0: mPrototypes[i]->TraceProtos(aTrc, aGCNumber); michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // XULDocument::ContextStack michael@0: // michael@0: michael@0: XULDocument::ContextStack::ContextStack() michael@0: : mTop(nullptr), mDepth(0) michael@0: { michael@0: } michael@0: michael@0: XULDocument::ContextStack::~ContextStack() michael@0: { michael@0: while (mTop) { michael@0: Entry* doomed = mTop; michael@0: mTop = mTop->mNext; michael@0: NS_IF_RELEASE(doomed->mElement); michael@0: delete doomed; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype, michael@0: nsIContent* aElement) michael@0: { michael@0: Entry* entry = new Entry; michael@0: if (! entry) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: entry->mPrototype = aPrototype; michael@0: entry->mElement = aElement; michael@0: NS_IF_ADDREF(entry->mElement); michael@0: entry->mIndex = 0; michael@0: michael@0: entry->mNext = mTop; michael@0: mTop = entry; michael@0: michael@0: ++mDepth; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ContextStack::Pop() michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: Entry* doomed = mTop; michael@0: mTop = mTop->mNext; michael@0: --mDepth; michael@0: michael@0: NS_IF_RELEASE(doomed->mElement); michael@0: delete doomed; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype, michael@0: nsIContent** aElement, michael@0: int32_t* aIndex) michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *aPrototype = mTop->mPrototype; michael@0: *aElement = mTop->mElement; michael@0: NS_IF_ADDREF(*aElement); michael@0: *aIndex = mTop->mIndex; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::ContextStack::SetTopIndex(int32_t aIndex) michael@0: { michael@0: if (mDepth == 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: mTop->mIndex = aIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // Content model walking routines michael@0: // michael@0: michael@0: nsresult michael@0: XULDocument::PrepareToWalk() michael@0: { michael@0: // Prepare to walk the mCurrentPrototype michael@0: nsresult rv; michael@0: michael@0: // Keep an owning reference to the prototype document so that its michael@0: // elements aren't yanked from beneath us. michael@0: mPrototypes.AppendElement(mCurrentPrototype); michael@0: michael@0: // Get the prototype's root element and initialize the context michael@0: // stack for the prototype walk. michael@0: nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement(); michael@0: michael@0: if (! proto) { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_ERROR)) { michael@0: nsCOMPtr url = mCurrentPrototype->GetURI(); michael@0: michael@0: nsAutoCString urlspec; michael@0: rv = url->GetSpec(urlspec); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: PR_LOG(gXULLog, PR_LOG_ERROR, michael@0: ("xul: error parsing '%s'", urlspec.get())); michael@0: } michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t piInsertionPoint = 0; michael@0: if (mState != eState_Master) { michael@0: int32_t indexOfRoot = IndexOf(GetRootElement()); michael@0: NS_ASSERTION(indexOfRoot >= 0, michael@0: "No root content when preparing to walk overlay!"); michael@0: piInsertionPoint = indexOfRoot; michael@0: } michael@0: michael@0: const nsTArray >& processingInstructions = michael@0: mCurrentPrototype->GetProcessingInstructions(); michael@0: michael@0: uint32_t total = processingInstructions.Length(); michael@0: for (uint32_t i = 0; i < total; ++i) { michael@0: rv = CreateAndInsertPI(processingInstructions[i], michael@0: this, piInsertionPoint + i); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // Now check the chrome registry for any additional overlays. michael@0: rv = AddChromeOverlays(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Do one-time initialization if we're preparing to walk the michael@0: // master document's prototype. michael@0: nsRefPtr root; michael@0: michael@0: if (mState == eState_Master) { michael@0: // Add the root element michael@0: rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = AppendChildTo(root, false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = AddElementToRefMap(root); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Block onload until we've finished building the complete michael@0: // document content model. michael@0: BlockOnload(); michael@0: } michael@0: michael@0: // There'd better not be anything on the context stack at this michael@0: // point! This is the basis case for our "induction" in michael@0: // ResumeWalk(), below, which'll assume that there's always a michael@0: // content element on the context stack if either 1) we're in the michael@0: // "master" document, or 2) we're in an overlay, and we've got michael@0: // more than one prototype element (the single, root "overlay" michael@0: // element) on the stack. michael@0: NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already"); michael@0: if (mContextStack.Depth() != 0) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: rv = mContextStack.Push(proto, root); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, michael@0: nsINode* aParent, uint32_t aIndex) michael@0: { michael@0: NS_PRECONDITION(aProtoPI, "null ptr"); michael@0: NS_PRECONDITION(aParent, "null ptr"); michael@0: michael@0: nsRefPtr node = michael@0: NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget, michael@0: aProtoPI->mData); michael@0: michael@0: nsresult rv; michael@0: if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) { michael@0: rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node); michael@0: } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) { michael@0: rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node); michael@0: } else { michael@0: // No special processing, just add the PI to the document. michael@0: rv = aParent->InsertChildAt(node, aIndex, false); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, michael@0: nsINode* aParent, michael@0: uint32_t aIndex, michael@0: nsIContent* aPINode) michael@0: { michael@0: nsCOMPtr ssle(do_QueryInterface(aPINode)); michael@0: NS_ASSERTION(ssle, "passed XML Stylesheet node does not " michael@0: "implement nsIStyleSheetLinkingElement!"); michael@0: michael@0: nsresult rv; michael@0: michael@0: ssle->InitStyleLinkElement(false); michael@0: // We want to be notified when the style sheet finishes loading, so michael@0: // disable style sheet loading for now. michael@0: ssle->SetEnableUpdates(false); michael@0: ssle->OverrideBaseURI(mCurrentPrototype->GetURI()); michael@0: michael@0: rv = aParent->InsertChildAt(aPINode, aIndex, false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: ssle->SetEnableUpdates(true); michael@0: michael@0: // load the stylesheet if necessary, passing ourselves as michael@0: // nsICSSObserver michael@0: bool willNotify; michael@0: bool isAlternate; michael@0: rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate); michael@0: if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) { michael@0: ++mPendingSheets; michael@0: } michael@0: michael@0: // Ignore errors from UpdateStyleSheet; we don't want failure to michael@0: // do that to break the XUL document load. But do propagate out michael@0: // NS_ERROR_OUT_OF_MEMORY. michael@0: if (rv == NS_ERROR_OUT_OF_MEMORY) { michael@0: return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI, michael@0: nsINode* aParent, michael@0: uint32_t aIndex, michael@0: nsIContent* aPINode) michael@0: { michael@0: nsresult rv; michael@0: michael@0: rv = aParent->InsertChildAt(aPINode, aIndex, false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // xul-overlay PI is special only in prolog michael@0: if (!nsContentUtils::InProlog(aPINode)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString href; michael@0: nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData, michael@0: nsGkAtoms::href, michael@0: href); michael@0: michael@0: // If there was no href, we can't do anything with this PI michael@0: if (href.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Add the overlay to our list of overlays that need to be processed. michael@0: nsCOMPtr uri; michael@0: michael@0: rv = NS_NewURI(getter_AddRefs(uri), href, nullptr, michael@0: mCurrentPrototype->GetURI()); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // We insert overlays into mUnloadedOverlays at the same index in michael@0: // document order, so they end up in the reverse of the document michael@0: // order in mUnloadedOverlays. michael@0: // This is needed because the code in ResumeWalk loads the overlays michael@0: // by processing the last item of mUnloadedOverlays and removing it michael@0: // from the array. michael@0: mUnloadedOverlays.InsertElementAt(0, uri); michael@0: rv = NS_OK; michael@0: } else if (rv == NS_ERROR_MALFORMED_URI) { michael@0: // The URL is bad, move along. Don't propagate for now. michael@0: // XXX report this to the Error Console (bug 359846) michael@0: rv = NS_OK; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddChromeOverlays() michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr docUri = mCurrentPrototype->GetURI(); michael@0: michael@0: /* overlays only apply to chrome or about URIs */ michael@0: if (!IsOverlayAllowed(docUri)) return NS_OK; michael@0: michael@0: nsCOMPtr chromeReg = michael@0: mozilla::services::GetXULOverlayProviderService(); michael@0: // In embedding situations, the chrome registry may not provide overlays, michael@0: // or even exist at all; that's OK. michael@0: NS_ENSURE_TRUE(chromeReg, NS_OK); michael@0: michael@0: nsCOMPtr overlays; michael@0: rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool moreOverlays; michael@0: nsCOMPtr next; michael@0: nsCOMPtr uri; michael@0: michael@0: while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) && michael@0: moreOverlays) { michael@0: michael@0: rv = overlays->GetNext(getter_AddRefs(next)); michael@0: if (NS_FAILED(rv) || !next) break; michael@0: michael@0: uri = do_QueryInterface(next); michael@0: if (!uri) { michael@0: NS_ERROR("Chrome registry handed me a non-nsIURI object!"); michael@0: continue; michael@0: } michael@0: michael@0: // Same comment as in XULDocument::InsertXULOverlayPI michael@0: mUnloadedOverlays.InsertElementAt(0, uri); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr uri; michael@0: rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (aObserver) { michael@0: nsIObserver* obs = nullptr; michael@0: if (!mOverlayLoadObservers) { michael@0: mOverlayLoadObservers = new nsInterfaceHashtable; michael@0: } michael@0: obs = mOverlayLoadObservers->GetWeak(uri); michael@0: michael@0: if (obs) { michael@0: // We don't support loading the same overlay twice into the same michael@0: // document - that doesn't make sense anyway. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: mOverlayLoadObservers->Put(uri, aObserver); michael@0: } michael@0: bool shouldReturn, failureFromContent; michael@0: rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent); michael@0: if (NS_FAILED(rv) && mOverlayLoadObservers) michael@0: mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, michael@0: bool* aShouldReturn, michael@0: bool* aFailureFromContent) michael@0: { michael@0: nsresult rv; michael@0: michael@0: *aShouldReturn = false; michael@0: *aFailureFromContent = false; michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { michael@0: nsAutoCString urlspec; michael@0: aURI->GetSpec(urlspec); michael@0: nsAutoCString parentDoc; michael@0: nsCOMPtr uri; michael@0: nsresult rv = mChannel->GetOriginalURI(getter_AddRefs(uri)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = uri->GetSpec(parentDoc); michael@0: if (!(parentDoc.get())) michael@0: parentDoc = ""; michael@0: michael@0: PR_LOG(gXULLog, PR_LOG_DEBUG, michael@0: ("xul: %s loading overlay %s", parentDoc.get(), urlspec.get())); michael@0: } michael@0: #endif michael@0: michael@0: if (aIsDynamic) michael@0: mResolutionPhase = nsForwardReference::eStart; michael@0: michael@0: // Chrome documents are allowed to load overlays from anywhere. michael@0: // In all other cases, the overlay is only allowed to load if michael@0: // the master document and prototype document have the same origin. michael@0: michael@0: bool documentIsChrome = IsChromeURI(mDocumentURI); michael@0: if (!documentIsChrome) { michael@0: // Make sure we're allowed to load this overlay. michael@0: rv = NodePrincipal()->CheckMayLoad(aURI, true, false); michael@0: if (NS_FAILED(rv)) { michael@0: *aFailureFromContent = true; michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: // Look in the prototype cache for the prototype document with michael@0: // the specified overlay URI. Only use the cache if the containing michael@0: // document is chrome otherwise it may not have a system principal and michael@0: // the cached document will, see bug 565610. michael@0: bool overlayIsChrome = IsChromeURI(aURI); michael@0: mCurrentPrototype = overlayIsChrome && documentIsChrome ? michael@0: nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr; michael@0: michael@0: // Same comment as nsChromeProtocolHandler::NewChannel and michael@0: // XULDocument::StartDocumentLoad michael@0: // - Ben Goodger michael@0: // michael@0: // We don't abort on failure here because there are too many valid michael@0: // cases that can return failure, and the null-ness of |proto| is michael@0: // enough to trigger the fail-safe parse-from-disk solution. michael@0: // Example failure cases (for reference) include: michael@0: // michael@0: // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file, michael@0: // parse from disk michael@0: // other: the FastLoad file, XUL.mfl, could not be found, probably michael@0: // due to being accessed before a profile has been selected michael@0: // (e.g. loading chrome for the profile manager itself). michael@0: // The .xul file must be parsed from disk. michael@0: michael@0: bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); michael@0: if (useXULCache && mCurrentPrototype) { michael@0: bool loaded; michael@0: rv = mCurrentPrototype->AwaitLoadDone(this, &loaded); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (! loaded) { michael@0: // Return to the main event loop and eagerly await the michael@0: // prototype overlay load's completion. When the content michael@0: // sink completes, it will trigger an EndLoad(), which'll michael@0: // wind us back up here, in ResumeWalk(). michael@0: *aShouldReturn = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached")); michael@0: michael@0: // Found the overlay's prototype in the cache, fully loaded. If michael@0: // this is a dynamic overlay, this will call ResumeWalk. michael@0: // Otherwise, we'll return to ResumeWalk, which called us. michael@0: return OnPrototypeLoadDone(aIsDynamic); michael@0: } michael@0: else { michael@0: // Not there. Initiate a load. michael@0: PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached")); michael@0: michael@0: if (mIsGoingAway) { michael@0: PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: ...and document already destroyed")); michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // We'll set the right principal on the proto doc when we get michael@0: // OnStartRequest from the parser, so just pass in a null principal for michael@0: // now. michael@0: nsCOMPtr parser; michael@0: rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // Predicate mIsWritingFastLoad on the XUL cache being enabled, michael@0: // so we don't have to re-check whether the cache is enabled all michael@0: // the time. michael@0: mIsWritingFastLoad = useXULCache; michael@0: michael@0: nsCOMPtr listener = do_QueryInterface(parser); michael@0: if (! listener) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Add an observer to the parser; this'll get called when michael@0: // Necko fires its On[Start|Stop]Request() notifications, michael@0: // and will let us recover from a missing overlay. michael@0: ParserObserver* parserObserver = michael@0: new ParserObserver(this, mCurrentPrototype); michael@0: if (! parserObserver) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: NS_ADDREF(parserObserver); michael@0: parser->Parse(aURI, parserObserver); michael@0: NS_RELEASE(parserObserver); michael@0: michael@0: nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); michael@0: nsCOMPtr channel; michael@0: rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, group); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Set the owner of the channel to be our principal so michael@0: // that the overlay's JSObjects etc end up being created michael@0: // with the right principal and in the correct michael@0: // compartment. michael@0: channel->SetOwner(NodePrincipal()); michael@0: michael@0: rv = channel->AsyncOpen(listener, nullptr); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // Abandon this prototype michael@0: mCurrentPrototype = nullptr; michael@0: michael@0: // The parser won't get an OnStartRequest and michael@0: // OnStopRequest, so it needs a Terminate. michael@0: parser->Terminate(); michael@0: michael@0: // Just move on to the next overlay. NS_OpenURI could fail michael@0: // just because a channel could not be opened, which can happen michael@0: // if a file or chrome package does not exist. michael@0: ReportMissingOverlay(aURI); michael@0: michael@0: // XXX the error could indicate an internal error as well... michael@0: *aFailureFromContent = true; michael@0: return rv; michael@0: } michael@0: michael@0: // If it's a 'chrome:' prototype document, then put it into michael@0: // the prototype cache; other XUL documents will be reloaded michael@0: // each time. We must do this after NS_OpenURI and AsyncOpen, michael@0: // or chrome code will wrongly create a cached chrome channel michael@0: // instead of a real one. Prototypes are only cached when the michael@0: // document to be overlayed is chrome to avoid caching overlay michael@0: // scripts with incorrect principals, see bug 565610. michael@0: if (useXULCache && overlayIsChrome && documentIsChrome) { michael@0: nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); michael@0: } michael@0: michael@0: // Return to the main event loop and eagerly await the michael@0: // overlay load's completion. When the content sink michael@0: // completes, it will trigger an EndLoad(), which'll wind michael@0: // us back in ResumeWalk(). michael@0: if (!aIsDynamic) michael@0: *aShouldReturn = true; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr& aObserver, void* aClosure) michael@0: { michael@0: aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get()); michael@0: michael@0: typedef nsInterfaceHashtable table; michael@0: table* observers = static_cast(aClosure); michael@0: if (observers) { michael@0: observers->Remove(aKey); michael@0: } michael@0: michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ResumeWalk() michael@0: { michael@0: // Walk the prototype and build the delegate content model. The michael@0: // walk is performed in a top-down, left-to-right fashion. That michael@0: // is, a parent is built before any of its children; a node is michael@0: // only built after all of its siblings to the left are fully michael@0: // constructed. michael@0: // michael@0: // It is interruptable so that transcluded documents (e.g., michael@0: // ) can be properly re-loaded if the michael@0: // cached copy of the document becomes stale. michael@0: nsresult rv; michael@0: nsCOMPtr overlayURI = michael@0: mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr; michael@0: michael@0: while (1) { michael@0: // Begin (or resume) walking the current prototype. michael@0: michael@0: while (mContextStack.Depth() > 0) { michael@0: // Look at the top of the stack to determine what we're michael@0: // currently working on. michael@0: // This will always be a node already constructed and michael@0: // inserted to the actual document. michael@0: nsXULPrototypeElement* proto; michael@0: nsCOMPtr element; michael@0: int32_t indx; // all children of proto before indx (not michael@0: // inclusive) have already been constructed michael@0: rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (indx >= (int32_t)proto->mChildren.Length()) { michael@0: if (element) { michael@0: // We've processed all of the prototype's children. If michael@0: // we're in the master prototype, do post-order michael@0: // document-level hookup. (An overlay will get its michael@0: // document hookup done when it's successfully michael@0: // resolved.) michael@0: if (mState == eState_Master) { michael@0: AddElementToDocumentPost(element->AsElement()); michael@0: michael@0: if (element->NodeInfo()->Equals(nsGkAtoms::style, michael@0: kNameSpaceID_XHTML) || michael@0: element->NodeInfo()->Equals(nsGkAtoms::style, michael@0: kNameSpaceID_SVG)) { michael@0: // XXX sucks that we have to do this - michael@0: // see bug 370111 michael@0: nsCOMPtr ssle = michael@0: do_QueryInterface(element); michael@0: NS_ASSERTION(ssle, " doesn't implement " michael@0: "nsIStyleSheetLinkingElement?"); michael@0: bool willNotify; michael@0: bool isAlternate; michael@0: ssle->UpdateStyleSheet(nullptr, &willNotify, michael@0: &isAlternate); michael@0: } michael@0: } michael@0: } michael@0: // Now pop the context stack back up to the parent michael@0: // element and continue the prototype walk. michael@0: mContextStack.Pop(); michael@0: continue; michael@0: } michael@0: michael@0: // Grab the next child, and advance the current context stack michael@0: // to the next sibling to our right. michael@0: nsXULPrototypeNode* childproto = proto->mChildren[indx]; michael@0: mContextStack.SetTopIndex(++indx); michael@0: michael@0: // Whether we're in the "first ply" of an overlay: michael@0: // the "hookup" nodes. In the case !processingOverlayHookupNodes, michael@0: // we're in the master document -or- we're in an overlay, and far michael@0: // enough down into the overlay's content that we can simply build michael@0: // the delegates and attach them to the parent node. michael@0: bool processingOverlayHookupNodes = (mState == eState_Overlay) && michael@0: (mContextStack.Depth() == 1); michael@0: michael@0: NS_ASSERTION(element || processingOverlayHookupNodes, michael@0: "no element on context stack"); michael@0: michael@0: switch (childproto->mType) { michael@0: case nsXULPrototypeNode::eType_Element: { michael@0: // An 'element', which may contain more content. michael@0: nsXULPrototypeElement* protoele = michael@0: static_cast(childproto); michael@0: michael@0: nsRefPtr child; michael@0: michael@0: if (!processingOverlayHookupNodes) { michael@0: rv = CreateElementFromPrototype(protoele, michael@0: getter_AddRefs(child), michael@0: false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // ...and append it to the content model. michael@0: rv = element->AppendChildTo(child, false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // If we're only restoring persisted things on michael@0: // some elements, store the ID here to do that. michael@0: if (mRestrictPersistence) { michael@0: nsIAtom* id = child->GetID(); michael@0: if (id) { michael@0: mPersistenceIds.PutEntry(nsDependentAtomString(id)); michael@0: } michael@0: } michael@0: michael@0: // do pre-order document-level hookup, but only if michael@0: // we're in the master document. For an overlay, michael@0: // this will happen when the overlay is michael@0: // successfully resolved. michael@0: if (mState == eState_Master) michael@0: AddElementToDocumentPre(child); michael@0: } michael@0: else { michael@0: // We're in the "first ply" of an overlay: the michael@0: // "hookup" nodes. Create an 'overlay' element so michael@0: // that we can continue to build content, and michael@0: // enter a forward reference so we can hook it up michael@0: // later. michael@0: rv = CreateOverlayElement(protoele, getter_AddRefs(child)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: // If it has children, push the element onto the context michael@0: // stack and begin to process them. michael@0: if (protoele->mChildren.Length() > 0) { michael@0: rv = mContextStack.Push(protoele, child); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: else { michael@0: if (mState == eState_Master) { michael@0: // If there are no children, and we're in the michael@0: // master document, do post-order document hookup michael@0: // immediately. michael@0: AddElementToDocumentPost(child); michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case nsXULPrototypeNode::eType_Script: { michael@0: // A script reference. Execute the script immediately; michael@0: // this may have side effects in the content model. michael@0: nsXULPrototypeScript* scriptproto = michael@0: static_cast(childproto); michael@0: michael@0: if (scriptproto->mSrcURI) { michael@0: // A transcluded script reference; this may michael@0: // "block" our prototype walk if the script isn't michael@0: // cached, or the cached copy of the script is michael@0: // stale and must be reloaded. michael@0: bool blocked; michael@0: rv = LoadScript(scriptproto, &blocked); michael@0: // If the script cannot be loaded, just keep going! michael@0: michael@0: if (NS_SUCCEEDED(rv) && blocked) michael@0: return NS_OK; michael@0: } michael@0: else if (scriptproto->GetScriptObject()) { michael@0: // An inline script michael@0: rv = ExecuteScript(scriptproto); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case nsXULPrototypeNode::eType_Text: { michael@0: // A simple text node. michael@0: michael@0: if (!processingOverlayHookupNodes) { michael@0: // This does mean that text nodes that are direct children michael@0: // of get ignored. michael@0: michael@0: nsRefPtr text = michael@0: new nsTextNode(mNodeInfoManager); michael@0: michael@0: nsXULPrototypeText* textproto = michael@0: static_cast(childproto); michael@0: text->SetText(textproto->mValue, false); michael@0: michael@0: rv = element->AppendChildTo(text, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case nsXULPrototypeNode::eType_PI: { michael@0: nsXULPrototypePI* piProto = michael@0: static_cast(childproto); michael@0: michael@0: // and don't have effect michael@0: // outside the prolog, like they used to. Issue a warning. michael@0: michael@0: if (piProto->mTarget.EqualsLiteral("xml-stylesheet") || michael@0: piProto->mTarget.EqualsLiteral("xul-overlay")) { michael@0: michael@0: const char16_t* params[] = { piProto->mTarget.get() }; michael@0: michael@0: nsContentUtils::ReportToConsole( michael@0: nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("XUL Document"), nullptr, michael@0: nsContentUtils::eXUL_PROPERTIES, michael@0: "PINotInProlog", michael@0: params, ArrayLength(params), michael@0: overlayURI); michael@0: } michael@0: michael@0: nsIContent* parent = processingOverlayHookupNodes ? michael@0: GetRootElement() : element.get(); michael@0: michael@0: if (parent) { michael@0: // an inline script could have removed the root element michael@0: rv = CreateAndInsertPI(piProto, parent, michael@0: parent->GetChildCount()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value"); michael@0: } michael@0: } michael@0: michael@0: // Once we get here, the context stack will have been michael@0: // depleted. That means that the entire prototype has been michael@0: // walked and content has been constructed. michael@0: michael@0: // If we're not already, mark us as now processing overlays. michael@0: mState = eState_Overlay; michael@0: michael@0: // If there are no overlay URIs, then we're done. michael@0: uint32_t count = mUnloadedOverlays.Length(); michael@0: if (! count) michael@0: break; michael@0: michael@0: nsCOMPtr uri = mUnloadedOverlays[count-1]; michael@0: mUnloadedOverlays.RemoveElementAt(count - 1); michael@0: michael@0: bool shouldReturn, failureFromContent; michael@0: rv = LoadOverlayInternal(uri, false, &shouldReturn, michael@0: &failureFromContent); michael@0: if (failureFromContent) michael@0: // The failure |rv| was the result of a problem in the content michael@0: // rather than an unexpected problem in our implementation, so michael@0: // just continue with the next overlay. michael@0: continue; michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: if (mOverlayLoadObservers) { michael@0: nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI); michael@0: if (obs) { michael@0: // This overlay has an unloaded overlay, so it will never michael@0: // notify. The best we can do is to notify for the unloaded michael@0: // overlay instead, assuming nobody is already notifiable michael@0: // for it. Note that this will confuse the observer. michael@0: if (!mOverlayLoadObservers->GetWeak(uri)) michael@0: mOverlayLoadObservers->Put(uri, obs); michael@0: mOverlayLoadObservers->Remove(overlayURI); michael@0: } michael@0: } michael@0: if (shouldReturn) michael@0: return NS_OK; michael@0: overlayURI.swap(uri); michael@0: } michael@0: michael@0: // If we get here, there is nothing left for us to walk. The content michael@0: // model is built and ready for layout. michael@0: rv = ResolveForwardReferences(); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: ApplyPersistentAttributes(); michael@0: michael@0: mStillWalking = false; michael@0: if (mPendingSheets == 0) { michael@0: rv = DoneWalking(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::DoneWalking() michael@0: { michael@0: NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded"); michael@0: NS_PRECONDITION(!mStillWalking, "walk not done"); michael@0: michael@0: // XXXldb This is where we should really be setting the chromehidden michael@0: // attribute. michael@0: michael@0: uint32_t count = mOverlaySheets.Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: AddStyleSheet(mOverlaySheets[i]); michael@0: } michael@0: mOverlaySheets.Clear(); michael@0: michael@0: if (!mDocumentLoaded) { michael@0: // Make sure we don't reenter here from StartLayout(). Note that michael@0: // setting mDocumentLoaded to true here means that if StartLayout() michael@0: // causes ResumeWalk() to be reentered, we'll take the other branch of michael@0: // the |if (!mDocumentLoaded)| check above and since michael@0: // mInitialLayoutComplete will be false will follow the else branch michael@0: // there too. See the big comment there for how such reentry can michael@0: // happen. michael@0: mDocumentLoaded = true; michael@0: michael@0: NotifyPossibleTitleChange(false); michael@0: michael@0: // Before starting layout, check whether we're a toplevel chrome michael@0: // window. If we are, set our chrome flags now, so that we don't have michael@0: // to restyle the whole frame tree after StartLayout. michael@0: nsCOMPtr item = GetDocShell(); michael@0: if (item) { michael@0: nsCOMPtr owner; michael@0: item->GetTreeOwner(getter_AddRefs(owner)); michael@0: nsCOMPtr xulWin = do_GetInterface(owner); michael@0: if (xulWin) { michael@0: nsCOMPtr xulWinShell; michael@0: xulWin->GetDocShell(getter_AddRefs(xulWinShell)); michael@0: if (SameCOMIdentity(xulWinShell, item)) { michael@0: // We're the chrome document! Apply our chrome flags now. michael@0: xulWin->ApplyChromeFlags(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: StartLayout(); michael@0: michael@0: if (mIsWritingFastLoad && IsChromeURI(mDocumentURI)) michael@0: nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype); michael@0: michael@0: NS_ASSERTION(mDelayFrameLoaderInitialization, michael@0: "mDelayFrameLoaderInitialization should be true!"); michael@0: mDelayFrameLoaderInitialization = false; michael@0: NS_WARN_IF_FALSE(mUpdateNestLevel == 0, michael@0: "Constructing XUL document in middle of an update?"); michael@0: if (mUpdateNestLevel == 0) { michael@0: MaybeInitializeFinalizeFrameLoaders(); michael@0: } michael@0: michael@0: NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); michael@0: michael@0: // DispatchContentLoadedEvents undoes the onload-blocking we michael@0: // did in PrepareToWalk(). michael@0: DispatchContentLoadedEvents(); michael@0: michael@0: mInitialLayoutComplete = true; michael@0: michael@0: // Walk the set of pending load notifications and notify any observers. michael@0: // See below for detail. michael@0: if (mPendingOverlayLoadNotifications) michael@0: mPendingOverlayLoadNotifications->Enumerate( michael@0: FirePendingMergeNotification, mOverlayLoadObservers.get()); michael@0: } michael@0: else { michael@0: if (mOverlayLoadObservers) { michael@0: nsCOMPtr overlayURI = mCurrentPrototype->GetURI(); michael@0: nsCOMPtr obs; michael@0: if (mInitialLayoutComplete) { michael@0: // We have completed initial layout, so just send the notification. michael@0: mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs)); michael@0: if (obs) michael@0: obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get()); michael@0: mOverlayLoadObservers->Remove(overlayURI); michael@0: } michael@0: else { michael@0: // If we have not yet displayed the document for the first time michael@0: // (i.e. we came in here as the result of a dynamic overlay load michael@0: // which was spawned by a binding-attached event caused by michael@0: // StartLayout() on the master prototype - we must remember that michael@0: // this overlay has been merged and tell the listeners after michael@0: // StartLayout() is completely finished rather than doing so michael@0: // immediately - otherwise we may be executing code that needs to michael@0: // access XBL Binding implementations on nodes for which frames michael@0: // have not yet been constructed because their bindings have not michael@0: // yet been attached. This can be a race condition because dynamic michael@0: // overlay loading can take varying amounts of time depending on michael@0: // whether or not the overlay prototype is in the XUL cache. The michael@0: // most likely effect of this bug is odd UI initialization due to michael@0: // methods and properties that do not work. michael@0: // XXXbz really, we shouldn't be firing binding constructors michael@0: // until after StartLayout returns! michael@0: michael@0: if (!mPendingOverlayLoadNotifications) { michael@0: mPendingOverlayLoadNotifications = michael@0: new nsInterfaceHashtable; michael@0: } michael@0: michael@0: mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs)); michael@0: if (!obs) { michael@0: mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs)); michael@0: NS_ASSERTION(obs, "null overlay load observer?"); michael@0: mPendingOverlayLoadNotifications->Put(overlayURI, obs); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::StyleSheetLoaded(nsCSSStyleSheet* aSheet, michael@0: bool aWasAlternate, michael@0: nsresult aStatus) michael@0: { michael@0: if (!aWasAlternate) { michael@0: // Don't care about when alternate sheets finish loading michael@0: michael@0: NS_ASSERTION(mPendingSheets > 0, michael@0: "Unexpected StyleSheetLoaded notification"); michael@0: michael@0: --mPendingSheets; michael@0: michael@0: if (!mStillWalking && mPendingSheets == 0) { michael@0: return DoneWalking(); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XULDocument::MaybeBroadcast() michael@0: { michael@0: // Only broadcast when not in an update and when safe to run scripts. michael@0: if (mUpdateNestLevel == 0 && michael@0: (mDelayedAttrChangeBroadcasts.Length() || michael@0: mDelayedBroadcasters.Length())) { michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: if (!mInDestructor) { michael@0: nsContentUtils::AddScriptRunner( michael@0: NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast)); michael@0: } michael@0: return; michael@0: } michael@0: if (!mHandlingDelayedAttrChange) { michael@0: mHandlingDelayedAttrChange = true; michael@0: for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) { michael@0: nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName; michael@0: if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) { michael@0: nsCOMPtr listener = michael@0: do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener); michael@0: nsString value = mDelayedAttrChangeBroadcasts[i].mAttr; michael@0: if (mDelayedAttrChangeBroadcasts[i].mSetAttr) { michael@0: listener->SetAttr(kNameSpaceID_None, attrName, value, michael@0: true); michael@0: } else { michael@0: listener->UnsetAttr(kNameSpaceID_None, attrName, michael@0: true); michael@0: } michael@0: } michael@0: ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster, michael@0: mDelayedAttrChangeBroadcasts[i].mListener, michael@0: attrName); michael@0: } michael@0: mDelayedAttrChangeBroadcasts.Clear(); michael@0: mHandlingDelayedAttrChange = false; michael@0: } michael@0: michael@0: uint32_t length = mDelayedBroadcasters.Length(); michael@0: if (length) { michael@0: bool oldValue = mHandlingDelayedBroadcasters; michael@0: mHandlingDelayedBroadcasters = true; michael@0: nsTArray delayedBroadcasters; michael@0: mDelayedBroadcasters.SwapElements(delayedBroadcasters); michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster, michael@0: delayedBroadcasters[i].mListener, michael@0: delayedBroadcasters[i].mAttr); michael@0: } michael@0: mHandlingDelayedBroadcasters = oldValue; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULDocument::EndUpdate(nsUpdateType aUpdateType) michael@0: { michael@0: XMLDocument::EndUpdate(aUpdateType); michael@0: michael@0: MaybeBroadcast(); michael@0: } michael@0: michael@0: void michael@0: XULDocument::ReportMissingOverlay(nsIURI* aURI) michael@0: { michael@0: NS_PRECONDITION(aURI, "Must have a URI"); michael@0: michael@0: nsAutoCString spec; michael@0: aURI->GetSpec(spec); michael@0: michael@0: NS_ConvertUTF8toUTF16 utfSpec(spec); michael@0: const char16_t* params[] = { utfSpec.get() }; michael@0: nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, michael@0: NS_LITERAL_CSTRING("XUL Document"), this, michael@0: nsContentUtils::eXUL_PROPERTIES, michael@0: "MissingOverlay", michael@0: params, ArrayLength(params)); michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock) michael@0: { michael@0: // Load a transcluded script michael@0: nsresult rv; michael@0: michael@0: bool isChromeDoc = IsChromeURI(mDocumentURI); michael@0: michael@0: if (isChromeDoc && aScriptProto->GetScriptObject()) { michael@0: rv = ExecuteScript(aScriptProto); michael@0: michael@0: // Ignore return value from execution, and don't block michael@0: *aBlock = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Try the XUL script cache, in case two XUL documents source the same michael@0: // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul). michael@0: // XXXbe the cache relies on aScriptProto's GC root! michael@0: bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); michael@0: michael@0: if (isChromeDoc && useXULCache) { michael@0: JSScript* newScriptObject = michael@0: nsXULPrototypeCache::GetInstance()->GetScript( michael@0: aScriptProto->mSrcURI); michael@0: if (newScriptObject) { michael@0: // The script language for a proto must remain constant - we michael@0: // can't just change it for this unexpected language. michael@0: aScriptProto->Set(newScriptObject); michael@0: } michael@0: michael@0: if (aScriptProto->GetScriptObject()) { michael@0: rv = ExecuteScript(aScriptProto); michael@0: michael@0: // Ignore return value from execution, and don't block michael@0: *aBlock = false; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Allow security manager and content policies to veto the load. Note that michael@0: // at this point we already lost context information of the script. michael@0: rv = nsScriptLoader::ShouldLoadScript( michael@0: this, michael@0: static_cast(this), michael@0: aScriptProto->mSrcURI, michael@0: NS_LITERAL_STRING("application/x-javascript")); michael@0: if (NS_FAILED(rv)) { michael@0: *aBlock = false; michael@0: return rv; michael@0: } michael@0: michael@0: // Release script objects from FastLoad since we decided against using them michael@0: aScriptProto->UnlinkJSObjects(); michael@0: michael@0: // Set the current script prototype so that OnStreamComplete can report michael@0: // the right file if there are errors in the script. michael@0: NS_ASSERTION(!mCurrentScriptProto, michael@0: "still loading a script when starting another load?"); michael@0: mCurrentScriptProto = aScriptProto; michael@0: michael@0: if (aScriptProto->mSrcLoading) { michael@0: // Another XULDocument load has started, which is still in progress. michael@0: // Remember to ResumeWalk this document when the load completes. michael@0: mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters; michael@0: aScriptProto->mSrcLoadWaiters = this; michael@0: NS_ADDREF_THIS(); michael@0: } michael@0: else { michael@0: nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); michael@0: michael@0: // Note: the loader will keep itself alive while it's loading. michael@0: nsCOMPtr loader; michael@0: rv = NS_NewStreamLoader(getter_AddRefs(loader), aScriptProto->mSrcURI, michael@0: this, nullptr, group); michael@0: if (NS_FAILED(rv)) { michael@0: mCurrentScriptProto = nullptr; michael@0: return rv; michael@0: } michael@0: michael@0: aScriptProto->mSrcLoading = true; michael@0: } michael@0: michael@0: // Block until OnStreamComplete resumes us. michael@0: *aBlock = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::OnStreamComplete(nsIStreamLoader* aLoader, michael@0: nsISupports* context, michael@0: nsresult aStatus, michael@0: uint32_t stringLen, michael@0: const uint8_t* string) michael@0: { michael@0: nsCOMPtr request; michael@0: aLoader->GetRequest(getter_AddRefs(request)); michael@0: nsCOMPtr channel = do_QueryInterface(request); michael@0: michael@0: #ifdef DEBUG michael@0: // print a load error on bad status michael@0: if (NS_FAILED(aStatus)) { michael@0: if (channel) { michael@0: nsCOMPtr uri; michael@0: channel->GetURI(getter_AddRefs(uri)); michael@0: if (uri) { michael@0: nsAutoCString uriSpec; michael@0: uri->GetSpec(uriSpec); michael@0: printf("Failed to load %s\n", uriSpec.get()); michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // This is the completion routine that will be called when a michael@0: // transcluded script completes. Compile and execute the script michael@0: // if the load was successful, then continue building content michael@0: // from the prototype. michael@0: nsresult rv = aStatus; michael@0: michael@0: NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading, michael@0: "script source not loading on unichar stream complete?"); michael@0: if (!mCurrentScriptProto) { michael@0: // XXX Wallpaper for bug 270042 michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(aStatus)) { michael@0: // If the including XUL document is a FastLoad document, and we're michael@0: // compiling an out-of-line script (one with src=...), then we must michael@0: // be writing a new FastLoad file. If we were reading this script michael@0: // from the FastLoad file, XULContentSinkImpl::OpenScript (over in michael@0: // nsXULContentSink.cpp) would have already deserialized a non-null michael@0: // script->mScriptObject, causing control flow at the top of LoadScript michael@0: // not to reach here. michael@0: nsCOMPtr uri = mCurrentScriptProto->mSrcURI; michael@0: michael@0: // XXX should also check nsIHttpChannel::requestSucceeded michael@0: michael@0: MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 && michael@0: !mOffThreadCompileStringBuf), michael@0: "XULDocument can't load multiple scripts at once"); michael@0: michael@0: rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen, michael@0: EmptyString(), this, michael@0: mOffThreadCompileStringBuf, michael@0: mOffThreadCompileStringLength); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Attempt to give ownership of the buffer to the JS engine. If michael@0: // we hit offthread compilation, however, we will have to take it michael@0: // back below in order to keep the memory alive until compilation michael@0: // completes. michael@0: JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf, michael@0: mOffThreadCompileStringLength, michael@0: JS::SourceBufferHolder::GiveOwnership); michael@0: mOffThreadCompileStringBuf = nullptr; michael@0: mOffThreadCompileStringLength = 0; michael@0: michael@0: rv = mCurrentScriptProto->Compile(srcBuf, michael@0: uri, 1, this, michael@0: mMasterPrototype, michael@0: this); michael@0: if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->GetScriptObject()) { michael@0: // We will be notified via OnOffThreadCompileComplete when the michael@0: // compile finishes. Keep the contents of the compiled script michael@0: // alive until the compilation finishes. michael@0: mOffThreadCompiling = true; michael@0: // If the JS engine did not take the source buffer, then take michael@0: // it back here to ensure it remains alive. michael@0: mOffThreadCompileStringBuf = srcBuf.take(); michael@0: if (mOffThreadCompileStringBuf) { michael@0: mOffThreadCompileStringLength = srcBuf.length(); michael@0: } michael@0: BlockOnload(); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) michael@0: { michael@0: // When compiling off thread the script will not have been attached to the michael@0: // script proto yet. michael@0: if (aScript && !mCurrentScriptProto->GetScriptObject()) michael@0: mCurrentScriptProto->Set(aScript); michael@0: michael@0: // Allow load events to be fired once off thread compilation finishes. michael@0: if (mOffThreadCompiling) { michael@0: mOffThreadCompiling = false; michael@0: UnblockOnload(false); michael@0: } michael@0: michael@0: // After compilation finishes the script's characters are no longer needed. michael@0: if (mOffThreadCompileStringBuf) { michael@0: js_free(mOffThreadCompileStringBuf); michael@0: mOffThreadCompileStringBuf = nullptr; michael@0: mOffThreadCompileStringLength = 0; michael@0: } michael@0: michael@0: // Clear mCurrentScriptProto now, but save it first for use below in michael@0: // the execute code, and in the while loop that resumes walks of other michael@0: // documents that raced to load this script. michael@0: nsXULPrototypeScript* scriptProto = mCurrentScriptProto; michael@0: mCurrentScriptProto = nullptr; michael@0: michael@0: // Clear the prototype's loading flag before executing the script or michael@0: // resuming document walks, in case any of those control flows starts a michael@0: // new script load. michael@0: scriptProto->mSrcLoading = false; michael@0: michael@0: nsresult rv = aStatus; michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = ExecuteScript(scriptProto); michael@0: michael@0: // If the XUL cache is enabled, save the script object there in michael@0: // case different XUL documents source the same script. michael@0: // michael@0: // But don't save the script in the cache unless the master XUL michael@0: // document URL is a chrome: URL. It is valid for a URL such as michael@0: // about:config to translate into a master document URL, whose michael@0: // prototype document nodes -- including prototype scripts that michael@0: // hold GC roots protecting their mJSObject pointers -- are not michael@0: // cached in the XUL prototype cache. See StartDocumentLoad, michael@0: // the fillXULCache logic. michael@0: // michael@0: // A document such as about:config is free to load a script via michael@0: // a URL such as chrome://global/content/config.js, and we must michael@0: // not cache that script object without a prototype cache entry michael@0: // containing a companion nsXULPrototypeScript node that owns a michael@0: // GC root protecting the script object. Otherwise, the script michael@0: // cache entry will dangle once the uncached prototype document michael@0: // is released when its owning XULDocument is unloaded. michael@0: // michael@0: // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for michael@0: // the true crime story.) michael@0: bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); michael@0: michael@0: if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->GetScriptObject()) { michael@0: nsXULPrototypeCache::GetInstance()->PutScript( michael@0: scriptProto->mSrcURI, michael@0: scriptProto->GetScriptObject()); michael@0: } michael@0: michael@0: if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) { michael@0: // If we are loading an overlay script, try to serialize michael@0: // it to the FastLoad file here. Master scripts will be michael@0: // serialized when the master prototype document gets michael@0: // written, at the bottom of ResumeWalk. That way, master michael@0: // out-of-line scripts are serialized in the same order that michael@0: // they'll be read, in the FastLoad file, which reduces the michael@0: // number of seeks that dump the underlying stream's buffer. michael@0: // michael@0: // Ignore the return value, as we don't need to propagate michael@0: // a failure to write to the FastLoad file, because this michael@0: // method aborts that whole process on error. michael@0: scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype); michael@0: } michael@0: // ignore any evaluation errors michael@0: } michael@0: michael@0: rv = ResumeWalk(); michael@0: michael@0: // Load a pointer to the prototype-script's list of XULDocuments who michael@0: // raced to load the same script michael@0: XULDocument** docp = &scriptProto->mSrcLoadWaiters; michael@0: michael@0: // Resume walking other documents that waited for this one's load, first michael@0: // executing the script we just compiled, in each doc's script context michael@0: XULDocument* doc; michael@0: while ((doc = *docp) != nullptr) { michael@0: NS_ASSERTION(doc->mCurrentScriptProto == scriptProto, michael@0: "waiting for wrong script to load?"); michael@0: doc->mCurrentScriptProto = nullptr; michael@0: michael@0: // Unlink doc from scriptProto's list before executing and resuming michael@0: *docp = doc->mNextSrcLoadWaiter; michael@0: doc->mNextSrcLoadWaiter = nullptr; michael@0: michael@0: // Execute only if we loaded and compiled successfully, then resume michael@0: if (NS_SUCCEEDED(aStatus) && scriptProto->GetScriptObject()) { michael@0: doc->ExecuteScript(scriptProto); michael@0: } michael@0: doc->ResumeWalk(); michael@0: NS_RELEASE(doc); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ExecuteScript(nsIScriptContext * aContext, michael@0: JS::Handle aScriptObject) michael@0: { michael@0: NS_PRECONDITION(aScriptObject != nullptr && aContext != nullptr, "null ptr"); michael@0: if (! aScriptObject || ! aContext) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: // Execute the precompiled script with the given version michael@0: nsAutoMicroTask mt; michael@0: JSContext *cx = aContext->GetNativeContext(); michael@0: AutoCxPusher pusher(cx); michael@0: JS::Rooted global(cx, mScriptGlobalObject->GetGlobalJSObject()); michael@0: NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); michael@0: NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK); michael@0: JS::ExposeObjectToActiveJS(global); michael@0: xpc_UnmarkGrayScript(aScriptObject); michael@0: JSAutoCompartment ac(cx, global); michael@0: michael@0: // The script is in the compilation scope. Clone it into the target scope michael@0: // and execute it. michael@0: if (!JS::CloneAndExecuteScript(cx, global, aScriptObject)) michael@0: nsJSUtils::ReportPendingException(cx); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::ExecuteScript(nsXULPrototypeScript *aScript) michael@0: { michael@0: NS_PRECONDITION(aScript != nullptr, "null ptr"); michael@0: NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER); michael@0: NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsresult rv; michael@0: rv = mScriptGlobalObject->EnsureScriptEnvironment(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr context = michael@0: mScriptGlobalObject->GetScriptContext(); michael@0: // failure getting a script context is fatal. michael@0: NS_ENSURE_TRUE(context != nullptr, NS_ERROR_UNEXPECTED); michael@0: michael@0: if (aScript->GetScriptObject()) michael@0: rv = ExecuteScript(context, aScript->GetScriptObject()); michael@0: else michael@0: rv = NS_ERROR_UNEXPECTED; michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, michael@0: Element** aResult, michael@0: bool aIsRoot) michael@0: { michael@0: // Create a content model element from a prototype element. michael@0: NS_PRECONDITION(aPrototype != nullptr, "null ptr"); michael@0: if (! aPrototype) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: *aResult = nullptr; michael@0: nsresult rv = NS_OK; michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { michael@0: PR_LOG(gXULLog, PR_LOG_NOTICE, michael@0: ("xul: creating <%s> from prototype", michael@0: NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get())); michael@0: } michael@0: #endif michael@0: michael@0: nsRefPtr result; michael@0: michael@0: if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { michael@0: // If it's a XUL element, it'll be lightweight until somebody michael@0: // monkeys with it. michael@0: rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result)); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: else { michael@0: // If it's not a XUL element, it's gonna be heavyweight no matter michael@0: // what. So we need to copy everything out of the prototype michael@0: // into the element. Get a nodeinfo from our nodeinfo manager michael@0: // for this node. michael@0: nsCOMPtr newNodeInfo; michael@0: newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(), michael@0: aPrototype->mNodeInfo->GetPrefixAtom(), michael@0: aPrototype->mNodeInfo->NamespaceID(), michael@0: nsIDOMNode::ELEMENT_NODE); michael@0: if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY; michael@0: nsCOMPtr xtfNi = newNodeInfo; michael@0: rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(), michael@0: NOT_FROM_PARSER); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = AddAttributes(aPrototype, result); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: result.swap(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype, michael@0: Element** aResult) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsRefPtr element; michael@0: rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: OverlayForwardReference* fwdref = michael@0: new OverlayForwardReference(this, element); michael@0: if (! fwdref) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: // transferring ownership to ya... michael@0: rv = AddForwardReference(fwdref); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ADDREF(*aResult = element); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype, michael@0: nsIContent* aElement) michael@0: { michael@0: nsresult rv; michael@0: michael@0: for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { michael@0: nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]); michael@0: nsAutoString valueStr; michael@0: protoattr->mValue.ToString(valueStr); michael@0: michael@0: rv = aElement->SetAttr(protoattr->mName.NamespaceID(), michael@0: protoattr->mName.LocalName(), michael@0: protoattr->mName.GetPrefix(), michael@0: valueStr, michael@0: false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement, michael@0: bool* aNeedsHookup) michael@0: { michael@0: // See if the element already has a `database' attribute. If it michael@0: // does, then the template builder has already been created. michael@0: // michael@0: // XXX This approach will crash and burn (well, maybe not _that_ michael@0: // bad) if aElement is not a XUL element. michael@0: // michael@0: // XXXvarga Do we still want to support non XUL content? michael@0: nsCOMPtr xulElement = do_QueryInterface(aElement); michael@0: if (xulElement) { michael@0: nsCOMPtr ds; michael@0: xulElement->GetDatabase(getter_AddRefs(ds)); michael@0: if (ds) { michael@0: *aNeedsHookup = false; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // Check aElement for a 'datasources' attribute, if it has michael@0: // one a XUL template builder needs to be hooked up. michael@0: *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None, michael@0: nsGkAtoms::datasources); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ nsresult michael@0: XULDocument::CreateTemplateBuilder(nsIContent* aElement) michael@0: { michael@0: // Check if need to construct a tree builder or content builder. michael@0: bool isTreeBuilder = false; michael@0: michael@0: // return successful if the element is not is a document, as an inline michael@0: // script could have removed it michael@0: nsIDocument *document = aElement->GetCurrentDoc(); michael@0: NS_ENSURE_TRUE(document, NS_OK); michael@0: michael@0: int32_t nameSpaceID; michael@0: nsIAtom* baseTag = document->BindingManager()-> michael@0: ResolveTag(aElement, &nameSpaceID); michael@0: michael@0: if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) { michael@0: // By default, we build content for a tree and then we attach michael@0: // the tree content view. However, if the `dont-build-content' michael@0: // flag is set, then we we'll attach a tree builder which michael@0: // directly implements the tree view. michael@0: michael@0: nsAutoString flags; michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags); michael@0: if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) { michael@0: isTreeBuilder = true; michael@0: } michael@0: } michael@0: michael@0: if (isTreeBuilder) { michael@0: // Create and initialize a tree builder. michael@0: nsCOMPtr builder = michael@0: do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1"); michael@0: michael@0: if (! builder) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: builder->Init(aElement); michael@0: michael@0: // Create a if one isn't there already. michael@0: // XXXvarga what about attributes? michael@0: nsCOMPtr bodyContent; michael@0: nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, michael@0: nsGkAtoms::treechildren, michael@0: getter_AddRefs(bodyContent)); michael@0: michael@0: if (! bodyContent) { michael@0: nsresult rv = michael@0: document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren), michael@0: nullptr, kNameSpaceID_XUL, michael@0: getter_AddRefs(bodyContent)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: aElement->AppendChildTo(bodyContent, false); michael@0: } michael@0: } michael@0: else { michael@0: // Create and initialize a content builder. michael@0: nsCOMPtr builder michael@0: = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1"); michael@0: michael@0: if (! builder) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: builder->Init(aElement); michael@0: builder->CreateContents(aElement, false); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::AddPrototypeSheets() michael@0: { michael@0: nsresult rv; michael@0: michael@0: const nsCOMArray& sheets = mCurrentPrototype->GetStyleSheetReferences(); michael@0: michael@0: for (int32_t i = 0; i < sheets.Count(); i++) { michael@0: nsCOMPtr uri = sheets[i]; michael@0: michael@0: nsRefPtr incompleteSheet; michael@0: rv = CSSLoader()->LoadSheet(uri, michael@0: mCurrentPrototype->DocumentPrincipal(), michael@0: EmptyCString(), this, michael@0: getter_AddRefs(incompleteSheet)); michael@0: michael@0: // XXXldb We need to prevent bogus sheets from being held in the michael@0: // prototype's list, but until then, don't propagate the failure michael@0: // from LoadSheet (and thus exit the loop). michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ++mPendingSheets; michael@0: if (!mOverlaySheets.AppendElement(incompleteSheet)) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // XULDocument::OverlayForwardReference michael@0: // michael@0: michael@0: nsForwardReference::Result michael@0: XULDocument::OverlayForwardReference::Resolve() michael@0: { michael@0: // Resolve a forward reference from an overlay element; attempt to michael@0: // hook it up into the main document. michael@0: nsresult rv; michael@0: nsCOMPtr target; michael@0: michael@0: nsIPresShell *shell = mDocument->GetShell(); michael@0: bool notify = shell && shell->DidInitialize(); michael@0: michael@0: nsAutoString id; michael@0: mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); michael@0: if (id.IsEmpty()) { michael@0: // mOverlay is a direct child of and has no id. michael@0: // Insert it under the root element in the base document. michael@0: Element* root = mDocument->GetRootElement(); michael@0: if (!root) { michael@0: return eResolve_Error; michael@0: } michael@0: michael@0: rv = mDocument->InsertElement(root, mOverlay, notify); michael@0: if (NS_FAILED(rv)) return eResolve_Error; michael@0: michael@0: target = mOverlay; michael@0: } michael@0: else { michael@0: // The hook-up element has an id, try to match it with an element michael@0: // with the same id in the base document. michael@0: target = mDocument->GetElementById(id); michael@0: michael@0: // If we can't find the element in the document, defer the hookup michael@0: // until later. michael@0: if (!target) michael@0: return eResolve_Later; michael@0: michael@0: rv = Merge(target, mOverlay, notify); michael@0: if (NS_FAILED(rv)) return eResolve_Error; michael@0: } michael@0: michael@0: // Check if 'target' is still in our document --- it might not be! michael@0: if (!notify && target->GetCurrentDoc() == mDocument) { michael@0: // Add child and any descendants to the element map michael@0: // XXX this is bogus, the content in 'target' might already be michael@0: // in the document michael@0: rv = mDocument->AddSubtreeToDocument(target); michael@0: if (NS_FAILED(rv)) return eResolve_Error; michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { michael@0: nsAutoCString idC; michael@0: idC.AssignWithConversion(id); michael@0: PR_LOG(gXULLog, PR_LOG_NOTICE, michael@0: ("xul: overlay resolved '%s'", michael@0: idC.get())); michael@0: } michael@0: #endif michael@0: michael@0: mResolved = true; michael@0: return eResolve_Succeeded; michael@0: } michael@0: michael@0: michael@0: michael@0: nsresult michael@0: XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, michael@0: nsIContent* aOverlayNode, michael@0: bool aNotify) michael@0: { michael@0: // This function is given: michael@0: // aTargetNode: the node in the document whose 'id' attribute michael@0: // matches a toplevel node in our overlay. michael@0: // aOverlayNode: the node in the overlay document that matches michael@0: // a node in the actual document. michael@0: // aNotify: whether or not content manipulation methods should michael@0: // use the aNotify parameter. After the initial michael@0: // reflow (i.e. in the dynamic overlay merge case), michael@0: // we want all the content manipulation methods we michael@0: // call to notify so that frames are constructed michael@0: // etc. Otherwise do not, since that's during initial michael@0: // document construction before StartLayout has been michael@0: // called which will do everything for us. michael@0: // michael@0: // This function merges the tree from the overlay into the tree in michael@0: // the document, overwriting attributes and appending child content michael@0: // nodes appropriately. (See XUL overlay reference for details) michael@0: michael@0: nsresult rv; michael@0: michael@0: // Merge attributes from the overlay content node to that of the michael@0: // actual document. michael@0: uint32_t i; michael@0: const nsAttrName* name; michael@0: for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) { michael@0: // We don't want to swap IDs, they should be the same. michael@0: if (name->Equals(nsGkAtoms::id)) michael@0: continue; michael@0: michael@0: // In certain cases merging command or observes is unsafe, so don't. michael@0: if (!aNotify) { michael@0: if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes, michael@0: kNameSpaceID_XUL)) michael@0: continue; michael@0: michael@0: if (name->Equals(nsGkAtoms::observes) && michael@0: aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes)) michael@0: continue; michael@0: michael@0: if (name->Equals(nsGkAtoms::command) && michael@0: aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) && michael@0: !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key, michael@0: kNameSpaceID_XUL) && michael@0: !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem, michael@0: kNameSpaceID_XUL)) michael@0: continue; michael@0: } michael@0: michael@0: int32_t nameSpaceID = name->NamespaceID(); michael@0: nsIAtom* attr = name->LocalName(); michael@0: nsIAtom* prefix = name->GetPrefix(); michael@0: michael@0: nsAutoString value; michael@0: aOverlayNode->GetAttr(nameSpaceID, attr, value); michael@0: michael@0: // Element in the overlay has the 'removeelement' attribute set michael@0: // so remove it from the actual document. michael@0: if (attr == nsGkAtoms::removeelement && michael@0: value.EqualsLiteral("true")) { michael@0: michael@0: nsCOMPtr parent = aTargetNode->GetParentNode(); michael@0: if (!parent) return NS_ERROR_FAILURE; michael@0: rv = RemoveElement(parent, aTargetNode); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify); michael@0: if (!NS_FAILED(rv) && !aNotify) michael@0: rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode, michael@0: nameSpaceID, michael@0: attr, prefix, michael@0: value); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: michael@0: // Walk our child nodes, looking for elements that have the 'id' michael@0: // attribute set. If we find any, we must do a parent check in the michael@0: // actual document to ensure that the structure matches that of michael@0: // the actual document. If it does, we can call ourselves and attempt michael@0: // to merge inside that subtree. If not, we just append the tree to michael@0: // the parent like any other. michael@0: michael@0: uint32_t childCount = aOverlayNode->GetChildCount(); michael@0: michael@0: // This must be a strong reference since it will be the only michael@0: // reference to a content object during part of this loop. michael@0: nsCOMPtr currContent; michael@0: michael@0: for (i = 0; i < childCount; ++i) { michael@0: currContent = aOverlayNode->GetFirstChild(); michael@0: michael@0: nsIAtom *idAtom = currContent->GetID(); michael@0: michael@0: nsIContent *elementInDocument = nullptr; michael@0: if (idAtom) { michael@0: nsDependentAtomString id(idAtom); michael@0: michael@0: if (!id.IsEmpty()) { michael@0: nsIDocument *doc = aTargetNode->GetDocument(); michael@0: if (!doc) return NS_ERROR_FAILURE; michael@0: michael@0: elementInDocument = doc->GetElementById(id); michael@0: } michael@0: } michael@0: michael@0: // The item has an 'id' attribute set, and we need to check with michael@0: // the actual document to see if an item with this id exists at michael@0: // this locale. If so, we want to merge the subtree under that michael@0: // node. Otherwise, we just do an append as if the element had michael@0: // no id attribute. michael@0: if (elementInDocument) { michael@0: // Given two parents, aTargetNode and aOverlayNode, we want michael@0: // to call merge on currContent if we find an associated michael@0: // node in the document with the same id as currContent that michael@0: // also has aTargetNode as its parent. michael@0: michael@0: nsIContent *elementParent = elementInDocument->GetParent(); michael@0: michael@0: nsIAtom *parentID = elementParent->GetID(); michael@0: if (parentID && michael@0: aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, michael@0: nsDependentAtomString(parentID), michael@0: eCaseMatters)) { michael@0: // The element matches. "Go Deep!" michael@0: rv = Merge(elementInDocument, currContent, aNotify); michael@0: if (NS_FAILED(rv)) return rv; michael@0: aOverlayNode->RemoveChildAt(0, false); michael@0: michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: aOverlayNode->RemoveChildAt(0, false); michael@0: michael@0: rv = InsertElement(aTargetNode, currContent, aNotify); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: michael@0: XULDocument::OverlayForwardReference::~OverlayForwardReference() michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { michael@0: nsAutoString id; michael@0: mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); michael@0: michael@0: nsAutoCString idC; michael@0: idC.AssignWithConversion(id); michael@0: michael@0: nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI(); michael@0: nsAutoCString urlspec; michael@0: protoURI->GetSpec(urlspec); michael@0: michael@0: nsCOMPtr docURI; michael@0: nsAutoCString parentDoc; michael@0: nsresult rv = mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: docURI->GetSpec(parentDoc); michael@0: PR_LOG(gXULLog, PR_LOG_WARNING, michael@0: ("xul: %s overlay failed to resolve '%s' in %s", michael@0: urlspec.get(), idC.get(), parentDoc.get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // XULDocument::BroadcasterHookup michael@0: // michael@0: michael@0: nsForwardReference::Result michael@0: XULDocument::BroadcasterHookup::Resolve() michael@0: { michael@0: nsresult rv; michael@0: michael@0: bool listener; michael@0: rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved); michael@0: if (NS_FAILED(rv)) return eResolve_Error; michael@0: michael@0: return mResolved ? eResolve_Succeeded : eResolve_Later; michael@0: } michael@0: michael@0: michael@0: XULDocument::BroadcasterHookup::~BroadcasterHookup() michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { michael@0: // Tell the world we failed michael@0: nsIAtom *tag = mObservesElement->Tag(); michael@0: michael@0: nsAutoString broadcasterID; michael@0: nsAutoString attribute; michael@0: michael@0: if (tag == nsGkAtoms::observes) { michael@0: mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID); michael@0: mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute); michael@0: } michael@0: else { michael@0: mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID); michael@0: attribute.AssignLiteral("*"); michael@0: } michael@0: michael@0: nsAutoCString attributeC,broadcasteridC; michael@0: attributeC.AssignWithConversion(attribute); michael@0: broadcasteridC.AssignWithConversion(broadcasterID); michael@0: PR_LOG(gXULLog, PR_LOG_WARNING, michael@0: ("xul: broadcaster hookup failed <%s attribute='%s'> to %s", michael@0: nsAtomCString(tag).get(), michael@0: attributeC.get(), michael@0: broadcasteridC.get())); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // XULDocument::TemplateBuilderHookup michael@0: // michael@0: michael@0: nsForwardReference::Result michael@0: XULDocument::TemplateBuilderHookup::Resolve() michael@0: { michael@0: bool needsHookup; michael@0: nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup); michael@0: if (NS_FAILED(rv)) michael@0: return eResolve_Error; michael@0: michael@0: if (needsHookup) { michael@0: rv = CreateTemplateBuilder(mElement); michael@0: if (NS_FAILED(rv)) michael@0: return eResolve_Error; michael@0: } michael@0: michael@0: return eResolve_Succeeded; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode, michael@0: int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: nsIAtom* aPrefix, michael@0: const nsAString& aValue) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute)) michael@0: return rv; michael@0: michael@0: if (!aNode->IsElement()) michael@0: return rv; michael@0: michael@0: BroadcasterMapEntry* entry = static_cast michael@0: (PL_DHashTableOperate(mBroadcasterMap, aNode->AsElement(), PL_DHASH_LOOKUP)); michael@0: if (!PL_DHASH_ENTRY_IS_BUSY(entry)) michael@0: return rv; michael@0: michael@0: // We've got listeners: push the value. michael@0: int32_t i; michael@0: for (i = entry->mListeners.Count() - 1; i >= 0; --i) { michael@0: BroadcastListener* bl = static_cast michael@0: (entry->mListeners[i]); michael@0: michael@0: if ((bl->mAttribute != aAttribute) && michael@0: (bl->mAttribute != nsGkAtoms::_asterix)) michael@0: continue; michael@0: michael@0: nsCOMPtr l = do_QueryReferent(bl->mListener); michael@0: if (l) { michael@0: rv = l->SetAttr(aNameSpaceID, aAttribute, michael@0: aPrefix, aValue, false); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::FindBroadcaster(Element* aElement, michael@0: Element** aListener, michael@0: nsString& aBroadcasterID, michael@0: nsString& aAttribute, michael@0: Element** aBroadcaster) michael@0: { michael@0: nsINodeInfo *ni = aElement->NodeInfo(); michael@0: *aListener = nullptr; michael@0: *aBroadcaster = nullptr; michael@0: michael@0: if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) { michael@0: // It's an element, which means that the actual michael@0: // listener is the _parent_ node. This element should have an michael@0: // 'element' attribute that specifies the ID of the michael@0: // broadcaster element, and an 'attribute' element, which michael@0: // specifies the name of the attribute to observe. michael@0: nsIContent* parent = aElement->GetParent(); michael@0: if (!parent) { michael@0: // is the root element michael@0: return NS_FINDBROADCASTER_NOT_FOUND; michael@0: } michael@0: michael@0: // If we're still parented by an 'overlay' tag, then we haven't michael@0: // made it into the real document yet. Defer hookup. michael@0: if (parent->NodeInfo()->Equals(nsGkAtoms::overlay, michael@0: kNameSpaceID_XUL)) { michael@0: return NS_FINDBROADCASTER_AWAIT_OVERLAYS; michael@0: } michael@0: michael@0: *aListener = parent->IsElement() ? parent->AsElement() : nullptr; michael@0: NS_IF_ADDREF(*aListener); michael@0: michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID); michael@0: if (aBroadcasterID.IsEmpty()) { michael@0: return NS_FINDBROADCASTER_NOT_FOUND; michael@0: } michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute); michael@0: } michael@0: else { michael@0: // It's a generic element, which means that we'll use the michael@0: // value of the 'observes' attribute to determine the ID of michael@0: // the broadcaster element, and we'll watch _all_ of its michael@0: // values. michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID); michael@0: michael@0: // Bail if there's no aBroadcasterID michael@0: if (aBroadcasterID.IsEmpty()) { michael@0: // Try the command attribute next. michael@0: aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID); michael@0: if (!aBroadcasterID.IsEmpty()) { michael@0: // We've got something in the command attribute. We michael@0: // only treat this as a normal broadcaster if we are michael@0: // not a menuitem or a key. michael@0: michael@0: if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) || michael@0: ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) { michael@0: return NS_FINDBROADCASTER_NOT_FOUND; michael@0: } michael@0: } michael@0: else { michael@0: return NS_FINDBROADCASTER_NOT_FOUND; michael@0: } michael@0: } michael@0: michael@0: *aListener = aElement; michael@0: NS_ADDREF(*aListener); michael@0: michael@0: aAttribute.AssignLiteral("*"); michael@0: } michael@0: michael@0: // Make sure we got a valid listener. michael@0: NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Try to find the broadcaster element in the document. michael@0: *aBroadcaster = GetElementById(aBroadcasterID); michael@0: michael@0: // If we can't find the broadcaster, then we'll need to defer the michael@0: // hookup. We may need to resolve some of the other overlays michael@0: // first. michael@0: if (! *aBroadcaster) { michael@0: return NS_FINDBROADCASTER_AWAIT_OVERLAYS; michael@0: } michael@0: michael@0: NS_ADDREF(*aBroadcaster); michael@0: michael@0: return NS_FINDBROADCASTER_FOUND; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::CheckBroadcasterHookup(Element* aElement, michael@0: bool* aNeedsHookup, michael@0: bool* aDidResolve) michael@0: { michael@0: // Resolve a broadcaster hookup. Look at the element that we're michael@0: // trying to resolve: it could be an '' element, or just michael@0: // a vanilla element with an 'observes' attribute on it. michael@0: nsresult rv; michael@0: michael@0: *aDidResolve = false; michael@0: michael@0: nsCOMPtr listener; michael@0: nsAutoString broadcasterID; michael@0: nsAutoString attribute; michael@0: nsCOMPtr broadcaster; michael@0: michael@0: rv = FindBroadcaster(aElement, getter_AddRefs(listener), michael@0: broadcasterID, attribute, getter_AddRefs(broadcaster)); michael@0: switch (rv) { michael@0: case NS_FINDBROADCASTER_NOT_FOUND: michael@0: *aNeedsHookup = false; michael@0: return NS_OK; michael@0: case NS_FINDBROADCASTER_AWAIT_OVERLAYS: michael@0: *aNeedsHookup = true; michael@0: return NS_OK; michael@0: case NS_FINDBROADCASTER_FOUND: michael@0: break; michael@0: default: michael@0: return rv; michael@0: } michael@0: michael@0: NS_ENSURE_ARG(broadcaster && listener); michael@0: ErrorResult domRv; michael@0: AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv); michael@0: if (domRv.Failed()) { michael@0: return domRv.ErrorCode(); michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: // Tell the world we succeeded michael@0: if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { michael@0: nsCOMPtr content = michael@0: do_QueryInterface(listener); michael@0: michael@0: NS_ASSERTION(content != nullptr, "not an nsIContent"); michael@0: if (! content) michael@0: return rv; michael@0: michael@0: nsAutoCString attributeC,broadcasteridC; michael@0: attributeC.AssignWithConversion(attribute); michael@0: broadcasteridC.AssignWithConversion(broadcasterID); michael@0: PR_LOG(gXULLog, PR_LOG_NOTICE, michael@0: ("xul: broadcaster hookup <%s attribute='%s'> to %s", michael@0: nsAtomCString(content->Tag()).get(), michael@0: attributeC.get(), michael@0: broadcasteridC.get())); michael@0: } michael@0: #endif michael@0: michael@0: *aNeedsHookup = false; michael@0: *aDidResolve = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, michael@0: bool aNotify) michael@0: { michael@0: // Insert aChild appropriately into aParent, accounting for a michael@0: // 'pos' attribute set on aChild. michael@0: michael@0: nsAutoString posStr; michael@0: bool wasInserted = false; michael@0: michael@0: // insert after an element of a given id michael@0: aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr); michael@0: bool isInsertAfter = true; michael@0: michael@0: if (posStr.IsEmpty()) { michael@0: aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr); michael@0: isInsertAfter = false; michael@0: } michael@0: michael@0: if (!posStr.IsEmpty()) { michael@0: nsIDocument *document = aParent->OwnerDoc(); michael@0: michael@0: nsIContent *content = nullptr; michael@0: michael@0: char* str = ToNewCString(posStr); michael@0: char* rest; michael@0: char* token = nsCRT::strtok(str, ", ", &rest); michael@0: michael@0: while (token) { michael@0: content = document->GetElementById(NS_ConvertASCIItoUTF16(token)); michael@0: if (content) michael@0: break; michael@0: michael@0: token = nsCRT::strtok(rest, ", ", &rest); michael@0: } michael@0: nsMemory::Free(str); michael@0: michael@0: if (content) { michael@0: int32_t pos = aParent->IndexOf(content); michael@0: michael@0: if (pos != -1) { michael@0: pos = isInsertAfter ? pos + 1 : pos; michael@0: nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: wasInserted = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!wasInserted) { michael@0: michael@0: aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr); michael@0: if (!posStr.IsEmpty()) { michael@0: nsresult rv; michael@0: // Positions are one-indexed. michael@0: int32_t pos = posStr.ToInteger(&rv); michael@0: // Note: if the insertion index (which is |pos - 1|) would be less michael@0: // than 0 or greater than the number of children aParent has, then michael@0: // don't insert, since the position is bogus. Just skip on to michael@0: // appending. michael@0: if (NS_SUCCEEDED(rv) && pos > 0 && michael@0: uint32_t(pos - 1) <= aParent->GetChildCount()) { michael@0: rv = aParent->InsertChildAt(aChild, pos - 1, aNotify); michael@0: if (NS_SUCCEEDED(rv)) michael@0: wasInserted = true; michael@0: // If the insertion fails, then we should still michael@0: // attempt an append. Thus, rather than returning rv michael@0: // immediately, we fall through to the final michael@0: // "catch-all" case that just does an AppendChildTo. michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!wasInserted) { michael@0: return aParent->AppendChildTo(aChild, aNotify); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild) michael@0: { michael@0: int32_t nodeOffset = aParent->IndexOf(aChild); michael@0: michael@0: aParent->RemoveChildAt(nodeOffset, true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // CachedChromeStreamListener michael@0: // michael@0: michael@0: XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded) michael@0: : mDocument(aDocument), michael@0: mProtoLoaded(aProtoLoaded) michael@0: { michael@0: NS_ADDREF(mDocument); michael@0: } michael@0: michael@0: michael@0: XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener() michael@0: { michael@0: NS_RELEASE(mDocument); michael@0: } michael@0: michael@0: michael@0: NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener, michael@0: nsIRequestObserver, nsIStreamListener) michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request, michael@0: nsISupports* acontext) michael@0: { michael@0: return NS_ERROR_PARSED_DATA_CACHED; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request, michael@0: nsISupports* aContext, michael@0: nsresult aStatus) michael@0: { michael@0: if (! mProtoLoaded) michael@0: return NS_OK; michael@0: michael@0: return mDocument->OnPrototypeLoadDone(true); michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request, michael@0: nsISupports* aContext, michael@0: nsIInputStream* aInStr, michael@0: uint64_t aSourceOffset, michael@0: uint32_t aCount) michael@0: { michael@0: NS_NOTREACHED("CachedChromeStream doesn't receive data"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // ParserObserver michael@0: // michael@0: michael@0: XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument, michael@0: nsXULPrototypeDocument* aPrototype) michael@0: : mDocument(aDocument), mPrototype(aPrototype) michael@0: { michael@0: } michael@0: michael@0: XULDocument::ParserObserver::~ParserObserver() michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver) michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::ParserObserver::OnStartRequest(nsIRequest *request, michael@0: nsISupports* aContext) michael@0: { michael@0: // Guard against buggy channels calling OnStartRequest multiple times. michael@0: if (mPrototype) { michael@0: nsCOMPtr channel = do_QueryInterface(request); michael@0: nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); michael@0: if (channel && secMan) { michael@0: nsCOMPtr principal; michael@0: secMan->GetChannelPrincipal(channel, getter_AddRefs(principal)); michael@0: michael@0: // Failure there is ok -- it'll just set a (safe) null principal michael@0: mPrototype->SetDocumentPrincipal(principal); michael@0: } michael@0: michael@0: // Make sure to avoid cycles michael@0: mPrototype = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::ParserObserver::OnStopRequest(nsIRequest *request, michael@0: nsISupports* aContext, michael@0: nsresult aStatus) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: if (NS_FAILED(aStatus)) { michael@0: // If an overlay load fails, we need to nudge the prototype michael@0: // walk along. michael@0: nsCOMPtr aChannel = do_QueryInterface(request); michael@0: if (aChannel) { michael@0: nsCOMPtr uri; michael@0: aChannel->GetOriginalURI(getter_AddRefs(uri)); michael@0: if (uri) { michael@0: mDocument->ReportMissingOverlay(uri); michael@0: } michael@0: } michael@0: michael@0: rv = mDocument->ResumeWalk(); michael@0: } michael@0: michael@0: // Drop the reference to the document to break cycle between the michael@0: // document, the parser, the content sink, and the parser michael@0: // observer. michael@0: mDocument = nullptr; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULDocument::GetWindowRoot() michael@0: { michael@0: nsCOMPtr ir(mDocumentContainer); michael@0: nsCOMPtr window(do_GetInterface(ir)); michael@0: nsCOMPtr piWin(do_QueryInterface(window)); michael@0: return piWin ? piWin->GetTopWindowRoot() : nullptr; michael@0: } michael@0: michael@0: bool michael@0: XULDocument::IsDocumentRightToLeft() michael@0: { michael@0: // setting the localedir attribute on the root element forces a michael@0: // specific direction for the document. michael@0: Element* element = GetRootElement(); michael@0: if (element) { michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr}; michael@0: switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir, michael@0: strings, eCaseMatters)) { michael@0: case 0: return false; michael@0: case 1: return true; michael@0: default: break; // otherwise, not a valid value, so fall through michael@0: } michael@0: } michael@0: michael@0: // otherwise, get the locale from the chrome registry and michael@0: // look up the intl.uidirection. preference michael@0: nsCOMPtr reg = michael@0: mozilla::services::GetXULChromeRegistryService(); michael@0: if (!reg) michael@0: return false; michael@0: michael@0: nsAutoCString package; michael@0: bool isChrome; michael@0: if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) && michael@0: isChrome) { michael@0: mDocumentURI->GetHostPort(package); michael@0: } michael@0: else { michael@0: // use the 'global' package for about and resource uris. michael@0: // otherwise, just default to left-to-right. michael@0: bool isAbout, isResource; michael@0: if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) && michael@0: isAbout) { michael@0: package.AssignLiteral("global"); michael@0: } michael@0: else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) && michael@0: isResource) { michael@0: package.AssignLiteral("global"); michael@0: } michael@0: else { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: bool isRTL = false; michael@0: reg->IsLocaleRTL(package, &isRTL); michael@0: return isRTL; michael@0: } michael@0: michael@0: void michael@0: XULDocument::ResetDocumentDirection() michael@0: { michael@0: DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE); michael@0: } michael@0: michael@0: void michael@0: XULDocument::DirectionChanged(const char* aPrefName, void* aData) michael@0: { michael@0: // Reset the direction and restyle the document if necessary. michael@0: XULDocument* doc = (XULDocument *)aData; michael@0: if (doc) { michael@0: doc->ResetDocumentDirection(); michael@0: } michael@0: } michael@0: michael@0: int michael@0: XULDocument::GetDocumentLWTheme() michael@0: { michael@0: if (mDocLWTheme == Doc_Theme_Uninitialized) { michael@0: mDocLWTheme = Doc_Theme_None; // No lightweight theme by default michael@0: michael@0: Element* element = GetRootElement(); michael@0: nsAutoString hasLWTheme; michael@0: if (element && michael@0: element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) && michael@0: !(hasLWTheme.IsEmpty()) && michael@0: hasLWTheme.EqualsLiteral("true")) { michael@0: mDocLWTheme = Doc_Theme_Neutral; michael@0: nsAutoString lwTheme; michael@0: element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme); michael@0: if (!(lwTheme.IsEmpty())) { michael@0: if (lwTheme.EqualsLiteral("dark")) michael@0: mDocLWTheme = Doc_Theme_Dark; michael@0: else if (lwTheme.EqualsLiteral("bright")) michael@0: mDocLWTheme = Doc_Theme_Bright; michael@0: } michael@0: } michael@0: } michael@0: return mDocLWTheme; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr el = do_QueryInterface(aElement); michael@0: *aResult = GetBoxObjectFor(el, rv).take(); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: JSObject* michael@0: XULDocument::WrapNode(JSContext *aCx) michael@0: { michael@0: return XULDocumentBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla