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