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