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