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/. */
9 An implementation for the XUL document. This implementation serves
10 as the basis for generating an NGLayout content model.
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".
24 #include "mozilla/ArrayUtils.h"
26 #include "XULDocument.h"
29 #include "nsIBoxObject.h"
30 #include "nsIChromeRegistry.h"
32 #include "nsViewManager.h"
33 #include "nsIContentViewer.h"
34 #include "nsIDOMXULElement.h"
35 #include "nsIStreamListener.h"
37 #include "nsDocShell.h"
38 #include "nsGkAtoms.h"
39 #include "nsXMLContentSink.h"
40 #include "nsXULContentSink.h"
41 #include "nsXULContentUtils.h"
42 #include "nsIXULOverlayProvider.h"
43 #include "nsIStringEnumerator.h"
44 #include "nsNetUtil.h"
45 #include "nsParserCIID.h"
46 #include "nsPIBoxObject.h"
47 #include "mozilla/dom/BoxObject.h"
48 #include "nsXPIDLString.h"
49 #include "nsPIDOMWindow.h"
50 #include "nsPIWindowRoot.h"
51 #include "nsXULCommandDispatcher.h"
52 #include "nsXULElement.h"
56 #include "nsXBLService.h"
57 #include "nsCExternalHandlerService.h"
58 #include "nsMimeTypes.h"
59 #include "nsIObjectInputStream.h"
60 #include "nsIObjectOutputStream.h"
61 #include "nsContentList.h"
62 #include "nsIScriptGlobalObject.h"
63 #include "nsIScriptSecurityManager.h"
64 #include "nsNodeInfoManager.h"
65 #include "nsContentCreatorFunctions.h"
66 #include "nsContentUtils.h"
67 #include "nsIParser.h"
68 #include "nsCharsetSource.h"
69 #include "nsIParserService.h"
70 #include "mozilla/CSSStyleSheet.h"
71 #include "mozilla/css/Loader.h"
72 #include "nsIScriptError.h"
73 #include "nsIStyleSheetLinkingElement.h"
74 #include "nsIObserverService.h"
75 #include "nsNodeUtils.h"
76 #include "nsIDocShellTreeOwner.h"
77 #include "nsIXULWindow.h"
78 #include "nsXULPopupManager.h"
79 #include "nsCCUncollectableMarker.h"
80 #include "nsURILoader.h"
81 #include "mozilla/AddonPathService.h"
82 #include "mozilla/BasicEvents.h"
83 #include "mozilla/dom/Element.h"
84 #include "mozilla/dom/NodeInfoInlines.h"
85 #include "mozilla/dom/ProcessingInstruction.h"
86 #include "mozilla/dom/ScriptSettings.h"
87 #include "mozilla/dom/XULDocumentBinding.h"
88 #include "mozilla/EventDispatcher.h"
89 #include "mozilla/LoadInfo.h"
90 #include "mozilla/Preferences.h"
91 #include "nsTextNode.h"
92 #include "nsJSUtils.h"
93 #include "mozilla/dom/URL.h"
94 #include "nsIContentPolicy.h"
96 using namespace mozilla
;
97 using namespace mozilla::dom
;
99 //----------------------------------------------------------------------
104 static NS_DEFINE_CID(kParserCID
, NS_PARSER_CID
);
106 static bool IsOverlayAllowed(nsIURI
* aURI
)
108 bool canOverlay
= false;
109 if (NS_SUCCEEDED(aURI
->SchemeIs("about", &canOverlay
)) && canOverlay
)
111 if (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &canOverlay
)) && canOverlay
)
116 //----------------------------------------------------------------------
118 // Miscellaneous Constants
121 const nsForwardReference::Phase
nsForwardReference::kPasses
[] = {
122 nsForwardReference::eConstruction
,
123 nsForwardReference::eHookup
,
124 nsForwardReference::eDone
127 //----------------------------------------------------------------------
132 int32_t XULDocument::gRefCnt
= 0;
134 PRLogModuleInfo
* XULDocument::gXULLog
;
136 //----------------------------------------------------------------------
138 struct BroadcasterMapEntry
: public PLDHashEntryHdr
{
139 Element
* mBroadcaster
; // [WEAK]
140 nsSmallVoidArray mListeners
; // [OWNING] of BroadcastListener objects
143 struct BroadcastListener
{
145 nsCOMPtr
<nsIAtom
> mAttribute
;
149 nsRefMapEntry::GetFirstElement()
151 return static_cast<Element
*>(mRefContentList
.SafeElementAt(0));
155 nsRefMapEntry::AppendAll(nsCOMArray
<nsIContent
>* aElements
)
157 for (int32_t i
= 0; i
< mRefContentList
.Count(); ++i
) {
158 aElements
->AppendObject(static_cast<nsIContent
*>(mRefContentList
[i
]));
163 nsRefMapEntry::AddElement(Element
* aElement
)
165 if (mRefContentList
.IndexOf(aElement
) >= 0)
167 return mRefContentList
.AppendElement(aElement
);
171 nsRefMapEntry::RemoveElement(Element
* aElement
)
173 mRefContentList
.RemoveElement(aElement
);
174 return mRefContentList
.Count() == 0;
177 //----------------------------------------------------------------------
185 XULDocument::XULDocument(void)
186 : XMLDocument("application/vnd.mozilla.xul+xml"),
187 mDocLWTheme(Doc_Theme_Uninitialized
),
188 mState(eState_Master
),
189 mResolutionPhase(nsForwardReference::eStart
)
191 // NOTE! nsDocument::operator new() zeroes out all members, so don't
192 // bother initializing members to 0.
194 // Override the default in nsDocument
195 mCharacterSet
.AssignLiteral("UTF-8");
197 mDefaultElementType
= kNameSpaceID_XUL
;
200 mDelayFrameLoaderInitialization
= true;
202 mAllowXULXBL
= eTriTrue
;
205 XULDocument::~XULDocument()
207 NS_ASSERTION(mNextSrcLoadWaiter
== nullptr,
208 "unreferenced document still waiting for script source to load?");
210 // In case we failed somewhere early on and the forward observer
211 // decls never got resolved.
212 mForwardReferences
.Clear();
213 // Likewise for any references we have to IDs where we might
214 // look for persisted data:
215 mPersistenceIds
.Clear();
217 // Destroy our broadcaster map.
218 if (mBroadcasterMap
) {
219 PL_DHashTableDestroy(mBroadcasterMap
);
222 delete mTemplateBuilderTable
;
224 Preferences::UnregisterCallback(XULDocument::DirectionChanged
,
225 "intl.uidirection.", this);
227 if (mOffThreadCompileStringBuf
) {
228 js_free(mOffThreadCompileStringBuf
);
233 } // namespace mozilla
236 NS_NewXULDocument(nsIXULDocument
** result
)
238 NS_PRECONDITION(result
!= nullptr, "null ptr");
240 return NS_ERROR_NULL_POINTER
;
242 XULDocument
* doc
= new XULDocument();
244 return NS_ERROR_OUT_OF_MEMORY
;
249 if (NS_FAILED(rv
= doc
->Init())) {
262 //----------------------------------------------------------------------
264 // nsISupports interface
267 static PLDHashOperator
268 TraverseTemplateBuilders(nsISupports
* aKey
, nsIXULTemplateBuilder
* aData
,
271 nsCycleCollectionTraversalCallback
*cb
=
272 static_cast<nsCycleCollectionTraversalCallback
*>(aContext
);
274 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mTemplateBuilderTable key");
275 cb
->NoteXPCOMChild(aKey
);
276 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mTemplateBuilderTable value");
277 cb
->NoteXPCOMChild(aData
);
279 return PL_DHASH_NEXT
;
282 static PLDHashOperator
283 TraverseObservers(nsIURI
* aKey
, nsIObserver
* aData
, void* aContext
)
285 nsCycleCollectionTraversalCallback
*cb
=
286 static_cast<nsCycleCollectionTraversalCallback
*>(aContext
);
288 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value");
289 cb
->NoteXPCOMChild(aData
);
291 return PL_DHASH_NEXT
;
294 NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument
)
296 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument
, XMLDocument
)
297 NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb
, tmp
->GetMarkedCCGeneration()),
298 "Shouldn't traverse XULDocument!");
299 // XXX tmp->mForwardReferences?
300 // XXX tmp->mContextStack?
302 // An element will only have a template builder as long as it's in the
303 // document, so we'll traverse the table here instead of from the element.
304 if (tmp
->mTemplateBuilderTable
)
305 tmp
->mTemplateBuilderTable
->EnumerateRead(TraverseTemplateBuilders
, &cb
);
307 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype
)
308 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype
)
309 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher
)
310 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes
);
311 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore
)
313 if (tmp
->mOverlayLoadObservers
) {
314 tmp
->mOverlayLoadObservers
->EnumerateRead(TraverseObservers
, &cb
);
316 if (tmp
->mPendingOverlayLoadNotifications
) {
317 tmp
->mPendingOverlayLoadNotifications
->EnumerateRead(TraverseObservers
, &cb
);
319 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
321 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument
, XMLDocument
)
322 delete tmp
->mTemplateBuilderTable
;
323 tmp
->mTemplateBuilderTable
= nullptr;
325 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher
)
326 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore
)
327 //XXX We should probably unlink all the objects we traverse.
328 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
330 NS_IMPL_ADDREF_INHERITED(XULDocument
, XMLDocument
)
331 NS_IMPL_RELEASE_INHERITED(XULDocument
, XMLDocument
)
334 // QueryInterface implementation for XULDocument
335 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument
)
336 NS_INTERFACE_TABLE_INHERITED(XULDocument
, nsIXULDocument
,
337 nsIDOMXULDocument
, nsIStreamLoaderObserver
,
338 nsICSSLoaderObserver
, nsIOffThreadScriptReceiver
)
339 NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument
)
342 //----------------------------------------------------------------------
344 // nsIDocument interface
348 XULDocument::Reset(nsIChannel
* aChannel
, nsILoadGroup
* aLoadGroup
)
350 NS_NOTREACHED("Reset");
354 XULDocument::ResetToURI(nsIURI
* aURI
, nsILoadGroup
* aLoadGroup
,
355 nsIPrincipal
* aPrincipal
)
357 NS_NOTREACHED("ResetToURI");
361 XULDocument::SetContentType(const nsAString
& aContentType
)
363 NS_ASSERTION(aContentType
.EqualsLiteral("application/vnd.mozilla.xul+xml"),
364 "xul-documents always has content-type application/vnd.mozilla.xul+xml");
365 // Don't do anything, xul always has the mimetype
366 // application/vnd.mozilla.xul+xml
369 // This is called when the master document begins loading, whether it's
370 // being cached or not.
372 XULDocument::StartDocumentLoad(const char* aCommand
, nsIChannel
* aChannel
,
373 nsILoadGroup
* aLoadGroup
,
374 nsISupports
* aContainer
,
375 nsIStreamListener
**aDocListener
,
376 bool aReset
, nsIContentSink
* aSink
)
379 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
)) {
381 nsCOMPtr
<nsIURI
> uri
;
382 nsresult rv
= aChannel
->GetOriginalURI(getter_AddRefs(uri
));
383 if (NS_SUCCEEDED(rv
)) {
384 nsAutoCString urlspec
;
385 rv
= uri
->GetSpec(urlspec
);
386 if (NS_SUCCEEDED(rv
)) {
387 PR_LOG(gXULLog
, PR_LOG_WARNING
,
388 ("xul: load document '%s'", urlspec
.get()));
393 // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
394 // we'll possibly need to reset our content type afterwards.
395 mStillWalking
= true;
396 mMayStartLayout
= false;
397 mDocumentLoadGroup
= do_GetWeakReference(aLoadGroup
);
401 mHaveInputEncoding
= true;
403 // Get the URI. Note that this should match nsDocShell::OnLoadingSite
405 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(mDocumentURI
));
406 NS_ENSURE_SUCCESS(rv
, rv
);
408 ResetStylesheetsToURI(mDocumentURI
);
410 RetrieveRelevantHeaders(aChannel
);
412 // Look in the chrome cache: we've got this puppy loaded
414 nsXULPrototypeDocument
* proto
= IsChromeURI(mDocumentURI
) ?
415 nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI
) :
418 // Same comment as nsChromeProtocolHandler::NewChannel and
419 // XULDocument::ResumeWalk
422 // We don't abort on failure here because there are too many valid
423 // cases that can return failure, and the null-ness of |proto| is enough
424 // to trigger the fail-safe parse-from-disk solution. Example failure cases
425 // (for reference) include:
427 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
429 // other: the startup cache file could not be found, probably
430 // due to being accessed before a profile has been selected (e.g.
431 // loading chrome for the profile manager itself). This must be
435 // If we're racing with another document to load proto, wait till the
436 // load has finished loading before trying to add cloned style sheets.
437 // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
438 // find all racing documents and notify them via OnPrototypeLoadDone,
439 // which will add style sheet clones to each document.
441 rv
= proto
->AwaitLoadDone(this, &loaded
);
442 if (NS_FAILED(rv
)) return rv
;
444 mMasterPrototype
= mCurrentPrototype
= proto
;
446 // Set up the right principal on ourselves.
447 SetPrincipal(proto
->DocumentPrincipal());
449 // We need a listener, even if proto is not yet loaded, in which
450 // event the listener's OnStopRequest method does nothing, and all
451 // the interesting work happens below XULDocument::EndLoad, from
452 // the call there to mCurrentPrototype->NotifyLoadDone().
453 *aDocListener
= new CachedChromeStreamListener(this, loaded
);
455 return NS_ERROR_OUT_OF_MEMORY
;
458 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
459 bool fillXULCache
= (useXULCache
&& IsChromeURI(mDocumentURI
));
462 // It's just a vanilla document load. Create a parser to deal
463 // with the stream n' stuff.
465 nsCOMPtr
<nsIParser
> parser
;
466 rv
= PrepareToLoad(aContainer
, aCommand
, aChannel
, aLoadGroup
,
467 getter_AddRefs(parser
));
468 if (NS_FAILED(rv
)) return rv
;
470 // Predicate mIsWritingFastLoad on the XUL cache being enabled,
471 // so we don't have to re-check whether the cache is enabled all
473 mIsWritingFastLoad
= useXULCache
;
475 nsCOMPtr
<nsIStreamListener
> listener
= do_QueryInterface(parser
, &rv
);
476 NS_ASSERTION(NS_SUCCEEDED(rv
), "parser doesn't support nsIStreamListener");
477 if (NS_FAILED(rv
)) return rv
;
479 *aDocListener
= listener
;
481 parser
->Parse(mDocumentURI
);
483 // Put the current prototype, created under PrepareToLoad, into the
484 // XUL prototype cache now. We can't do this under PrepareToLoad or
485 // overlay loading will break; search for PutPrototype in ResumeWalk
486 // and see the comment there.
488 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype
);
492 NS_IF_ADDREF(*aDocListener
);
496 // This gets invoked after a prototype for this document or one of
497 // its overlays is fully built in the content sink.
499 XULDocument::EndLoad()
501 // This can happen if an overlay fails to load
502 if (!mCurrentPrototype
)
507 // Whack the prototype document into the cache so that the next
508 // time somebody asks for it, they don't need to load it by hand.
510 nsCOMPtr
<nsIURI
> uri
= mCurrentPrototype
->GetURI();
511 bool isChrome
= IsChromeURI(uri
);
513 // Remember if the XUL cache is on
514 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
516 // If the current prototype is an overlay document (non-master prototype)
517 // and we're filling the FastLoad disk cache, tell the cache we're done
518 // loading it, and write the prototype. The master prototype is put into
519 // the cache earlier in XULDocument::StartDocumentLoad.
520 if (useXULCache
&& mIsWritingFastLoad
&& isChrome
&&
521 mMasterPrototype
!= mCurrentPrototype
) {
522 nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype
);
525 if (IsOverlayAllowed(uri
)) {
526 nsCOMPtr
<nsIXULOverlayProvider
> reg
=
527 mozilla::services::GetXULOverlayProviderService();
530 nsCOMPtr
<nsISimpleEnumerator
> overlays
;
531 rv
= reg
->GetStyleOverlays(uri
, getter_AddRefs(overlays
));
532 if (NS_FAILED(rv
)) return;
535 nsCOMPtr
<nsISupports
> next
;
536 nsCOMPtr
<nsIURI
> sheetURI
;
538 while (NS_SUCCEEDED(rv
= overlays
->HasMoreElements(&moreSheets
)) &&
540 overlays
->GetNext(getter_AddRefs(next
));
542 sheetURI
= do_QueryInterface(next
);
544 NS_ERROR("Chrome registry handed me a non-nsIURI object!");
548 if (IsChromeURI(sheetURI
)) {
549 mCurrentPrototype
->AddStyleSheetReference(sheetURI
);
554 if (isChrome
&& useXULCache
) {
555 // If it's a chrome prototype document, then notify any
556 // documents that raced to load the prototype, and awaited
557 // its load completion via proto->AwaitLoadDone().
558 rv
= mCurrentPrototype
->NotifyLoadDone();
559 if (NS_FAILED(rv
)) return;
563 OnPrototypeLoadDone(true);
565 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
)) {
566 nsAutoCString urlspec
;
567 rv
= uri
->GetSpec(urlspec
);
568 if (NS_SUCCEEDED(rv
)) {
569 PR_LOG(gXULLog
, PR_LOG_WARNING
,
570 ("xul: Finished loading document '%s'", urlspec
.get()));
577 XULDocument::OnPrototypeLoadDone(bool aResumeWalk
)
581 // Add the style overlays from chrome registry, if any.
582 rv
= AddPrototypeSheets();
583 if (NS_FAILED(rv
)) return rv
;
585 rv
= PrepareToWalk();
586 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to prepare for walk");
587 if (NS_FAILED(rv
)) return rv
;
595 // called when an error occurs parsing a document
597 XULDocument::OnDocumentParserError()
599 // don't report errors that are from overlays
600 if (mCurrentPrototype
&& mMasterPrototype
!= mCurrentPrototype
) {
601 nsCOMPtr
<nsIURI
> uri
= mCurrentPrototype
->GetURI();
602 if (IsChromeURI(uri
)) {
603 nsCOMPtr
<nsIObserverService
> os
=
604 mozilla::services::GetObserverService();
606 os
->NotifyObservers(uri
, "xul-overlay-parsererror",
607 EmptyString().get());
617 ClearBroadcasterMapEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aEntry
)
619 BroadcasterMapEntry
* entry
=
620 static_cast<BroadcasterMapEntry
*>(aEntry
);
621 for (int32_t i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
622 delete (BroadcastListener
*)entry
->mListeners
[i
];
625 // N.B. that we need to manually run the dtor because we
626 // constructed the nsSmallVoidArray object in-place.
627 entry
->mListeners
.~nsSmallVoidArray();
631 CanBroadcast(int32_t aNameSpaceID
, nsIAtom
* aAttribute
)
633 // Don't push changes to the |id|, |ref|, |persist|, |command| or
634 // |observes| attribute.
635 if (aNameSpaceID
== kNameSpaceID_None
) {
636 if ((aAttribute
== nsGkAtoms::id
) ||
637 (aAttribute
== nsGkAtoms::ref
) ||
638 (aAttribute
== nsGkAtoms::persist
) ||
639 (aAttribute
== nsGkAtoms::command
) ||
640 (aAttribute
== nsGkAtoms::observes
)) {
647 struct nsAttrNameInfo
649 nsAttrNameInfo(int32_t aNamespaceID
, nsIAtom
* aName
, nsIAtom
* aPrefix
) :
650 mNamespaceID(aNamespaceID
), mName(aName
), mPrefix(aPrefix
) {}
651 nsAttrNameInfo(const nsAttrNameInfo
& aOther
) :
652 mNamespaceID(aOther
.mNamespaceID
), mName(aOther
.mName
),
653 mPrefix(aOther
.mPrefix
) {}
654 int32_t mNamespaceID
;
655 nsCOMPtr
<nsIAtom
> mName
;
656 nsCOMPtr
<nsIAtom
> mPrefix
;
660 XULDocument::SynchronizeBroadcastListener(Element
*aBroadcaster
,
662 const nsAString
&aAttr
)
664 if (!nsContentUtils::IsSafeToRunScript()) {
665 nsDelayedBroadcastUpdate
delayedUpdate(aBroadcaster
, aListener
,
667 mDelayedBroadcasters
.AppendElement(delayedUpdate
);
671 bool notify
= mDocumentLoaded
|| mHandlingDelayedBroadcasters
;
673 if (aAttr
.EqualsLiteral("*")) {
674 uint32_t count
= aBroadcaster
->GetAttrCount();
675 nsTArray
<nsAttrNameInfo
> attributes(count
);
676 for (uint32_t i
= 0; i
< count
; ++i
) {
677 const nsAttrName
* attrName
= aBroadcaster
->GetAttrNameAt(i
);
678 int32_t nameSpaceID
= attrName
->NamespaceID();
679 nsIAtom
* name
= attrName
->LocalName();
681 // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
682 if (! CanBroadcast(nameSpaceID
, name
))
685 attributes
.AppendElement(nsAttrNameInfo(nameSpaceID
, name
,
686 attrName
->GetPrefix()));
689 count
= attributes
.Length();
690 while (count
-- > 0) {
691 int32_t nameSpaceID
= attributes
[count
].mNamespaceID
;
692 nsIAtom
* name
= attributes
[count
].mName
;
694 if (aBroadcaster
->GetAttr(nameSpaceID
, name
, value
)) {
695 aListener
->SetAttr(nameSpaceID
, name
, attributes
[count
].mPrefix
,
700 // XXX we don't fire the |onbroadcast| handler during
701 // initial hookup: doing so would potentially run the
702 // |onbroadcast| handler before the |onload| handler,
703 // which could define JS properties that mask XBL
705 ExecuteOnBroadcastHandlerFor(aBroadcaster
, aListener
, name
);
710 // Find out if the attribute is even present at all.
711 nsCOMPtr
<nsIAtom
> name
= do_GetAtom(aAttr
);
714 if (aBroadcaster
->GetAttr(kNameSpaceID_None
, name
, value
)) {
715 aListener
->SetAttr(kNameSpaceID_None
, name
, value
, notify
);
717 aListener
->UnsetAttr(kNameSpaceID_None
, name
, notify
);
721 // XXX we don't fire the |onbroadcast| handler during initial
722 // hookup: doing so would potentially run the |onbroadcast|
723 // handler before the |onload| handler, which could define JS
724 // properties that mask XBL properties, etc.
725 ExecuteOnBroadcastHandlerFor(aBroadcaster
, aListener
, name
);
731 XULDocument::AddBroadcastListenerFor(nsIDOMElement
* aBroadcaster
,
732 nsIDOMElement
* aListener
,
733 const nsAString
& aAttr
)
736 nsCOMPtr
<Element
> broadcaster
= do_QueryInterface(aBroadcaster
);
737 nsCOMPtr
<Element
> listener
= do_QueryInterface(aListener
);
738 NS_ENSURE_ARG(broadcaster
&& listener
);
739 AddBroadcastListenerFor(*broadcaster
, *listener
, aAttr
, rv
);
740 return rv
.ErrorCode();
744 XULDocument::AddBroadcastListenerFor(Element
& aBroadcaster
, Element
& aListener
,
745 const nsAString
& aAttr
, ErrorResult
& aRv
)
748 nsContentUtils::CheckSameOrigin(this, &aBroadcaster
);
755 rv
= nsContentUtils::CheckSameOrigin(this, &aListener
);
762 static const PLDHashTableOps gOps
= {
765 PL_DHashVoidPtrKeyStub
,
766 PL_DHashMatchEntryStub
,
767 PL_DHashMoveEntryStub
,
768 ClearBroadcasterMapEntry
,
769 PL_DHashFinalizeStub
,
773 if (! mBroadcasterMap
) {
775 PL_NewDHashTable(&gOps
, nullptr, sizeof(BroadcasterMapEntry
));
777 if (! mBroadcasterMap
) {
778 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
783 BroadcasterMapEntry
* entry
=
784 static_cast<BroadcasterMapEntry
*>
785 (PL_DHashTableLookup(mBroadcasterMap
, &aBroadcaster
));
787 if (PL_DHASH_ENTRY_IS_FREE(entry
)) {
789 static_cast<BroadcasterMapEntry
*>
790 (PL_DHashTableAdd(mBroadcasterMap
, &aBroadcaster
));
793 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
797 entry
->mBroadcaster
= &aBroadcaster
;
799 // N.B. placement new to construct the nsSmallVoidArray object
801 new (&entry
->mListeners
) nsSmallVoidArray();
804 // Only add the listener if it's not there already!
805 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(aAttr
);
807 BroadcastListener
* bl
;
808 for (int32_t i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
809 bl
= static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
811 nsCOMPtr
<Element
> blListener
= do_QueryReferent(bl
->mListener
);
813 if (blListener
== &aListener
&& bl
->mAttribute
== attr
)
817 bl
= new BroadcastListener
;
819 bl
->mListener
= do_GetWeakReference(&aListener
);
820 bl
->mAttribute
= attr
;
822 entry
->mListeners
.AppendElement(bl
);
824 SynchronizeBroadcastListener(&aBroadcaster
, &aListener
, aAttr
);
828 XULDocument::RemoveBroadcastListenerFor(nsIDOMElement
* aBroadcaster
,
829 nsIDOMElement
* aListener
,
830 const nsAString
& aAttr
)
832 nsCOMPtr
<Element
> broadcaster
= do_QueryInterface(aBroadcaster
);
833 nsCOMPtr
<Element
> listener
= do_QueryInterface(aListener
);
834 NS_ENSURE_ARG(broadcaster
&& listener
);
835 RemoveBroadcastListenerFor(*broadcaster
, *listener
, aAttr
);
840 XULDocument::RemoveBroadcastListenerFor(Element
& aBroadcaster
,
842 const nsAString
& aAttr
)
844 // If we haven't added any broadcast listeners, then there sure
845 // aren't any to remove.
846 if (! mBroadcasterMap
)
849 BroadcasterMapEntry
* entry
=
850 static_cast<BroadcasterMapEntry
*>
851 (PL_DHashTableLookup(mBroadcasterMap
, &aBroadcaster
));
853 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
854 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(aAttr
);
855 for (int32_t i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
856 BroadcastListener
* bl
=
857 static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
859 nsCOMPtr
<Element
> blListener
= do_QueryReferent(bl
->mListener
);
861 if (blListener
== &aListener
&& bl
->mAttribute
== attr
) {
862 entry
->mListeners
.RemoveElementAt(i
);
865 if (entry
->mListeners
.Count() == 0)
866 PL_DHashTableRemove(mBroadcasterMap
, &aBroadcaster
);
875 XULDocument::ExecuteOnBroadcastHandlerFor(Element
* aBroadcaster
,
879 // Now we execute the onchange handler in the context of the
880 // observer. We need to find the observer in order to
881 // execute the handler.
883 for (nsIContent
* child
= aListener
->GetFirstChild();
885 child
= child
->GetNextSibling()) {
887 // Look for an <observes> element beneath the listener. This
888 // ought to have an |element| attribute that refers to
889 // aBroadcaster, and an |attribute| element that tells us what
890 // attriubtes we're listening for.
891 if (!child
->NodeInfo()->Equals(nsGkAtoms::observes
, kNameSpaceID_XUL
))
894 // Is this the element that was listening to us?
895 nsAutoString listeningToID
;
896 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, listeningToID
);
898 nsAutoString broadcasterID
;
899 aBroadcaster
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, broadcasterID
);
901 if (listeningToID
!= broadcasterID
)
904 // We are observing the broadcaster, but is this the right
906 nsAutoString listeningToAttribute
;
907 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
,
908 listeningToAttribute
);
910 if (!aAttr
->Equals(listeningToAttribute
) &&
911 !listeningToAttribute
.EqualsLiteral("*")) {
915 // This is the right <observes> element. Execute the
916 // |onbroadcast| event handler
917 WidgetEvent
event(true, NS_XUL_BROADCAST
);
919 nsCOMPtr
<nsIPresShell
> shell
= GetShell();
921 nsRefPtr
<nsPresContext
> aPresContext
= shell
->GetPresContext();
923 // Handle the DOM event
924 nsEventStatus status
= nsEventStatus_eIgnore
;
925 EventDispatcher::Dispatch(child
, aPresContext
, &event
, nullptr,
934 XULDocument::AttributeWillChange(nsIDocument
* aDocument
,
935 Element
* aElement
, int32_t aNameSpaceID
,
936 nsIAtom
* aAttribute
, int32_t aModType
)
938 NS_ABORT_IF_FALSE(aElement
, "Null content!");
939 NS_PRECONDITION(aAttribute
, "Must have an attribute that's changing!");
941 // XXXbz check aNameSpaceID, dammit!
942 // See if we need to update our ref map.
943 if (aAttribute
== nsGkAtoms::ref
) {
944 // Might not need this, but be safe for now.
945 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
946 RemoveElementFromRefMap(aElement
);
951 XULDocument::AttributeChanged(nsIDocument
* aDocument
,
952 Element
* aElement
, int32_t aNameSpaceID
,
953 nsIAtom
* aAttribute
, int32_t aModType
)
955 NS_ASSERTION(aDocument
== this, "unexpected doc");
957 // Might not need this, but be safe for now.
958 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
960 // XXXbz check aNameSpaceID, dammit!
961 // See if we need to update our ref map.
962 if (aAttribute
== nsGkAtoms::ref
) {
963 AddElementToRefMap(aElement
);
968 // Synchronize broadcast listeners
969 if (mBroadcasterMap
&&
970 CanBroadcast(aNameSpaceID
, aAttribute
)) {
971 BroadcasterMapEntry
* entry
=
972 static_cast<BroadcasterMapEntry
*>
973 (PL_DHashTableLookup(mBroadcasterMap
, aElement
));
975 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
976 // We've got listeners: push the value.
978 bool attrSet
= aElement
->GetAttr(kNameSpaceID_None
, aAttribute
, value
);
981 for (i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
982 BroadcastListener
* bl
=
983 static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
985 if ((bl
->mAttribute
== aAttribute
) ||
986 (bl
->mAttribute
== nsGkAtoms::_asterix
)) {
987 nsCOMPtr
<Element
> listenerEl
988 = do_QueryReferent(bl
->mListener
);
990 nsAutoString currentValue
;
991 bool hasAttr
= listenerEl
->GetAttr(kNameSpaceID_None
,
994 // We need to update listener only if we're
995 // (1) removing an existing attribute,
996 // (2) adding a new attribute or
997 // (3) changing the value of an attribute.
998 bool needsAttrChange
=
999 attrSet
!= hasAttr
|| !value
.Equals(currentValue
);
1000 nsDelayedBroadcastUpdate
delayedUpdate(aElement
,
1008 mDelayedAttrChangeBroadcasts
.IndexOf(delayedUpdate
,
1009 0, nsDelayedBroadcastUpdate::Comparator());
1010 if (index
!= mDelayedAttrChangeBroadcasts
.NoIndex
) {
1011 if (mHandlingDelayedAttrChange
) {
1012 NS_WARNING("Broadcasting loop!");
1015 mDelayedAttrChangeBroadcasts
.RemoveElementAt(index
);
1018 mDelayedAttrChangeBroadcasts
.AppendElement(delayedUpdate
);
1025 // checks for modifications in broadcasters
1026 bool listener
, resolved
;
1027 CheckBroadcasterHookup(aElement
, &listener
, &resolved
);
1029 // See if there is anything we need to persist in the localstore.
1031 // XXX Namespace handling broken :-(
1032 nsAutoString persist
;
1033 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::persist
, persist
);
1034 if (!persist
.IsEmpty()) {
1035 // XXXldb This should check that it's a token, not just a substring.
1036 if (persist
.Find(nsDependentAtomString(aAttribute
)) >= 0) {
1037 rv
= Persist(aElement
, kNameSpaceID_None
, aAttribute
);
1038 if (NS_FAILED(rv
)) return;
1044 XULDocument::ContentAppended(nsIDocument
* aDocument
,
1045 nsIContent
* aContainer
,
1046 nsIContent
* aFirstNewContent
,
1047 int32_t aNewIndexInContainer
)
1049 NS_ASSERTION(aDocument
== this, "unexpected doc");
1051 // Might not need this, but be safe for now.
1052 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
1054 // Update our element map
1055 nsresult rv
= NS_OK
;
1056 for (nsIContent
* cur
= aFirstNewContent
; cur
&& NS_SUCCEEDED(rv
);
1057 cur
= cur
->GetNextSibling()) {
1058 rv
= AddSubtreeToDocument(cur
);
1063 XULDocument::ContentInserted(nsIDocument
* aDocument
,
1064 nsIContent
* aContainer
,
1066 int32_t aIndexInContainer
)
1068 NS_ASSERTION(aDocument
== this, "unexpected doc");
1070 // Might not need this, but be safe for now.
1071 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
1073 AddSubtreeToDocument(aChild
);
1077 XULDocument::ContentRemoved(nsIDocument
* aDocument
,
1078 nsIContent
* aContainer
,
1080 int32_t aIndexInContainer
,
1081 nsIContent
* aPreviousSibling
)
1083 NS_ASSERTION(aDocument
== this, "unexpected doc");
1085 // Might not need this, but be safe for now.
1086 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
1088 RemoveSubtreeFromDocument(aChild
);
1091 //----------------------------------------------------------------------
1093 // nsIXULDocument interface
1097 XULDocument::GetElementsForID(const nsAString
& aID
,
1098 nsCOMArray
<nsIContent
>& aElements
)
1102 nsIdentifierMapEntry
*entry
= mIdentifierMap
.GetEntry(aID
);
1104 entry
->AppendAllIdContent(&aElements
);
1106 nsRefMapEntry
*refEntry
= mRefMap
.GetEntry(aID
);
1108 refEntry
->AppendAll(&aElements
);
1113 XULDocument::AddForwardReference(nsForwardReference
* aRef
)
1115 if (mResolutionPhase
< aRef
->GetPhase()) {
1116 if (!mForwardReferences
.AppendElement(aRef
)) {
1118 return NS_ERROR_OUT_OF_MEMORY
;
1122 NS_ERROR("forward references have already been resolved");
1130 XULDocument::ResolveForwardReferences()
1132 if (mResolutionPhase
== nsForwardReference::eDone
)
1135 NS_ASSERTION(mResolutionPhase
== nsForwardReference::eStart
,
1136 "nested ResolveForwardReferences()");
1138 // Resolve each outstanding 'forward' reference. We iterate
1139 // through the list of forward references until no more forward
1140 // references can be resolved. This annealing process is
1141 // guaranteed to converge because we've "closed the gate" to new
1142 // forward references.
1144 const nsForwardReference::Phase
* pass
= nsForwardReference::kPasses
;
1145 while ((mResolutionPhase
= *pass
) != nsForwardReference::eDone
) {
1146 uint32_t previous
= 0;
1147 while (mForwardReferences
.Length() &&
1148 mForwardReferences
.Length() != previous
) {
1149 previous
= mForwardReferences
.Length();
1151 for (uint32_t i
= 0; i
< mForwardReferences
.Length(); ++i
) {
1152 nsForwardReference
* fwdref
= mForwardReferences
[i
];
1154 if (fwdref
->GetPhase() == *pass
) {
1155 nsForwardReference::Result result
= fwdref
->Resolve();
1158 case nsForwardReference::eResolve_Succeeded
:
1159 case nsForwardReference::eResolve_Error
:
1160 mForwardReferences
.RemoveElementAt(i
);
1162 // fixup because we removed from list
1166 case nsForwardReference::eResolve_Later
:
1167 // do nothing. we'll try again later
1171 if (mResolutionPhase
== nsForwardReference::eStart
) {
1172 // Resolve() loaded a dynamic overlay,
1173 // (see XULDocument::LoadOverlayInternal()).
1174 // Return for now, we will be called again.
1184 mForwardReferences
.Clear();
1188 //----------------------------------------------------------------------
1190 // nsIDOMDocument interface
1194 XULDocument::GetElementsByAttribute(const nsAString
& aAttribute
,
1195 const nsAString
& aValue
,
1196 nsIDOMNodeList
** aReturn
)
1198 *aReturn
= GetElementsByAttribute(aAttribute
, aValue
).take();
1202 already_AddRefed
<nsINodeList
>
1203 XULDocument::GetElementsByAttribute(const nsAString
& aAttribute
,
1204 const nsAString
& aValue
)
1206 nsCOMPtr
<nsIAtom
> attrAtom(do_GetAtom(aAttribute
));
1207 void* attrValue
= new nsString(aValue
);
1208 nsRefPtr
<nsContentList
> list
= new nsContentList(this,
1210 nsContentUtils::DestroyMatchString
,
1214 kNameSpaceID_Unknown
);
1216 return list
.forget();
1220 XULDocument::GetElementsByAttributeNS(const nsAString
& aNamespaceURI
,
1221 const nsAString
& aAttribute
,
1222 const nsAString
& aValue
,
1223 nsIDOMNodeList
** aReturn
)
1226 *aReturn
= GetElementsByAttributeNS(aNamespaceURI
, aAttribute
,
1228 return rv
.ErrorCode();
1231 already_AddRefed
<nsINodeList
>
1232 XULDocument::GetElementsByAttributeNS(const nsAString
& aNamespaceURI
,
1233 const nsAString
& aAttribute
,
1234 const nsAString
& aValue
,
1237 nsCOMPtr
<nsIAtom
> attrAtom(do_GetAtom(aAttribute
));
1238 void* attrValue
= new nsString(aValue
);
1240 int32_t nameSpaceId
= kNameSpaceID_Wildcard
;
1241 if (!aNamespaceURI
.EqualsLiteral("*")) {
1243 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI
,
1245 if (NS_FAILED(rv
)) {
1251 nsRefPtr
<nsContentList
> list
= new nsContentList(this,
1253 nsContentUtils::DestroyMatchString
,
1258 return list
.forget();
1262 XULDocument::Persist(const nsAString
& aID
,
1263 const nsAString
& aAttr
)
1265 // If we're currently reading persisted attributes out of the
1266 // localstore, _don't_ re-enter and try to set them again!
1267 if (mApplyingPersistedAttrs
)
1270 Element
* element
= nsDocument::GetElementById(aID
);
1274 nsCOMPtr
<nsIAtom
> tag
;
1275 int32_t nameSpaceID
;
1277 nsRefPtr
<mozilla::dom::NodeInfo
> ni
= element
->GetExistingAttrNameFromQName(aAttr
);
1280 tag
= ni
->NameAtom();
1281 nameSpaceID
= ni
->NamespaceID();
1284 // Make sure that this QName is going to be valid.
1285 const char16_t
*colon
;
1286 rv
= nsContentUtils::CheckQName(PromiseFlatString(aAttr
), true, &colon
);
1288 if (NS_FAILED(rv
)) {
1289 // There was an invalid character or it was malformed.
1290 return NS_ERROR_INVALID_ARG
;
1294 // We don't really handle namespace qualifiers in attribute names.
1295 return NS_ERROR_NOT_IMPLEMENTED
;
1298 tag
= do_GetAtom(aAttr
);
1299 NS_ENSURE_TRUE(tag
, NS_ERROR_OUT_OF_MEMORY
);
1301 nameSpaceID
= kNameSpaceID_None
;
1304 return Persist(element
, nameSpaceID
, tag
);
1308 XULDocument::Persist(nsIContent
* aElement
, int32_t aNameSpaceID
,
1309 nsIAtom
* aAttribute
)
1311 // For non-chrome documents, persistance is simply broken
1312 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
1313 return NS_ERROR_NOT_AVAILABLE
;
1316 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
1317 if (NS_WARN_IF(!mLocalStore
)) {
1318 return NS_ERROR_NOT_INITIALIZED
;
1324 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
1325 nsAtomString
attrstr(aAttribute
);
1327 nsAutoString valuestr
;
1328 aElement
->GetAttr(kNameSpaceID_None
, aAttribute
, valuestr
);
1330 nsAutoCString utf8uri
;
1331 nsresult rv
= mDocumentURI
->GetSpec(utf8uri
);
1332 if (NS_WARN_IF(NS_FAILED(rv
))) {
1335 NS_ConvertUTF8toUTF16
uri(utf8uri
);
1338 rv
= mLocalStore
->HasValue(uri
, id
, attrstr
, &hasAttr
);
1339 if (NS_WARN_IF(NS_FAILED(rv
))) {
1343 if (hasAttr
&& valuestr
.IsEmpty()) {
1344 return mLocalStore
->RemoveValue(uri
, id
, attrstr
);
1346 return mLocalStore
->SetValue(uri
, id
, attrstr
, valuestr
);
1352 XULDocument::GetViewportSize(int32_t* aWidth
,
1355 *aWidth
= *aHeight
= 0;
1357 FlushPendingNotifications(Flush_Layout
);
1359 nsIPresShell
*shell
= GetShell();
1360 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
1362 nsIFrame
* frame
= shell
->GetRootFrame();
1363 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
1365 nsSize size
= frame
->GetSize();
1367 *aWidth
= nsPresContext::AppUnitsToIntCSSPixels(size
.width
);
1368 *aHeight
= nsPresContext::AppUnitsToIntCSSPixels(size
.height
);
1374 XULDocument::GetWidth(int32_t* aWidth
)
1376 NS_ENSURE_ARG_POINTER(aWidth
);
1379 return GetViewportSize(aWidth
, &height
);
1383 XULDocument::GetWidth(ErrorResult
& aRv
)
1386 aRv
= GetWidth(&width
);
1391 XULDocument::GetHeight(int32_t* aHeight
)
1393 NS_ENSURE_ARG_POINTER(aHeight
);
1396 return GetViewportSize(&width
, aHeight
);
1400 XULDocument::GetHeight(ErrorResult
& aRv
)
1403 aRv
= GetHeight(&height
);
1408 GetScopeObjectOfNode(nsIDOMNode
* node
)
1410 MOZ_ASSERT(node
, "Must not be called with null.");
1412 // Window root occasionally keeps alive a node of a document whose
1413 // window is already dead. If in this brief period someone calls
1414 // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
1415 // because it will not know which scope this node belongs to. Returning
1416 // an orphan node like that to JS would be a bug anyway, so to avoid
1417 // this, let's do the same check as nsNodeSH::PreCreate does to
1418 // determine the scope and if it fails let's just return null in
1419 // XULDocument::GetPopupNode.
1420 nsCOMPtr
<nsINode
> inode
= do_QueryInterface(node
);
1421 MOZ_ASSERT(inode
, "How can this happen?");
1423 nsIDocument
* doc
= inode
->OwnerDoc();
1424 MOZ_ASSERT(inode
, "This should never happen.");
1426 nsIGlobalObject
* global
= doc
->GetScopeObject();
1427 return global
? global
->GetGlobalJSObject() : nullptr;
1430 //----------------------------------------------------------------------
1432 // nsIDOMXULDocument interface
1436 XULDocument::GetPopupNode(nsIDOMNode
** aNode
)
1440 nsCOMPtr
<nsIDOMNode
> node
;
1441 nsCOMPtr
<nsPIWindowRoot
> rootWin
= GetWindowRoot();
1443 node
= rootWin
->GetPopupNode(); // addref happens here
1446 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1448 node
= pm
->GetLastTriggerPopupNode(this);
1452 if (node
&& nsContentUtils::CanCallerAccess(node
)
1453 && GetScopeObjectOfNode(node
)) {
1460 already_AddRefed
<nsINode
>
1461 XULDocument::GetPopupNode()
1463 nsCOMPtr
<nsIDOMNode
> node
;
1464 DebugOnly
<nsresult
> rv
= GetPopupNode(getter_AddRefs(node
));
1465 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1466 nsCOMPtr
<nsINode
> retval(do_QueryInterface(node
));
1467 return retval
.forget();
1471 XULDocument::SetPopupNode(nsIDOMNode
* aNode
)
1474 // only allow real node objects
1475 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aNode
);
1476 NS_ENSURE_ARG(node
);
1479 nsCOMPtr
<nsPIWindowRoot
> rootWin
= GetWindowRoot();
1481 rootWin
->SetPopupNode(aNode
); // addref happens here
1487 XULDocument::SetPopupNode(nsINode
* aNode
)
1489 nsCOMPtr
<nsIDOMNode
> node(do_QueryInterface(aNode
));
1490 DebugOnly
<nsresult
> rv
= SetPopupNode(node
);
1491 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1494 // Returns the rangeOffset element from the XUL Popup Manager. This is for
1495 // chrome callers only.
1497 XULDocument::GetPopupRangeParent(nsIDOMNode
** aRangeParent
)
1499 NS_ENSURE_ARG_POINTER(aRangeParent
);
1500 *aRangeParent
= nullptr;
1502 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1504 return NS_ERROR_FAILURE
;
1507 pm
->GetMouseLocation(aRangeParent
, &offset
);
1509 if (*aRangeParent
&& !nsContentUtils::CanCallerAccess(*aRangeParent
)) {
1510 NS_RELEASE(*aRangeParent
);
1511 return NS_ERROR_DOM_SECURITY_ERR
;
1517 already_AddRefed
<nsINode
>
1518 XULDocument::GetPopupRangeParent(ErrorResult
& aRv
)
1520 nsCOMPtr
<nsIDOMNode
> node
;
1521 aRv
= GetPopupRangeParent(getter_AddRefs(node
));
1522 nsCOMPtr
<nsINode
> retval(do_QueryInterface(node
));
1523 return retval
.forget();
1527 // Returns the rangeOffset element from the XUL Popup Manager. We check the
1528 // rangeParent to determine if the caller has rights to access to the data.
1530 XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset
)
1533 *aRangeOffset
= GetPopupRangeOffset(rv
);
1534 return rv
.ErrorCode();
1538 XULDocument::GetPopupRangeOffset(ErrorResult
& aRv
)
1540 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1542 aRv
.Throw(NS_ERROR_FAILURE
);
1547 nsCOMPtr
<nsIDOMNode
> parent
;
1548 pm
->GetMouseLocation(getter_AddRefs(parent
), &offset
);
1550 if (parent
&& !nsContentUtils::CanCallerAccess(parent
)) {
1551 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1558 XULDocument::GetTooltipNode(nsIDOMNode
** aNode
)
1562 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1564 nsCOMPtr
<nsIDOMNode
> node
= pm
->GetLastTriggerTooltipNode(this);
1565 if (node
&& nsContentUtils::CanCallerAccess(node
))
1572 already_AddRefed
<nsINode
>
1573 XULDocument::GetTooltipNode()
1575 nsCOMPtr
<nsIDOMNode
> node
;
1576 DebugOnly
<nsresult
> rv
= GetTooltipNode(getter_AddRefs(node
));
1577 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1578 nsCOMPtr
<nsINode
> retval(do_QueryInterface(node
));
1579 return retval
.forget();
1583 XULDocument::SetTooltipNode(nsIDOMNode
* aNode
)
1591 XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher
** aTracker
)
1593 *aTracker
= mCommandDispatcher
;
1594 NS_IF_ADDREF(*aTracker
);
1599 XULDocument::GetElementById(const nsAString
& aId
)
1601 if (!CheckGetElementByIdArg(aId
))
1604 nsIdentifierMapEntry
*entry
= mIdentifierMap
.GetEntry(aId
);
1606 Element
* element
= entry
->GetIdElement();
1611 nsRefMapEntry
* refEntry
= mRefMap
.GetEntry(aId
);
1613 NS_ASSERTION(refEntry
->GetFirstElement(),
1614 "nsRefMapEntries should have nonempty content lists");
1615 return refEntry
->GetFirstElement();
1621 XULDocument::AddElementToDocumentPre(Element
* aElement
)
1623 // Do a bunch of work that's necessary when an element gets added
1624 // to the XUL Document.
1627 // 1. Add the element to the resource-to-element map. Also add it to
1628 // the id map, since it seems this can be called when creating
1629 // elements from prototypes.
1630 nsIAtom
* id
= aElement
->GetID();
1632 // FIXME: Shouldn't BindToTree take care of this?
1633 nsAutoScriptBlocker scriptBlocker
;
1634 AddToIdTable(aElement
, id
);
1636 rv
= AddElementToRefMap(aElement
);
1637 if (NS_FAILED(rv
)) return rv
;
1639 // 2. If the element is a 'command updater' (i.e., has a
1640 // "commandupdater='true'" attribute), then add the element to the
1641 // document's command dispatcher
1642 if (aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::commandupdater
,
1643 nsGkAtoms::_true
, eCaseMatters
)) {
1644 rv
= nsXULContentUtils::SetCommandUpdater(this, aElement
);
1645 if (NS_FAILED(rv
)) return rv
;
1648 // 3. Check for a broadcaster hookup attribute, in which case
1649 // we'll hook the node up as a listener on a broadcaster.
1650 bool listener
, resolved
;
1651 rv
= CheckBroadcasterHookup(aElement
, &listener
, &resolved
);
1652 if (NS_FAILED(rv
)) return rv
;
1654 // If it's not there yet, we may be able to defer hookup until
1656 if (listener
&& !resolved
&& (mResolutionPhase
!= nsForwardReference::eDone
)) {
1657 BroadcasterHookup
* hookup
= new BroadcasterHookup(this, aElement
);
1659 return NS_ERROR_OUT_OF_MEMORY
;
1661 rv
= AddForwardReference(hookup
);
1662 if (NS_FAILED(rv
)) return rv
;
1669 XULDocument::AddElementToDocumentPost(Element
* aElement
)
1671 // We need to pay special attention to the keyset tag to set up a listener
1672 if (aElement
->NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
1673 // Create our XUL key listener and hook it up.
1674 nsXBLService::AttachGlobalKeyHandler(aElement
);
1677 // See if we need to attach a XUL template to this node
1679 nsresult rv
= CheckTemplateBuilderHookup(aElement
, &needsHookup
);
1684 if (mResolutionPhase
== nsForwardReference::eDone
) {
1685 rv
= CreateTemplateBuilder(aElement
);
1690 TemplateBuilderHookup
* hookup
= new TemplateBuilderHookup(aElement
);
1692 return NS_ERROR_OUT_OF_MEMORY
;
1694 rv
= AddForwardReference(hookup
);
1704 XULDocument::AddSubtreeToDocument(nsIContent
* aContent
)
1706 NS_ASSERTION(aContent
->GetUncomposedDoc() == this, "Element not in doc!");
1707 // From here on we only care about elements.
1708 if (!aContent
->IsElement()) {
1712 Element
* aElement
= aContent
->AsElement();
1714 // Do pre-order addition magic
1715 nsresult rv
= AddElementToDocumentPre(aElement
);
1716 if (NS_FAILED(rv
)) return rv
;
1718 // Recurse to children
1719 for (nsIContent
* child
= aElement
->GetLastChild();
1721 child
= child
->GetPreviousSibling()) {
1723 rv
= AddSubtreeToDocument(child
);
1728 // Do post-order addition magic
1729 return AddElementToDocumentPost(aElement
);
1733 XULDocument::RemoveSubtreeFromDocument(nsIContent
* aContent
)
1735 // From here on we only care about elements.
1736 if (!aContent
->IsElement()) {
1740 Element
* aElement
= aContent
->AsElement();
1742 // Do a bunch of cleanup to remove an element from the XUL
1746 if (aElement
->NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
1747 nsXBLService::DetachGlobalKeyHandler(aElement
);
1750 // 1. Remove any children from the document.
1751 for (nsIContent
* child
= aElement
->GetLastChild();
1753 child
= child
->GetPreviousSibling()) {
1755 rv
= RemoveSubtreeFromDocument(child
);
1760 // 2. Remove the element from the resource-to-element map.
1761 // Also remove it from the id map, since we added it in
1762 // AddElementToDocumentPre().
1763 RemoveElementFromRefMap(aElement
);
1764 nsIAtom
* id
= aElement
->GetID();
1766 // FIXME: Shouldn't UnbindFromTree take care of this?
1767 nsAutoScriptBlocker scriptBlocker
;
1768 RemoveFromIdTable(aElement
, id
);
1771 // 3. If the element is a 'command updater', then remove the
1772 // element from the document's command dispatcher.
1773 if (aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::commandupdater
,
1774 nsGkAtoms::_true
, eCaseMatters
)) {
1775 nsCOMPtr
<nsIDOMElement
> domelement
= do_QueryInterface(aElement
);
1776 NS_ASSERTION(domelement
!= nullptr, "not a DOM element");
1778 return NS_ERROR_UNEXPECTED
;
1780 rv
= mCommandDispatcher
->RemoveCommandUpdater(domelement
);
1781 if (NS_FAILED(rv
)) return rv
;
1784 // 4. Remove the element from our broadcaster map, since it is no longer
1786 nsCOMPtr
<Element
> broadcaster
, listener
;
1787 nsAutoString attribute
, broadcasterID
;
1788 rv
= FindBroadcaster(aElement
, getter_AddRefs(listener
),
1789 broadcasterID
, attribute
, getter_AddRefs(broadcaster
));
1790 if (rv
== NS_FINDBROADCASTER_FOUND
) {
1791 RemoveBroadcastListenerFor(*broadcaster
, *listener
, attribute
);
1798 XULDocument::SetTemplateBuilderFor(nsIContent
* aContent
,
1799 nsIXULTemplateBuilder
* aBuilder
)
1801 if (! mTemplateBuilderTable
) {
1805 mTemplateBuilderTable
= new BuilderTable
;
1809 mTemplateBuilderTable
->Put(aContent
, aBuilder
);
1812 mTemplateBuilderTable
->Remove(aContent
);
1819 XULDocument::GetTemplateBuilderFor(nsIContent
* aContent
,
1820 nsIXULTemplateBuilder
** aResult
)
1822 if (mTemplateBuilderTable
) {
1823 mTemplateBuilderTable
->Get(aContent
, aResult
);
1832 GetRefMapAttribute(Element
* aElement
, nsAutoString
* aValue
)
1834 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::ref
, *aValue
);
1838 XULDocument::AddElementToRefMap(Element
* aElement
)
1840 // Look at the element's 'ref' attribute, and if set,
1841 // add an entry in the resource-to-element map to the element.
1843 GetRefMapAttribute(aElement
, &value
);
1844 if (!value
.IsEmpty()) {
1845 nsRefMapEntry
*entry
= mRefMap
.PutEntry(value
);
1847 return NS_ERROR_OUT_OF_MEMORY
;
1848 if (!entry
->AddElement(aElement
))
1849 return NS_ERROR_OUT_OF_MEMORY
;
1856 XULDocument::RemoveElementFromRefMap(Element
* aElement
)
1858 // Remove the element from the resource-to-element map.
1860 GetRefMapAttribute(aElement
, &value
);
1861 if (!value
.IsEmpty()) {
1862 nsRefMapEntry
*entry
= mRefMap
.GetEntry(value
);
1865 if (entry
->RemoveElement(aElement
)) {
1866 mRefMap
.RawRemoveEntry(entry
);
1871 //----------------------------------------------------------------------
1873 // nsIDOMNode interface
1877 XULDocument::Clone(mozilla::dom::NodeInfo
*aNodeInfo
, nsINode
**aResult
) const
1879 // We don't allow cloning of a XUL document
1881 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1885 //----------------------------------------------------------------------
1887 // Implementation methods
1893 nsresult rv
= XMLDocument::Init();
1894 NS_ENSURE_SUCCESS(rv
, rv
);
1896 // Create our command dispatcher and hook it up.
1897 mCommandDispatcher
= new nsXULCommandDispatcher(this);
1898 NS_ENSURE_TRUE(mCommandDispatcher
, NS_ERROR_OUT_OF_MEMORY
);
1900 if (gRefCnt
++ == 0) {
1901 // ensure that the XUL prototype cache is instantiated successfully,
1902 // so that we can use nsXULPrototypeCache::GetInstance() without
1903 // null-checks in the rest of the class.
1904 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1906 NS_ERROR("Could not instantiate nsXULPrototypeCache");
1907 return NS_ERROR_FAILURE
;
1911 Preferences::RegisterCallback(XULDocument::DirectionChanged
,
1912 "intl.uidirection.", this);
1916 gXULLog
= PR_NewLogModule("XULDocument");
1924 XULDocument::StartLayout(void)
1926 mMayStartLayout
= true;
1927 nsCOMPtr
<nsIPresShell
> shell
= GetShell();
1929 // Resize-reflow this time
1930 nsPresContext
*cx
= shell
->GetPresContext();
1931 NS_ASSERTION(cx
!= nullptr, "no pres context");
1933 return NS_ERROR_UNEXPECTED
;
1935 nsCOMPtr
<nsIDocShell
> docShell
= cx
->GetDocShell();
1936 NS_ASSERTION(docShell
!= nullptr, "container is not a docshell");
1938 return NS_ERROR_UNEXPECTED
;
1940 nsresult rv
= NS_OK
;
1941 nsRect r
= cx
->GetVisibleArea();
1942 rv
= shell
->Initialize(r
.width
, r
.height
);
1943 NS_ENSURE_SUCCESS(rv
, rv
);
1951 XULDocument::MatchAttribute(nsIContent
* aContent
,
1952 int32_t aNamespaceID
,
1956 NS_PRECONDITION(aContent
, "Must have content node to work with!");
1957 nsString
* attrValue
= static_cast<nsString
*>(aData
);
1958 if (aNamespaceID
!= kNameSpaceID_Unknown
&&
1959 aNamespaceID
!= kNameSpaceID_Wildcard
) {
1960 return attrValue
->EqualsLiteral("*") ?
1961 aContent
->HasAttr(aNamespaceID
, aAttrName
) :
1962 aContent
->AttrValueIs(aNamespaceID
, aAttrName
, *attrValue
,
1966 // Qualified name match. This takes more work.
1968 uint32_t count
= aContent
->GetAttrCount();
1969 for (uint32_t i
= 0; i
< count
; ++i
) {
1970 const nsAttrName
* name
= aContent
->GetAttrNameAt(i
);
1972 if (name
->IsAtom()) {
1973 nameMatch
= name
->Atom() == aAttrName
;
1974 } else if (aNamespaceID
== kNameSpaceID_Wildcard
) {
1975 nameMatch
= name
->NodeInfo()->Equals(aAttrName
);
1977 nameMatch
= name
->NodeInfo()->QualifiedNameEquals(aAttrName
);
1981 return attrValue
->EqualsLiteral("*") ||
1982 aContent
->AttrValueIs(name
->NamespaceID(), name
->LocalName(),
1983 *attrValue
, eCaseMatters
);
1991 XULDocument::PrepareToLoad(nsISupports
* aContainer
,
1992 const char* aCommand
,
1993 nsIChannel
* aChannel
,
1994 nsILoadGroup
* aLoadGroup
,
1995 nsIParser
** aResult
)
1997 // Get the document's principal
1998 nsCOMPtr
<nsIPrincipal
> principal
;
1999 nsContentUtils::GetSecurityManager()->
2000 GetChannelResultPrincipal(aChannel
, getter_AddRefs(principal
));
2001 return PrepareToLoadPrototype(mDocumentURI
, aCommand
, principal
, aResult
);
2006 XULDocument::PrepareToLoadPrototype(nsIURI
* aURI
, const char* aCommand
,
2007 nsIPrincipal
* aDocumentPrincipal
,
2008 nsIParser
** aResult
)
2012 // Create a new prototype document.
2013 rv
= NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype
));
2014 if (NS_FAILED(rv
)) return rv
;
2016 rv
= mCurrentPrototype
->InitPrincipal(aURI
, aDocumentPrincipal
);
2017 if (NS_FAILED(rv
)) {
2018 mCurrentPrototype
= nullptr;
2022 // Bootstrap the master document prototype.
2023 if (! mMasterPrototype
) {
2024 mMasterPrototype
= mCurrentPrototype
;
2025 // Set our principal based on the master proto.
2026 SetPrincipal(aDocumentPrincipal
);
2029 // Create a XUL content sink, a parser, and kick off a load for
2031 nsRefPtr
<XULContentSinkImpl
> sink
= new XULContentSinkImpl();
2032 if (!sink
) return NS_ERROR_OUT_OF_MEMORY
;
2034 rv
= sink
->Init(this, mCurrentPrototype
);
2035 NS_ASSERTION(NS_SUCCEEDED(rv
), "Unable to initialize datasource sink");
2036 if (NS_FAILED(rv
)) return rv
;
2038 nsCOMPtr
<nsIParser
> parser
= do_CreateInstance(kParserCID
, &rv
);
2039 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to create parser");
2040 if (NS_FAILED(rv
)) return rv
;
2042 parser
->SetCommand(nsCRT::strcmp(aCommand
, "view-source") ? eViewNormal
:
2045 parser
->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
2046 kCharsetFromDocTypeDefault
);
2047 parser
->SetContentSink(sink
); // grabs a reference to the parser
2050 NS_ADDREF(*aResult
);
2056 XULDocument::ApplyPersistentAttributes()
2058 // For non-chrome documents, persistance is simply broken
2059 if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
2060 return NS_ERROR_NOT_AVAILABLE
;
2062 // Add all of the 'persisted' attributes into the content
2065 mLocalStore
= do_GetService("@mozilla.org/xul/xulstore;1");
2066 if (NS_WARN_IF(!mLocalStore
)) {
2067 return NS_ERROR_NOT_INITIALIZED
;
2071 mApplyingPersistedAttrs
= true;
2072 ApplyPersistentAttributesInternal();
2073 mApplyingPersistedAttrs
= false;
2075 // After we've applied persistence once, we should only reapply
2076 // it to nodes created by overlays
2077 mRestrictPersistence
= true;
2078 mPersistenceIds
.Clear();
2085 XULDocument::ApplyPersistentAttributesInternal()
2087 nsCOMArray
<nsIContent
> elements
;
2089 nsAutoCString utf8uri
;
2090 nsresult rv
= mDocumentURI
->GetSpec(utf8uri
);
2091 if (NS_WARN_IF(NS_FAILED(rv
))) {
2094 NS_ConvertUTF8toUTF16
uri(utf8uri
);
2096 // Get a list of element IDs for which persisted values are available
2097 nsCOMPtr
<nsIStringEnumerator
> ids
;
2098 rv
= mLocalStore
->GetIDsEnumerator(uri
, getter_AddRefs(ids
));
2099 if (NS_WARN_IF(NS_FAILED(rv
))) {
2104 bool hasmore
= false;
2105 ids
->HasMore(&hasmore
);
2113 if (mRestrictPersistence
&& !mPersistenceIds
.Contains(id
)) {
2117 // This will clear the array if there are no elements.
2118 GetElementsForID(id
, elements
);
2119 if (!elements
.Count()) {
2123 rv
= ApplyPersistentAttributesToElements(id
, elements
);
2124 if (NS_WARN_IF(NS_FAILED(rv
))) {
2134 XULDocument::ApplyPersistentAttributesToElements(const nsAString
&aID
,
2135 nsCOMArray
<nsIContent
>& aElements
)
2137 nsAutoCString utf8uri
;
2138 nsresult rv
= mDocumentURI
->GetSpec(utf8uri
);
2139 if (NS_WARN_IF(NS_FAILED(rv
))) {
2142 NS_ConvertUTF8toUTF16
uri(utf8uri
);
2144 // Get a list of attributes for which persisted values are available
2145 nsCOMPtr
<nsIStringEnumerator
> attrs
;
2146 rv
= mLocalStore
->GetAttributeEnumerator(uri
, aID
, getter_AddRefs(attrs
));
2147 if (NS_WARN_IF(NS_FAILED(rv
))) {
2152 bool hasmore
= PR_FALSE
;
2153 attrs
->HasMore(&hasmore
);
2158 nsAutoString attrstr
;
2159 attrs
->GetNext(attrstr
);
2162 rv
= mLocalStore
->GetValue(uri
, aID
, attrstr
, value
);
2163 if (NS_WARN_IF(NS_FAILED(rv
))) {
2167 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(attrstr
);
2168 if (NS_WARN_IF(!attr
)) {
2169 return NS_ERROR_OUT_OF_MEMORY
;
2172 uint32_t cnt
= aElements
.Count();
2174 for (int32_t i
= int32_t(cnt
) - 1; i
>= 0; --i
) {
2175 nsCOMPtr
<nsIContent
> element
= aElements
.SafeObjectAt(i
);
2180 rv
= element
->SetAttr(kNameSpaceID_None
, attr
, value
, PR_TRUE
);
2188 XULDocument::TraceProtos(JSTracer
* aTrc
, uint32_t aGCNumber
)
2190 uint32_t i
, count
= mPrototypes
.Length();
2191 for (i
= 0; i
< count
; ++i
) {
2192 mPrototypes
[i
]->TraceProtos(aTrc
, aGCNumber
);
2196 //----------------------------------------------------------------------
2198 // XULDocument::ContextStack
2201 XULDocument::ContextStack::ContextStack()
2202 : mTop(nullptr), mDepth(0)
2206 XULDocument::ContextStack::~ContextStack()
2209 Entry
* doomed
= mTop
;
2211 NS_IF_RELEASE(doomed
->mElement
);
2217 XULDocument::ContextStack::Push(nsXULPrototypeElement
* aPrototype
,
2218 nsIContent
* aElement
)
2220 Entry
* entry
= new Entry
;
2222 return NS_ERROR_OUT_OF_MEMORY
;
2224 entry
->mPrototype
= aPrototype
;
2225 entry
->mElement
= aElement
;
2226 NS_IF_ADDREF(entry
->mElement
);
2229 entry
->mNext
= mTop
;
2237 XULDocument::ContextStack::Pop()
2240 return NS_ERROR_UNEXPECTED
;
2242 Entry
* doomed
= mTop
;
2246 NS_IF_RELEASE(doomed
->mElement
);
2252 XULDocument::ContextStack::Peek(nsXULPrototypeElement
** aPrototype
,
2253 nsIContent
** aElement
,
2257 return NS_ERROR_UNEXPECTED
;
2259 *aPrototype
= mTop
->mPrototype
;
2260 *aElement
= mTop
->mElement
;
2261 NS_IF_ADDREF(*aElement
);
2262 *aIndex
= mTop
->mIndex
;
2269 XULDocument::ContextStack::SetTopIndex(int32_t aIndex
)
2272 return NS_ERROR_UNEXPECTED
;
2274 mTop
->mIndex
= aIndex
;
2279 //----------------------------------------------------------------------
2281 // Content model walking routines
2285 XULDocument::PrepareToWalk()
2287 // Prepare to walk the mCurrentPrototype
2290 // Keep an owning reference to the prototype document so that its
2291 // elements aren't yanked from beneath us.
2292 mPrototypes
.AppendElement(mCurrentPrototype
);
2294 // Get the prototype's root element and initialize the context
2295 // stack for the prototype walk.
2296 nsXULPrototypeElement
* proto
= mCurrentPrototype
->GetRootElement();
2300 if (PR_LOG_TEST(gXULLog
, PR_LOG_ERROR
)) {
2301 nsCOMPtr
<nsIURI
> url
= mCurrentPrototype
->GetURI();
2303 nsAutoCString urlspec
;
2304 rv
= url
->GetSpec(urlspec
);
2305 if (NS_FAILED(rv
)) return rv
;
2307 PR_LOG(gXULLog
, PR_LOG_ERROR
,
2308 ("xul: error parsing '%s'", urlspec
.get()));
2315 uint32_t piInsertionPoint
= 0;
2316 if (mState
!= eState_Master
) {
2317 int32_t indexOfRoot
= IndexOf(GetRootElement());
2318 NS_ASSERTION(indexOfRoot
>= 0,
2319 "No root content when preparing to walk overlay!");
2320 piInsertionPoint
= indexOfRoot
;
2323 const nsTArray
<nsRefPtr
<nsXULPrototypePI
> >& processingInstructions
=
2324 mCurrentPrototype
->GetProcessingInstructions();
2326 uint32_t total
= processingInstructions
.Length();
2327 for (uint32_t i
= 0; i
< total
; ++i
) {
2328 rv
= CreateAndInsertPI(processingInstructions
[i
],
2329 this, piInsertionPoint
+ i
);
2330 if (NS_FAILED(rv
)) return rv
;
2333 // Now check the chrome registry for any additional overlays.
2334 rv
= AddChromeOverlays();
2335 if (NS_FAILED(rv
)) return rv
;
2337 // Do one-time initialization if we're preparing to walk the
2338 // master document's prototype.
2339 nsRefPtr
<Element
> root
;
2341 if (mState
== eState_Master
) {
2342 // Add the root element
2343 rv
= CreateElementFromPrototype(proto
, getter_AddRefs(root
), true);
2344 if (NS_FAILED(rv
)) return rv
;
2346 rv
= AppendChildTo(root
, false);
2347 if (NS_FAILED(rv
)) return rv
;
2349 rv
= AddElementToRefMap(root
);
2350 if (NS_FAILED(rv
)) return rv
;
2352 // Block onload until we've finished building the complete
2353 // document content model.
2357 // There'd better not be anything on the context stack at this
2358 // point! This is the basis case for our "induction" in
2359 // ResumeWalk(), below, which'll assume that there's always a
2360 // content element on the context stack if either 1) we're in the
2361 // "master" document, or 2) we're in an overlay, and we've got
2362 // more than one prototype element (the single, root "overlay"
2363 // element) on the stack.
2364 NS_ASSERTION(mContextStack
.Depth() == 0, "something's on the context stack already");
2365 if (mContextStack
.Depth() != 0)
2366 return NS_ERROR_UNEXPECTED
;
2368 rv
= mContextStack
.Push(proto
, root
);
2369 if (NS_FAILED(rv
)) return rv
;
2375 XULDocument::CreateAndInsertPI(const nsXULPrototypePI
* aProtoPI
,
2376 nsINode
* aParent
, uint32_t aIndex
)
2378 NS_PRECONDITION(aProtoPI
, "null ptr");
2379 NS_PRECONDITION(aParent
, "null ptr");
2381 nsRefPtr
<ProcessingInstruction
> node
=
2382 NS_NewXMLProcessingInstruction(mNodeInfoManager
, aProtoPI
->mTarget
,
2386 if (aProtoPI
->mTarget
.EqualsLiteral("xml-stylesheet")) {
2387 rv
= InsertXMLStylesheetPI(aProtoPI
, aParent
, aIndex
, node
);
2388 } else if (aProtoPI
->mTarget
.EqualsLiteral("xul-overlay")) {
2389 rv
= InsertXULOverlayPI(aProtoPI
, aParent
, aIndex
, node
);
2391 // No special processing, just add the PI to the document.
2392 rv
= aParent
->InsertChildAt(node
, aIndex
, false);
2399 XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI
* aProtoPI
,
2402 nsIContent
* aPINode
)
2404 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle(do_QueryInterface(aPINode
));
2405 NS_ASSERTION(ssle
, "passed XML Stylesheet node does not "
2406 "implement nsIStyleSheetLinkingElement!");
2410 ssle
->InitStyleLinkElement(false);
2411 // We want to be notified when the style sheet finishes loading, so
2412 // disable style sheet loading for now.
2413 ssle
->SetEnableUpdates(false);
2414 ssle
->OverrideBaseURI(mCurrentPrototype
->GetURI());
2416 rv
= aParent
->InsertChildAt(aPINode
, aIndex
, false);
2417 if (NS_FAILED(rv
)) return rv
;
2419 ssle
->SetEnableUpdates(true);
2421 // load the stylesheet if necessary, passing ourselves as
2425 rv
= ssle
->UpdateStyleSheet(this, &willNotify
, &isAlternate
);
2426 if (NS_SUCCEEDED(rv
) && willNotify
&& !isAlternate
) {
2430 // Ignore errors from UpdateStyleSheet; we don't want failure to
2431 // do that to break the XUL document load. But do propagate out
2432 // NS_ERROR_OUT_OF_MEMORY.
2433 if (rv
== NS_ERROR_OUT_OF_MEMORY
) {
2441 XULDocument::InsertXULOverlayPI(const nsXULPrototypePI
* aProtoPI
,
2444 nsIContent
* aPINode
)
2448 rv
= aParent
->InsertChildAt(aPINode
, aIndex
, false);
2449 if (NS_FAILED(rv
)) return rv
;
2451 // xul-overlay PI is special only in prolog
2452 if (!nsContentUtils::InProlog(aPINode
)) {
2457 nsContentUtils::GetPseudoAttributeValue(aProtoPI
->mData
,
2461 // If there was no href, we can't do anything with this PI
2462 if (href
.IsEmpty()) {
2466 // Add the overlay to our list of overlays that need to be processed.
2467 nsCOMPtr
<nsIURI
> uri
;
2469 rv
= NS_NewURI(getter_AddRefs(uri
), href
, nullptr,
2470 mCurrentPrototype
->GetURI());
2471 if (NS_SUCCEEDED(rv
)) {
2472 // We insert overlays into mUnloadedOverlays at the same index in
2473 // document order, so they end up in the reverse of the document
2474 // order in mUnloadedOverlays.
2475 // This is needed because the code in ResumeWalk loads the overlays
2476 // by processing the last item of mUnloadedOverlays and removing it
2478 mUnloadedOverlays
.InsertElementAt(0, uri
);
2480 } else if (rv
== NS_ERROR_MALFORMED_URI
) {
2481 // The URL is bad, move along. Don't propagate for now.
2482 // XXX report this to the Error Console (bug 359846)
2490 XULDocument::AddChromeOverlays()
2494 nsCOMPtr
<nsIURI
> docUri
= mCurrentPrototype
->GetURI();
2496 /* overlays only apply to chrome or about URIs */
2497 if (!IsOverlayAllowed(docUri
)) return NS_OK
;
2499 nsCOMPtr
<nsIXULOverlayProvider
> chromeReg
=
2500 mozilla::services::GetXULOverlayProviderService();
2501 // In embedding situations, the chrome registry may not provide overlays,
2502 // or even exist at all; that's OK.
2503 NS_ENSURE_TRUE(chromeReg
, NS_OK
);
2505 nsCOMPtr
<nsISimpleEnumerator
> overlays
;
2506 rv
= chromeReg
->GetXULOverlays(docUri
, getter_AddRefs(overlays
));
2507 NS_ENSURE_SUCCESS(rv
, rv
);
2510 nsCOMPtr
<nsISupports
> next
;
2511 nsCOMPtr
<nsIURI
> uri
;
2513 while (NS_SUCCEEDED(rv
= overlays
->HasMoreElements(&moreOverlays
)) &&
2516 rv
= overlays
->GetNext(getter_AddRefs(next
));
2517 if (NS_FAILED(rv
) || !next
) break;
2519 uri
= do_QueryInterface(next
);
2521 NS_ERROR("Chrome registry handed me a non-nsIURI object!");
2525 // Same comment as in XULDocument::InsertXULOverlayPI
2526 mUnloadedOverlays
.InsertElementAt(0, uri
);
2533 XULDocument::LoadOverlay(const nsAString
& aURL
, nsIObserver
* aObserver
)
2537 nsCOMPtr
<nsIURI
> uri
;
2538 rv
= NS_NewURI(getter_AddRefs(uri
), aURL
, nullptr);
2539 if (NS_FAILED(rv
)) return rv
;
2542 nsIObserver
* obs
= nullptr;
2543 if (!mOverlayLoadObservers
) {
2544 mOverlayLoadObservers
= new nsInterfaceHashtable
<nsURIHashKey
,nsIObserver
>;
2546 obs
= mOverlayLoadObservers
->GetWeak(uri
);
2549 // We don't support loading the same overlay twice into the same
2550 // document - that doesn't make sense anyway.
2551 return NS_ERROR_FAILURE
;
2553 mOverlayLoadObservers
->Put(uri
, aObserver
);
2555 bool shouldReturn
, failureFromContent
;
2556 rv
= LoadOverlayInternal(uri
, true, &shouldReturn
, &failureFromContent
);
2557 if (NS_FAILED(rv
) && mOverlayLoadObservers
)
2558 mOverlayLoadObservers
->Remove(uri
); // remove the observer if LoadOverlayInternal generated an error
2563 XULDocument::LoadOverlayInternal(nsIURI
* aURI
, bool aIsDynamic
,
2564 bool* aShouldReturn
,
2565 bool* aFailureFromContent
)
2569 *aShouldReturn
= false;
2570 *aFailureFromContent
= false;
2573 if (PR_LOG_TEST(gXULLog
, PR_LOG_DEBUG
)) {
2574 nsAutoCString urlspec
;
2575 aURI
->GetSpec(urlspec
);
2576 nsAutoCString parentDoc
;
2577 nsCOMPtr
<nsIURI
> uri
;
2578 nsresult rv
= mChannel
->GetOriginalURI(getter_AddRefs(uri
));
2579 if (NS_SUCCEEDED(rv
))
2580 rv
= uri
->GetSpec(parentDoc
);
2581 if (!(parentDoc
.get()))
2584 PR_LOG(gXULLog
, PR_LOG_DEBUG
,
2585 ("xul: %s loading overlay %s", parentDoc
.get(), urlspec
.get()));
2590 mResolutionPhase
= nsForwardReference::eStart
;
2592 // Chrome documents are allowed to load overlays from anywhere.
2593 // In all other cases, the overlay is only allowed to load if
2594 // the master document and prototype document have the same origin.
2596 bool documentIsChrome
= IsChromeURI(mDocumentURI
);
2597 if (!documentIsChrome
) {
2598 // Make sure we're allowed to load this overlay.
2599 rv
= NodePrincipal()->CheckMayLoad(aURI
, true, false);
2600 if (NS_FAILED(rv
)) {
2601 *aFailureFromContent
= true;
2606 // Look in the prototype cache for the prototype document with
2607 // the specified overlay URI. Only use the cache if the containing
2608 // document is chrome otherwise it may not have a system principal and
2609 // the cached document will, see bug 565610.
2610 bool overlayIsChrome
= IsChromeURI(aURI
);
2611 mCurrentPrototype
= overlayIsChrome
&& documentIsChrome
?
2612 nsXULPrototypeCache::GetInstance()->GetPrototype(aURI
) : nullptr;
2614 // Same comment as nsChromeProtocolHandler::NewChannel and
2615 // XULDocument::StartDocumentLoad
2618 // We don't abort on failure here because there are too many valid
2619 // cases that can return failure, and the null-ness of |proto| is
2620 // enough to trigger the fail-safe parse-from-disk solution.
2621 // Example failure cases (for reference) include:
2623 // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
2625 // other: the FastLoad file, XUL.mfl, could not be found, probably
2626 // due to being accessed before a profile has been selected
2627 // (e.g. loading chrome for the profile manager itself).
2628 // The .xul file must be parsed from disk.
2630 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
2631 if (useXULCache
&& mCurrentPrototype
) {
2633 rv
= mCurrentPrototype
->AwaitLoadDone(this, &loaded
);
2634 if (NS_FAILED(rv
)) return rv
;
2637 // Return to the main event loop and eagerly await the
2638 // prototype overlay load's completion. When the content
2639 // sink completes, it will trigger an EndLoad(), which'll
2640 // wind us back up here, in ResumeWalk().
2641 *aShouldReturn
= true;
2645 PR_LOG(gXULLog
, PR_LOG_DEBUG
, ("xul: overlay was cached"));
2647 // Found the overlay's prototype in the cache, fully loaded. If
2648 // this is a dynamic overlay, this will call ResumeWalk.
2649 // Otherwise, we'll return to ResumeWalk, which called us.
2650 return OnPrototypeLoadDone(aIsDynamic
);
2653 // Not there. Initiate a load.
2654 PR_LOG(gXULLog
, PR_LOG_DEBUG
, ("xul: overlay was not cached"));
2657 PR_LOG(gXULLog
, PR_LOG_DEBUG
, ("xul: ...and document already destroyed"));
2658 return NS_ERROR_NOT_AVAILABLE
;
2661 // We'll set the right principal on the proto doc when we get
2662 // OnStartRequest from the parser, so just pass in a null principal for
2664 nsCOMPtr
<nsIParser
> parser
;
2665 rv
= PrepareToLoadPrototype(aURI
, "view", nullptr, getter_AddRefs(parser
));
2666 if (NS_FAILED(rv
)) return rv
;
2668 // Predicate mIsWritingFastLoad on the XUL cache being enabled,
2669 // so we don't have to re-check whether the cache is enabled all
2671 mIsWritingFastLoad
= useXULCache
;
2673 nsCOMPtr
<nsIStreamListener
> listener
= do_QueryInterface(parser
);
2675 return NS_ERROR_UNEXPECTED
;
2677 // Add an observer to the parser; this'll get called when
2678 // Necko fires its On[Start|Stop]Request() notifications,
2679 // and will let us recover from a missing overlay.
2680 ParserObserver
* parserObserver
=
2681 new ParserObserver(this, mCurrentPrototype
);
2682 if (! parserObserver
)
2683 return NS_ERROR_OUT_OF_MEMORY
;
2685 NS_ADDREF(parserObserver
);
2686 parser
->Parse(aURI
, parserObserver
);
2687 NS_RELEASE(parserObserver
);
2689 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
2690 nsCOMPtr
<nsIChannel
> channel
;
2691 // Set the owner of the channel to be our principal so
2692 // that the overlay's JSObjects etc end up being created
2693 // with the right principal and in the correct
2695 rv
= NS_NewChannel(getter_AddRefs(channel
),
2698 nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL
,
2699 nsIContentPolicy::TYPE_OTHER
,
2702 if (NS_SUCCEEDED(rv
)) {
2703 rv
= channel
->AsyncOpen(listener
, nullptr);
2706 if (NS_FAILED(rv
)) {
2707 // Abandon this prototype
2708 mCurrentPrototype
= nullptr;
2710 // The parser won't get an OnStartRequest and
2711 // OnStopRequest, so it needs a Terminate.
2712 parser
->Terminate();
2714 // Just move on to the next overlay.
2715 ReportMissingOverlay(aURI
);
2717 // XXX the error could indicate an internal error as well...
2718 *aFailureFromContent
= true;
2722 // If it's a 'chrome:' prototype document, then put it into
2723 // the prototype cache; other XUL documents will be reloaded
2724 // each time. We must do this after AsyncOpen,
2725 // or chrome code will wrongly create a cached chrome channel
2726 // instead of a real one. Prototypes are only cached when the
2727 // document to be overlayed is chrome to avoid caching overlay
2728 // scripts with incorrect principals, see bug 565610.
2729 if (useXULCache
&& overlayIsChrome
&& documentIsChrome
) {
2730 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype
);
2733 // Return to the main event loop and eagerly await the
2734 // overlay load's completion. When the content sink
2735 // completes, it will trigger an EndLoad(), which'll wind
2736 // us back in ResumeWalk().
2738 *aShouldReturn
= true;
2743 static PLDHashOperator
2744 FirePendingMergeNotification(nsIURI
* aKey
, nsCOMPtr
<nsIObserver
>& aObserver
, void* aClosure
)
2746 aObserver
->Observe(aKey
, "xul-overlay-merged", EmptyString().get());
2748 typedef nsInterfaceHashtable
<nsURIHashKey
,nsIObserver
> table
;
2749 table
* observers
= static_cast<table
*>(aClosure
);
2751 observers
->Remove(aKey
);
2754 return PL_DHASH_REMOVE
;
2758 XULDocument::ResumeWalk()
2760 // Walk the prototype and build the delegate content model. The
2761 // walk is performed in a top-down, left-to-right fashion. That
2762 // is, a parent is built before any of its children; a node is
2763 // only built after all of its siblings to the left are fully
2766 // It is interruptable so that transcluded documents (e.g.,
2767 // <html:script src="..." />) can be properly re-loaded if the
2768 // cached copy of the document becomes stale.
2770 nsCOMPtr
<nsIURI
> overlayURI
=
2771 mCurrentPrototype
? mCurrentPrototype
->GetURI() : nullptr;
2774 // Begin (or resume) walking the current prototype.
2776 while (mContextStack
.Depth() > 0) {
2777 // Look at the top of the stack to determine what we're
2778 // currently working on.
2779 // This will always be a node already constructed and
2780 // inserted to the actual document.
2781 nsXULPrototypeElement
* proto
;
2782 nsCOMPtr
<nsIContent
> element
;
2783 int32_t indx
; // all children of proto before indx (not
2784 // inclusive) have already been constructed
2785 rv
= mContextStack
.Peek(&proto
, getter_AddRefs(element
), &indx
);
2786 if (NS_FAILED(rv
)) return rv
;
2788 if (indx
>= (int32_t)proto
->mChildren
.Length()) {
2790 // We've processed all of the prototype's children. If
2791 // we're in the master prototype, do post-order
2792 // document-level hookup. (An overlay will get its
2793 // document hookup done when it's successfully
2795 if (mState
== eState_Master
) {
2796 AddElementToDocumentPost(element
->AsElement());
2798 if (element
->NodeInfo()->Equals(nsGkAtoms::style
,
2799 kNameSpaceID_XHTML
) ||
2800 element
->NodeInfo()->Equals(nsGkAtoms::style
,
2801 kNameSpaceID_SVG
)) {
2802 // XXX sucks that we have to do this -
2804 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle
=
2805 do_QueryInterface(element
);
2806 NS_ASSERTION(ssle
, "<html:style> doesn't implement "
2807 "nsIStyleSheetLinkingElement?");
2810 ssle
->UpdateStyleSheet(nullptr, &willNotify
,
2815 // Now pop the context stack back up to the parent
2816 // element and continue the prototype walk.
2817 mContextStack
.Pop();
2821 // Grab the next child, and advance the current context stack
2822 // to the next sibling to our right.
2823 nsXULPrototypeNode
* childproto
= proto
->mChildren
[indx
];
2824 mContextStack
.SetTopIndex(++indx
);
2826 // Whether we're in the "first ply" of an overlay:
2827 // the "hookup" nodes. In the case !processingOverlayHookupNodes,
2828 // we're in the master document -or- we're in an overlay, and far
2829 // enough down into the overlay's content that we can simply build
2830 // the delegates and attach them to the parent node.
2831 bool processingOverlayHookupNodes
= (mState
== eState_Overlay
) &&
2832 (mContextStack
.Depth() == 1);
2834 NS_ASSERTION(element
|| processingOverlayHookupNodes
,
2835 "no element on context stack");
2837 switch (childproto
->mType
) {
2838 case nsXULPrototypeNode::eType_Element
: {
2839 // An 'element', which may contain more content.
2840 nsXULPrototypeElement
* protoele
=
2841 static_cast<nsXULPrototypeElement
*>(childproto
);
2843 nsRefPtr
<Element
> child
;
2845 if (!processingOverlayHookupNodes
) {
2846 rv
= CreateElementFromPrototype(protoele
,
2847 getter_AddRefs(child
),
2849 if (NS_FAILED(rv
)) return rv
;
2851 // ...and append it to the content model.
2852 rv
= element
->AppendChildTo(child
, false);
2853 if (NS_FAILED(rv
)) return rv
;
2855 // If we're only restoring persisted things on
2856 // some elements, store the ID here to do that.
2857 if (mRestrictPersistence
) {
2858 nsIAtom
* id
= child
->GetID();
2860 mPersistenceIds
.PutEntry(nsDependentAtomString(id
));
2864 // do pre-order document-level hookup, but only if
2865 // we're in the master document. For an overlay,
2866 // this will happen when the overlay is
2867 // successfully resolved.
2868 if (mState
== eState_Master
)
2869 AddElementToDocumentPre(child
);
2872 // We're in the "first ply" of an overlay: the
2873 // "hookup" nodes. Create an 'overlay' element so
2874 // that we can continue to build content, and
2875 // enter a forward reference so we can hook it up
2877 rv
= CreateOverlayElement(protoele
, getter_AddRefs(child
));
2878 if (NS_FAILED(rv
)) return rv
;
2881 // If it has children, push the element onto the context
2882 // stack and begin to process them.
2883 if (protoele
->mChildren
.Length() > 0) {
2884 rv
= mContextStack
.Push(protoele
, child
);
2885 if (NS_FAILED(rv
)) return rv
;
2888 if (mState
== eState_Master
) {
2889 // If there are no children, and we're in the
2890 // master document, do post-order document hookup
2892 AddElementToDocumentPost(child
);
2898 case nsXULPrototypeNode::eType_Script
: {
2899 // A script reference. Execute the script immediately;
2900 // this may have side effects in the content model.
2901 nsXULPrototypeScript
* scriptproto
=
2902 static_cast<nsXULPrototypeScript
*>(childproto
);
2904 if (scriptproto
->mSrcURI
) {
2905 // A transcluded script reference; this may
2906 // "block" our prototype walk if the script isn't
2907 // cached, or the cached copy of the script is
2908 // stale and must be reloaded.
2910 rv
= LoadScript(scriptproto
, &blocked
);
2911 // If the script cannot be loaded, just keep going!
2913 if (NS_SUCCEEDED(rv
) && blocked
)
2916 else if (scriptproto
->GetScriptObject()) {
2918 rv
= ExecuteScript(scriptproto
);
2919 if (NS_FAILED(rv
)) return rv
;
2924 case nsXULPrototypeNode::eType_Text
: {
2925 // A simple text node.
2927 if (!processingOverlayHookupNodes
) {
2928 // This does mean that text nodes that are direct children
2929 // of <overlay> get ignored.
2931 nsRefPtr
<nsTextNode
> text
=
2932 new nsTextNode(mNodeInfoManager
);
2934 nsXULPrototypeText
* textproto
=
2935 static_cast<nsXULPrototypeText
*>(childproto
);
2936 text
->SetText(textproto
->mValue
, false);
2938 rv
= element
->AppendChildTo(text
, false);
2939 NS_ENSURE_SUCCESS(rv
, rv
);
2944 case nsXULPrototypeNode::eType_PI
: {
2945 nsXULPrototypePI
* piProto
=
2946 static_cast<nsXULPrototypePI
*>(childproto
);
2948 // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
2949 // outside the prolog, like they used to. Issue a warning.
2951 if (piProto
->mTarget
.EqualsLiteral("xml-stylesheet") ||
2952 piProto
->mTarget
.EqualsLiteral("xul-overlay")) {
2954 const char16_t
* params
[] = { piProto
->mTarget
.get() };
2956 nsContentUtils::ReportToConsole(
2957 nsIScriptError::warningFlag
,
2958 NS_LITERAL_CSTRING("XUL Document"), nullptr,
2959 nsContentUtils::eXUL_PROPERTIES
,
2961 params
, ArrayLength(params
),
2965 nsIContent
* parent
= processingOverlayHookupNodes
?
2966 GetRootElement() : element
.get();
2969 // an inline script could have removed the root element
2970 rv
= CreateAndInsertPI(piProto
, parent
,
2971 parent
->GetChildCount());
2972 NS_ENSURE_SUCCESS(rv
, rv
);
2978 NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
2982 // Once we get here, the context stack will have been
2983 // depleted. That means that the entire prototype has been
2984 // walked and content has been constructed.
2986 // If we're not already, mark us as now processing overlays.
2987 mState
= eState_Overlay
;
2989 // If there are no overlay URIs, then we're done.
2990 uint32_t count
= mUnloadedOverlays
.Length();
2994 nsCOMPtr
<nsIURI
> uri
= mUnloadedOverlays
[count
-1];
2995 mUnloadedOverlays
.RemoveElementAt(count
- 1);
2997 bool shouldReturn
, failureFromContent
;
2998 rv
= LoadOverlayInternal(uri
, false, &shouldReturn
,
2999 &failureFromContent
);
3000 if (failureFromContent
)
3001 // The failure |rv| was the result of a problem in the content
3002 // rather than an unexpected problem in our implementation, so
3003 // just continue with the next overlay.
3007 if (mOverlayLoadObservers
) {
3008 nsIObserver
*obs
= mOverlayLoadObservers
->GetWeak(overlayURI
);
3010 // This overlay has an unloaded overlay, so it will never
3011 // notify. The best we can do is to notify for the unloaded
3012 // overlay instead, assuming nobody is already notifiable
3013 // for it. Note that this will confuse the observer.
3014 if (!mOverlayLoadObservers
->GetWeak(uri
))
3015 mOverlayLoadObservers
->Put(uri
, obs
);
3016 mOverlayLoadObservers
->Remove(overlayURI
);
3021 overlayURI
.swap(uri
);
3024 // If we get here, there is nothing left for us to walk. The content
3025 // model is built and ready for layout.
3026 rv
= ResolveForwardReferences();
3027 if (NS_FAILED(rv
)) return rv
;
3029 ApplyPersistentAttributes();
3031 mStillWalking
= false;
3032 if (mPendingSheets
== 0) {
3039 XULDocument::DoneWalking()
3041 NS_PRECONDITION(mPendingSheets
== 0, "there are sheets to be loaded");
3042 NS_PRECONDITION(!mStillWalking
, "walk not done");
3044 // XXXldb This is where we should really be setting the chromehidden
3047 uint32_t count
= mOverlaySheets
.Length();
3048 for (uint32_t i
= 0; i
< count
; ++i
) {
3049 AddStyleSheet(mOverlaySheets
[i
]);
3051 mOverlaySheets
.Clear();
3053 if (!mDocumentLoaded
) {
3054 // Make sure we don't reenter here from StartLayout(). Note that
3055 // setting mDocumentLoaded to true here means that if StartLayout()
3056 // causes ResumeWalk() to be reentered, we'll take the other branch of
3057 // the |if (!mDocumentLoaded)| check above and since
3058 // mInitialLayoutComplete will be false will follow the else branch
3059 // there too. See the big comment there for how such reentry can
3061 mDocumentLoaded
= true;
3063 NotifyPossibleTitleChange(false);
3065 // Before starting layout, check whether we're a toplevel chrome
3066 // window. If we are, set our chrome flags now, so that we don't have
3067 // to restyle the whole frame tree after StartLayout.
3068 nsCOMPtr
<nsIDocShellTreeItem
> item
= GetDocShell();
3070 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
3071 item
->GetTreeOwner(getter_AddRefs(owner
));
3072 nsCOMPtr
<nsIXULWindow
> xulWin
= do_GetInterface(owner
);
3074 nsCOMPtr
<nsIDocShell
> xulWinShell
;
3075 xulWin
->GetDocShell(getter_AddRefs(xulWinShell
));
3076 if (SameCOMIdentity(xulWinShell
, item
)) {
3077 // We're the chrome document! Apply our chrome flags now.
3078 xulWin
->ApplyChromeFlags();
3085 if (mIsWritingFastLoad
&& IsChromeURI(mDocumentURI
))
3086 nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype
);
3088 NS_ASSERTION(mDelayFrameLoaderInitialization
,
3089 "mDelayFrameLoaderInitialization should be true!");
3090 mDelayFrameLoaderInitialization
= false;
3091 NS_WARN_IF_FALSE(mUpdateNestLevel
== 0,
3092 "Constructing XUL document in middle of an update?");
3093 if (mUpdateNestLevel
== 0) {
3094 MaybeInitializeFinalizeFrameLoaders();
3097 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad
, (this));
3099 // DispatchContentLoadedEvents undoes the onload-blocking we
3100 // did in PrepareToWalk().
3101 DispatchContentLoadedEvents();
3103 mInitialLayoutComplete
= true;
3105 // Walk the set of pending load notifications and notify any observers.
3106 // See below for detail.
3107 if (mPendingOverlayLoadNotifications
)
3108 mPendingOverlayLoadNotifications
->Enumerate(
3109 FirePendingMergeNotification
, mOverlayLoadObservers
.get());
3112 if (mOverlayLoadObservers
) {
3113 nsCOMPtr
<nsIURI
> overlayURI
= mCurrentPrototype
->GetURI();
3114 nsCOMPtr
<nsIObserver
> obs
;
3115 if (mInitialLayoutComplete
) {
3116 // We have completed initial layout, so just send the notification.
3117 mOverlayLoadObservers
->Get(overlayURI
, getter_AddRefs(obs
));
3119 obs
->Observe(overlayURI
, "xul-overlay-merged", EmptyString().get());
3120 mOverlayLoadObservers
->Remove(overlayURI
);
3123 // If we have not yet displayed the document for the first time
3124 // (i.e. we came in here as the result of a dynamic overlay load
3125 // which was spawned by a binding-attached event caused by
3126 // StartLayout() on the master prototype - we must remember that
3127 // this overlay has been merged and tell the listeners after
3128 // StartLayout() is completely finished rather than doing so
3129 // immediately - otherwise we may be executing code that needs to
3130 // access XBL Binding implementations on nodes for which frames
3131 // have not yet been constructed because their bindings have not
3132 // yet been attached. This can be a race condition because dynamic
3133 // overlay loading can take varying amounts of time depending on
3134 // whether or not the overlay prototype is in the XUL cache. The
3135 // most likely effect of this bug is odd UI initialization due to
3136 // methods and properties that do not work.
3137 // XXXbz really, we shouldn't be firing binding constructors
3138 // until after StartLayout returns!
3140 if (!mPendingOverlayLoadNotifications
) {
3141 mPendingOverlayLoadNotifications
=
3142 new nsInterfaceHashtable
<nsURIHashKey
,nsIObserver
>;
3145 mPendingOverlayLoadNotifications
->Get(overlayURI
, getter_AddRefs(obs
));
3147 mOverlayLoadObservers
->Get(overlayURI
, getter_AddRefs(obs
));
3148 NS_ASSERTION(obs
, "null overlay load observer?");
3149 mPendingOverlayLoadNotifications
->Put(overlayURI
, obs
);
3159 XULDocument::StyleSheetLoaded(CSSStyleSheet
* aSheet
,
3163 if (!aWasAlternate
) {
3164 // Don't care about when alternate sheets finish loading
3166 NS_ASSERTION(mPendingSheets
> 0,
3167 "Unexpected StyleSheetLoaded notification");
3171 if (!mStillWalking
&& mPendingSheets
== 0) {
3172 return DoneWalking();
3180 XULDocument::MaybeBroadcast()
3182 // Only broadcast when not in an update and when safe to run scripts.
3183 if (mUpdateNestLevel
== 0 &&
3184 (mDelayedAttrChangeBroadcasts
.Length() ||
3185 mDelayedBroadcasters
.Length())) {
3186 if (!nsContentUtils::IsSafeToRunScript()) {
3187 if (!mInDestructor
) {
3188 nsContentUtils::AddScriptRunner(
3189 NS_NewRunnableMethod(this, &XULDocument::MaybeBroadcast
));
3193 if (!mHandlingDelayedAttrChange
) {
3194 mHandlingDelayedAttrChange
= true;
3195 for (uint32_t i
= 0; i
< mDelayedAttrChangeBroadcasts
.Length(); ++i
) {
3196 nsIAtom
* attrName
= mDelayedAttrChangeBroadcasts
[i
].mAttrName
;
3197 if (mDelayedAttrChangeBroadcasts
[i
].mNeedsAttrChange
) {
3198 nsCOMPtr
<nsIContent
> listener
=
3199 do_QueryInterface(mDelayedAttrChangeBroadcasts
[i
].mListener
);
3200 nsString value
= mDelayedAttrChangeBroadcasts
[i
].mAttr
;
3201 if (mDelayedAttrChangeBroadcasts
[i
].mSetAttr
) {
3202 listener
->SetAttr(kNameSpaceID_None
, attrName
, value
,
3205 listener
->UnsetAttr(kNameSpaceID_None
, attrName
,
3209 ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts
[i
].mBroadcaster
,
3210 mDelayedAttrChangeBroadcasts
[i
].mListener
,
3213 mDelayedAttrChangeBroadcasts
.Clear();
3214 mHandlingDelayedAttrChange
= false;
3217 uint32_t length
= mDelayedBroadcasters
.Length();
3219 bool oldValue
= mHandlingDelayedBroadcasters
;
3220 mHandlingDelayedBroadcasters
= true;
3221 nsTArray
<nsDelayedBroadcastUpdate
> delayedBroadcasters
;
3222 mDelayedBroadcasters
.SwapElements(delayedBroadcasters
);
3223 for (uint32_t i
= 0; i
< length
; ++i
) {
3224 SynchronizeBroadcastListener(delayedBroadcasters
[i
].mBroadcaster
,
3225 delayedBroadcasters
[i
].mListener
,
3226 delayedBroadcasters
[i
].mAttr
);
3228 mHandlingDelayedBroadcasters
= oldValue
;
3234 XULDocument::EndUpdate(nsUpdateType aUpdateType
)
3236 XMLDocument::EndUpdate(aUpdateType
);
3242 XULDocument::ReportMissingOverlay(nsIURI
* aURI
)
3244 NS_PRECONDITION(aURI
, "Must have a URI");
3247 aURI
->GetSpec(spec
);
3249 NS_ConvertUTF8toUTF16
utfSpec(spec
);
3250 const char16_t
* params
[] = { utfSpec
.get() };
3251 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3252 NS_LITERAL_CSTRING("XUL Document"), this,
3253 nsContentUtils::eXUL_PROPERTIES
,
3255 params
, ArrayLength(params
));
3259 XULDocument::LoadScript(nsXULPrototypeScript
* aScriptProto
, bool* aBlock
)
3261 // Load a transcluded script
3264 bool isChromeDoc
= IsChromeURI(mDocumentURI
);
3266 if (isChromeDoc
&& aScriptProto
->GetScriptObject()) {
3267 rv
= ExecuteScript(aScriptProto
);
3269 // Ignore return value from execution, and don't block
3274 // Try the XUL script cache, in case two XUL documents source the same
3275 // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
3276 // XXXbe the cache relies on aScriptProto's GC root!
3277 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
3279 if (isChromeDoc
&& useXULCache
) {
3280 JSScript
* newScriptObject
=
3281 nsXULPrototypeCache::GetInstance()->GetScript(
3282 aScriptProto
->mSrcURI
);
3283 if (newScriptObject
) {
3284 // The script language for a proto must remain constant - we
3285 // can't just change it for this unexpected language.
3286 aScriptProto
->Set(newScriptObject
);
3289 if (aScriptProto
->GetScriptObject()) {
3290 rv
= ExecuteScript(aScriptProto
);
3292 // Ignore return value from execution, and don't block
3298 // Allow security manager and content policies to veto the load. Note that
3299 // at this point we already lost context information of the script.
3300 rv
= nsScriptLoader::ShouldLoadScript(
3302 static_cast<nsIDocument
*>(this),
3303 aScriptProto
->mSrcURI
,
3304 NS_LITERAL_STRING("application/x-javascript"));
3305 if (NS_FAILED(rv
)) {
3310 // Release script objects from FastLoad since we decided against using them
3311 aScriptProto
->UnlinkJSObjects();
3313 // Set the current script prototype so that OnStreamComplete can report
3314 // the right file if there are errors in the script.
3315 NS_ASSERTION(!mCurrentScriptProto
,
3316 "still loading a script when starting another load?");
3317 mCurrentScriptProto
= aScriptProto
;
3319 if (aScriptProto
->mSrcLoading
) {
3320 // Another XULDocument load has started, which is still in progress.
3321 // Remember to ResumeWalk this document when the load completes.
3322 mNextSrcLoadWaiter
= aScriptProto
->mSrcLoadWaiters
;
3323 aScriptProto
->mSrcLoadWaiters
= this;
3327 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
3329 // Note: the loader will keep itself alive while it's loading.
3330 nsCOMPtr
<nsIStreamLoader
> loader
;
3331 rv
= NS_NewStreamLoader(getter_AddRefs(loader
),
3332 aScriptProto
->mSrcURI
,
3334 this, // aRequestingContext
3335 nsILoadInfo::SEC_NORMAL
,
3336 nsIContentPolicy::TYPE_OTHER
,
3337 nullptr, // aContext
3340 if (NS_FAILED(rv
)) {
3341 mCurrentScriptProto
= nullptr;
3345 aScriptProto
->mSrcLoading
= true;
3348 // Block until OnStreamComplete resumes us.
3354 XULDocument::OnStreamComplete(nsIStreamLoader
* aLoader
,
3355 nsISupports
* context
,
3358 const uint8_t* string
)
3360 nsCOMPtr
<nsIRequest
> request
;
3361 aLoader
->GetRequest(getter_AddRefs(request
));
3362 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
3365 // print a load error on bad status
3366 if (NS_FAILED(aStatus
)) {
3368 nsCOMPtr
<nsIURI
> uri
;
3369 channel
->GetURI(getter_AddRefs(uri
));
3371 nsAutoCString uriSpec
;
3372 uri
->GetSpec(uriSpec
);
3373 printf("Failed to load %s\n", uriSpec
.get());
3379 // This is the completion routine that will be called when a
3380 // transcluded script completes. Compile and execute the script
3381 // if the load was successful, then continue building content
3382 // from the prototype.
3383 nsresult rv
= aStatus
;
3385 NS_ASSERTION(mCurrentScriptProto
&& mCurrentScriptProto
->mSrcLoading
,
3386 "script source not loading on unichar stream complete?");
3387 if (!mCurrentScriptProto
) {
3388 // XXX Wallpaper for bug 270042
3392 if (NS_SUCCEEDED(aStatus
)) {
3393 // If the including XUL document is a FastLoad document, and we're
3394 // compiling an out-of-line script (one with src=...), then we must
3395 // be writing a new FastLoad file. If we were reading this script
3396 // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
3397 // nsXULContentSink.cpp) would have already deserialized a non-null
3398 // script->mScriptObject, causing control flow at the top of LoadScript
3399 // not to reach here.
3400 nsCOMPtr
<nsIURI
> uri
= mCurrentScriptProto
->mSrcURI
;
3402 // XXX should also check nsIHttpChannel::requestSucceeded
3404 MOZ_ASSERT(!mOffThreadCompiling
&& (mOffThreadCompileStringLength
== 0 &&
3405 !mOffThreadCompileStringBuf
),
3406 "XULDocument can't load multiple scripts at once");
3408 rv
= nsScriptLoader::ConvertToUTF16(channel
, string
, stringLen
,
3409 EmptyString(), this,
3410 mOffThreadCompileStringBuf
,
3411 mOffThreadCompileStringLength
);
3412 if (NS_SUCCEEDED(rv
)) {
3413 // Attempt to give ownership of the buffer to the JS engine. If
3414 // we hit offthread compilation, however, we will have to take it
3415 // back below in order to keep the memory alive until compilation
3417 JS::SourceBufferHolder
srcBuf(mOffThreadCompileStringBuf
,
3418 mOffThreadCompileStringLength
,
3419 JS::SourceBufferHolder::GiveOwnership
);
3420 mOffThreadCompileStringBuf
= nullptr;
3421 mOffThreadCompileStringLength
= 0;
3423 rv
= mCurrentScriptProto
->Compile(srcBuf
, uri
, 1, this, this);
3424 if (NS_SUCCEEDED(rv
) && !mCurrentScriptProto
->GetScriptObject()) {
3425 // We will be notified via OnOffThreadCompileComplete when the
3426 // compile finishes. Keep the contents of the compiled script
3427 // alive until the compilation finishes.
3428 mOffThreadCompiling
= true;
3429 // If the JS engine did not take the source buffer, then take
3430 // it back here to ensure it remains alive.
3431 mOffThreadCompileStringBuf
= srcBuf
.take();
3432 if (mOffThreadCompileStringBuf
) {
3433 mOffThreadCompileStringLength
= srcBuf
.length();
3441 return OnScriptCompileComplete(mCurrentScriptProto
->GetScriptObject(), rv
);
3445 XULDocument::OnScriptCompileComplete(JSScript
* aScript
, nsresult aStatus
)
3447 // When compiling off thread the script will not have been attached to the
3448 // script proto yet.
3449 if (aScript
&& !mCurrentScriptProto
->GetScriptObject())
3450 mCurrentScriptProto
->Set(aScript
);
3452 // Allow load events to be fired once off thread compilation finishes.
3453 if (mOffThreadCompiling
) {
3454 mOffThreadCompiling
= false;
3455 UnblockOnload(false);
3458 // After compilation finishes the script's characters are no longer needed.
3459 if (mOffThreadCompileStringBuf
) {
3460 js_free(mOffThreadCompileStringBuf
);
3461 mOffThreadCompileStringBuf
= nullptr;
3462 mOffThreadCompileStringLength
= 0;
3465 // Clear mCurrentScriptProto now, but save it first for use below in
3466 // the execute code, and in the while loop that resumes walks of other
3467 // documents that raced to load this script.
3468 nsXULPrototypeScript
* scriptProto
= mCurrentScriptProto
;
3469 mCurrentScriptProto
= nullptr;
3471 // Clear the prototype's loading flag before executing the script or
3472 // resuming document walks, in case any of those control flows starts a
3474 scriptProto
->mSrcLoading
= false;
3476 nsresult rv
= aStatus
;
3477 if (NS_SUCCEEDED(rv
)) {
3478 rv
= ExecuteScript(scriptProto
);
3480 // If the XUL cache is enabled, save the script object there in
3481 // case different XUL documents source the same script.
3483 // But don't save the script in the cache unless the master XUL
3484 // document URL is a chrome: URL. It is valid for a URL such as
3485 // about:config to translate into a master document URL, whose
3486 // prototype document nodes -- including prototype scripts that
3487 // hold GC roots protecting their mJSObject pointers -- are not
3488 // cached in the XUL prototype cache. See StartDocumentLoad,
3489 // the fillXULCache logic.
3491 // A document such as about:config is free to load a script via
3492 // a URL such as chrome://global/content/config.js, and we must
3493 // not cache that script object without a prototype cache entry
3494 // containing a companion nsXULPrototypeScript node that owns a
3495 // GC root protecting the script object. Otherwise, the script
3496 // cache entry will dangle once the uncached prototype document
3497 // is released when its owning XULDocument is unloaded.
3499 // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
3500 // the true crime story.)
3501 bool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
3503 if (useXULCache
&& IsChromeURI(mDocumentURI
) && scriptProto
->GetScriptObject()) {
3504 nsXULPrototypeCache::GetInstance()->PutScript(
3505 scriptProto
->mSrcURI
,
3506 scriptProto
->GetScriptObject());
3509 if (mIsWritingFastLoad
&& mCurrentPrototype
!= mMasterPrototype
) {
3510 // If we are loading an overlay script, try to serialize
3511 // it to the FastLoad file here. Master scripts will be
3512 // serialized when the master prototype document gets
3513 // written, at the bottom of ResumeWalk. That way, master
3514 // out-of-line scripts are serialized in the same order that
3515 // they'll be read, in the FastLoad file, which reduces the
3516 // number of seeks that dump the underlying stream's buffer.
3518 // Ignore the return value, as we don't need to propagate
3519 // a failure to write to the FastLoad file, because this
3520 // method aborts that whole process on error.
3521 scriptProto
->SerializeOutOfLine(nullptr, mCurrentPrototype
);
3523 // ignore any evaluation errors
3528 // Load a pointer to the prototype-script's list of XULDocuments who
3529 // raced to load the same script
3530 XULDocument
** docp
= &scriptProto
->mSrcLoadWaiters
;
3532 // Resume walking other documents that waited for this one's load, first
3533 // executing the script we just compiled, in each doc's script context
3535 while ((doc
= *docp
) != nullptr) {
3536 NS_ASSERTION(doc
->mCurrentScriptProto
== scriptProto
,
3537 "waiting for wrong script to load?");
3538 doc
->mCurrentScriptProto
= nullptr;
3540 // Unlink doc from scriptProto's list before executing and resuming
3541 *docp
= doc
->mNextSrcLoadWaiter
;
3542 doc
->mNextSrcLoadWaiter
= nullptr;
3544 // Execute only if we loaded and compiled successfully, then resume
3545 if (NS_SUCCEEDED(aStatus
) && scriptProto
->GetScriptObject()) {
3546 doc
->ExecuteScript(scriptProto
);
3556 XULDocument::ExecuteScript(nsXULPrototypeScript
*aScript
)
3558 NS_PRECONDITION(aScript
!= nullptr, "null ptr");
3559 NS_ENSURE_TRUE(aScript
, NS_ERROR_NULL_POINTER
);
3560 NS_ENSURE_TRUE(mScriptGlobalObject
, NS_ERROR_NOT_INITIALIZED
);
3563 rv
= mScriptGlobalObject
->EnsureScriptEnvironment();
3564 NS_ENSURE_SUCCESS(rv
, rv
);
3566 JS::HandleScript scriptObject
= aScript
->GetScriptObject();
3567 NS_ENSURE_TRUE(scriptObject
, NS_ERROR_UNEXPECTED
);
3569 // Execute the precompiled script with the given version
3572 // We're about to run script via JS::CloneAndExecuteScript, so we need an
3573 // AutoEntryScript. This is Gecko specific and not in any spec.
3574 AutoEntryScript
aes(mScriptGlobalObject
);
3575 aes
.TakeOwnershipOfErrorReporting();
3576 JSContext
* cx
= aes
.cx();
3577 JS::Rooted
<JSObject
*> baseGlobal(cx
, JS::CurrentGlobalOrNull(cx
));
3578 NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(baseGlobal
), NS_OK
);
3580 JSAddonId
* addonId
= mCurrentPrototype
? MapURIToAddonID(mCurrentPrototype
->GetURI()) : nullptr;
3581 JS::Rooted
<JSObject
*> global(cx
, xpc::GetAddonScope(cx
, baseGlobal
, addonId
));
3582 NS_ENSURE_TRUE(global
, NS_ERROR_FAILURE
);
3584 JS::ExposeObjectToActiveJS(global
);
3585 xpc_UnmarkGrayScript(scriptObject
);
3586 JSAutoCompartment
ac(cx
, global
);
3588 // The script is in the compilation scope. Clone it into the target scope
3589 // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
3590 // there is no need to manually check the return value.
3591 JS::CloneAndExecuteScript(cx
, global
, scriptObject
);
3598 XULDocument::CreateElementFromPrototype(nsXULPrototypeElement
* aPrototype
,
3602 // Create a content model element from a prototype element.
3603 NS_PRECONDITION(aPrototype
!= nullptr, "null ptr");
3605 return NS_ERROR_NULL_POINTER
;
3608 nsresult rv
= NS_OK
;
3611 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
3612 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
3613 ("xul: creating <%s> from prototype",
3614 NS_ConvertUTF16toUTF8(aPrototype
->mNodeInfo
->QualifiedName()).get()));
3618 nsRefPtr
<Element
> result
;
3620 if (aPrototype
->mNodeInfo
->NamespaceEquals(kNameSpaceID_XUL
)) {
3621 // If it's a XUL element, it'll be lightweight until somebody
3623 rv
= nsXULElement::Create(aPrototype
, this, true, aIsRoot
, getter_AddRefs(result
));
3624 if (NS_FAILED(rv
)) return rv
;
3627 // If it's not a XUL element, it's gonna be heavyweight no matter
3628 // what. So we need to copy everything out of the prototype
3629 // into the element. Get a nodeinfo from our nodeinfo manager
3631 nsRefPtr
<mozilla::dom::NodeInfo
> newNodeInfo
;
3632 newNodeInfo
= mNodeInfoManager
->GetNodeInfo(aPrototype
->mNodeInfo
->NameAtom(),
3633 aPrototype
->mNodeInfo
->GetPrefixAtom(),
3634 aPrototype
->mNodeInfo
->NamespaceID(),
3635 nsIDOMNode::ELEMENT_NODE
);
3636 if (!newNodeInfo
) return NS_ERROR_OUT_OF_MEMORY
;
3637 nsRefPtr
<mozilla::dom::NodeInfo
> xtfNi
= newNodeInfo
;
3638 rv
= NS_NewElement(getter_AddRefs(result
), newNodeInfo
.forget(),
3643 rv
= AddAttributes(aPrototype
, result
);
3644 if (NS_FAILED(rv
)) return rv
;
3647 result
.swap(*aResult
);
3653 XULDocument::CreateOverlayElement(nsXULPrototypeElement
* aPrototype
,
3658 nsRefPtr
<Element
> element
;
3659 rv
= CreateElementFromPrototype(aPrototype
, getter_AddRefs(element
), false);
3660 if (NS_FAILED(rv
)) return rv
;
3662 OverlayForwardReference
* fwdref
=
3663 new OverlayForwardReference(this, element
);
3665 return NS_ERROR_OUT_OF_MEMORY
;
3667 // transferring ownership to ya...
3668 rv
= AddForwardReference(fwdref
);
3669 if (NS_FAILED(rv
)) return rv
;
3671 NS_ADDREF(*aResult
= element
);
3676 XULDocument::AddAttributes(nsXULPrototypeElement
* aPrototype
,
3677 nsIContent
* aElement
)
3681 for (uint32_t i
= 0; i
< aPrototype
->mNumAttributes
; ++i
) {
3682 nsXULPrototypeAttribute
* protoattr
= &(aPrototype
->mAttributes
[i
]);
3683 nsAutoString valueStr
;
3684 protoattr
->mValue
.ToString(valueStr
);
3686 rv
= aElement
->SetAttr(protoattr
->mName
.NamespaceID(),
3687 protoattr
->mName
.LocalName(),
3688 protoattr
->mName
.GetPrefix(),
3691 if (NS_FAILED(rv
)) return rv
;
3699 XULDocument::CheckTemplateBuilderHookup(nsIContent
* aElement
,
3702 // See if the element already has a `database' attribute. If it
3703 // does, then the template builder has already been created.
3705 // XXX This approach will crash and burn (well, maybe not _that_
3706 // bad) if aElement is not a XUL element.
3708 // XXXvarga Do we still want to support non XUL content?
3709 nsCOMPtr
<nsIDOMXULElement
> xulElement
= do_QueryInterface(aElement
);
3711 nsCOMPtr
<nsIRDFCompositeDataSource
> ds
;
3712 xulElement
->GetDatabase(getter_AddRefs(ds
));
3714 *aNeedsHookup
= false;
3719 // Check aElement for a 'datasources' attribute, if it has
3720 // one a XUL template builder needs to be hooked up.
3721 *aNeedsHookup
= aElement
->HasAttr(kNameSpaceID_None
,
3722 nsGkAtoms::datasources
);
3726 /* static */ nsresult
3727 XULDocument::CreateTemplateBuilder(nsIContent
* aElement
)
3729 // Check if need to construct a tree builder or content builder.
3730 bool isTreeBuilder
= false;
3732 // return successful if the element is not is a document, as an inline
3733 // script could have removed it
3734 nsIDocument
* document
= aElement
->GetUncomposedDoc();
3735 NS_ENSURE_TRUE(document
, NS_OK
);
3737 int32_t nameSpaceID
;
3738 nsIAtom
* baseTag
= document
->BindingManager()->
3739 ResolveTag(aElement
, &nameSpaceID
);
3741 if ((nameSpaceID
== kNameSpaceID_XUL
) && (baseTag
== nsGkAtoms::tree
)) {
3742 // By default, we build content for a tree and then we attach
3743 // the tree content view. However, if the `dont-build-content'
3744 // flag is set, then we we'll attach a tree builder which
3745 // directly implements the tree view.
3748 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::flags
, flags
);
3749 if (flags
.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
3750 isTreeBuilder
= true;
3754 if (isTreeBuilder
) {
3755 // Create and initialize a tree builder.
3756 nsCOMPtr
<nsIXULTemplateBuilder
> builder
=
3757 do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
3760 return NS_ERROR_FAILURE
;
3762 builder
->Init(aElement
);
3764 // Create a <treechildren> if one isn't there already.
3765 // XXXvarga what about attributes?
3766 nsCOMPtr
<nsIContent
> bodyContent
;
3767 nsXULContentUtils::FindChildByTag(aElement
, kNameSpaceID_XUL
,
3768 nsGkAtoms::treechildren
,
3769 getter_AddRefs(bodyContent
));
3771 if (! bodyContent
) {
3773 document
->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren
),
3774 nullptr, kNameSpaceID_XUL
,
3775 getter_AddRefs(bodyContent
));
3776 NS_ENSURE_SUCCESS(rv
, rv
);
3778 aElement
->AppendChildTo(bodyContent
, false);
3782 // Create and initialize a content builder.
3783 nsCOMPtr
<nsIXULTemplateBuilder
> builder
3784 = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
3787 return NS_ERROR_FAILURE
;
3789 builder
->Init(aElement
);
3790 builder
->CreateContents(aElement
, false);
3798 XULDocument::AddPrototypeSheets()
3802 const nsCOMArray
<nsIURI
>& sheets
= mCurrentPrototype
->GetStyleSheetReferences();
3804 for (int32_t i
= 0; i
< sheets
.Count(); i
++) {
3805 nsCOMPtr
<nsIURI
> uri
= sheets
[i
];
3807 nsRefPtr
<CSSStyleSheet
> incompleteSheet
;
3808 rv
= CSSLoader()->LoadSheet(uri
,
3809 mCurrentPrototype
->DocumentPrincipal(),
3810 EmptyCString(), this,
3811 getter_AddRefs(incompleteSheet
));
3813 // XXXldb We need to prevent bogus sheets from being held in the
3814 // prototype's list, but until then, don't propagate the failure
3815 // from LoadSheet (and thus exit the loop).
3816 if (NS_SUCCEEDED(rv
)) {
3818 if (!mOverlaySheets
.AppendElement(incompleteSheet
)) {
3819 return NS_ERROR_OUT_OF_MEMORY
;
3828 //----------------------------------------------------------------------
3830 // XULDocument::OverlayForwardReference
3833 nsForwardReference::Result
3834 XULDocument::OverlayForwardReference::Resolve()
3836 // Resolve a forward reference from an overlay element; attempt to
3837 // hook it up into the main document.
3839 nsCOMPtr
<nsIContent
> target
;
3841 nsIPresShell
*shell
= mDocument
->GetShell();
3842 bool notify
= shell
&& shell
->DidInitialize();
3845 mOverlay
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
3847 // mOverlay is a direct child of <overlay> and has no id.
3848 // Insert it under the root element in the base document.
3849 Element
* root
= mDocument
->GetRootElement();
3851 return eResolve_Error
;
3854 rv
= mDocument
->InsertElement(root
, mOverlay
, notify
);
3855 if (NS_FAILED(rv
)) return eResolve_Error
;
3860 // The hook-up element has an id, try to match it with an element
3861 // with the same id in the base document.
3862 target
= mDocument
->GetElementById(id
);
3864 // If we can't find the element in the document, defer the hookup
3867 return eResolve_Later
;
3869 rv
= Merge(target
, mOverlay
, notify
);
3870 if (NS_FAILED(rv
)) return eResolve_Error
;
3873 // Check if 'target' is still in our document --- it might not be!
3874 if (!notify
&& target
->GetUncomposedDoc() == mDocument
) {
3875 // Add child and any descendants to the element map
3876 // XXX this is bogus, the content in 'target' might already be
3878 rv
= mDocument
->AddSubtreeToDocument(target
);
3879 if (NS_FAILED(rv
)) return eResolve_Error
;
3883 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
3885 idC
.AssignWithConversion(id
);
3886 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
3887 ("xul: overlay resolved '%s'",
3893 return eResolve_Succeeded
;
3899 XULDocument::OverlayForwardReference::Merge(nsIContent
* aTargetNode
,
3900 nsIContent
* aOverlayNode
,
3903 // This function is given:
3904 // aTargetNode: the node in the document whose 'id' attribute
3905 // matches a toplevel node in our overlay.
3906 // aOverlayNode: the node in the overlay document that matches
3907 // a node in the actual document.
3908 // aNotify: whether or not content manipulation methods should
3909 // use the aNotify parameter. After the initial
3910 // reflow (i.e. in the dynamic overlay merge case),
3911 // we want all the content manipulation methods we
3912 // call to notify so that frames are constructed
3913 // etc. Otherwise do not, since that's during initial
3914 // document construction before StartLayout has been
3915 // called which will do everything for us.
3917 // This function merges the tree from the overlay into the tree in
3918 // the document, overwriting attributes and appending child content
3919 // nodes appropriately. (See XUL overlay reference for details)
3923 // Merge attributes from the overlay content node to that of the
3926 const nsAttrName
* name
;
3927 for (i
= 0; (name
= aOverlayNode
->GetAttrNameAt(i
)); ++i
) {
3928 // We don't want to swap IDs, they should be the same.
3929 if (name
->Equals(nsGkAtoms::id
))
3932 // In certain cases merging command or observes is unsafe, so don't.
3934 if (aTargetNode
->NodeInfo()->Equals(nsGkAtoms::observes
,
3938 if (name
->Equals(nsGkAtoms::observes
) &&
3939 aTargetNode
->HasAttr(kNameSpaceID_None
, nsGkAtoms::observes
))
3942 if (name
->Equals(nsGkAtoms::command
) &&
3943 aTargetNode
->HasAttr(kNameSpaceID_None
, nsGkAtoms::command
) &&
3944 !aTargetNode
->NodeInfo()->Equals(nsGkAtoms::key
,
3945 kNameSpaceID_XUL
) &&
3946 !aTargetNode
->NodeInfo()->Equals(nsGkAtoms::menuitem
,
3951 int32_t nameSpaceID
= name
->NamespaceID();
3952 nsIAtom
* attr
= name
->LocalName();
3953 nsIAtom
* prefix
= name
->GetPrefix();
3956 aOverlayNode
->GetAttr(nameSpaceID
, attr
, value
);
3958 // Element in the overlay has the 'removeelement' attribute set
3959 // so remove it from the actual document.
3960 if (attr
== nsGkAtoms::removeelement
&&
3961 value
.EqualsLiteral("true")) {
3963 nsCOMPtr
<nsINode
> parent
= aTargetNode
->GetParentNode();
3964 if (!parent
) return NS_ERROR_FAILURE
;
3965 rv
= RemoveElement(parent
, aTargetNode
);
3966 if (NS_FAILED(rv
)) return rv
;
3971 rv
= aTargetNode
->SetAttr(nameSpaceID
, attr
, prefix
, value
, aNotify
);
3972 if (!NS_FAILED(rv
) && !aNotify
)
3973 rv
= mDocument
->BroadcastAttributeChangeFromOverlay(aTargetNode
,
3977 if (NS_FAILED(rv
)) return rv
;
3981 // Walk our child nodes, looking for elements that have the 'id'
3982 // attribute set. If we find any, we must do a parent check in the
3983 // actual document to ensure that the structure matches that of
3984 // the actual document. If it does, we can call ourselves and attempt
3985 // to merge inside that subtree. If not, we just append the tree to
3986 // the parent like any other.
3988 uint32_t childCount
= aOverlayNode
->GetChildCount();
3990 // This must be a strong reference since it will be the only
3991 // reference to a content object during part of this loop.
3992 nsCOMPtr
<nsIContent
> currContent
;
3994 for (i
= 0; i
< childCount
; ++i
) {
3995 currContent
= aOverlayNode
->GetFirstChild();
3997 nsIAtom
*idAtom
= currContent
->GetID();
3999 nsIContent
*elementInDocument
= nullptr;
4001 nsDependentAtomString
id(idAtom
);
4003 if (!id
.IsEmpty()) {
4004 nsIDocument
*doc
= aTargetNode
->GetUncomposedDoc();
4005 //XXXsmaug should we use ShadowRoot::GetElementById()
4007 if (!doc
) return NS_ERROR_FAILURE
;
4009 elementInDocument
= doc
->GetElementById(id
);
4013 // The item has an 'id' attribute set, and we need to check with
4014 // the actual document to see if an item with this id exists at
4015 // this locale. If so, we want to merge the subtree under that
4016 // node. Otherwise, we just do an append as if the element had
4018 if (elementInDocument
) {
4019 // Given two parents, aTargetNode and aOverlayNode, we want
4020 // to call merge on currContent if we find an associated
4021 // node in the document with the same id as currContent that
4022 // also has aTargetNode as its parent.
4024 nsIContent
*elementParent
= elementInDocument
->GetParent();
4026 nsIAtom
*parentID
= elementParent
->GetID();
4028 aTargetNode
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::id
,
4029 nsDependentAtomString(parentID
),
4031 // The element matches. "Go Deep!"
4032 rv
= Merge(elementInDocument
, currContent
, aNotify
);
4033 if (NS_FAILED(rv
)) return rv
;
4034 aOverlayNode
->RemoveChildAt(0, false);
4040 aOverlayNode
->RemoveChildAt(0, false);
4042 rv
= InsertElement(aTargetNode
, currContent
, aNotify
);
4043 if (NS_FAILED(rv
)) return rv
;
4051 XULDocument::OverlayForwardReference::~OverlayForwardReference()
4054 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
) && !mResolved
) {
4056 mOverlay
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
4059 idC
.AssignWithConversion(id
);
4061 nsIURI
*protoURI
= mDocument
->mCurrentPrototype
->GetURI();
4062 nsAutoCString urlspec
;
4063 protoURI
->GetSpec(urlspec
);
4065 nsCOMPtr
<nsIURI
> docURI
;
4066 nsAutoCString parentDoc
;
4067 nsresult rv
= mDocument
->mChannel
->GetOriginalURI(getter_AddRefs(docURI
));
4068 if (NS_SUCCEEDED(rv
))
4069 docURI
->GetSpec(parentDoc
);
4070 PR_LOG(gXULLog
, PR_LOG_WARNING
,
4071 ("xul: %s overlay failed to resolve '%s' in %s",
4072 urlspec
.get(), idC
.get(), parentDoc
.get()));
4078 //----------------------------------------------------------------------
4080 // XULDocument::BroadcasterHookup
4083 nsForwardReference::Result
4084 XULDocument::BroadcasterHookup::Resolve()
4089 rv
= mDocument
->CheckBroadcasterHookup(mObservesElement
, &listener
, &mResolved
);
4090 if (NS_FAILED(rv
)) return eResolve_Error
;
4092 return mResolved
? eResolve_Succeeded
: eResolve_Later
;
4096 XULDocument::BroadcasterHookup::~BroadcasterHookup()
4099 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
) && !mResolved
) {
4100 // Tell the world we failed
4101 nsIAtom
*tag
= mObservesElement
->Tag();
4103 nsAutoString broadcasterID
;
4104 nsAutoString attribute
;
4106 if (tag
== nsGkAtoms::observes
) {
4107 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, broadcasterID
);
4108 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
, attribute
);
4111 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::observes
, broadcasterID
);
4112 attribute
.Assign('*');
4115 nsAutoCString attributeC
,broadcasteridC
;
4116 attributeC
.AssignWithConversion(attribute
);
4117 broadcasteridC
.AssignWithConversion(broadcasterID
);
4118 PR_LOG(gXULLog
, PR_LOG_WARNING
,
4119 ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
4120 nsAtomCString(tag
).get(),
4122 broadcasteridC
.get()));
4128 //----------------------------------------------------------------------
4130 // XULDocument::TemplateBuilderHookup
4133 nsForwardReference::Result
4134 XULDocument::TemplateBuilderHookup::Resolve()
4137 nsresult rv
= CheckTemplateBuilderHookup(mElement
, &needsHookup
);
4139 return eResolve_Error
;
4142 rv
= CreateTemplateBuilder(mElement
);
4144 return eResolve_Error
;
4147 return eResolve_Succeeded
;
4151 //----------------------------------------------------------------------
4154 XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent
* aNode
,
4155 int32_t aNameSpaceID
,
4156 nsIAtom
* aAttribute
,
4158 const nsAString
& aValue
)
4160 nsresult rv
= NS_OK
;
4162 if (!mBroadcasterMap
|| !CanBroadcast(aNameSpaceID
, aAttribute
))
4165 if (!aNode
->IsElement())
4168 BroadcasterMapEntry
* entry
= static_cast<BroadcasterMapEntry
*>
4169 (PL_DHashTableLookup(mBroadcasterMap
, aNode
->AsElement()));
4170 if (!PL_DHASH_ENTRY_IS_BUSY(entry
))
4173 // We've got listeners: push the value.
4175 for (i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
4176 BroadcastListener
* bl
= static_cast<BroadcastListener
*>
4177 (entry
->mListeners
[i
]);
4179 if ((bl
->mAttribute
!= aAttribute
) &&
4180 (bl
->mAttribute
!= nsGkAtoms::_asterix
))
4183 nsCOMPtr
<nsIContent
> l
= do_QueryReferent(bl
->mListener
);
4185 rv
= l
->SetAttr(aNameSpaceID
, aAttribute
,
4186 aPrefix
, aValue
, false);
4187 if (NS_FAILED(rv
)) return rv
;
4194 XULDocument::FindBroadcaster(Element
* aElement
,
4195 Element
** aListener
,
4196 nsString
& aBroadcasterID
,
4197 nsString
& aAttribute
,
4198 Element
** aBroadcaster
)
4200 mozilla::dom::NodeInfo
*ni
= aElement
->NodeInfo();
4201 *aListener
= nullptr;
4202 *aBroadcaster
= nullptr;
4204 if (ni
->Equals(nsGkAtoms::observes
, kNameSpaceID_XUL
)) {
4205 // It's an <observes> element, which means that the actual
4206 // listener is the _parent_ node. This element should have an
4207 // 'element' attribute that specifies the ID of the
4208 // broadcaster element, and an 'attribute' element, which
4209 // specifies the name of the attribute to observe.
4210 nsIContent
* parent
= aElement
->GetParent();
4212 // <observes> is the root element
4213 return NS_FINDBROADCASTER_NOT_FOUND
;
4216 // If we're still parented by an 'overlay' tag, then we haven't
4217 // made it into the real document yet. Defer hookup.
4218 if (parent
->NodeInfo()->Equals(nsGkAtoms::overlay
,
4219 kNameSpaceID_XUL
)) {
4220 return NS_FINDBROADCASTER_AWAIT_OVERLAYS
;
4223 *aListener
= parent
->IsElement() ? parent
->AsElement() : nullptr;
4224 NS_IF_ADDREF(*aListener
);
4226 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, aBroadcasterID
);
4227 if (aBroadcasterID
.IsEmpty()) {
4228 return NS_FINDBROADCASTER_NOT_FOUND
;
4230 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
, aAttribute
);
4233 // It's a generic element, which means that we'll use the
4234 // value of the 'observes' attribute to determine the ID of
4235 // the broadcaster element, and we'll watch _all_ of its
4237 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::observes
, aBroadcasterID
);
4239 // Bail if there's no aBroadcasterID
4240 if (aBroadcasterID
.IsEmpty()) {
4241 // Try the command attribute next.
4242 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, aBroadcasterID
);
4243 if (!aBroadcasterID
.IsEmpty()) {
4244 // We've got something in the command attribute. We
4245 // only treat this as a normal broadcaster if we are
4246 // not a menuitem or a key.
4248 if (ni
->Equals(nsGkAtoms::menuitem
, kNameSpaceID_XUL
) ||
4249 ni
->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
4250 return NS_FINDBROADCASTER_NOT_FOUND
;
4254 return NS_FINDBROADCASTER_NOT_FOUND
;
4258 *aListener
= aElement
;
4259 NS_ADDREF(*aListener
);
4261 aAttribute
.Assign('*');
4264 // Make sure we got a valid listener.
4265 NS_ENSURE_TRUE(*aListener
, NS_ERROR_UNEXPECTED
);
4267 // Try to find the broadcaster element in the document.
4268 *aBroadcaster
= GetElementById(aBroadcasterID
);
4270 // If we can't find the broadcaster, then we'll need to defer the
4271 // hookup. We may need to resolve some of the other overlays
4273 if (! *aBroadcaster
) {
4274 return NS_FINDBROADCASTER_AWAIT_OVERLAYS
;
4277 NS_ADDREF(*aBroadcaster
);
4279 return NS_FINDBROADCASTER_FOUND
;
4283 XULDocument::CheckBroadcasterHookup(Element
* aElement
,
4287 // Resolve a broadcaster hookup. Look at the element that we're
4288 // trying to resolve: it could be an '<observes>' element, or just
4289 // a vanilla element with an 'observes' attribute on it.
4292 *aDidResolve
= false;
4294 nsCOMPtr
<Element
> listener
;
4295 nsAutoString broadcasterID
;
4296 nsAutoString attribute
;
4297 nsCOMPtr
<Element
> broadcaster
;
4299 rv
= FindBroadcaster(aElement
, getter_AddRefs(listener
),
4300 broadcasterID
, attribute
, getter_AddRefs(broadcaster
));
4302 case NS_FINDBROADCASTER_NOT_FOUND
:
4303 *aNeedsHookup
= false;
4305 case NS_FINDBROADCASTER_AWAIT_OVERLAYS
:
4306 *aNeedsHookup
= true;
4308 case NS_FINDBROADCASTER_FOUND
:
4314 NS_ENSURE_ARG(broadcaster
&& listener
);
4316 AddBroadcastListenerFor(*broadcaster
, *listener
, attribute
, domRv
);
4317 if (domRv
.Failed()) {
4318 return domRv
.ErrorCode();
4322 // Tell the world we succeeded
4323 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
4324 nsCOMPtr
<nsIContent
> content
=
4325 do_QueryInterface(listener
);
4327 NS_ASSERTION(content
!= nullptr, "not an nsIContent");
4331 nsAutoCString attributeC
,broadcasteridC
;
4332 attributeC
.AssignWithConversion(attribute
);
4333 broadcasteridC
.AssignWithConversion(broadcasterID
);
4334 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
4335 ("xul: broadcaster hookup <%s attribute='%s'> to %s",
4336 nsAtomCString(content
->Tag()).get(),
4338 broadcasteridC
.get()));
4342 *aNeedsHookup
= false;
4343 *aDidResolve
= true;
4348 XULDocument::InsertElement(nsINode
* aParent
, nsIContent
* aChild
,
4351 // Insert aChild appropriately into aParent, accounting for a
4352 // 'pos' attribute set on aChild.
4354 nsAutoString posStr
;
4355 bool wasInserted
= false;
4357 // insert after an element of a given id
4358 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::insertafter
, posStr
);
4359 bool isInsertAfter
= true;
4361 if (posStr
.IsEmpty()) {
4362 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::insertbefore
, posStr
);
4363 isInsertAfter
= false;
4366 if (!posStr
.IsEmpty()) {
4367 nsIDocument
*document
= aParent
->OwnerDoc();
4369 nsIContent
*content
= nullptr;
4371 char* str
= ToNewCString(posStr
);
4373 char* token
= nsCRT::strtok(str
, ", ", &rest
);
4376 content
= document
->GetElementById(NS_ConvertASCIItoUTF16(token
));
4380 token
= nsCRT::strtok(rest
, ", ", &rest
);
4382 nsMemory::Free(str
);
4385 int32_t pos
= aParent
->IndexOf(content
);
4388 pos
= isInsertAfter
? pos
+ 1 : pos
;
4389 nsresult rv
= aParent
->InsertChildAt(aChild
, pos
, aNotify
);
4400 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::position
, posStr
);
4401 if (!posStr
.IsEmpty()) {
4403 // Positions are one-indexed.
4404 int32_t pos
= posStr
.ToInteger(&rv
);
4405 // Note: if the insertion index (which is |pos - 1|) would be less
4406 // than 0 or greater than the number of children aParent has, then
4407 // don't insert, since the position is bogus. Just skip on to
4409 if (NS_SUCCEEDED(rv
) && pos
> 0 &&
4410 uint32_t(pos
- 1) <= aParent
->GetChildCount()) {
4411 rv
= aParent
->InsertChildAt(aChild
, pos
- 1, aNotify
);
4412 if (NS_SUCCEEDED(rv
))
4414 // If the insertion fails, then we should still
4415 // attempt an append. Thus, rather than returning rv
4416 // immediately, we fall through to the final
4417 // "catch-all" case that just does an AppendChildTo.
4423 return aParent
->AppendChildTo(aChild
, aNotify
);
4429 XULDocument::RemoveElement(nsINode
* aParent
, nsINode
* aChild
)
4431 int32_t nodeOffset
= aParent
->IndexOf(aChild
);
4433 aParent
->RemoveChildAt(nodeOffset
, true);
4437 //----------------------------------------------------------------------
4439 // CachedChromeStreamListener
4442 XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument
* aDocument
, bool aProtoLoaded
)
4443 : mDocument(aDocument
),
4444 mProtoLoaded(aProtoLoaded
)
4446 NS_ADDREF(mDocument
);
4450 XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
4452 NS_RELEASE(mDocument
);
4456 NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener
,
4457 nsIRequestObserver
, nsIStreamListener
)
4460 XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest
*request
,
4461 nsISupports
* acontext
)
4463 return NS_ERROR_PARSED_DATA_CACHED
;
4468 XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest
*request
,
4469 nsISupports
* aContext
,
4475 return mDocument
->OnPrototypeLoadDone(true);
4480 XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest
*request
,
4481 nsISupports
* aContext
,
4482 nsIInputStream
* aInStr
,
4483 uint64_t aSourceOffset
,
4486 NS_NOTREACHED("CachedChromeStream doesn't receive data");
4487 return NS_ERROR_UNEXPECTED
;
4490 //----------------------------------------------------------------------
4495 XULDocument::ParserObserver::ParserObserver(XULDocument
* aDocument
,
4496 nsXULPrototypeDocument
* aPrototype
)
4497 : mDocument(aDocument
), mPrototype(aPrototype
)
4501 XULDocument::ParserObserver::~ParserObserver()
4505 NS_IMPL_ISUPPORTS(XULDocument::ParserObserver
, nsIRequestObserver
)
4508 XULDocument::ParserObserver::OnStartRequest(nsIRequest
*request
,
4509 nsISupports
* aContext
)
4511 // Guard against buggy channels calling OnStartRequest multiple times.
4513 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
4514 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
4515 if (channel
&& secMan
) {
4516 nsCOMPtr
<nsIPrincipal
> principal
;
4517 secMan
->GetChannelResultPrincipal(channel
, getter_AddRefs(principal
));
4519 // Failure there is ok -- it'll just set a (safe) null principal
4520 mPrototype
->SetDocumentPrincipal(principal
);
4523 // Make sure to avoid cycles
4524 mPrototype
= nullptr;
4531 XULDocument::ParserObserver::OnStopRequest(nsIRequest
*request
,
4532 nsISupports
* aContext
,
4535 nsresult rv
= NS_OK
;
4537 if (NS_FAILED(aStatus
)) {
4538 // If an overlay load fails, we need to nudge the prototype
4540 nsCOMPtr
<nsIChannel
> aChannel
= do_QueryInterface(request
);
4542 nsCOMPtr
<nsIURI
> uri
;
4543 aChannel
->GetOriginalURI(getter_AddRefs(uri
));
4545 mDocument
->ReportMissingOverlay(uri
);
4549 rv
= mDocument
->ResumeWalk();
4552 // Drop the reference to the document to break cycle between the
4553 // document, the parser, the content sink, and the parser
4555 mDocument
= nullptr;
4560 already_AddRefed
<nsPIWindowRoot
>
4561 XULDocument::GetWindowRoot()
4563 if (!mDocumentContainer
) {
4567 nsCOMPtr
<nsPIDOMWindow
> piWin
= mDocumentContainer
->GetWindow();
4568 return piWin
? piWin
->GetTopWindowRoot() : nullptr;
4572 XULDocument::IsDocumentRightToLeft()
4574 // setting the localedir attribute on the root element forces a
4575 // specific direction for the document.
4576 Element
* element
= GetRootElement();
4578 static nsIContent::AttrValuesArray strings
[] =
4579 {&nsGkAtoms::ltr
, &nsGkAtoms::rtl
, nullptr};
4580 switch (element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::localedir
,
4581 strings
, eCaseMatters
)) {
4582 case 0: return false;
4583 case 1: return true;
4584 default: break; // otherwise, not a valid value, so fall through
4588 // otherwise, get the locale from the chrome registry and
4589 // look up the intl.uidirection.<locale> preference
4590 nsCOMPtr
<nsIXULChromeRegistry
> reg
=
4591 mozilla::services::GetXULChromeRegistryService();
4595 nsAutoCString package
;
4597 if (NS_SUCCEEDED(mDocumentURI
->SchemeIs("chrome", &isChrome
)) &&
4599 mDocumentURI
->GetHostPort(package
);
4602 // use the 'global' package for about and resource uris.
4603 // otherwise, just default to left-to-right.
4604 bool isAbout
, isResource
;
4605 if (NS_SUCCEEDED(mDocumentURI
->SchemeIs("about", &isAbout
)) &&
4607 package
.AssignLiteral("global");
4609 else if (NS_SUCCEEDED(mDocumentURI
->SchemeIs("resource", &isResource
)) &&
4611 package
.AssignLiteral("global");
4619 reg
->IsLocaleRTL(package
, &isRTL
);
4624 XULDocument::ResetDocumentDirection()
4626 DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE
);
4630 XULDocument::DirectionChanged(const char* aPrefName
, void* aData
)
4632 // Reset the direction and restyle the document if necessary.
4633 XULDocument
* doc
= (XULDocument
*)aData
;
4635 doc
->ResetDocumentDirection();
4640 XULDocument::GetDocumentLWTheme()
4642 if (mDocLWTheme
== Doc_Theme_Uninitialized
) {
4643 mDocLWTheme
= Doc_Theme_None
; // No lightweight theme by default
4645 Element
* element
= GetRootElement();
4646 nsAutoString hasLWTheme
;
4648 element
->GetAttr(kNameSpaceID_None
, nsGkAtoms::lwtheme
, hasLWTheme
) &&
4649 !(hasLWTheme
.IsEmpty()) &&
4650 hasLWTheme
.EqualsLiteral("true")) {
4651 mDocLWTheme
= Doc_Theme_Neutral
;
4652 nsAutoString lwTheme
;
4653 element
->GetAttr(kNameSpaceID_None
, nsGkAtoms::lwthemetextcolor
, lwTheme
);
4654 if (!(lwTheme
.IsEmpty())) {
4655 if (lwTheme
.EqualsLiteral("dark"))
4656 mDocLWTheme
= Doc_Theme_Dark
;
4657 else if (lwTheme
.EqualsLiteral("bright"))
4658 mDocLWTheme
= Doc_Theme_Bright
;
4666 XULDocument::GetBoxObjectFor(nsIDOMElement
* aElement
, nsIBoxObject
** aResult
)
4669 nsCOMPtr
<Element
> el
= do_QueryInterface(aElement
);
4670 *aResult
= GetBoxObjectFor(el
, rv
).take();
4671 return rv
.ErrorCode();
4675 XULDocument::WrapNode(JSContext
*aCx
)
4677 return XULDocumentBinding::Wrap(aCx
, this);
4681 } // namespace mozilla