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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is Mozilla Communicator client code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Chris Waterson <waterson@netscape.com>
25 * Ben Goodger <ben@netscape.com>
26 * Pete Collins <petejc@collab.net>
27 * Dan Rosen <dr@netscape.com>
28 * Johnny Stenback <jst@netscape.com>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
46 An implementation for the XUL document. This implementation serves
47 as the basis for generating an NGLayout content model.
52 1. We do some monkey business in the document observer methods to`
53 keep the element map in sync for HTML elements. Why don't we just
54 do it for _all_ elements? Well, in the case of XUL elements,
55 which may be lazily created during frame construction, the
56 document observer methods will never be called because we'll be
57 adding the XUL nodes into the content model "quietly".
61 // Note the ALPHABETICAL ORDERING
62 #include "nsXULDocument.h"
64 #include "nsDOMError.h"
65 #include "nsIBoxObject.h"
66 #include "nsIChromeRegistry.h"
67 #include "nsIScrollableView.h"
69 #include "nsIViewManager.h"
70 #include "nsIContentViewer.h"
71 #include "nsGUIEvent.h"
72 #include "nsIDOMNSUIEvent.h"
73 #include "nsIDOMXULElement.h"
74 #include "nsIPrivateDOMEvent.h"
75 #include "nsIRDFNode.h"
76 #include "nsIRDFRemoteDataSource.h"
77 #include "nsIRDFService.h"
78 #include "nsIStreamListener.h"
80 #include "nsIDocShell.h"
81 #include "nsGkAtoms.h"
82 #include "nsXMLContentSink.h"
83 #include "nsXULContentSink.h"
84 #include "nsXULContentUtils.h"
85 #include "nsIXULOverlayProvider.h"
86 #include "nsNetUtil.h"
87 #include "nsParserUtils.h"
88 #include "nsParserCIID.h"
89 #include "nsPIBoxObject.h"
91 #include "nsILocalStore.h"
92 #include "nsXPIDLString.h"
93 #include "nsPIDOMWindow.h"
94 #include "nsXULCommandDispatcher.h"
95 #include "nsXULDocument.h"
96 #include "nsXULElement.h"
100 #include "nsIXBLService.h"
101 #include "nsCExternalHandlerService.h"
102 #include "nsMimeTypes.h"
103 #include "nsIObjectInputStream.h"
104 #include "nsIObjectOutputStream.h"
105 #include "nsIFocusController.h"
106 #include "nsContentList.h"
107 #include "nsIScriptGlobalObject.h"
108 #include "nsIScriptGlobalObjectOwner.h"
109 #include "nsIScriptRuntime.h"
110 #include "nsIScriptSecurityManager.h"
111 #include "nsNodeInfoManager.h"
112 #include "nsContentCreatorFunctions.h"
113 #include "nsContentUtils.h"
114 #include "nsIParser.h"
115 #include "nsIParserService.h"
116 #include "nsICSSStyleSheet.h"
117 #include "nsIScriptError.h"
118 #include "nsIStyleSheetLinkingElement.h"
119 #include "nsEventDispatcher.h"
120 #include "nsContentErrors.h"
121 #include "nsIObserverService.h"
122 #include "nsNodeUtils.h"
123 #include "nsIDocShellTreeItem.h"
124 #include "nsIDocShellTreeOwner.h"
125 #include "nsIXULWindow.h"
126 #include "nsXULPopupManager.h"
127 #include "nsCCUncollectableMarker.h"
129 //----------------------------------------------------------------------
134 static NS_DEFINE_CID(kParserCID
, NS_PARSER_CID
);
136 static PRBool
IsChromeURI(nsIURI
* aURI
)
138 // why is this check a member function of nsXULDocument? -gagan
139 PRBool isChrome
= PR_FALSE
;
140 if (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &isChrome
)) && isChrome
)
145 //----------------------------------------------------------------------
147 // Miscellaneous Constants
150 const nsForwardReference::Phase
nsForwardReference::kPasses
[] = {
151 nsForwardReference::eConstruction
,
152 nsForwardReference::eHookup
,
153 nsForwardReference::eDone
156 const PRUint32 kMaxAttrNameLength
= 512;
157 const PRUint32 kMaxAttributeLength
= 4096;
159 //----------------------------------------------------------------------
164 PRInt32
nsXULDocument::gRefCnt
= 0;
166 nsIRDFService
* nsXULDocument::gRDFService
;
167 nsIRDFResource
* nsXULDocument::kNC_persist
;
168 nsIRDFResource
* nsXULDocument::kNC_attribute
;
169 nsIRDFResource
* nsXULDocument::kNC_value
;
171 PRLogModuleInfo
* nsXULDocument::gXULLog
;
173 //----------------------------------------------------------------------
175 struct BroadcasterMapEntry
: public PLDHashEntryHdr
{
176 nsIDOMElement
* mBroadcaster
; // [WEAK]
177 nsSmallVoidArray mListeners
; // [OWNING] of BroadcastListener objects
180 struct BroadcastListener
{
182 nsCOMPtr
<nsIAtom
> mAttribute
;
186 nsRefMapEntry::GetFirstContent()
188 return static_cast<nsIContent
*>(mRefContentList
.SafeElementAt(0));
192 nsRefMapEntry::AppendAll(nsCOMArray
<nsIContent
>* aElements
)
194 for (PRInt32 i
= 0; i
< mRefContentList
.Count(); ++i
) {
195 aElements
->AppendObject(static_cast<nsIContent
*>(mRefContentList
[i
]));
200 nsRefMapEntry::AddContent(nsIContent
* aContent
)
202 if (mRefContentList
.IndexOf(aContent
) >= 0)
204 return mRefContentList
.AppendElement(aContent
);
208 nsRefMapEntry::RemoveContent(nsIContent
* aContent
)
210 mRefContentList
.RemoveElement(aContent
);
211 return mRefContentList
.Count() == 0;
214 //----------------------------------------------------------------------
219 // NOTE! nsDocument::operator new() zeroes out all members, so
220 // don't bother initializing members to 0.
222 nsXULDocument::nsXULDocument(void)
223 : nsXMLDocument("application/vnd.mozilla.xul+xml"),
224 mState(eState_Master
),
225 mResolutionPhase(nsForwardReference::eStart
)
228 // NOTE! nsDocument::operator new() zeroes out all members, so don't
229 // bother initializing members to 0.
231 // Override the default in nsDocument
232 mCharacterSet
.AssignLiteral("UTF-8");
234 mDefaultElementType
= kNameSpaceID_XUL
;
236 mDelayFrameLoaderInitialization
= PR_TRUE
;
239 nsXULDocument::~nsXULDocument()
241 NS_ASSERTION(mNextSrcLoadWaiter
== nsnull
,
242 "unreferenced document still waiting for script source to load?");
244 // In case we failed somewhere early on and the forward observer
245 // decls never got resolved.
246 mForwardReferences
.Clear();
248 // Destroy our broadcaster map.
249 if (mBroadcasterMap
) {
250 PL_DHashTableDestroy(mBroadcasterMap
);
254 nsCOMPtr
<nsIRDFRemoteDataSource
> remote
=
255 do_QueryInterface(mLocalStore
);
260 delete mTemplateBuilderTable
;
262 if (--gRefCnt
== 0) {
263 NS_IF_RELEASE(gRDFService
);
265 NS_IF_RELEASE(kNC_persist
);
266 NS_IF_RELEASE(kNC_attribute
);
267 NS_IF_RELEASE(kNC_value
);
269 // Remove the current document here from the FastLoad table in
270 // case the document did not make it past StartLayout in
271 // ResumeWalk. The FastLoad table must be clear of entries so
272 // that the FastLoad file footer can be properly written.
274 nsXULPrototypeCache::GetInstance()->RemoveFromFastLoadSet(mDocumentURI
);
279 NS_NewXULDocument(nsIXULDocument
** result
)
281 NS_PRECONDITION(result
!= nsnull
, "null ptr");
283 return NS_ERROR_NULL_POINTER
;
285 nsXULDocument
* doc
= new nsXULDocument();
287 return NS_ERROR_OUT_OF_MEMORY
;
292 if (NS_FAILED(rv
= doc
->Init())) {
302 //----------------------------------------------------------------------
304 // nsISupports interface
307 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULDocument
)
309 static PLDHashOperator
310 TraverseTemplateBuilders(nsISupports
* aKey
, nsIXULTemplateBuilder
* aData
,
313 nsCycleCollectionTraversalCallback
*cb
=
314 static_cast<nsCycleCollectionTraversalCallback
*>(aContext
);
316 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mTemplateBuilderTable key");
317 cb
->NoteXPCOMChild(aKey
);
318 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mTemplateBuilderTable value");
319 cb
->NoteXPCOMChild(aData
);
321 return PL_DHASH_NEXT
;
324 static PLDHashOperator
325 TraverseObservers(nsIURI
* aKey
, nsIObserver
* aData
, void* aContext
)
327 nsCycleCollectionTraversalCallback
*cb
=
328 static_cast<nsCycleCollectionTraversalCallback
*>(aContext
);
330 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb
, "mOverlayLoadObservers/mPendingOverlayLoadNotifications value");
331 cb
->NoteXPCOMChild(aData
);
333 return PL_DHASH_NEXT
;
336 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULDocument
, nsXMLDocument
)
337 if (nsCCUncollectableMarker::InGeneration(tmp
->GetMarkedCCGeneration())) {
340 // XXX tmp->mForwardReferences?
341 // XXX tmp->mContextStack?
343 // An element will only have a template builder as long as it's in the
344 // document, so we'll traverse the table here instead of from the element.
345 if (tmp
->mTemplateBuilderTable
)
346 tmp
->mTemplateBuilderTable
->EnumerateRead(TraverseTemplateBuilders
, &cb
);
348 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCurrentPrototype
,
349 nsIScriptGlobalObjectOwner
)
350 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mMasterPrototype
,
351 nsIScriptGlobalObjectOwner
)
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mCommandDispatcher
,
353 nsIDOMXULCommandDispatcher
)
355 PRUint32 i
, count
= tmp
->mPrototypes
.Length();
356 for (i
= 0; i
< count
; ++i
) {
357 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mPrototypes[i]");
358 cb
.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner
*>(tmp
->mPrototypes
[i
]));
361 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTooltipNode
)
362 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLocalStore
)
364 if (tmp
->mOverlayLoadObservers
.IsInitialized())
365 tmp
->mOverlayLoadObservers
.EnumerateRead(TraverseObservers
, &cb
);
366 if (tmp
->mPendingOverlayLoadNotifications
.IsInitialized())
367 tmp
->mPendingOverlayLoadNotifications
.EnumerateRead(TraverseObservers
, &cb
);
368 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
370 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULDocument
, nsXMLDocument
)
371 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTooltipNode
)
372 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
374 NS_IMPL_ADDREF_INHERITED(nsXULDocument
, nsXMLDocument
)
375 NS_IMPL_RELEASE_INHERITED(nsXULDocument
, nsXMLDocument
)
378 // QueryInterface implementation for nsXULDocument
379 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULDocument
)
380 NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsXULDocument
)
381 NS_INTERFACE_TABLE_ENTRY(nsXULDocument
, nsIXULDocument
)
382 NS_INTERFACE_TABLE_ENTRY(nsXULDocument
, nsIDOMXULDocument
)
383 NS_INTERFACE_TABLE_ENTRY(nsXULDocument
, nsIStreamLoaderObserver
)
384 NS_INTERFACE_TABLE_ENTRY(nsXULDocument
, nsICSSLoaderObserver
)
385 NS_OFFSET_AND_INTERFACE_TABLE_END
386 NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
387 NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XULDocument
)
388 NS_INTERFACE_MAP_END_INHERITING(nsXMLDocument
)
391 //----------------------------------------------------------------------
393 // nsIDocument interface
397 nsXULDocument::Reset(nsIChannel
* aChannel
, nsILoadGroup
* aLoadGroup
)
399 NS_NOTREACHED("Reset");
403 nsXULDocument::ResetToURI(nsIURI
* aURI
, nsILoadGroup
* aLoadGroup
,
404 nsIPrincipal
* aPrincipal
)
406 NS_NOTREACHED("ResetToURI");
409 // Override the nsDocument.cpp method to keep from returning the
410 // "cached XUL" type which is completely internal and may confuse
413 nsXULDocument::GetContentType(nsAString
& aContentType
)
415 aContentType
.AssignLiteral("application/vnd.mozilla.xul+xml");
420 nsXULDocument::SetContentType(const nsAString
& aContentType
)
422 NS_ASSERTION(aContentType
.EqualsLiteral("application/vnd.mozilla.xul+xml"),
423 "xul-documents always has content-type application/vnd.mozilla.xul+xml");
424 // Don't do anything, xul always has the mimetype
425 // application/vnd.mozilla.xul+xml
428 // This is called when the master document begins loading, whether it's
429 // fastloaded or not.
431 nsXULDocument::StartDocumentLoad(const char* aCommand
, nsIChannel
* aChannel
,
432 nsILoadGroup
* aLoadGroup
,
433 nsISupports
* aContainer
,
434 nsIStreamListener
**aDocListener
,
435 PRBool aReset
, nsIContentSink
* aSink
)
437 // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
438 // we'll possibly need to reset our content type afterwards.
439 mStillWalking
= PR_TRUE
;
440 mMayStartLayout
= PR_FALSE
;
441 mDocumentLoadGroup
= do_GetWeakReference(aLoadGroup
);
445 mHaveInputEncoding
= PR_TRUE
;
447 // Get the URI. Note that this should match nsDocShell::OnLoadingSite
449 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(mDocumentURI
));
450 NS_ENSURE_SUCCESS(rv
, rv
);
452 rv
= ResetStylesheetsToURI(mDocumentURI
);
453 if (NS_FAILED(rv
)) return rv
;
455 RetrieveRelevantHeaders(aChannel
);
457 // Look in the chrome cache: we've got this puppy loaded
459 nsXULPrototypeDocument
* proto
= IsChromeURI(mDocumentURI
) ?
460 nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI
) :
463 // Same comment as nsChromeProtocolHandler::NewChannel and
464 // nsXULDocument::ResumeWalk
467 // We don't abort on failure here because there are too many valid
468 // cases that can return failure, and the null-ness of |proto| is enough
469 // to trigger the fail-safe parse-from-disk solution. Example failure cases
470 // (for reference) include:
472 // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
474 // other: the FastLoad cache file, XUL.mfl, could not be found, probably
475 // due to being accessed before a profile has been selected (e.g.
476 // loading chrome for the profile manager itself). This must be
480 // If we're racing with another document to load proto, wait till the
481 // load has finished loading before trying to add cloned style sheets.
482 // nsXULDocument::EndLoad will call proto->NotifyLoadDone, which will
483 // find all racing documents and notify them via OnPrototypeLoadDone,
484 // which will add style sheet clones to each document.
486 rv
= proto
->AwaitLoadDone(this, &loaded
);
487 if (NS_FAILED(rv
)) return rv
;
489 mMasterPrototype
= mCurrentPrototype
= proto
;
491 // Set up the right principal on ourselves.
492 SetPrincipal(proto
->DocumentPrincipal());
494 // We need a listener, even if proto is not yet loaded, in which
495 // event the listener's OnStopRequest method does nothing, and all
496 // the interesting work happens below nsXULDocument::EndLoad, from
497 // the call there to mCurrentPrototype->NotifyLoadDone().
498 *aDocListener
= new CachedChromeStreamListener(this, loaded
);
500 return NS_ERROR_OUT_OF_MEMORY
;
503 PRBool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
504 PRBool fillXULCache
= (useXULCache
&& IsChromeURI(mDocumentURI
));
507 // It's just a vanilla document load. Create a parser to deal
508 // with the stream n' stuff.
510 nsCOMPtr
<nsIParser
> parser
;
511 rv
= PrepareToLoad(aContainer
, aCommand
, aChannel
, aLoadGroup
,
512 getter_AddRefs(parser
));
513 if (NS_FAILED(rv
)) return rv
;
515 // Predicate mIsWritingFastLoad on the XUL cache being enabled,
516 // so we don't have to re-check whether the cache is enabled all
518 mIsWritingFastLoad
= useXULCache
;
520 nsCOMPtr
<nsIStreamListener
> listener
= do_QueryInterface(parser
, &rv
);
521 NS_ASSERTION(NS_SUCCEEDED(rv
), "parser doesn't support nsIStreamListener");
522 if (NS_FAILED(rv
)) return rv
;
524 *aDocListener
= listener
;
526 parser
->Parse(mDocumentURI
);
528 // Put the current prototype, created under PrepareToLoad, into the
529 // XUL prototype cache now. We can't do this under PrepareToLoad or
530 // overlay loading will break; search for PutPrototype in ResumeWalk
531 // and see the comment there.
533 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype
);
537 NS_IF_ADDREF(*aDocListener
);
541 // This gets invoked after a prototype for this document or one of
542 // its overlays is fully built in the content sink.
544 nsXULDocument::EndLoad()
546 // This can happen if an overlay fails to load
547 if (!mCurrentPrototype
)
552 // Whack the prototype document into the cache so that the next
553 // time somebody asks for it, they don't need to load it by hand.
555 nsCOMPtr
<nsIURI
> uri
= mCurrentPrototype
->GetURI();
556 PRBool isChrome
= IsChromeURI(uri
);
558 // Remember if the XUL cache is on
559 PRBool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
561 // If the current prototype is an overlay document (non-master prototype)
562 // and we're filling the FastLoad disk cache, tell the cache we're done
563 // loading it, and write the prototype. The master prototype is put into
564 // the cache earlier in nsXULDocument::StartDocumentLoad.
565 if (useXULCache
&& mIsWritingFastLoad
&& isChrome
&&
566 mMasterPrototype
!= mCurrentPrototype
) {
567 nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype
);
571 nsCOMPtr
<nsIXULOverlayProvider
> reg
=
572 do_GetService(NS_CHROMEREGISTRY_CONTRACTID
);
575 nsCOMPtr
<nsISimpleEnumerator
> overlays
;
576 rv
= reg
->GetStyleOverlays(uri
, getter_AddRefs(overlays
));
577 if (NS_FAILED(rv
)) return;
580 nsCOMPtr
<nsISupports
> next
;
581 nsCOMPtr
<nsIURI
> sheetURI
;
583 while (NS_SUCCEEDED(rv
= overlays
->HasMoreElements(&moreSheets
)) &&
585 overlays
->GetNext(getter_AddRefs(next
));
587 sheetURI
= do_QueryInterface(next
);
589 NS_ERROR("Chrome registry handed me a non-nsIURI object!");
593 if (IsChromeURI(sheetURI
)) {
594 mCurrentPrototype
->AddStyleSheetReference(sheetURI
);
600 // If it's a chrome prototype document, then notify any
601 // documents that raced to load the prototype, and awaited
602 // its load completion via proto->AwaitLoadDone().
603 rv
= mCurrentPrototype
->NotifyLoadDone();
604 if (NS_FAILED(rv
)) return;
608 OnPrototypeLoadDone(PR_TRUE
);
612 nsXULDocument::OnPrototypeLoadDone(PRBool aResumeWalk
)
616 // Add the style overlays from chrome registry, if any.
617 rv
= AddPrototypeSheets();
618 if (NS_FAILED(rv
)) return rv
;
620 rv
= PrepareToWalk();
621 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to prepare for walk");
622 if (NS_FAILED(rv
)) return rv
;
630 // called when an error occurs parsing a document
632 nsXULDocument::OnDocumentParserError()
634 // don't report errors that are from overlays
635 if (mCurrentPrototype
&& mMasterPrototype
!= mCurrentPrototype
) {
636 nsCOMPtr
<nsIURI
> uri
= mCurrentPrototype
->GetURI();
637 if (IsChromeURI(uri
)) {
638 nsCOMPtr
<nsIObserverService
> os(
639 do_GetService("@mozilla.org/observer-service;1"));
641 os
->NotifyObservers(uri
, "xul-overlay-parsererror",
642 EmptyString().get());
652 ClearBroadcasterMapEntry(PLDHashTable
* aTable
, PLDHashEntryHdr
* aEntry
)
654 BroadcasterMapEntry
* entry
=
655 static_cast<BroadcasterMapEntry
*>(aEntry
);
656 for (PRInt32 i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
657 delete (BroadcastListener
*)entry
->mListeners
[i
];
660 // N.B. that we need to manually run the dtor because we
661 // constructed the nsSmallVoidArray object in-place.
662 entry
->mListeners
.~nsSmallVoidArray();
666 CanBroadcast(PRInt32 aNameSpaceID
, nsIAtom
* aAttribute
)
668 // Don't push changes to the |id|, |ref|, |persist|, |command| or
669 // |observes| attribute.
670 if (aNameSpaceID
== kNameSpaceID_None
) {
671 if ((aAttribute
== nsGkAtoms::id
) ||
672 (aAttribute
== nsGkAtoms::ref
) ||
673 (aAttribute
== nsGkAtoms::persist
) ||
674 (aAttribute
== nsGkAtoms::command
) ||
675 (aAttribute
== nsGkAtoms::observes
)) {
682 struct nsAttrNameInfo
684 nsAttrNameInfo(PRInt32 aNamespaceID
, nsIAtom
* aName
, nsIAtom
* aPrefix
) :
685 mNamespaceID(aNamespaceID
), mName(aName
), mPrefix(aPrefix
) {}
686 nsAttrNameInfo(const nsAttrNameInfo
& aOther
) :
687 mNamespaceID(aOther
.mNamespaceID
), mName(aOther
.mName
),
688 mPrefix(aOther
.mPrefix
) {}
689 PRInt32 mNamespaceID
;
690 nsCOMPtr
<nsIAtom
> mName
;
691 nsCOMPtr
<nsIAtom
> mPrefix
;
695 nsXULDocument::SynchronizeBroadcastListener(nsIDOMElement
*aBroadcaster
,
696 nsIDOMElement
*aListener
,
697 const nsAString
&aAttr
)
699 if (mUpdateNestLevel
> 0) {
700 nsDelayedBroadcastUpdate
delayedUpdate(aBroadcaster
, aListener
,
702 mDelayedBroadcasters
.AppendElement(delayedUpdate
);
705 nsCOMPtr
<nsIContent
> broadcaster
= do_QueryInterface(aBroadcaster
);
706 nsCOMPtr
<nsIContent
> listener
= do_QueryInterface(aListener
);
708 // We may be copying event handlers etc, so we must also copy
709 // the script-type to the listener.
710 listener
->SetScriptTypeID(broadcaster
->GetScriptTypeID());
712 if (aAttr
.EqualsLiteral("*")) {
713 PRUint32 count
= broadcaster
->GetAttrCount();
714 nsTArray
<nsAttrNameInfo
> attributes(count
);
715 for (PRUint32 i
= 0; i
< count
; ++i
) {
716 const nsAttrName
* attrName
= broadcaster
->GetAttrNameAt(i
);
717 PRInt32 nameSpaceID
= attrName
->NamespaceID();
718 nsIAtom
* name
= attrName
->LocalName();
720 // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
721 if (! CanBroadcast(nameSpaceID
, name
))
724 attributes
.AppendElement(nsAttrNameInfo(nameSpaceID
, name
,
725 attrName
->GetPrefix()));
728 count
= attributes
.Length();
729 while (count
-- > 0) {
730 PRInt32 nameSpaceID
= attributes
[count
].mNamespaceID
;
731 nsIAtom
* name
= attributes
[count
].mName
;
733 if (broadcaster
->GetAttr(nameSpaceID
, name
, value
)) {
734 listener
->SetAttr(nameSpaceID
, name
, attributes
[count
].mPrefix
,
735 value
, mInitialLayoutComplete
);
739 // XXX we don't fire the |onbroadcast| handler during
740 // initial hookup: doing so would potentially run the
741 // |onbroadcast| handler before the |onload| handler,
742 // which could define JS properties that mask XBL
744 ExecuteOnBroadcastHandlerFor(broadcaster
, aListener
, name
);
749 // Find out if the attribute is even present at all.
750 nsCOMPtr
<nsIAtom
> name
= do_GetAtom(aAttr
);
753 if (broadcaster
->GetAttr(kNameSpaceID_None
, name
, value
)) {
754 listener
->SetAttr(kNameSpaceID_None
, name
, value
,
755 mInitialLayoutComplete
);
757 listener
->UnsetAttr(kNameSpaceID_None
, name
,
758 mInitialLayoutComplete
);
762 // XXX we don't fire the |onbroadcast| handler during initial
763 // hookup: doing so would potentially run the |onbroadcast|
764 // handler before the |onload| handler, which could define JS
765 // properties that mask XBL properties, etc.
766 ExecuteOnBroadcastHandlerFor(broadcaster
, aListener
, name
);
772 nsXULDocument::AddBroadcastListenerFor(nsIDOMElement
* aBroadcaster
,
773 nsIDOMElement
* aListener
,
774 const nsAString
& aAttr
)
776 NS_ENSURE_ARG(aBroadcaster
&& aListener
);
779 nsContentUtils::CheckSameOrigin(static_cast<nsDocument
*>(this),
786 rv
= nsContentUtils::CheckSameOrigin(static_cast<nsDocument
*>(this),
793 static PLDHashTableOps gOps
= {
796 PL_DHashVoidPtrKeyStub
,
797 PL_DHashMatchEntryStub
,
798 PL_DHashMoveEntryStub
,
799 ClearBroadcasterMapEntry
,
800 PL_DHashFinalizeStub
,
804 if (! mBroadcasterMap
) {
806 PL_NewDHashTable(&gOps
, nsnull
, sizeof(BroadcasterMapEntry
),
809 if (! mBroadcasterMap
)
810 return NS_ERROR_OUT_OF_MEMORY
;
813 BroadcasterMapEntry
* entry
=
814 static_cast<BroadcasterMapEntry
*>
815 (PL_DHashTableOperate(mBroadcasterMap
, aBroadcaster
,
818 if (PL_DHASH_ENTRY_IS_FREE(entry
)) {
820 static_cast<BroadcasterMapEntry
*>
821 (PL_DHashTableOperate(mBroadcasterMap
, aBroadcaster
,
825 return NS_ERROR_OUT_OF_MEMORY
;
827 entry
->mBroadcaster
= aBroadcaster
;
829 // N.B. placement new to construct the nsSmallVoidArray object
831 new (&entry
->mListeners
) nsSmallVoidArray();
834 // Only add the listener if it's not there already!
835 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(aAttr
);
837 BroadcastListener
* bl
;
838 for (PRInt32 i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
839 bl
= static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
841 nsCOMPtr
<nsIDOMElement
> blListener
= do_QueryReferent(bl
->mListener
);
843 if ((blListener
== aListener
) && (bl
->mAttribute
== attr
))
847 bl
= new BroadcastListener
;
849 return NS_ERROR_OUT_OF_MEMORY
;
851 bl
->mListener
= do_GetWeakReference(aListener
);
852 bl
->mAttribute
= attr
;
854 entry
->mListeners
.AppendElement(bl
);
856 SynchronizeBroadcastListener(aBroadcaster
, aListener
, aAttr
);
861 nsXULDocument::RemoveBroadcastListenerFor(nsIDOMElement
* aBroadcaster
,
862 nsIDOMElement
* aListener
,
863 const nsAString
& aAttr
)
865 // If we haven't added any broadcast listeners, then there sure
866 // aren't any to remove.
867 if (! mBroadcasterMap
)
870 BroadcasterMapEntry
* entry
=
871 static_cast<BroadcasterMapEntry
*>
872 (PL_DHashTableOperate(mBroadcasterMap
, aBroadcaster
,
875 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
876 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(aAttr
);
877 for (PRInt32 i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
878 BroadcastListener
* bl
=
879 static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
881 nsCOMPtr
<nsIDOMElement
> blListener
= do_QueryReferent(bl
->mListener
);
883 if ((blListener
== aListener
) && (bl
->mAttribute
== attr
)) {
884 entry
->mListeners
.RemoveElementAt(i
);
887 if (entry
->mListeners
.Count() == 0)
888 PL_DHashTableOperate(mBroadcasterMap
, aBroadcaster
,
900 nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent
* aBroadcaster
,
901 nsIDOMElement
* aListener
,
904 // Now we execute the onchange handler in the context of the
905 // observer. We need to find the observer in order to
906 // execute the handler.
908 nsCOMPtr
<nsIContent
> listener
= do_QueryInterface(aListener
);
909 PRUint32 count
= listener
->GetChildCount();
910 for (PRUint32 i
= 0; i
< count
; ++i
) {
911 // Look for an <observes> element beneath the listener. This
912 // ought to have an |element| attribute that refers to
913 // aBroadcaster, and an |attribute| element that tells us what
914 // attriubtes we're listening for.
915 nsIContent
*child
= listener
->GetChildAt(i
);
917 if (!child
->NodeInfo()->Equals(nsGkAtoms::observes
, kNameSpaceID_XUL
))
920 // Is this the element that was listening to us?
921 nsAutoString listeningToID
;
922 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, listeningToID
);
924 nsAutoString broadcasterID
;
925 aBroadcaster
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, broadcasterID
);
927 if (listeningToID
!= broadcasterID
)
930 // We are observing the broadcaster, but is this the right
932 nsAutoString listeningToAttribute
;
933 child
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
,
934 listeningToAttribute
);
936 if (!aAttr
->Equals(listeningToAttribute
) &&
937 !listeningToAttribute
.EqualsLiteral("*")) {
941 // This is the right <observes> element. Execute the
942 // |onbroadcast| event handler
943 nsEvent
event(PR_TRUE
, NS_XUL_BROADCAST
);
945 nsPresShellIterator
iter(this);
946 nsCOMPtr
<nsIPresShell
> shell
;
947 while ((shell
= iter
.GetNextShell())) {
949 nsCOMPtr
<nsPresContext
> aPresContext
= shell
->GetPresContext();
951 // Handle the DOM event
952 nsEventStatus status
= nsEventStatus_eIgnore
;
953 nsEventDispatcher::Dispatch(child
, aPresContext
, &event
, nsnull
,
962 nsXULDocument::AttributeWillChange(nsIContent
* aContent
, PRInt32 aNameSpaceID
,
965 NS_ABORT_IF_FALSE(aContent
, "Null content!");
966 NS_PRECONDITION(aAttribute
, "Must have an attribute that's changing!");
968 // XXXbz check aNameSpaceID, dammit!
969 // See if we need to update our ref map.
970 if (aAttribute
== nsGkAtoms::ref
||
971 (aAttribute
== nsGkAtoms::id
&& !aContent
->GetIDAttributeName())) {
972 RemoveElementFromRefMap(aContent
);
975 nsXMLDocument::AttributeWillChange(aContent
, aNameSpaceID
, aAttribute
);
979 nsXULDocument::AttributeChanged(nsIDocument
* aDocument
,
980 nsIContent
* aElement
, PRInt32 aNameSpaceID
,
981 nsIAtom
* aAttribute
, PRInt32 aModType
,
984 NS_ASSERTION(aDocument
== this, "unexpected doc");
986 // Do this here so that all the exit paths below don't leave this undone
987 nsXMLDocument::AttributeChanged(aDocument
, aElement
, aNameSpaceID
,
988 aAttribute
, aModType
, aStateMask
);
990 // XXXbz check aNameSpaceID, dammit!
991 // See if we need to update our ref map.
992 if (aAttribute
== nsGkAtoms::ref
||
993 (aAttribute
== nsGkAtoms::id
&& !aElement
->GetIDAttributeName())) {
994 AddElementToRefMap(aElement
);
999 // Synchronize broadcast listeners
1000 nsCOMPtr
<nsIDOMElement
> domele
= do_QueryInterface(aElement
);
1001 if (domele
&& mBroadcasterMap
&&
1002 CanBroadcast(aNameSpaceID
, aAttribute
)) {
1003 BroadcasterMapEntry
* entry
=
1004 static_cast<BroadcasterMapEntry
*>
1005 (PL_DHashTableOperate(mBroadcasterMap
, domele
.get(),
1008 if (PL_DHASH_ENTRY_IS_BUSY(entry
)) {
1009 // We've got listeners: push the value.
1011 PRBool attrSet
= aElement
->GetAttr(kNameSpaceID_None
, aAttribute
, value
);
1014 for (i
= entry
->mListeners
.Count() - 1; i
>= 0; --i
) {
1015 BroadcastListener
* bl
=
1016 static_cast<BroadcastListener
*>(entry
->mListeners
[i
]);
1018 if ((bl
->mAttribute
== aAttribute
) ||
1019 (bl
->mAttribute
== nsGkAtoms::_asterix
)) {
1020 nsCOMPtr
<nsIDOMElement
> listenerEl
1021 = do_QueryReferent(bl
->mListener
);
1023 nsDelayedBroadcastUpdate
delayedUpdate(domele
,
1028 mDelayedAttrChangeBroadcasts
.AppendElement(delayedUpdate
);
1035 // checks for modifications in broadcasters
1036 PRBool listener
, resolved
;
1037 CheckBroadcasterHookup(aElement
, &listener
, &resolved
);
1039 // See if there is anything we need to persist in the localstore.
1041 // XXX Namespace handling broken :-(
1042 nsAutoString persist
;
1043 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::persist
, persist
);
1044 if (!persist
.IsEmpty()) {
1046 rv
= aAttribute
->ToString(attr
);
1047 if (NS_FAILED(rv
)) return;
1049 // XXXldb This should check that it's a token, not just a substring.
1050 if (persist
.Find(attr
) >= 0) {
1051 rv
= Persist(aElement
, kNameSpaceID_None
, aAttribute
);
1052 if (NS_FAILED(rv
)) return;
1058 nsXULDocument::ContentAppended(nsIDocument
* aDocument
,
1059 nsIContent
* aContainer
,
1060 PRInt32 aNewIndexInContainer
)
1062 NS_ASSERTION(aDocument
== this, "unexpected doc");
1064 // Update our element map
1065 PRUint32 count
= aContainer
->GetChildCount();
1067 nsresult rv
= NS_OK
;
1068 for (PRUint32 i
= aNewIndexInContainer
; i
< count
&& NS_SUCCEEDED(rv
);
1070 rv
= AddSubtreeToDocument(aContainer
->GetChildAt(i
));
1073 nsXMLDocument::ContentAppended(aDocument
, aContainer
, aNewIndexInContainer
);
1077 nsXULDocument::ContentInserted(nsIDocument
* aDocument
,
1078 nsIContent
* aContainer
,
1080 PRInt32 aIndexInContainer
)
1082 NS_ASSERTION(aDocument
== this, "unexpected doc");
1084 AddSubtreeToDocument(aChild
);
1086 nsXMLDocument::ContentInserted(aDocument
, aContainer
, aChild
, aIndexInContainer
);
1090 nsXULDocument::ContentRemoved(nsIDocument
* aDocument
,
1091 nsIContent
* aContainer
,
1093 PRInt32 aIndexInContainer
)
1095 NS_ASSERTION(aDocument
== this, "unexpected doc");
1097 RemoveSubtreeFromDocument(aChild
);
1099 nsXMLDocument::ContentRemoved(aDocument
, aContainer
, aChild
, aIndexInContainer
);
1102 //----------------------------------------------------------------------
1104 // nsIXULDocument interface
1108 nsXULDocument::AddElementForID(nsIContent
* aElement
)
1110 NS_PRECONDITION(aElement
!= nsnull
, "null ptr");
1112 return NS_ERROR_NULL_POINTER
;
1114 UpdateIdTableEntry(aElement
);
1119 nsXULDocument::GetElementsForID(const nsAString
& aID
,
1120 nsCOMArray
<nsIContent
>& aElements
)
1124 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(aID
);
1126 return NS_ERROR_OUT_OF_MEMORY
;
1127 nsIdentifierMapEntry
*entry
= mIdentifierMap
.GetEntry(atom
);
1129 entry
->AppendAllIdContent(&aElements
);
1131 nsRefMapEntry
*refEntry
= mRefMap
.GetEntry(atom
);
1133 refEntry
->AppendAll(&aElements
);
1139 nsXULDocument::AddForwardReference(nsForwardReference
* aRef
)
1141 if (mResolutionPhase
< aRef
->GetPhase()) {
1142 if (!mForwardReferences
.AppendElement(aRef
)) {
1144 return NS_ERROR_OUT_OF_MEMORY
;
1148 NS_ERROR("forward references have already been resolved");
1157 nsXULDocument::ResolveForwardReferences()
1159 if (mResolutionPhase
== nsForwardReference::eDone
)
1162 // Resolve each outstanding 'forward' reference. We iterate
1163 // through the list of forward references until no more forward
1164 // references can be resolved. This annealing process is
1165 // guaranteed to converge because we've "closed the gate" to new
1166 // forward references.
1168 const nsForwardReference::Phase
* pass
= nsForwardReference::kPasses
;
1169 while ((mResolutionPhase
= *pass
) != nsForwardReference::eDone
) {
1170 PRUint32 previous
= 0;
1171 while (mForwardReferences
.Length() &&
1172 mForwardReferences
.Length() != previous
) {
1173 previous
= mForwardReferences
.Length();
1175 for (PRUint32 i
= 0; i
< mForwardReferences
.Length(); ++i
) {
1176 nsForwardReference
* fwdref
= mForwardReferences
[i
];
1178 if (fwdref
->GetPhase() == *pass
) {
1179 nsForwardReference::Result result
= fwdref
->Resolve();
1182 case nsForwardReference::eResolve_Succeeded
:
1183 case nsForwardReference::eResolve_Error
:
1184 mForwardReferences
.RemoveElementAt(i
);
1186 // fixup because we removed from list
1190 case nsForwardReference::eResolve_Later
:
1191 // do nothing. we'll try again later
1201 mForwardReferences
.Clear();
1206 nsXULDocument::GetScriptGlobalObjectOwner(nsIScriptGlobalObjectOwner
** aGlobalOwner
)
1208 NS_IF_ADDREF(*aGlobalOwner
= mMasterPrototype
);
1212 //----------------------------------------------------------------------
1214 // nsIDOMDocument interface
1218 nsXULDocument::GetElementsByAttribute(const nsAString
& aAttribute
,
1219 const nsAString
& aValue
,
1220 nsIDOMNodeList
** aReturn
)
1222 nsCOMPtr
<nsIAtom
> attrAtom(do_GetAtom(aAttribute
));
1223 NS_ENSURE_TRUE(attrAtom
, NS_ERROR_OUT_OF_MEMORY
);
1224 void* attrValue
= new nsString(aValue
);
1225 NS_ENSURE_TRUE(attrValue
, NS_ERROR_OUT_OF_MEMORY
);
1226 nsContentList
*list
= new nsContentList(this,
1228 nsContentUtils::DestroyMatchString
,
1232 kNameSpaceID_Unknown
);
1233 NS_ENSURE_TRUE(list
, NS_ERROR_OUT_OF_MEMORY
);
1235 NS_ADDREF(*aReturn
= list
);
1240 nsXULDocument::GetElementsByAttributeNS(const nsAString
& aNamespaceURI
,
1241 const nsAString
& aAttribute
,
1242 const nsAString
& aValue
,
1243 nsIDOMNodeList
** aReturn
)
1245 nsCOMPtr
<nsIAtom
> attrAtom(do_GetAtom(aAttribute
));
1246 NS_ENSURE_TRUE(attrAtom
, NS_ERROR_OUT_OF_MEMORY
);
1247 void* attrValue
= new nsString(aValue
);
1248 NS_ENSURE_TRUE(attrValue
, NS_ERROR_OUT_OF_MEMORY
);
1250 PRInt32 nameSpaceId
= kNameSpaceID_Wildcard
;
1251 if (!aNamespaceURI
.EqualsLiteral("*")) {
1253 nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI
,
1255 NS_ENSURE_SUCCESS(rv
, rv
);
1258 nsContentList
*list
= new nsContentList(this,
1260 nsContentUtils::DestroyMatchString
,
1265 NS_ENSURE_TRUE(list
, NS_ERROR_OUT_OF_MEMORY
);
1267 NS_ADDREF(*aReturn
= list
);
1272 nsXULDocument::Persist(const nsAString
& aID
,
1273 const nsAString
& aAttr
)
1275 // If we're currently reading persisted attributes out of the
1276 // localstore, _don't_ re-enter and try to set them again!
1277 if (mApplyingPersistedAttrs
)
1282 nsCOMPtr
<nsIDOMElement
> domelement
;
1283 rv
= GetElementById(aID
, getter_AddRefs(domelement
));
1284 if (NS_FAILED(rv
)) return rv
;
1289 nsCOMPtr
<nsIContent
> element
= do_QueryInterface(domelement
);
1290 NS_ASSERTION(element
!= nsnull
, "null ptr");
1292 return NS_ERROR_UNEXPECTED
;
1294 nsCOMPtr
<nsIAtom
> tag
;
1295 PRInt32 nameSpaceID
;
1297 nsCOMPtr
<nsINodeInfo
> ni
= element
->GetExistingAttrNameFromQName(aAttr
);
1299 tag
= ni
->NameAtom();
1300 nameSpaceID
= ni
->NamespaceID();
1303 // Make sure that this QName is going to be valid.
1304 nsIParserService
*parserService
= nsContentUtils::GetParserService();
1305 NS_ASSERTION(parserService
, "Running scripts during shutdown?");
1307 const PRUnichar
*colon
;
1308 rv
= parserService
->CheckQName(PromiseFlatString(aAttr
), PR_TRUE
, &colon
);
1309 if (NS_FAILED(rv
)) {
1310 // There was an invalid character or it was malformed.
1311 return NS_ERROR_INVALID_ARG
;
1315 // We don't really handle namespace qualifiers in attribute names.
1316 return NS_ERROR_NOT_IMPLEMENTED
;
1319 tag
= do_GetAtom(aAttr
);
1320 NS_ENSURE_TRUE(tag
, NS_ERROR_OUT_OF_MEMORY
);
1322 nameSpaceID
= kNameSpaceID_None
;
1325 rv
= Persist(element
, nameSpaceID
, tag
);
1326 if (NS_FAILED(rv
)) return rv
;
1333 nsXULDocument::IsCapabilityEnabled(const char* aCapabilityLabel
)
1337 // NodePrincipal is guarantied to be non-null
1338 PRBool enabled
= PR_FALSE
;
1339 rv
= NodePrincipal()->IsCapabilityEnabled(aCapabilityLabel
, nsnull
, &enabled
);
1348 nsXULDocument::Persist(nsIContent
* aElement
, PRInt32 aNameSpaceID
,
1349 nsIAtom
* aAttribute
)
1351 // For non-chrome documents, persistance is simply broken
1352 if (!IsCapabilityEnabled("UniversalBrowserWrite"))
1353 return NS_ERROR_NOT_AVAILABLE
;
1355 // First make sure we _have_ a local store to stuff the persisted
1356 // information into. (We might not have one if profile information
1357 // hasn't been loaded yet...)
1363 nsCOMPtr
<nsIRDFResource
> element
;
1364 rv
= nsXULContentUtils::GetElementResource(aElement
, getter_AddRefs(element
));
1365 if (NS_FAILED(rv
)) return rv
;
1367 // No ID, so nothing to persist.
1371 // Ick. Construct a property from the attribute. Punt on
1372 // namespaces for now.
1373 const char* attrstr
;
1374 rv
= aAttribute
->GetUTF8String(&attrstr
);
1375 if (NS_FAILED(rv
)) return rv
;
1377 // Don't bother with unreasonable attributes. We clamp long values,
1378 // but truncating attribute names turns it into a different attribute
1379 // so there's no point in persisting anything at all
1380 if (!attrstr
|| strlen(attrstr
) > kMaxAttrNameLength
) {
1381 NS_WARNING("Can't persist, Attribute name too long");
1382 return NS_ERROR_ILLEGAL_VALUE
;
1385 nsCOMPtr
<nsIRDFResource
> attr
;
1386 rv
= gRDFService
->GetResource(nsDependentCString(attrstr
),
1387 getter_AddRefs(attr
));
1388 if (NS_FAILED(rv
)) return rv
;
1390 // Turn the value into a literal
1391 nsAutoString valuestr
;
1392 aElement
->GetAttr(kNameSpaceID_None
, aAttribute
, valuestr
);
1394 // prevent over-long attributes that choke the parser (bug 319846)
1395 // (can't simply Truncate without testing, it's implemented
1396 // using SetLength and will grow a short string)
1397 if (valuestr
.Length() > kMaxAttributeLength
) {
1398 NS_WARNING("Truncating persisted attribute value");
1399 valuestr
.Truncate(kMaxAttributeLength
);
1402 // See if there was an old value...
1403 nsCOMPtr
<nsIRDFNode
> oldvalue
;
1404 rv
= mLocalStore
->GetTarget(element
, attr
, PR_TRUE
, getter_AddRefs(oldvalue
));
1405 if (NS_FAILED(rv
)) return rv
;
1407 if (oldvalue
&& valuestr
.IsEmpty()) {
1408 // ...there was an oldvalue, and they've removed it. XXXThis
1409 // handling isn't quite right...
1410 rv
= mLocalStore
->Unassert(element
, attr
, oldvalue
);
1413 // Now either 'change' or 'assert' based on whether there was
1415 nsCOMPtr
<nsIRDFLiteral
> newvalue
;
1416 rv
= gRDFService
->GetLiteral(valuestr
.get(), getter_AddRefs(newvalue
));
1417 if (NS_FAILED(rv
)) return rv
;
1420 if (oldvalue
!= newvalue
)
1421 rv
= mLocalStore
->Change(element
, attr
, oldvalue
, newvalue
);
1426 rv
= mLocalStore
->Assert(element
, attr
, newvalue
, PR_TRUE
);
1430 if (NS_FAILED(rv
)) return rv
;
1432 // Add it to the persisted set for this document (if it's not
1435 nsCAutoString docurl
;
1436 rv
= mDocumentURI
->GetSpec(docurl
);
1437 if (NS_FAILED(rv
)) return rv
;
1439 nsCOMPtr
<nsIRDFResource
> doc
;
1440 rv
= gRDFService
->GetResource(docurl
, getter_AddRefs(doc
));
1441 if (NS_FAILED(rv
)) return rv
;
1443 PRBool hasAssertion
;
1444 rv
= mLocalStore
->HasAssertion(doc
, kNC_persist
, element
, PR_TRUE
, &hasAssertion
);
1445 if (NS_FAILED(rv
)) return rv
;
1447 if (! hasAssertion
) {
1448 rv
= mLocalStore
->Assert(doc
, kNC_persist
, element
, PR_TRUE
);
1449 if (NS_FAILED(rv
)) return rv
;
1458 nsXULDocument::GetViewportSize(PRInt32
* aWidth
,
1461 *aWidth
= *aHeight
= 0;
1463 FlushPendingNotifications(Flush_Layout
);
1465 nsIPresShell
*shell
= GetPrimaryShell();
1466 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
1468 nsIFrame
* frame
= shell
->GetRootFrame();
1469 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
1471 nsSize size
= frame
->GetSize();
1473 *aWidth
= nsPresContext::AppUnitsToIntCSSPixels(size
.width
);
1474 *aHeight
= nsPresContext::AppUnitsToIntCSSPixels(size
.height
);
1480 nsXULDocument::GetWidth(PRInt32
* aWidth
)
1482 NS_ENSURE_ARG_POINTER(aWidth
);
1485 return GetViewportSize(aWidth
, &height
);
1489 nsXULDocument::GetHeight(PRInt32
* aHeight
)
1491 NS_ENSURE_ARG_POINTER(aHeight
);
1494 return GetViewportSize(&width
, aHeight
);
1497 //----------------------------------------------------------------------
1499 // nsIDOMXULDocument interface
1503 nsXULDocument::GetPopupNode(nsIDOMNode
** aNode
)
1506 nsresult rv
= TrustedGetPopupNode(aNode
); // addref happens here
1508 if (NS_SUCCEEDED(rv
) && *aNode
&& !nsContentUtils::CanCallerAccess(*aNode
)) {
1510 return NS_ERROR_DOM_SECURITY_ERR
;
1517 nsXULDocument::TrustedGetPopupNode(nsIDOMNode
** aNode
)
1519 // Get the focus controller.
1520 nsCOMPtr
<nsIFocusController
> focusController
;
1521 GetFocusController(getter_AddRefs(focusController
));
1522 NS_ENSURE_TRUE(focusController
, NS_ERROR_FAILURE
);
1524 // Get the popup node.
1525 return focusController
->GetPopupNode(aNode
); // addref happens here
1529 nsXULDocument::SetPopupNode(nsIDOMNode
* aNode
)
1533 // get focus controller
1534 nsCOMPtr
<nsIFocusController
> focusController
;
1535 GetFocusController(getter_AddRefs(focusController
));
1536 NS_ENSURE_TRUE(focusController
, NS_ERROR_FAILURE
);
1538 rv
= focusController
->SetPopupNode(aNode
);
1543 // Returns the rangeOffset element from the XUL Popup Manager. This is for
1544 // chrome callers only.
1546 nsXULDocument::GetPopupRangeParent(nsIDOMNode
** aRangeParent
)
1548 NS_ENSURE_ARG_POINTER(aRangeParent
);
1549 *aRangeParent
= nsnull
;
1551 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1553 return NS_ERROR_FAILURE
;
1556 pm
->GetMouseLocation(aRangeParent
, &offset
);
1558 if (*aRangeParent
&& !nsContentUtils::CanCallerAccess(*aRangeParent
)) {
1559 NS_RELEASE(*aRangeParent
);
1560 return NS_ERROR_DOM_SECURITY_ERR
;
1566 // Returns the rangeOffset element from the XUL Popup Manager. We check the
1567 // rangeParent to determine if the caller has rights to access to the data.
1569 nsXULDocument::GetPopupRangeOffset(PRInt32
* aRangeOffset
)
1571 NS_ENSURE_ARG_POINTER(aRangeOffset
);
1573 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
1575 return NS_ERROR_FAILURE
;
1578 nsCOMPtr
<nsIDOMNode
> parent
;
1579 pm
->GetMouseLocation(getter_AddRefs(parent
), &offset
);
1581 if (parent
&& !nsContentUtils::CanCallerAccess(parent
))
1582 return NS_ERROR_DOM_SECURITY_ERR
;
1584 *aRangeOffset
= offset
;
1589 nsXULDocument::GetTooltipNode(nsIDOMNode
** aNode
)
1591 if (mTooltipNode
&& !nsContentUtils::CanCallerAccess(mTooltipNode
)) {
1592 return NS_ERROR_DOM_SECURITY_ERR
;
1594 *aNode
= mTooltipNode
;
1595 NS_IF_ADDREF(*aNode
);
1600 nsXULDocument::TrustedGetTooltipNode(nsIDOMNode
** aNode
)
1602 NS_IF_ADDREF(*aNode
= mTooltipNode
);
1607 nsXULDocument::SetTooltipNode(nsIDOMNode
* aNode
)
1609 mTooltipNode
= aNode
;
1615 nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher
** aTracker
)
1617 *aTracker
= mCommandDispatcher
;
1618 NS_IF_ADDREF(*aTracker
);
1623 nsXULDocument::GetElementById(const nsAString
& aId
,
1624 nsIDOMElement
** aReturn
)
1626 NS_ENSURE_ARG_POINTER(aReturn
);
1629 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(aId
);
1631 return NS_ERROR_OUT_OF_MEMORY
;
1633 if (!CheckGetElementByIdArg(atom
))
1636 nsIdentifierMapEntry
*entry
= mIdentifierMap
.GetEntry(atom
);
1638 nsIContent
* content
= entry
->GetIdContent();
1640 return CallQueryInterface(content
, aReturn
);
1642 nsRefMapEntry
* refEntry
= mRefMap
.GetEntry(atom
);
1644 NS_ASSERTION(refEntry
->GetFirstContent(),
1645 "nsRefMapEntries should have nonempty content lists");
1646 return CallQueryInterface(refEntry
->GetFirstContent(), aReturn
);
1652 nsXULDocument::AddElementToDocumentPre(nsIContent
* aElement
)
1654 // Do a bunch of work that's necessary when an element gets added
1655 // to the XUL Document.
1658 // 1. Add the element to the resource-to-element map. Also add it to
1659 // the id map, since it seems this can be called when creating
1660 // elements from prototypes.
1661 UpdateIdTableEntry(aElement
);
1662 rv
= AddElementToRefMap(aElement
);
1663 if (NS_FAILED(rv
)) return rv
;
1665 // 2. If the element is a 'command updater' (i.e., has a
1666 // "commandupdater='true'" attribute), then add the element to the
1667 // document's command dispatcher
1668 if (aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::commandupdater
,
1669 nsGkAtoms::_true
, eCaseMatters
)) {
1670 rv
= nsXULContentUtils::SetCommandUpdater(this, aElement
);
1671 if (NS_FAILED(rv
)) return rv
;
1674 // 3. Check for a broadcaster hookup attribute, in which case
1675 // we'll hook the node up as a listener on a broadcaster.
1676 PRBool listener
, resolved
;
1677 rv
= CheckBroadcasterHookup(aElement
, &listener
, &resolved
);
1678 if (NS_FAILED(rv
)) return rv
;
1680 // If it's not there yet, we may be able to defer hookup until
1682 if (listener
&& !resolved
&& (mResolutionPhase
!= nsForwardReference::eDone
)) {
1683 BroadcasterHookup
* hookup
= new BroadcasterHookup(this, aElement
);
1685 return NS_ERROR_OUT_OF_MEMORY
;
1687 rv
= AddForwardReference(hookup
);
1688 if (NS_FAILED(rv
)) return rv
;
1695 nsXULDocument::AddElementToDocumentPost(nsIContent
* aElement
)
1697 // We need to pay special attention to the keyset tag to set up a listener
1698 if (aElement
->NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
1699 // Create our XUL key listener and hook it up.
1700 nsCOMPtr
<nsIXBLService
> xblService(do_GetService("@mozilla.org/xbl;1"));
1702 nsCOMPtr
<nsPIDOMEventTarget
> piTarget(do_QueryInterface(aElement
));
1703 xblService
->AttachGlobalKeyHandler(piTarget
);
1707 // See if we need to attach a XUL template to this node
1709 nsresult rv
= CheckTemplateBuilderHookup(aElement
, &needsHookup
);
1714 if (mResolutionPhase
== nsForwardReference::eDone
) {
1715 rv
= CreateTemplateBuilder(aElement
);
1720 TemplateBuilderHookup
* hookup
= new TemplateBuilderHookup(aElement
);
1722 return NS_ERROR_OUT_OF_MEMORY
;
1724 rv
= AddForwardReference(hookup
);
1734 nsXULDocument::AddSubtreeToDocument(nsIContent
* aElement
)
1736 // From here on we only care about elements.
1737 if (!aElement
->IsNodeOfType(nsINode::eELEMENT
)) {
1741 // Do pre-order addition magic
1742 nsresult rv
= AddElementToDocumentPre(aElement
);
1743 if (NS_FAILED(rv
)) return rv
;
1745 // Recurse to children
1746 PRUint32 count
= aElement
->GetChildCount();
1748 while (count
-- > 0) {
1749 rv
= AddSubtreeToDocument(aElement
->GetChildAt(count
));
1754 // Do post-order addition magic
1755 return AddElementToDocumentPost(aElement
);
1759 nsXULDocument::RemoveSubtreeFromDocument(nsIContent
* aElement
)
1761 // From here on we only care about elements.
1762 if (!aElement
->IsNodeOfType(nsINode::eELEMENT
)) {
1766 // Do a bunch of cleanup to remove an element from the XUL
1770 if (aElement
->NodeInfo()->Equals(nsGkAtoms::keyset
, kNameSpaceID_XUL
)) {
1771 nsCOMPtr
<nsIXBLService
> xblService(do_GetService("@mozilla.org/xbl;1"));
1773 nsCOMPtr
<nsPIDOMEventTarget
> piTarget(do_QueryInterface(aElement
));
1774 xblService
->DetachGlobalKeyHandler(piTarget
);
1778 // 1. Remove any children from the document.
1779 PRUint32 count
= aElement
->GetChildCount();
1781 while (count
-- > 0) {
1782 rv
= RemoveSubtreeFromDocument(aElement
->GetChildAt(count
));
1787 // 2. Remove the element from the resource-to-element map.
1788 // Also remove it from the id map, since we added it in
1789 // AddElementToDocumentPre().
1790 RemoveElementFromRefMap(aElement
);
1791 RemoveFromIdTable(aElement
);
1793 // 3. If the element is a 'command updater', then remove the
1794 // element from the document's command dispatcher.
1795 if (aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::commandupdater
,
1796 nsGkAtoms::_true
, eCaseMatters
)) {
1797 nsCOMPtr
<nsIDOMElement
> domelement
= do_QueryInterface(aElement
);
1798 NS_ASSERTION(domelement
!= nsnull
, "not a DOM element");
1800 return NS_ERROR_UNEXPECTED
;
1802 rv
= mCommandDispatcher
->RemoveCommandUpdater(domelement
);
1803 if (NS_FAILED(rv
)) return rv
;
1806 // 4. Remove the element from our broadcaster map, since it is no longer
1808 nsCOMPtr
<nsIDOMElement
> broadcaster
, listener
;
1809 nsAutoString attribute
, broadcasterID
;
1810 rv
= FindBroadcaster(aElement
, getter_AddRefs(listener
),
1811 broadcasterID
, attribute
, getter_AddRefs(broadcaster
));
1812 if (rv
== NS_FINDBROADCASTER_FOUND
) {
1813 RemoveBroadcastListenerFor(broadcaster
, listener
, attribute
);
1820 nsXULDocument::SetTemplateBuilderFor(nsIContent
* aContent
,
1821 nsIXULTemplateBuilder
* aBuilder
)
1823 if (! mTemplateBuilderTable
) {
1824 mTemplateBuilderTable
= new BuilderTable
;
1825 if (! mTemplateBuilderTable
|| !mTemplateBuilderTable
->Init()) {
1826 mTemplateBuilderTable
= nsnull
;
1827 return NS_ERROR_OUT_OF_MEMORY
;
1832 mTemplateBuilderTable
->Put(aContent
, aBuilder
);
1835 mTemplateBuilderTable
->Remove(aContent
);
1842 nsXULDocument::GetTemplateBuilderFor(nsIContent
* aContent
,
1843 nsIXULTemplateBuilder
** aResult
)
1845 if (mTemplateBuilderTable
) {
1846 mTemplateBuilderTable
->Get(aContent
, aResult
);
1855 GetRefMapAttribute(nsIContent
* aElement
, nsAutoString
* aValue
)
1857 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::ref
, *aValue
);
1858 if (aValue
->IsEmpty() && !aElement
->GetIDAttributeName()) {
1859 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, *aValue
);
1864 nsXULDocument::AddElementToRefMap(nsIContent
* aElement
)
1866 // Look at the element's 'ref' attribute, and if set,
1867 // add an entry in the resource-to-element map to the element.
1869 GetRefMapAttribute(aElement
, &value
);
1870 if (!value
.IsEmpty()) {
1871 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(value
);
1873 return NS_ERROR_OUT_OF_MEMORY
;
1874 nsRefMapEntry
*entry
= mRefMap
.PutEntry(atom
);
1876 return NS_ERROR_OUT_OF_MEMORY
;
1877 if (!entry
->AddContent(aElement
))
1878 return NS_ERROR_OUT_OF_MEMORY
;
1885 nsXULDocument::RemoveElementFromRefMap(nsIContent
* aElement
)
1887 // Remove the element from the resource-to-element map.
1889 GetRefMapAttribute(aElement
, &value
);
1890 if (!value
.IsEmpty()) {
1891 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(value
);
1894 nsRefMapEntry
*entry
= mRefMap
.GetEntry(atom
);
1897 if (entry
->RemoveContent(aElement
)) {
1898 mRefMap
.RemoveEntry(atom
);
1903 //----------------------------------------------------------------------
1905 // nsIDOMNode interface
1909 nsXULDocument::CloneNode(PRBool aDeep
, nsIDOMNode
** aReturn
)
1911 // We don't allow cloning of a document
1913 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
1917 //----------------------------------------------------------------------
1919 // Implementation methods
1923 nsXULDocument::Init()
1928 nsresult rv
= nsXMLDocument::Init();
1929 NS_ENSURE_SUCCESS(rv
, rv
);
1931 // Create our command dispatcher and hook it up.
1932 mCommandDispatcher
= new nsXULCommandDispatcher(this);
1933 NS_ENSURE_TRUE(mCommandDispatcher
, NS_ERROR_OUT_OF_MEMORY
);
1935 // this _could_ fail; e.g., if we've tried to grab the local store
1936 // before profiles have initialized. If so, no big deal; nothing
1938 mLocalStore
= do_GetService(NS_LOCALSTORE_CONTRACTID
);
1940 if (gRefCnt
++ == 0) {
1941 // Keep the RDF service cached in a member variable to make using
1942 // it a bit less painful
1943 rv
= CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService
);
1944 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to get RDF Service");
1945 if (NS_FAILED(rv
)) return rv
;
1947 gRDFService
->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI
"persist"),
1949 gRDFService
->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI
"attribute"),
1951 gRDFService
->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI
"value"),
1954 // ensure that the XUL prototype cache is instantiated successfully,
1955 // so that we can use nsXULPrototypeCache::GetInstance() without
1956 // null-checks in the rest of the class.
1957 nsXULPrototypeCache
* cache
= nsXULPrototypeCache::GetInstance();
1959 NS_ERROR("Could not instantiate nsXULPrototypeCache");
1960 return NS_ERROR_FAILURE
;
1966 gXULLog
= PR_NewLogModule("nsXULDocument");
1974 nsXULDocument::StartLayout(void)
1976 if (!GetRootContent()) {
1978 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
)) {
1979 nsCAutoString urlspec
;
1980 mDocumentURI
->GetSpec(urlspec
);
1982 PR_LOG(gXULLog
, PR_LOG_WARNING
,
1983 ("xul: unable to layout '%s'; no root content", urlspec
.get()));
1989 nsPresShellIterator
iter(this);
1990 nsCOMPtr
<nsIPresShell
> shell
;
1991 while ((shell
= iter
.GetNextShell())) {
1993 // Resize-reflow this time
1994 nsPresContext
*cx
= shell
->GetPresContext();
1995 NS_ASSERTION(cx
!= nsnull
, "no pres context");
1997 return NS_ERROR_UNEXPECTED
;
1999 nsCOMPtr
<nsISupports
> container
= cx
->GetContainer();
2000 NS_ASSERTION(container
!= nsnull
, "pres context has no container");
2002 return NS_ERROR_UNEXPECTED
;
2004 nsCOMPtr
<nsIDocShell
> docShell(do_QueryInterface(container
));
2005 NS_ASSERTION(docShell
!= nsnull
, "container is not a docshell");
2007 return NS_ERROR_UNEXPECTED
;
2009 nsRect r
= cx
->GetVisibleArea();
2011 // Trigger a refresh before the call to InitialReflow(),
2012 // because the view manager's UpdateView() function is
2013 // dropping dirty rects if refresh is disabled rather than
2014 // accumulating them until refresh is enabled and then
2015 // triggering a repaint...
2016 // XXXbz Is that still the case?
2017 nsresult rv
= NS_OK
;
2018 nsIViewManager
* vm
= shell
->GetViewManager();
2020 nsCOMPtr
<nsIContentViewer
> contentViewer
;
2021 rv
= docShell
->GetContentViewer(getter_AddRefs(contentViewer
));
2022 if (NS_SUCCEEDED(rv
) && (contentViewer
!= nsnull
)) {
2024 contentViewer
->GetEnableRendering(&enabled
);
2026 vm
->EnableRefresh(NS_VMREFRESH_IMMEDIATE
);
2031 mMayStartLayout
= PR_TRUE
;
2033 // Make sure we're holding a strong ref to |shell| before we call
2035 nsCOMPtr
<nsIPresShell
> shellGrip
= shell
;
2036 rv
= shell
->InitialReflow(r
.width
, r
.height
);
2037 NS_ENSURE_SUCCESS(rv
, rv
);
2045 nsXULDocument::MatchAttribute(nsIContent
* aContent
,
2046 PRInt32 aNamespaceID
,
2050 NS_PRECONDITION(aContent
, "Must have content node to work with!");
2051 nsString
* attrValue
= static_cast<nsString
*>(aData
);
2052 if (aNamespaceID
!= kNameSpaceID_Unknown
&&
2053 aNamespaceID
!= kNameSpaceID_Wildcard
) {
2054 return attrValue
->EqualsLiteral("*") ?
2055 aContent
->HasAttr(aNamespaceID
, aAttrName
) :
2056 aContent
->AttrValueIs(aNamespaceID
, aAttrName
, *attrValue
,
2060 // Qualified name match. This takes more work.
2062 PRUint32 count
= aContent
->GetAttrCount();
2063 for (PRUint32 i
= 0; i
< count
; ++i
) {
2064 const nsAttrName
* name
= aContent
->GetAttrNameAt(i
);
2066 if (name
->IsAtom()) {
2067 nameMatch
= name
->Atom() == aAttrName
;
2068 } else if (aNamespaceID
== kNameSpaceID_Wildcard
) {
2069 nameMatch
= name
->NodeInfo()->Equals(aAttrName
);
2071 nameMatch
= name
->NodeInfo()->QualifiedNameEquals(aAttrName
);
2075 return attrValue
->EqualsLiteral("*") ||
2076 aContent
->AttrValueIs(name
->NamespaceID(), name
->LocalName(),
2077 *attrValue
, eCaseMatters
);
2085 nsXULDocument::PrepareToLoad(nsISupports
* aContainer
,
2086 const char* aCommand
,
2087 nsIChannel
* aChannel
,
2088 nsILoadGroup
* aLoadGroup
,
2089 nsIParser
** aResult
)
2091 // Get the document's principal
2092 nsCOMPtr
<nsIPrincipal
> principal
;
2093 nsContentUtils::GetSecurityManager()->
2094 GetChannelPrincipal(aChannel
, getter_AddRefs(principal
));
2095 return PrepareToLoadPrototype(mDocumentURI
, aCommand
, principal
, aResult
);
2100 nsXULDocument::PrepareToLoadPrototype(nsIURI
* aURI
, const char* aCommand
,
2101 nsIPrincipal
* aDocumentPrincipal
,
2102 nsIParser
** aResult
)
2106 // Create a new prototype document.
2107 rv
= NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype
));
2108 if (NS_FAILED(rv
)) return rv
;
2110 rv
= mCurrentPrototype
->InitPrincipal(aURI
, aDocumentPrincipal
);
2111 if (NS_FAILED(rv
)) {
2112 mCurrentPrototype
= nsnull
;
2116 // Bootstrap the master document prototype.
2117 if (! mMasterPrototype
) {
2118 mMasterPrototype
= mCurrentPrototype
;
2119 // Set our principal based on the master proto.
2120 SetPrincipal(aDocumentPrincipal
);
2123 // Create a XUL content sink, a parser, and kick off a load for
2125 nsRefPtr
<XULContentSinkImpl
> sink
= new XULContentSinkImpl();
2126 if (!sink
) return NS_ERROR_OUT_OF_MEMORY
;
2128 rv
= sink
->Init(this, mCurrentPrototype
);
2129 NS_ASSERTION(NS_SUCCEEDED(rv
), "Unable to initialize datasource sink");
2130 if (NS_FAILED(rv
)) return rv
;
2132 nsCOMPtr
<nsIParser
> parser
= do_CreateInstance(kParserCID
, &rv
);
2133 NS_ASSERTION(NS_SUCCEEDED(rv
), "unable to create parser");
2134 if (NS_FAILED(rv
)) return rv
;
2136 parser
->SetCommand(nsCRT::strcmp(aCommand
, "view-source") ? eViewNormal
:
2139 parser
->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
2140 kCharsetFromDocTypeDefault
);
2141 parser
->SetContentSink(sink
); // grabs a reference to the parser
2144 NS_ADDREF(*aResult
);
2150 nsXULDocument::ApplyPersistentAttributes()
2152 // For non-chrome documents, persistance is simply broken
2153 if (!IsCapabilityEnabled("UniversalBrowserRead"))
2154 return NS_ERROR_NOT_AVAILABLE
;
2156 // Add all of the 'persisted' attributes into the content
2161 mApplyingPersistedAttrs
= PR_TRUE
;
2162 ApplyPersistentAttributesInternal();
2163 mApplyingPersistedAttrs
= PR_FALSE
;
2170 nsXULDocument::ApplyPersistentAttributesInternal()
2172 nsCOMArray
<nsIContent
> elements
;
2174 nsCAutoString docurl
;
2175 mDocumentURI
->GetSpec(docurl
);
2177 nsCOMPtr
<nsIRDFResource
> doc
;
2178 gRDFService
->GetResource(docurl
, getter_AddRefs(doc
));
2180 nsCOMPtr
<nsISimpleEnumerator
> persisted
;
2181 mLocalStore
->GetTargets(doc
, kNC_persist
, PR_TRUE
, getter_AddRefs(persisted
));
2184 PRBool hasmore
= PR_FALSE
;
2185 persisted
->HasMoreElements(&hasmore
);
2189 nsCOMPtr
<nsISupports
> isupports
;
2190 persisted
->GetNext(getter_AddRefs(isupports
));
2192 nsCOMPtr
<nsIRDFResource
> resource
= do_QueryInterface(isupports
);
2194 NS_WARNING("expected element to be a resource");
2199 resource
->GetValueConst(&uri
);
2204 nsXULContentUtils::MakeElementID(this, nsDependentCString(uri
), id
);
2209 // This will clear the array if there are no elements.
2210 GetElementsForID(id
, elements
);
2212 if (!elements
.Count())
2215 ApplyPersistentAttributesToElements(resource
, elements
);
2223 nsXULDocument::ApplyPersistentAttributesToElements(nsIRDFResource
* aResource
,
2224 nsCOMArray
<nsIContent
>& aElements
)
2228 nsCOMPtr
<nsISimpleEnumerator
> attrs
;
2229 rv
= mLocalStore
->ArcLabelsOut(aResource
, getter_AddRefs(attrs
));
2230 if (NS_FAILED(rv
)) return rv
;
2234 rv
= attrs
->HasMoreElements(&hasmore
);
2235 if (NS_FAILED(rv
)) return rv
;
2240 nsCOMPtr
<nsISupports
> isupports
;
2241 rv
= attrs
->GetNext(getter_AddRefs(isupports
));
2242 if (NS_FAILED(rv
)) return rv
;
2244 nsCOMPtr
<nsIRDFResource
> property
= do_QueryInterface(isupports
);
2246 NS_WARNING("expected a resource");
2250 const char* attrname
;
2251 rv
= property
->GetValueConst(&attrname
);
2252 if (NS_FAILED(rv
)) return rv
;
2254 nsCOMPtr
<nsIAtom
> attr
= do_GetAtom(attrname
);
2256 return NS_ERROR_OUT_OF_MEMORY
;
2258 // XXX could hang namespace off here, as well...
2260 nsCOMPtr
<nsIRDFNode
> node
;
2261 rv
= mLocalStore
->GetTarget(aResource
, property
, PR_TRUE
,
2262 getter_AddRefs(node
));
2263 if (NS_FAILED(rv
)) return rv
;
2265 nsCOMPtr
<nsIRDFLiteral
> literal
= do_QueryInterface(node
);
2267 NS_WARNING("expected a literal");
2271 const PRUnichar
* value
;
2272 rv
= literal
->GetValueConst(&value
);
2273 if (NS_FAILED(rv
)) return rv
;
2275 nsDependentString
wrapper(value
);
2277 PRUint32 cnt
= aElements
.Count();
2279 for (PRInt32 i
= PRInt32(cnt
) - 1; i
>= 0; --i
) {
2280 nsCOMPtr
<nsIContent
> element
= aElements
.SafeObjectAt(i
);
2284 rv
= element
->SetAttr(/* XXX */ kNameSpaceID_None
,
2294 //----------------------------------------------------------------------
2296 // nsXULDocument::ContextStack
2299 nsXULDocument::ContextStack::ContextStack()
2300 : mTop(nsnull
), mDepth(0)
2304 nsXULDocument::ContextStack::~ContextStack()
2307 Entry
* doomed
= mTop
;
2309 NS_IF_RELEASE(doomed
->mElement
);
2315 nsXULDocument::ContextStack::Push(nsXULPrototypeElement
* aPrototype
,
2316 nsIContent
* aElement
)
2318 Entry
* entry
= new Entry
;
2320 return NS_ERROR_OUT_OF_MEMORY
;
2322 entry
->mPrototype
= aPrototype
;
2323 entry
->mElement
= aElement
;
2324 NS_IF_ADDREF(entry
->mElement
);
2327 entry
->mNext
= mTop
;
2335 nsXULDocument::ContextStack::Pop()
2338 return NS_ERROR_UNEXPECTED
;
2340 Entry
* doomed
= mTop
;
2344 NS_IF_RELEASE(doomed
->mElement
);
2350 nsXULDocument::ContextStack::Peek(nsXULPrototypeElement
** aPrototype
,
2351 nsIContent
** aElement
,
2355 return NS_ERROR_UNEXPECTED
;
2357 *aPrototype
= mTop
->mPrototype
;
2358 *aElement
= mTop
->mElement
;
2359 NS_IF_ADDREF(*aElement
);
2360 *aIndex
= mTop
->mIndex
;
2367 nsXULDocument::ContextStack::SetTopIndex(PRInt32 aIndex
)
2370 return NS_ERROR_UNEXPECTED
;
2372 mTop
->mIndex
= aIndex
;
2378 nsXULDocument::ContextStack::IsInsideXULTemplate()
2381 for (nsIContent
* element
= mTop
->mElement
; element
;
2382 element
= element
->GetParent()) {
2384 if (element
->NodeInfo()->Equals(nsGkAtoms::_template
,
2385 kNameSpaceID_XUL
)) {
2394 //----------------------------------------------------------------------
2396 // Content model walking routines
2400 nsXULDocument::PrepareToWalk()
2402 // Prepare to walk the mCurrentPrototype
2405 // Keep an owning reference to the prototype document so that its
2406 // elements aren't yanked from beneath us.
2407 mPrototypes
.AppendElement(mCurrentPrototype
);
2409 // Get the prototype's root element and initialize the context
2410 // stack for the prototype walk.
2411 nsXULPrototypeElement
* proto
= mCurrentPrototype
->GetRootElement();
2415 if (PR_LOG_TEST(gXULLog
, PR_LOG_ERROR
)) {
2416 nsCOMPtr
<nsIURI
> url
= mCurrentPrototype
->GetURI();
2418 nsCAutoString urlspec
;
2419 rv
= url
->GetSpec(urlspec
);
2420 if (NS_FAILED(rv
)) return rv
;
2422 PR_LOG(gXULLog
, PR_LOG_ERROR
,
2423 ("xul: error parsing '%s'", urlspec
.get()));
2430 PRUint32 piInsertionPoint
= 0;
2431 if (mState
!= eState_Master
) {
2432 piInsertionPoint
= IndexOf(GetRootContent());
2433 NS_ASSERTION(piInsertionPoint
>= 0,
2434 "No root content when preparing to walk overlay!");
2437 const nsTArray
<nsRefPtr
<nsXULPrototypePI
> >& processingInstructions
=
2438 mCurrentPrototype
->GetProcessingInstructions();
2440 PRUint32 total
= processingInstructions
.Length();
2441 for (PRUint32 i
= 0; i
< total
; ++i
) {
2442 rv
= CreateAndInsertPI(processingInstructions
[i
],
2443 this, piInsertionPoint
+ i
);
2444 if (NS_FAILED(rv
)) return rv
;
2447 // Now check the chrome registry for any additional overlays.
2448 rv
= AddChromeOverlays();
2449 if (NS_FAILED(rv
)) return rv
;
2451 // Do one-time initialization if we're preparing to walk the
2452 // master document's prototype.
2453 nsCOMPtr
<nsIContent
> root
;
2455 if (mState
== eState_Master
) {
2456 // Add the root element
2457 rv
= CreateElementFromPrototype(proto
, getter_AddRefs(root
));
2458 if (NS_FAILED(rv
)) return rv
;
2460 rv
= AppendChildTo(root
, PR_FALSE
);
2461 if (NS_FAILED(rv
)) return rv
;
2463 // Add the root element to the XUL document's ID-to-element map.
2464 UpdateIdTableEntry(root
);
2465 rv
= AddElementToRefMap(root
);
2466 if (NS_FAILED(rv
)) return rv
;
2468 // Block onload until we've finished building the complete
2469 // document content model.
2473 // There'd better not be anything on the context stack at this
2474 // point! This is the basis case for our "induction" in
2475 // ResumeWalk(), below, which'll assume that there's always a
2476 // content element on the context stack if either 1) we're in the
2477 // "master" document, or 2) we're in an overlay, and we've got
2478 // more than one prototype element (the single, root "overlay"
2479 // element) on the stack.
2480 NS_ASSERTION(mContextStack
.Depth() == 0, "something's on the context stack already");
2481 if (mContextStack
.Depth() != 0)
2482 return NS_ERROR_UNEXPECTED
;
2484 rv
= mContextStack
.Push(proto
, root
);
2485 if (NS_FAILED(rv
)) return rv
;
2491 nsXULDocument::CreateAndInsertPI(const nsXULPrototypePI
* aProtoPI
,
2492 nsINode
* aParent
, PRUint32 aIndex
)
2494 NS_PRECONDITION(aProtoPI
, "null ptr");
2495 NS_PRECONDITION(aParent
, "null ptr");
2498 nsCOMPtr
<nsIContent
> node
;
2500 rv
= NS_NewXMLProcessingInstruction(getter_AddRefs(node
),
2504 if (NS_FAILED(rv
)) return rv
;
2506 if (aProtoPI
->mTarget
.EqualsLiteral("xml-stylesheet")) {
2507 rv
= InsertXMLStylesheetPI(aProtoPI
, aParent
, aIndex
, node
);
2508 } else if (aProtoPI
->mTarget
.EqualsLiteral("xul-overlay")) {
2509 rv
= InsertXULOverlayPI(aProtoPI
, aParent
, aIndex
, node
);
2511 // No special processing, just add the PI to the document.
2512 rv
= aParent
->InsertChildAt(node
, aIndex
, PR_FALSE
);
2519 nsXULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI
* aProtoPI
,
2522 nsIContent
* aPINode
)
2524 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle(do_QueryInterface(aPINode
));
2525 NS_ASSERTION(ssle
, "passed XML Stylesheet node does not "
2526 "implement nsIStyleSheetLinkingElement!");
2530 ssle
->InitStyleLinkElement(PR_FALSE
);
2531 // We want to be notified when the style sheet finishes loading, so
2532 // disable style sheet loading for now.
2533 ssle
->SetEnableUpdates(PR_FALSE
);
2534 ssle
->OverrideBaseURI(mCurrentPrototype
->GetURI());
2536 rv
= aParent
->InsertChildAt(aPINode
, aIndex
, PR_FALSE
);
2537 if (NS_FAILED(rv
)) return rv
;
2539 ssle
->SetEnableUpdates(PR_TRUE
);
2541 // load the stylesheet if necessary, passing ourselves as
2545 rv
= ssle
->UpdateStyleSheet(this, &willNotify
, &isAlternate
);
2546 if (NS_SUCCEEDED(rv
) && willNotify
&& !isAlternate
) {
2550 // Ignore errors from UpdateStyleSheet; we don't want failure to
2551 // do that to break the XUL document load. But do propagate out
2552 // NS_ERROR_OUT_OF_MEMORY.
2553 if (rv
== NS_ERROR_OUT_OF_MEMORY
) {
2561 nsXULDocument::InsertXULOverlayPI(const nsXULPrototypePI
* aProtoPI
,
2564 nsIContent
* aPINode
)
2568 rv
= aParent
->InsertChildAt(aPINode
, aIndex
, PR_FALSE
);
2569 if (NS_FAILED(rv
)) return rv
;
2571 // xul-overlay PI is special only in prolog
2572 if (!nsContentUtils::InProlog(aPINode
)) {
2577 nsParserUtils::GetQuotedAttributeValue(aProtoPI
->mData
,
2581 // If there was no href, we can't do anything with this PI
2582 if (href
.IsEmpty()) {
2586 // Add the overlay to our list of overlays that need to be processed.
2587 nsCOMPtr
<nsIURI
> uri
;
2589 rv
= NS_NewURI(getter_AddRefs(uri
), href
, nsnull
,
2590 mCurrentPrototype
->GetURI());
2591 if (NS_SUCCEEDED(rv
)) {
2592 // We insert overlays into mUnloadedOverlays at the same index in
2593 // document order, so they end up in the reverse of the document
2594 // order in mUnloadedOverlays.
2595 // This is needed because the code in ResumeWalk loads the overlays
2596 // by processing the last item of mUnloadedOverlays and removing it
2598 rv
= mUnloadedOverlays
.InsertObjectAt(uri
, 0);
2599 } else if (rv
== NS_ERROR_MALFORMED_URI
) {
2600 // The URL is bad, move along. Don't propagate for now.
2601 // XXX report this to the Error Console (bug 359846)
2609 nsXULDocument::AddChromeOverlays()
2613 nsCOMPtr
<nsIURI
> docUri
= mCurrentPrototype
->GetURI();
2615 /* overlays only apply to chrome, skip all content URIs */
2616 if (!IsChromeURI(docUri
)) return NS_OK
;
2618 nsCOMPtr
<nsIXULOverlayProvider
> chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID
));
2619 // In embedding situations, the chrome registry may not provide overlays,
2620 // or even exist at all; that's OK.
2621 NS_ENSURE_TRUE(chromeReg
, NS_OK
);
2623 nsCOMPtr
<nsISimpleEnumerator
> overlays
;
2624 rv
= chromeReg
->GetXULOverlays(docUri
, getter_AddRefs(overlays
));
2625 NS_ENSURE_SUCCESS(rv
, rv
);
2627 PRBool moreOverlays
;
2628 nsCOMPtr
<nsISupports
> next
;
2629 nsCOMPtr
<nsIURI
> uri
;
2631 while (NS_SUCCEEDED(rv
= overlays
->HasMoreElements(&moreOverlays
)) &&
2634 rv
= overlays
->GetNext(getter_AddRefs(next
));
2635 if (NS_FAILED(rv
) || !next
) break;
2637 uri
= do_QueryInterface(next
);
2639 NS_ERROR("Chrome registry handed me a non-nsIURI object!");
2643 // Same comment as in nsXULDocument::InsertXULOverlayPI
2644 rv
= mUnloadedOverlays
.InsertObjectAt(uri
, 0);
2645 if (NS_FAILED(rv
)) break;
2652 nsXULDocument::LoadOverlay(const nsAString
& aURL
, nsIObserver
* aObserver
)
2656 nsCOMPtr
<nsIURI
> uri
;
2657 rv
= NS_NewURI(getter_AddRefs(uri
), aURL
, nsnull
);
2658 if (NS_FAILED(rv
)) return rv
;
2661 nsIObserver
* obs
= nsnull
;
2662 NS_ENSURE_TRUE(mOverlayLoadObservers
.IsInitialized() || mOverlayLoadObservers
.Init(),
2663 NS_ERROR_OUT_OF_MEMORY
);
2665 obs
= mOverlayLoadObservers
.GetWeak(uri
);
2668 // We don't support loading the same overlay twice into the same
2669 // document - that doesn't make sense anyway.
2670 return NS_ERROR_FAILURE
;
2672 mOverlayLoadObservers
.Put(uri
, aObserver
);
2674 PRBool shouldReturn
, failureFromContent
;
2675 rv
= LoadOverlayInternal(uri
, PR_TRUE
, &shouldReturn
, &failureFromContent
);
2676 if (NS_FAILED(rv
) && mOverlayLoadObservers
.IsInitialized())
2677 mOverlayLoadObservers
.Remove(uri
); // remove the observer if LoadOverlayInternal generated an error
2682 nsXULDocument::LoadOverlayInternal(nsIURI
* aURI
, PRBool aIsDynamic
,
2683 PRBool
* aShouldReturn
,
2684 PRBool
* aFailureFromContent
)
2688 *aShouldReturn
= PR_FALSE
;
2689 *aFailureFromContent
= PR_FALSE
;
2692 if (PR_LOG_TEST(gXULLog
, PR_LOG_DEBUG
)) {
2693 nsCAutoString urlspec
;
2694 aURI
->GetSpec(urlspec
);
2696 PR_LOG(gXULLog
, PR_LOG_DEBUG
,
2697 ("xul: loading overlay %s", urlspec
.get()));
2702 mResolutionPhase
= nsForwardReference::eStart
;
2704 // Chrome documents are allowed to load overlays from anywhere.
2705 // In all other cases, the overlay is only allowed to load if
2706 // the master document and prototype document have the same origin.
2708 if (!IsChromeURI(mDocumentURI
)) {
2709 // Make sure we're allowed to load this overlay.
2710 rv
= NodePrincipal()->CheckMayLoad(aURI
, PR_TRUE
);
2711 if (NS_FAILED(rv
)) {
2712 *aFailureFromContent
= PR_TRUE
;
2717 // Look in the prototype cache for the prototype document with
2718 // the specified overlay URI.
2719 PRBool overlayIsChrome
= IsChromeURI(aURI
);
2720 mCurrentPrototype
= overlayIsChrome
?
2721 nsXULPrototypeCache::GetInstance()->GetPrototype(aURI
) : nsnull
;
2723 // Same comment as nsChromeProtocolHandler::NewChannel and
2724 // nsXULDocument::StartDocumentLoad
2727 // We don't abort on failure here because there are too many valid
2728 // cases that can return failure, and the null-ness of |proto| is
2729 // enough to trigger the fail-safe parse-from-disk solution.
2730 // Example failure cases (for reference) include:
2732 // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
2734 // other: the FastLoad file, XUL.mfl, could not be found, probably
2735 // due to being accessed before a profile has been selected
2736 // (e.g. loading chrome for the profile manager itself).
2737 // The .xul file must be parsed from disk.
2739 PRBool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
2741 mIsWritingFastLoad
= useXULCache
;
2743 if (useXULCache
&& mCurrentPrototype
) {
2745 rv
= mCurrentPrototype
->AwaitLoadDone(this, &loaded
);
2746 if (NS_FAILED(rv
)) return rv
;
2749 // Return to the main event loop and eagerly await the
2750 // prototype overlay load's completion. When the content
2751 // sink completes, it will trigger an EndLoad(), which'll
2752 // wind us back up here, in ResumeWalk().
2753 *aShouldReturn
= PR_TRUE
;
2757 PR_LOG(gXULLog
, PR_LOG_DEBUG
, ("xul: overlay was cached"));
2759 // Found the overlay's prototype in the cache, fully loaded. If
2760 // this is a dynamic overlay, this will call ResumeWalk.
2761 // Otherwise, we'll return to ResumeWalk, which called us.
2762 return OnPrototypeLoadDone(aIsDynamic
);
2765 // Not there. Initiate a load.
2766 PR_LOG(gXULLog
, PR_LOG_DEBUG
, ("xul: overlay was not cached"));
2768 // We'll set the right principal on the proto doc when we get
2769 // OnStartRequest from the parser, so just pass in a null principal for
2771 nsCOMPtr
<nsIParser
> parser
;
2772 rv
= PrepareToLoadPrototype(aURI
, "view", nsnull
, getter_AddRefs(parser
));
2773 if (NS_FAILED(rv
)) return rv
;
2775 // Predicate mIsWritingFastLoad on the XUL cache being enabled,
2776 // so we don't have to re-check whether the cache is enabled all
2778 mIsWritingFastLoad
= useXULCache
;
2780 nsCOMPtr
<nsIStreamListener
> listener
= do_QueryInterface(parser
);
2782 return NS_ERROR_UNEXPECTED
;
2784 // Add an observer to the parser; this'll get called when
2785 // Necko fires its On[Start|Stop]Request() notifications,
2786 // and will let us recover from a missing overlay.
2787 ParserObserver
* parserObserver
=
2788 new ParserObserver(this, mCurrentPrototype
);
2789 if (! parserObserver
)
2790 return NS_ERROR_OUT_OF_MEMORY
;
2792 NS_ADDREF(parserObserver
);
2793 parser
->Parse(aURI
, parserObserver
);
2794 NS_RELEASE(parserObserver
);
2796 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
2797 rv
= NS_OpenURI(listener
, nsnull
, aURI
, nsnull
, group
);
2798 if (NS_FAILED(rv
)) {
2799 // Abandon this prototype
2800 mCurrentPrototype
= nsnull
;
2802 // The parser won't get an OnStartRequest and
2803 // OnStopRequest, so it needs a Terminate.
2804 parser
->Terminate();
2806 // Just move on to the next overlay. NS_OpenURI could fail
2807 // just because a channel could not be opened, which can happen
2808 // if a file or chrome package does not exist.
2809 ReportMissingOverlay(aURI
);
2811 // XXX the error could indicate an internal error as well...
2812 *aFailureFromContent
= PR_TRUE
;
2816 // If it's a 'chrome:' prototype document, then put it into
2817 // the prototype cache; other XUL documents will be reloaded
2818 // each time. We must do this after NS_OpenURI and AsyncOpen,
2819 // or chrome code will wrongly create a cached chrome channel
2820 // instead of a real one.
2821 if (useXULCache
&& overlayIsChrome
) {
2822 nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype
);
2825 // Return to the main event loop and eagerly await the
2826 // overlay load's completion. When the content sink
2827 // completes, it will trigger an EndLoad(), which'll wind
2828 // us back in ResumeWalk().
2830 *aShouldReturn
= PR_TRUE
;
2835 static PLDHashOperator
2836 FirePendingMergeNotification(nsIURI
* aKey
, nsCOMPtr
<nsIObserver
>& aObserver
, void* aClosure
)
2838 aObserver
->Observe(aKey
, "xul-overlay-merged", EmptyString().get());
2840 typedef nsInterfaceHashtable
<nsURIHashKey
,nsIObserver
> table
;
2841 table
* observers
= static_cast<table
*>(aClosure
);
2842 observers
->Remove(aKey
);
2844 return PL_DHASH_REMOVE
;
2848 nsXULDocument::ResumeWalk()
2850 // Walk the prototype and build the delegate content model. The
2851 // walk is performed in a top-down, left-to-right fashion. That
2852 // is, a parent is built before any of its children; a node is
2853 // only built after all of its siblings to the left are fully
2856 // It is interruptable so that transcluded documents (e.g.,
2857 // <html:script src="..." />) can be properly re-loaded if the
2858 // cached copy of the document becomes stale.
2860 nsCOMPtr
<nsIURI
> overlayURI
= mCurrentPrototype
->GetURI();
2863 // Begin (or resume) walking the current prototype.
2865 while (mContextStack
.Depth() > 0) {
2866 // Look at the top of the stack to determine what we're
2867 // currently working on.
2868 // This will always be a node already constructed and
2869 // inserted to the actual document.
2870 nsXULPrototypeElement
* proto
;
2871 nsCOMPtr
<nsIContent
> element
;
2872 PRInt32 indx
; // all children of proto before indx (not
2873 // inclusive) have already been constructed
2874 rv
= mContextStack
.Peek(&proto
, getter_AddRefs(element
), &indx
);
2875 if (NS_FAILED(rv
)) return rv
;
2877 if (indx
>= (PRInt32
)proto
->mChildren
.Length()) {
2879 // We've processed all of the prototype's children. If
2880 // we're in the master prototype, do post-order
2881 // document-level hookup. (An overlay will get its
2882 // document hookup done when it's successfully
2884 if (mState
== eState_Master
) {
2885 AddElementToDocumentPost(element
);
2887 if (element
->NodeInfo()->Equals(nsGkAtoms::style
,
2888 kNameSpaceID_XHTML
) ||
2889 element
->NodeInfo()->Equals(nsGkAtoms::style
,
2890 kNameSpaceID_SVG
)) {
2891 // XXX sucks that we have to do this -
2893 nsCOMPtr
<nsIStyleSheetLinkingElement
> ssle
=
2894 do_QueryInterface(element
);
2895 NS_ASSERTION(ssle
, "<html:style> doesn't implement "
2896 "nsIStyleSheetLinkingElement?");
2899 ssle
->UpdateStyleSheet(nsnull
, &willNotify
,
2905 if (element
->GetNameSpaceID() > kNameSpaceID_LastBuiltin
) {
2906 element
->DoneAddingChildren(PR_FALSE
);
2910 // Now pop the context stack back up to the parent
2911 // element and continue the prototype walk.
2912 mContextStack
.Pop();
2916 // Grab the next child, and advance the current context stack
2917 // to the next sibling to our right.
2918 nsXULPrototypeNode
* childproto
= proto
->mChildren
[indx
];
2919 mContextStack
.SetTopIndex(++indx
);
2921 // Whether we're in the "first ply" of an overlay:
2922 // the "hookup" nodes. In the case !processingOverlayHookupNodes,
2923 // we're in the master document -or- we're in an overlay, and far
2924 // enough down into the overlay's content that we can simply build
2925 // the delegates and attach them to the parent node.
2926 PRBool processingOverlayHookupNodes
= (mState
== eState_Overlay
) &&
2927 (mContextStack
.Depth() == 1);
2929 NS_ASSERTION(element
|| processingOverlayHookupNodes
,
2930 "no element on context stack");
2932 switch (childproto
->mType
) {
2933 case nsXULPrototypeNode::eType_Element
: {
2934 // An 'element', which may contain more content.
2935 nsXULPrototypeElement
* protoele
=
2936 static_cast<nsXULPrototypeElement
*>(childproto
);
2938 nsCOMPtr
<nsIContent
> child
;
2940 if (!processingOverlayHookupNodes
) {
2941 rv
= CreateElementFromPrototype(protoele
,
2942 getter_AddRefs(child
));
2943 if (NS_FAILED(rv
)) return rv
;
2945 // ...and append it to the content model.
2946 rv
= element
->AppendChildTo(child
, PR_FALSE
);
2947 if (NS_FAILED(rv
)) return rv
;
2949 // do pre-order document-level hookup, but only if
2950 // we're in the master document. For an overlay,
2951 // this will happen when the overlay is
2952 // successfully resolved.
2953 if (mState
== eState_Master
)
2954 AddElementToDocumentPre(child
);
2957 // We're in the "first ply" of an overlay: the
2958 // "hookup" nodes. Create an 'overlay' element so
2959 // that we can continue to build content, and
2960 // enter a forward reference so we can hook it up
2962 rv
= CreateOverlayElement(protoele
, getter_AddRefs(child
));
2963 if (NS_FAILED(rv
)) return rv
;
2966 // If it has children, push the element onto the context
2967 // stack and begin to process them.
2968 if (protoele
->mChildren
.Length() > 0) {
2969 rv
= mContextStack
.Push(protoele
, child
);
2970 if (NS_FAILED(rv
)) return rv
;
2973 if (mState
== eState_Master
) {
2974 // If there are no children, and we're in the
2975 // master document, do post-order document hookup
2977 AddElementToDocumentPost(child
);
2981 child
->GetNameSpaceID() > kNameSpaceID_LastBuiltin
) {
2982 child
->DoneAddingChildren(PR_FALSE
);
2989 case nsXULPrototypeNode::eType_Script
: {
2990 // A script reference. Execute the script immediately;
2991 // this may have side effects in the content model.
2992 nsXULPrototypeScript
* scriptproto
=
2993 static_cast<nsXULPrototypeScript
*>(childproto
);
2995 if (scriptproto
->mSrcURI
) {
2996 // A transcluded script reference; this may
2997 // "block" our prototype walk if the script isn't
2998 // cached, or the cached copy of the script is
2999 // stale and must be reloaded.
3001 rv
= LoadScript(scriptproto
, &blocked
);
3002 // If the script cannot be loaded, just keep going!
3004 if (NS_SUCCEEDED(rv
) && blocked
)
3007 else if (scriptproto
->mScriptObject
.mObject
) {
3009 rv
= ExecuteScript(scriptproto
);
3010 if (NS_FAILED(rv
)) return rv
;
3015 case nsXULPrototypeNode::eType_Text
: {
3016 // A simple text node.
3018 if (!processingOverlayHookupNodes
) {
3019 // This does mean that text nodes that are direct children
3020 // of <overlay> get ignored.
3022 nsCOMPtr
<nsIContent
> text
;
3023 rv
= NS_NewTextNode(getter_AddRefs(text
),
3025 NS_ENSURE_SUCCESS(rv
, rv
);
3027 nsXULPrototypeText
* textproto
=
3028 static_cast<nsXULPrototypeText
*>(childproto
);
3029 text
->SetText(textproto
->mValue
, PR_FALSE
);
3031 rv
= element
->AppendChildTo(text
, PR_FALSE
);
3032 NS_ENSURE_SUCCESS(rv
, rv
);
3037 case nsXULPrototypeNode::eType_PI
: {
3038 nsXULPrototypePI
* piProto
=
3039 static_cast<nsXULPrototypePI
*>(childproto
);
3041 // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
3042 // outside the prolog, like they used to. Issue a warning.
3044 if (piProto
->mTarget
.EqualsLiteral("xml-stylesheet") ||
3045 piProto
->mTarget
.EqualsLiteral("xul-overlay")) {
3047 const PRUnichar
* params
[] = { piProto
->mTarget
.get() };
3049 nsContentUtils::ReportToConsole(
3050 nsContentUtils::eXUL_PROPERTIES
,
3052 params
, NS_ARRAY_LENGTH(params
),
3054 EmptyString(), /* source line */
3055 0, /* line number */
3056 0, /* column number */
3057 nsIScriptError::warningFlag
,
3061 nsIContent
* parent
= processingOverlayHookupNodes
?
3062 GetRootContent() : element
.get();
3065 // an inline script could have removed the root element
3066 rv
= CreateAndInsertPI(piProto
, parent
,
3067 parent
->GetChildCount());
3068 NS_ENSURE_SUCCESS(rv
, rv
);
3074 NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
3078 // Once we get here, the context stack will have been
3079 // depleted. That means that the entire prototype has been
3080 // walked and content has been constructed.
3082 // If we're not already, mark us as now processing overlays.
3083 mState
= eState_Overlay
;
3085 // If there are no overlay URIs, then we're done.
3086 PRUint32 count
= mUnloadedOverlays
.Count();
3090 nsCOMPtr
<nsIURI
> uri
= mUnloadedOverlays
[count
-1];
3091 mUnloadedOverlays
.RemoveObjectAt(count
-1);
3093 PRBool shouldReturn
, failureFromContent
;
3094 rv
= LoadOverlayInternal(uri
, PR_FALSE
, &shouldReturn
,
3095 &failureFromContent
);
3096 if (failureFromContent
)
3097 // The failure |rv| was the result of a problem in the content
3098 // rather than an unexpected problem in our implementation, so
3099 // just continue with the next overlay.
3103 if (mOverlayLoadObservers
.IsInitialized()) {
3104 nsIObserver
*obs
= mOverlayLoadObservers
.GetWeak(overlayURI
);
3106 // This overlay has an unloaded overlay, so it will never
3107 // notify. The best we can do is to notify for the unloaded
3108 // overlay instead, assuming nobody is already notifiable
3109 // for it. Note that this will confuse the observer.
3110 if (!mOverlayLoadObservers
.GetWeak(uri
))
3111 mOverlayLoadObservers
.Put(uri
, obs
);
3112 mOverlayLoadObservers
.Remove(overlayURI
);
3117 overlayURI
.swap(uri
);
3120 // If we get here, there is nothing left for us to walk. The content
3121 // model is built and ready for layout.
3122 rv
= ResolveForwardReferences();
3123 if (NS_FAILED(rv
)) return rv
;
3125 ApplyPersistentAttributes();
3127 mStillWalking
= PR_FALSE
;
3128 if (mPendingSheets
== 0) {
3135 nsXULDocument::DoneWalking()
3137 NS_PRECONDITION(mPendingSheets
== 0, "there are sheets to be loaded");
3138 NS_PRECONDITION(!mStillWalking
, "walk not done");
3140 // XXXldb This is where we should really be setting the chromehidden
3143 PRUint32 count
= mOverlaySheets
.Count();
3144 for (PRUint32 i
= 0; i
< count
; ++i
) {
3145 AddStyleSheet(mOverlaySheets
[i
]);
3147 mOverlaySheets
.Clear();
3149 if (!mDocumentLoaded
) {
3150 // Make sure we don't reenter here from StartLayout(). Note that
3151 // setting mDocumentLoaded to true here means that if StartLayout()
3152 // causes ResumeWalk() to be reentered, we'll take the other branch of
3153 // the |if (!mDocumentLoaded)| check above and since
3154 // mInitialLayoutComplete will be false will follow the else branch
3155 // there too. See the big comment there for how such reentry can
3157 mDocumentLoaded
= PR_TRUE
;
3159 NotifyPossibleTitleChange(PR_FALSE
);
3161 // Before starting layout, check whether we're a toplevel chrome
3162 // window. If we are, set our chrome flags now, so that we don't have
3163 // to restyle the whole frame tree after StartLayout.
3164 nsCOMPtr
<nsISupports
> container
= GetContainer();
3165 nsCOMPtr
<nsIDocShellTreeItem
> item
= do_QueryInterface(container
);
3167 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
3168 item
->GetTreeOwner(getter_AddRefs(owner
));
3169 nsCOMPtr
<nsIXULWindow
> xulWin
= do_GetInterface(owner
);
3171 nsCOMPtr
<nsIDocShell
> xulWinShell
;
3172 xulWin
->GetDocShell(getter_AddRefs(xulWinShell
));
3173 if (SameCOMIdentity(xulWinShell
, container
)) {
3174 // We're the chrome document! Apply our chrome flags now.
3175 xulWin
->ApplyChromeFlags();
3182 if (mIsWritingFastLoad
&& IsChromeURI(mDocumentURI
))
3183 nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype
);
3185 NS_ASSERTION(mDelayFrameLoaderInitialization
,
3186 "mDelayFrameLoaderInitialization should be true!");
3187 mDelayFrameLoaderInitialization
= PR_FALSE
;
3188 NS_WARN_IF_FALSE(mUpdateNestLevel
== 0,
3189 "Constructing XUL document in middle of an update?");
3190 if (mUpdateNestLevel
== 0) {
3191 InitializeFinalizeFrameLoaders();
3194 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad
, (this));
3196 // DispatchContentLoadedEvents undoes the onload-blocking we
3197 // did in PrepareToWalk().
3198 DispatchContentLoadedEvents();
3200 mInitialLayoutComplete
= PR_TRUE
;
3202 // Walk the set of pending load notifications and notify any observers.
3203 // See below for detail.
3204 if (mPendingOverlayLoadNotifications
.IsInitialized())
3205 mPendingOverlayLoadNotifications
.Enumerate(FirePendingMergeNotification
, (void*)&mOverlayLoadObservers
);
3208 if (mOverlayLoadObservers
.IsInitialized()) {
3209 nsCOMPtr
<nsIURI
> overlayURI
= mCurrentPrototype
->GetURI();
3210 nsCOMPtr
<nsIObserver
> obs
;
3211 if (mInitialLayoutComplete
) {
3212 // We have completed initial layout, so just send the notification.
3213 mOverlayLoadObservers
.Get(overlayURI
, getter_AddRefs(obs
));
3215 obs
->Observe(overlayURI
, "xul-overlay-merged", EmptyString().get());
3216 mOverlayLoadObservers
.Remove(overlayURI
);
3219 // If we have not yet displayed the document for the first time
3220 // (i.e. we came in here as the result of a dynamic overlay load
3221 // which was spawned by a binding-attached event caused by
3222 // StartLayout() on the master prototype - we must remember that
3223 // this overlay has been merged and tell the listeners after
3224 // StartLayout() is completely finished rather than doing so
3225 // immediately - otherwise we may be executing code that needs to
3226 // access XBL Binding implementations on nodes for which frames
3227 // have not yet been constructed because their bindings have not
3228 // yet been attached. This can be a race condition because dynamic
3229 // overlay loading can take varying amounts of time depending on
3230 // whether or not the overlay prototype is in the XUL cache. The
3231 // most likely effect of this bug is odd UI initialization due to
3232 // methods and properties that do not work.
3233 // XXXbz really, we shouldn't be firing binding constructors
3234 // until after StartLayout returns!
3236 NS_ENSURE_TRUE(mPendingOverlayLoadNotifications
.IsInitialized() || mPendingOverlayLoadNotifications
.Init(),
3237 NS_ERROR_OUT_OF_MEMORY
);
3239 mPendingOverlayLoadNotifications
.Get(overlayURI
, getter_AddRefs(obs
));
3241 mOverlayLoadObservers
.Get(overlayURI
, getter_AddRefs(obs
));
3242 NS_ASSERTION(obs
, "null overlay load observer?");
3243 mPendingOverlayLoadNotifications
.Put(overlayURI
, obs
);
3253 nsXULDocument::StyleSheetLoaded(nsICSSStyleSheet
* aSheet
,
3254 PRBool aWasAlternate
,
3257 if (!aWasAlternate
) {
3258 // Don't care about when alternate sheets finish loading
3260 NS_ASSERTION(mPendingSheets
> 0,
3261 "Unexpected StyleSheetLoaded notification");
3265 if (!mStillWalking
&& mPendingSheets
== 0) {
3266 return DoneWalking();
3274 nsXULDocument::EndUpdate(nsUpdateType aUpdateType
)
3276 nsXMLDocument::EndUpdate(aUpdateType
);
3277 if (mUpdateNestLevel
== 0) {
3278 PRUint32 length
= mDelayedAttrChangeBroadcasts
.Length();
3280 nsTArray
<nsDelayedBroadcastUpdate
> delayedAttrChangeBroadcasts
;
3281 mDelayedAttrChangeBroadcasts
.SwapElements(
3282 delayedAttrChangeBroadcasts
);
3283 for (PRUint32 i
= 0; i
< length
; ++i
) {
3284 nsCOMPtr
<nsIContent
> listener
=
3285 do_QueryInterface(delayedAttrChangeBroadcasts
[i
].mListener
);
3286 nsIAtom
* attrName
= delayedAttrChangeBroadcasts
[i
].mAttrName
;
3287 nsString value
= delayedAttrChangeBroadcasts
[i
].mAttr
;
3288 if (delayedAttrChangeBroadcasts
[i
].mSetAttr
) {
3289 listener
->SetAttr(kNameSpaceID_None
, attrName
, value
,
3293 listener
->UnsetAttr(kNameSpaceID_None
, attrName
,
3296 nsCOMPtr
<nsIContent
> broadcaster
=
3297 do_QueryInterface(delayedAttrChangeBroadcasts
[i
].mBroadcaster
);
3298 ExecuteOnBroadcastHandlerFor(broadcaster
,
3299 delayedAttrChangeBroadcasts
[i
].mListener
,
3304 length
= mDelayedBroadcasters
.Length();
3306 nsTArray
<nsDelayedBroadcastUpdate
> delayedBroadcasters
;
3307 mDelayedBroadcasters
.SwapElements(delayedBroadcasters
);
3308 for (PRUint32 i
= 0; i
< length
; ++i
) {
3309 SynchronizeBroadcastListener(delayedBroadcasters
[i
].mBroadcaster
,
3310 delayedBroadcasters
[i
].mListener
,
3311 delayedBroadcasters
[i
].mAttr
);
3318 nsXULDocument::ReportMissingOverlay(nsIURI
* aURI
)
3320 NS_PRECONDITION(aURI
, "Must have a URI");
3323 aURI
->GetSpec(spec
);
3325 NS_ConvertUTF8toUTF16
utfSpec(spec
);
3326 const PRUnichar
* params
[] = { utfSpec
.get() };
3328 nsContentUtils::ReportToConsole(nsContentUtils::eXUL_PROPERTIES
,
3330 params
, NS_ARRAY_LENGTH(params
),
3332 EmptyString(), /* source line */
3333 0, /* line number */
3334 0, /* column number */
3335 nsIScriptError::warningFlag
,
3340 nsXULDocument::LoadScript(nsXULPrototypeScript
* aScriptProto
, PRBool
* aBlock
)
3342 // Load a transcluded script
3345 PRBool isChromeDoc
= IsChromeURI(mDocumentURI
);
3347 if (isChromeDoc
&& aScriptProto
->mScriptObject
.mObject
) {
3348 rv
= ExecuteScript(aScriptProto
);
3350 // Ignore return value from execution, and don't block
3355 // Try the XUL script cache, in case two XUL documents source the same
3356 // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
3357 // XXXbe the cache relies on aScriptProto's GC root!
3358 PRBool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
3360 if (isChromeDoc
&& useXULCache
) {
3361 PRUint32 fetchedLang
= nsIProgrammingLanguage::UNKNOWN
;
3362 void *newScriptObject
=
3363 nsXULPrototypeCache::GetInstance()->GetScript(
3364 aScriptProto
->mSrcURI
,
3366 if (newScriptObject
) {
3367 // The script language for a proto must remain constant - we
3368 // can't just change it for this unexpected language.
3369 if (aScriptProto
->mScriptObject
.mLangID
!= fetchedLang
) {
3370 NS_ERROR("XUL cache gave me an incorrect script language");
3371 return NS_ERROR_UNEXPECTED
;
3373 aScriptProto
->Set(newScriptObject
);
3376 if (aScriptProto
->mScriptObject
.mObject
) {
3377 rv
= ExecuteScript(aScriptProto
);
3379 // Ignore return value from execution, and don't block
3385 // Set the current script prototype so that OnStreamComplete can report
3386 // the right file if there are errors in the script.
3387 NS_ASSERTION(!mCurrentScriptProto
,
3388 "still loading a script when starting another load?");
3389 mCurrentScriptProto
= aScriptProto
;
3391 if (aScriptProto
->mSrcLoading
) {
3392 // Another XULDocument load has started, which is still in progress.
3393 // Remember to ResumeWalk this document when the load completes.
3394 mNextSrcLoadWaiter
= aScriptProto
->mSrcLoadWaiters
;
3395 aScriptProto
->mSrcLoadWaiters
= this;
3399 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
3401 // Note: the loader will keep itself alive while it's loading.
3402 nsCOMPtr
<nsIStreamLoader
> loader
;
3403 rv
= NS_NewStreamLoader(getter_AddRefs(loader
), aScriptProto
->mSrcURI
,
3404 this, nsnull
, group
);
3405 if (NS_FAILED(rv
)) {
3406 mCurrentScriptProto
= nsnull
;
3410 aScriptProto
->mSrcLoading
= PR_TRUE
;
3413 // Block until OnStreamComplete resumes us.
3420 nsXULDocument::OnStreamComplete(nsIStreamLoader
* aLoader
,
3421 nsISupports
* context
,
3424 const PRUint8
* string
)
3426 nsCOMPtr
<nsIRequest
> request
;
3427 aLoader
->GetRequest(getter_AddRefs(request
));
3428 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
3431 // print a load error on bad status
3432 if (NS_FAILED(aStatus
)) {
3434 nsCOMPtr
<nsIURI
> uri
;
3435 channel
->GetURI(getter_AddRefs(uri
));
3437 nsCAutoString uriSpec
;
3438 uri
->GetSpec(uriSpec
);
3439 printf("Failed to load %s\n", uriSpec
.get());
3445 // This is the completion routine that will be called when a
3446 // transcluded script completes. Compile and execute the script
3447 // if the load was successful, then continue building content
3448 // from the prototype.
3451 NS_ASSERTION(mCurrentScriptProto
&& mCurrentScriptProto
->mSrcLoading
,
3452 "script source not loading on unichar stream complete?");
3454 // Clear mCurrentScriptProto now, but save it first for use below in
3455 // the compile/execute code, and in the while loop that resumes walks
3456 // of other documents that raced to load this script
3457 nsXULPrototypeScript
* scriptProto
= mCurrentScriptProto
;
3458 mCurrentScriptProto
= nsnull
;
3460 // Clear the prototype's loading flag before executing the script or
3461 // resuming document walks, in case any of those control flows starts a
3463 scriptProto
->mSrcLoading
= PR_FALSE
;
3465 if (NS_SUCCEEDED(aStatus
)) {
3466 // If the including XUL document is a FastLoad document, and we're
3467 // compiling an out-of-line script (one with src=...), then we must
3468 // be writing a new FastLoad file. If we were reading this script
3469 // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
3470 // nsXULContentSink.cpp) would have already deserialized a non-null
3471 // script->mScriptObject, causing control flow at the top of LoadScript
3472 // not to reach here.
3473 nsCOMPtr
<nsIURI
> uri
= scriptProto
->mSrcURI
;
3475 // XXX should also check nsIHttpChannel::requestSucceeded
3478 rv
= nsScriptLoader::ConvertToUTF16(channel
, string
, stringLen
,
3479 EmptyString(), this, stringStr
);
3480 if (NS_SUCCEEDED(rv
)) {
3481 rv
= scriptProto
->Compile(stringStr
.get(), stringStr
.Length(),
3482 uri
, 1, this, mCurrentPrototype
);
3486 if (NS_SUCCEEDED(rv
)) {
3487 if (nsScriptLoader::ShouldExecuteScript(this, channel
)) {
3488 rv
= ExecuteScript(scriptProto
);
3491 // If the XUL cache is enabled, save the script object there in
3492 // case different XUL documents source the same script.
3494 // But don't save the script in the cache unless the master XUL
3495 // document URL is a chrome: URL. It is valid for a URL such as
3496 // about:config to translate into a master document URL, whose
3497 // prototype document nodes -- including prototype scripts that
3498 // hold GC roots protecting their mJSObject pointers -- are not
3499 // cached in the XUL prototype cache. See StartDocumentLoad,
3500 // the fillXULCache logic.
3502 // A document such as about:config is free to load a script via
3503 // a URL such as chrome://global/content/config.js, and we must
3504 // not cache that script object without a prototype cache entry
3505 // containing a companion nsXULPrototypeScript node that owns a
3506 // GC root protecting the script object. Otherwise, the script
3507 // cache entry will dangle once the uncached prototype document
3508 // is released when its owning nsXULDocument is unloaded.
3510 // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
3511 // the true crime story.)
3512 PRBool useXULCache
= nsXULPrototypeCache::GetInstance()->IsEnabled();
3514 if (useXULCache
&& IsChromeURI(mDocumentURI
)) {
3515 nsXULPrototypeCache::GetInstance()->PutScript(
3516 scriptProto
->mSrcURI
,
3517 scriptProto
->mScriptObject
.mLangID
,
3518 scriptProto
->mScriptObject
.mObject
);
3521 if (mIsWritingFastLoad
&& mCurrentPrototype
!= mMasterPrototype
) {
3522 // If we are loading an overlay script, try to serialize
3523 // it to the FastLoad file here. Master scripts will be
3524 // serialized when the master prototype document gets
3525 // written, at the bottom of ResumeWalk. That way, master
3526 // out-of-line scripts are serialized in the same order that
3527 // they'll be read, in the FastLoad file, which reduces the
3528 // number of seeks that dump the underlying stream's buffer.
3530 // Ignore the return value, as we don't need to propagate
3531 // a failure to write to the FastLoad file, because this
3532 // method aborts that whole process on error.
3533 nsIScriptGlobalObject
* global
=
3534 mCurrentPrototype
->GetScriptGlobalObject();
3536 NS_ASSERTION(global
!= nsnull
, "master prototype w/o global?!");
3538 PRUint32 stid
= scriptProto
->mScriptObject
.mLangID
;
3539 nsIScriptContext
*scriptContext
= \
3540 global
->GetScriptContext(stid
);
3541 NS_ASSERTION(scriptContext
!= nsnull
,
3542 "Failed to get script context for language");
3544 scriptProto
->SerializeOutOfLine(nsnull
, global
);
3548 // ignore any evaluation errors
3553 // Load a pointer to the prototype-script's list of nsXULDocuments who
3554 // raced to load the same script
3555 nsXULDocument
** docp
= &scriptProto
->mSrcLoadWaiters
;
3557 // Resume walking other documents that waited for this one's load, first
3558 // executing the script we just compiled, in each doc's script context
3560 while ((doc
= *docp
) != nsnull
) {
3561 NS_ASSERTION(doc
->mCurrentScriptProto
== scriptProto
,
3562 "waiting for wrong script to load?");
3563 doc
->mCurrentScriptProto
= nsnull
;
3565 // Unlink doc from scriptProto's list before executing and resuming
3566 *docp
= doc
->mNextSrcLoadWaiter
;
3567 doc
->mNextSrcLoadWaiter
= nsnull
;
3569 // Execute only if we loaded and compiled successfully, then resume
3570 if (NS_SUCCEEDED(aStatus
) && scriptProto
->mScriptObject
.mObject
&&
3571 nsScriptLoader::ShouldExecuteScript(doc
, channel
)) {
3572 doc
->ExecuteScript(scriptProto
);
3583 nsXULDocument::ExecuteScript(nsIScriptContext
* aContext
, void * aScriptObject
)
3585 NS_PRECONDITION(aScriptObject
!= nsnull
&& aContext
!= nsnull
, "null ptr");
3586 if (! aScriptObject
|| ! aContext
)
3587 return NS_ERROR_NULL_POINTER
;
3589 NS_ENSURE_TRUE(mScriptGlobalObject
, NS_ERROR_NOT_INITIALIZED
);
3591 // Execute the precompiled script with the given version
3593 void *global
= mScriptGlobalObject
->GetScriptGlobal(
3594 aContext
->GetScriptTypeID());
3595 rv
= aContext
->ExecuteScript(aScriptObject
,
3603 nsXULDocument::ExecuteScript(nsXULPrototypeScript
*aScript
)
3605 NS_PRECONDITION(aScript
!= nsnull
, "null ptr");
3606 NS_ENSURE_TRUE(aScript
, NS_ERROR_NULL_POINTER
);
3607 NS_ENSURE_TRUE(mScriptGlobalObject
, NS_ERROR_NOT_INITIALIZED
);
3608 PRUint32 stid
= aScript
->mScriptObject
.mLangID
;
3611 rv
= mScriptGlobalObject
->EnsureScriptEnvironment(stid
);
3612 NS_ENSURE_SUCCESS(rv
, rv
);
3614 nsCOMPtr
<nsIScriptContext
> context
=
3615 mScriptGlobalObject
->GetScriptContext(stid
);
3616 // failure getting a script context is fatal.
3617 NS_ENSURE_TRUE(context
!= nsnull
, NS_ERROR_UNEXPECTED
);
3619 if (aScript
->mScriptObject
.mObject
)
3620 rv
= ExecuteScript(context
, aScript
->mScriptObject
.mObject
);
3622 rv
= NS_ERROR_UNEXPECTED
;
3628 nsXULDocument::CreateElementFromPrototype(nsXULPrototypeElement
* aPrototype
,
3629 nsIContent
** aResult
)
3631 // Create a content model element from a prototype element.
3632 NS_PRECONDITION(aPrototype
!= nsnull
, "null ptr");
3634 return NS_ERROR_NULL_POINTER
;
3637 nsresult rv
= NS_OK
;
3640 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
3641 nsAutoString tagstr
;
3642 aPrototype
->mNodeInfo
->GetQualifiedName(tagstr
);
3644 nsCAutoString tagstrC
;
3645 tagstrC
.AssignWithConversion(tagstr
);
3646 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
3647 ("xul: creating <%s> from prototype",
3652 nsCOMPtr
<nsIContent
> result
;
3654 if (aPrototype
->mNodeInfo
->NamespaceEquals(kNameSpaceID_XUL
)) {
3655 // If it's a XUL element, it'll be lightweight until somebody
3657 rv
= nsXULElement::Create(aPrototype
, this, PR_TRUE
, getter_AddRefs(result
));
3658 if (NS_FAILED(rv
)) return rv
;
3661 // If it's not a XUL element, it's gonna be heavyweight no matter
3662 // what. So we need to copy everything out of the prototype
3663 // into the element. Get a nodeinfo from our nodeinfo manager
3665 nsCOMPtr
<nsINodeInfo
> newNodeInfo
;
3666 newNodeInfo
= mNodeInfoManager
->GetNodeInfo(aPrototype
->mNodeInfo
->NameAtom(),
3667 aPrototype
->mNodeInfo
->GetPrefixAtom(),
3668 aPrototype
->mNodeInfo
->NamespaceID());
3669 if (!newNodeInfo
) return NS_ERROR_OUT_OF_MEMORY
;
3670 rv
= NS_NewElement(getter_AddRefs(result
), newNodeInfo
->NamespaceID(),
3671 newNodeInfo
, PR_FALSE
);
3672 if (NS_FAILED(rv
)) return rv
;
3675 if (result
&& newNodeInfo
->NamespaceID() > kNameSpaceID_LastBuiltin
) {
3676 result
->BeginAddingChildren();
3680 rv
= AddAttributes(aPrototype
, result
);
3681 if (NS_FAILED(rv
)) return rv
;
3684 result
.swap(*aResult
);
3690 nsXULDocument::CreateOverlayElement(nsXULPrototypeElement
* aPrototype
,
3691 nsIContent
** aResult
)
3695 nsCOMPtr
<nsIContent
> element
;
3696 rv
= CreateElementFromPrototype(aPrototype
, getter_AddRefs(element
));
3697 if (NS_FAILED(rv
)) return rv
;
3699 OverlayForwardReference
* fwdref
=
3700 new OverlayForwardReference(this, element
);
3702 return NS_ERROR_OUT_OF_MEMORY
;
3704 // transferring ownership to ya...
3705 rv
= AddForwardReference(fwdref
);
3706 if (NS_FAILED(rv
)) return rv
;
3708 NS_ADDREF(*aResult
= element
);
3713 nsXULDocument::AddAttributes(nsXULPrototypeElement
* aPrototype
,
3714 nsIContent
* aElement
)
3718 for (PRUint32 i
= 0; i
< aPrototype
->mNumAttributes
; ++i
) {
3719 nsXULPrototypeAttribute
* protoattr
= &(aPrototype
->mAttributes
[i
]);
3720 nsAutoString valueStr
;
3721 protoattr
->mValue
.ToString(valueStr
);
3723 rv
= aElement
->SetAttr(protoattr
->mName
.NamespaceID(),
3724 protoattr
->mName
.LocalName(),
3725 protoattr
->mName
.GetPrefix(),
3728 if (NS_FAILED(rv
)) return rv
;
3736 nsXULDocument::CheckTemplateBuilderHookup(nsIContent
* aElement
,
3737 PRBool
* aNeedsHookup
)
3739 // See if the element already has a `database' attribute. If it
3740 // does, then the template builder has already been created.
3742 // XXX This approach will crash and burn (well, maybe not _that_
3743 // bad) if aElement is not a XUL element.
3745 // XXXvarga Do we still want to support non XUL content?
3746 nsCOMPtr
<nsIDOMXULElement
> xulElement
= do_QueryInterface(aElement
);
3748 nsCOMPtr
<nsIRDFCompositeDataSource
> ds
;
3749 xulElement
->GetDatabase(getter_AddRefs(ds
));
3751 *aNeedsHookup
= PR_FALSE
;
3756 // Check aElement for a 'datasources' attribute, if it has
3757 // one a XUL template builder needs to be hooked up.
3758 *aNeedsHookup
= aElement
->HasAttr(kNameSpaceID_None
,
3759 nsGkAtoms::datasources
);
3763 /* static */ nsresult
3764 nsXULDocument::CreateTemplateBuilder(nsIContent
* aElement
)
3766 // Check if need to construct a tree builder or content builder.
3767 PRBool isTreeBuilder
= PR_FALSE
;
3769 nsIDocument
*document
= aElement
->GetOwnerDoc();
3770 NS_ASSERTION(document
, "no document");
3771 NS_ENSURE_TRUE(document
, NS_ERROR_UNEXPECTED
);
3773 PRInt32 nameSpaceID
;
3774 nsIAtom
* baseTag
= document
->BindingManager()->
3775 ResolveTag(aElement
, &nameSpaceID
);
3777 if ((nameSpaceID
== kNameSpaceID_XUL
) && (baseTag
== nsGkAtoms::tree
)) {
3778 // By default, we build content for a tree and then we attach
3779 // the tree content view. However, if the `dont-build-content'
3780 // flag is set, then we we'll attach a tree builder which
3781 // directly implements the tree view.
3784 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::flags
, flags
);
3785 if (flags
.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
3786 isTreeBuilder
= PR_TRUE
;
3790 if (isTreeBuilder
) {
3791 // Create and initialize a tree builder.
3792 nsCOMPtr
<nsIXULTemplateBuilder
> builder
=
3793 do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
3796 return NS_ERROR_FAILURE
;
3798 builder
->Init(aElement
);
3800 // Create a <treechildren> if one isn't there already.
3801 // XXXvarga what about attributes?
3802 nsCOMPtr
<nsIContent
> bodyContent
;
3803 nsXULContentUtils::FindChildByTag(aElement
, kNameSpaceID_XUL
,
3804 nsGkAtoms::treechildren
,
3805 getter_AddRefs(bodyContent
));
3807 if (! bodyContent
) {
3808 nsresult rv
= document
->CreateElem(nsGkAtoms::treechildren
,
3809 nsnull
, kNameSpaceID_XUL
,
3811 getter_AddRefs(bodyContent
));
3812 NS_ENSURE_SUCCESS(rv
, rv
);
3814 aElement
->AppendChildTo(bodyContent
, PR_FALSE
);
3818 // Create and initialize a content builder.
3819 nsCOMPtr
<nsIXULTemplateBuilder
> builder
3820 = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
3823 return NS_ERROR_FAILURE
;
3825 builder
->Init(aElement
);
3826 builder
->CreateContents(aElement
, PR_FALSE
);
3834 nsXULDocument::AddPrototypeSheets()
3838 const nsCOMArray
<nsIURI
>& sheets
= mCurrentPrototype
->GetStyleSheetReferences();
3840 for (PRInt32 i
= 0; i
< sheets
.Count(); i
++) {
3841 nsCOMPtr
<nsIURI
> uri
= sheets
[i
];
3843 nsCOMPtr
<nsICSSStyleSheet
> incompleteSheet
;
3844 rv
= CSSLoader()->LoadSheet(uri
,
3845 mCurrentPrototype
->DocumentPrincipal(),
3846 this, getter_AddRefs(incompleteSheet
));
3848 // XXXldb We need to prevent bogus sheets from being held in the
3849 // prototype's list, but until then, don't propagate the failure
3850 // from LoadSheet (and thus exit the loop).
3851 if (NS_SUCCEEDED(rv
)) {
3853 if (!mOverlaySheets
.AppendObject(incompleteSheet
)) {
3854 return NS_ERROR_OUT_OF_MEMORY
;
3863 //----------------------------------------------------------------------
3865 // nsXULDocument::OverlayForwardReference
3868 nsForwardReference::Result
3869 nsXULDocument::OverlayForwardReference::Resolve()
3871 // Resolve a forward reference from an overlay element; attempt to
3872 // hook it up into the main document.
3874 nsCOMPtr
<nsIContent
> target
;
3876 PRBool notify
= PR_FALSE
;
3877 nsIPresShell
*shell
= mDocument
->GetPrimaryShell();
3879 shell
->GetDidInitialReflow(¬ify
);
3882 mOverlay
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
3884 // mOverlay is a direct child of <overlay> and has no id.
3885 // Insert it under the root element in the base document.
3886 nsIContent
* root
= mDocument
->GetRootContent();
3888 return eResolve_Error
;
3891 rv
= mDocument
->InsertElement(root
, mOverlay
, notify
);
3892 if (NS_FAILED(rv
)) return eResolve_Error
;
3897 // The hook-up element has an id, try to match it with an element
3898 // with the same id in the base document.
3899 nsCOMPtr
<nsIDOMElement
> domtarget
;
3900 rv
= mDocument
->GetElementById(id
, getter_AddRefs(domtarget
));
3901 if (NS_FAILED(rv
)) return eResolve_Error
;
3903 // If we can't find the element in the document, defer the hookup
3905 target
= do_QueryInterface(domtarget
);
3906 NS_ASSERTION(!domtarget
|| target
, "not an nsIContent");
3908 return eResolve_Later
;
3910 // While merging, set the default script language of the element to be
3911 // the language from the overlay - attributes will then be correctly
3912 // hooked up with the appropriate language (while child nodes ignore
3913 // the default language - they have it in their proto.
3914 PRUint32 oldDefLang
= target
->GetScriptTypeID();
3915 target
->SetScriptTypeID(mOverlay
->GetScriptTypeID());
3916 rv
= Merge(target
, mOverlay
, notify
);
3917 target
->SetScriptTypeID(oldDefLang
);
3918 if (NS_FAILED(rv
)) return eResolve_Error
;
3922 // Add child and any descendants to the element map
3923 rv
= mDocument
->AddSubtreeToDocument(target
);
3924 if (NS_FAILED(rv
)) return eResolve_Error
;
3928 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
3930 idC
.AssignWithConversion(id
);
3931 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
3932 ("xul: overlay resolved '%s'",
3937 mResolved
= PR_TRUE
;
3938 return eResolve_Succeeded
;
3944 nsXULDocument::OverlayForwardReference::Merge(nsIContent
* aTargetNode
,
3945 nsIContent
* aOverlayNode
,
3948 // This function is given:
3949 // aTargetNode: the node in the document whose 'id' attribute
3950 // matches a toplevel node in our overlay.
3951 // aOverlayNode: the node in the overlay document that matches
3952 // a node in the actual document.
3953 // aNotify: whether or not content manipulation methods should
3954 // use the aNotify parameter. After the initial
3955 // reflow (i.e. in the dynamic overlay merge case),
3956 // we want all the content manipulation methods we
3957 // call to notify so that frames are constructed
3958 // etc. Otherwise do not, since that's during initial
3959 // document construction before StartLayout has been
3960 // called which will do everything for us.
3962 // This function merges the tree from the overlay into the tree in
3963 // the document, overwriting attributes and appending child content
3964 // nodes appropriately. (See XUL overlay reference for details)
3968 // Merge attributes from the overlay content node to that of the
3971 const nsAttrName
* name
;
3972 for (i
= 0; (name
= aOverlayNode
->GetAttrNameAt(i
)); ++i
) {
3973 // We don't want to swap IDs, they should be the same.
3974 if (name
->Equals(nsGkAtoms::id
))
3977 // In certain cases merging command or observes is unsafe, so don't.
3979 if (aTargetNode
->NodeInfo()->Equals(nsGkAtoms::observes
,
3983 if (name
->Equals(nsGkAtoms::observes
) &&
3984 aTargetNode
->HasAttr(kNameSpaceID_None
, nsGkAtoms::observes
))
3987 if (name
->Equals(nsGkAtoms::command
) &&
3988 aTargetNode
->HasAttr(kNameSpaceID_None
, nsGkAtoms::command
) &&
3989 !aTargetNode
->NodeInfo()->Equals(nsGkAtoms::key
,
3990 kNameSpaceID_XUL
) &&
3991 !aTargetNode
->NodeInfo()->Equals(nsGkAtoms::menuitem
,
3996 PRInt32 nameSpaceID
= name
->NamespaceID();
3997 nsIAtom
* attr
= name
->LocalName();
3998 nsIAtom
* prefix
= name
->GetPrefix();
4001 aOverlayNode
->GetAttr(nameSpaceID
, attr
, value
);
4003 // Element in the overlay has the 'removeelement' attribute set
4004 // so remove it from the actual document.
4005 if (attr
== nsGkAtoms::removeelement
&&
4006 value
.EqualsLiteral("true")) {
4008 rv
= RemoveElement(aTargetNode
->GetParent(), aTargetNode
);
4009 if (NS_FAILED(rv
)) return rv
;
4014 rv
= aTargetNode
->SetAttr(nameSpaceID
, attr
, prefix
, value
, aNotify
);
4015 if (NS_FAILED(rv
)) return rv
;
4019 // Walk our child nodes, looking for elements that have the 'id'
4020 // attribute set. If we find any, we must do a parent check in the
4021 // actual document to ensure that the structure matches that of
4022 // the actual document. If it does, we can call ourselves and attempt
4023 // to merge inside that subtree. If not, we just append the tree to
4024 // the parent like any other.
4026 PRUint32 childCount
= aOverlayNode
->GetChildCount();
4028 // This must be a strong reference since it will be the only
4029 // reference to a content object during part of this loop.
4030 nsCOMPtr
<nsIContent
> currContent
;
4032 for (i
= 0; i
< childCount
; ++i
) {
4033 currContent
= aOverlayNode
->GetChildAt(0);
4036 currContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
4038 nsCOMPtr
<nsIDOMElement
> nodeInDocument
;
4039 if (!id
.IsEmpty()) {
4040 nsCOMPtr
<nsIDOMDocument
> domDocument(
4041 do_QueryInterface(aTargetNode
->GetDocument()));
4042 if (!domDocument
) return NS_ERROR_FAILURE
;
4044 rv
= domDocument
->GetElementById(id
, getter_AddRefs(nodeInDocument
));
4045 if (NS_FAILED(rv
)) return rv
;
4048 // The item has an 'id' attribute set, and we need to check with
4049 // the actual document to see if an item with this id exists at
4050 // this locale. If so, we want to merge the subtree under that
4051 // node. Otherwise, we just do an append as if the element had
4053 if (nodeInDocument
) {
4054 // Given two parents, aTargetNode and aOverlayNode, we want
4055 // to call merge on currContent if we find an associated
4056 // node in the document with the same id as currContent that
4057 // also has aTargetNode as its parent.
4059 nsCOMPtr
<nsIDOMNode
> nodeParent
;
4060 rv
= nodeInDocument
->GetParentNode(getter_AddRefs(nodeParent
));
4061 if (NS_FAILED(rv
)) return rv
;
4062 nsCOMPtr
<nsIDOMElement
> elementParent(do_QueryInterface(nodeParent
));
4064 nsAutoString parentID
;
4065 elementParent
->GetAttribute(NS_LITERAL_STRING("id"), parentID
);
4066 if (aTargetNode
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::id
,
4067 parentID
, eCaseMatters
)) {
4068 // The element matches. "Go Deep!"
4069 nsCOMPtr
<nsIContent
> childDocumentContent(do_QueryInterface(nodeInDocument
));
4070 rv
= Merge(childDocumentContent
, currContent
, aNotify
);
4071 if (NS_FAILED(rv
)) return rv
;
4072 rv
= aOverlayNode
->RemoveChildAt(0, PR_FALSE
);
4073 if (NS_FAILED(rv
)) return rv
;
4079 rv
= aOverlayNode
->RemoveChildAt(0, PR_FALSE
);
4080 if (NS_FAILED(rv
)) return rv
;
4082 rv
= InsertElement(aTargetNode
, currContent
, aNotify
);
4083 if (NS_FAILED(rv
)) return rv
;
4091 nsXULDocument::OverlayForwardReference::~OverlayForwardReference()
4094 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
) && !mResolved
) {
4096 mOverlay
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, id
);
4099 idC
.AssignWithConversion(id
);
4100 PR_LOG(gXULLog
, PR_LOG_WARNING
,
4101 ("xul: overlay failed to resolve '%s'",
4108 //----------------------------------------------------------------------
4110 // nsXULDocument::BroadcasterHookup
4113 nsForwardReference::Result
4114 nsXULDocument::BroadcasterHookup::Resolve()
4119 rv
= mDocument
->CheckBroadcasterHookup(mObservesElement
, &listener
, &mResolved
);
4120 if (NS_FAILED(rv
)) return eResolve_Error
;
4122 return mResolved
? eResolve_Succeeded
: eResolve_Later
;
4126 nsXULDocument::BroadcasterHookup::~BroadcasterHookup()
4129 if (PR_LOG_TEST(gXULLog
, PR_LOG_WARNING
) && !mResolved
) {
4130 // Tell the world we failed
4133 nsIAtom
*tag
= mObservesElement
->Tag();
4135 nsAutoString broadcasterID
;
4136 nsAutoString attribute
;
4138 if (tag
== nsGkAtoms::observes
) {
4139 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, broadcasterID
);
4140 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
, attribute
);
4143 mObservesElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::observes
, broadcasterID
);
4144 attribute
.AssignLiteral("*");
4147 nsAutoString tagStr
;
4148 rv
= tag
->ToString(tagStr
);
4149 if (NS_FAILED(rv
)) return;
4151 nsCAutoString tagstrC
, attributeC
,broadcasteridC
;
4152 tagstrC
.AssignWithConversion(tagStr
);
4153 attributeC
.AssignWithConversion(attribute
);
4154 broadcasteridC
.AssignWithConversion(broadcasterID
);
4155 PR_LOG(gXULLog
, PR_LOG_WARNING
,
4156 ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
4159 broadcasteridC
.get()));
4165 //----------------------------------------------------------------------
4167 // nsXULDocument::TemplateBuilderHookup
4170 nsForwardReference::Result
4171 nsXULDocument::TemplateBuilderHookup::Resolve()
4174 nsresult rv
= CheckTemplateBuilderHookup(mElement
, &needsHookup
);
4176 return eResolve_Error
;
4179 rv
= CreateTemplateBuilder(mElement
);
4181 return eResolve_Error
;
4184 return eResolve_Succeeded
;
4188 //----------------------------------------------------------------------
4191 nsXULDocument::FindBroadcaster(nsIContent
* aElement
,
4192 nsIDOMElement
** aListener
,
4193 nsString
& aBroadcasterID
,
4194 nsString
& aAttribute
,
4195 nsIDOMElement
** aBroadcaster
)
4197 NS_ASSERTION(aElement
->IsNodeOfType(nsINode::eELEMENT
),
4198 "Only pass elements into FindBroadcaster!");
4201 nsINodeInfo
*ni
= aElement
->NodeInfo();
4202 *aListener
= nsnull
;
4203 *aBroadcaster
= nsnull
;
4205 if (ni
->Equals(nsGkAtoms::observes
, kNameSpaceID_XUL
)) {
4206 // It's an <observes> element, which means that the actual
4207 // listener is the _parent_ node. This element should have an
4208 // 'element' attribute that specifies the ID of the
4209 // broadcaster element, and an 'attribute' element, which
4210 // specifies the name of the attribute to observe.
4211 nsIContent
* parent
= aElement
->GetParent();
4213 // <observes> is the root element
4214 return NS_FINDBROADCASTER_NOT_FOUND
;
4217 // If we're still parented by an 'overlay' tag, then we haven't
4218 // made it into the real document yet. Defer hookup.
4219 if (parent
->NodeInfo()->Equals(nsGkAtoms::overlay
,
4220 kNameSpaceID_XUL
)) {
4221 return NS_FINDBROADCASTER_AWAIT_OVERLAYS
;
4224 if (NS_FAILED(CallQueryInterface(parent
, aListener
)))
4225 *aListener
= nsnull
;
4227 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::element
, aBroadcasterID
);
4228 if (aBroadcasterID
.IsEmpty()) {
4229 return NS_FINDBROADCASTER_NOT_FOUND
;
4231 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::attribute
, aAttribute
);
4234 // It's a generic element, which means that we'll use the
4235 // value of the 'observes' attribute to determine the ID of
4236 // the broadcaster element, and we'll watch _all_ of its
4238 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::observes
, aBroadcasterID
);
4240 // Bail if there's no aBroadcasterID
4241 if (aBroadcasterID
.IsEmpty()) {
4242 // Try the command attribute next.
4243 aElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::command
, aBroadcasterID
);
4244 if (!aBroadcasterID
.IsEmpty()) {
4245 // We've got something in the command attribute. We
4246 // only treat this as a normal broadcaster if we are
4247 // not a menuitem or a key.
4249 if (ni
->Equals(nsGkAtoms::menuitem
, kNameSpaceID_XUL
) ||
4250 ni
->Equals(nsGkAtoms::key
, kNameSpaceID_XUL
)) {
4251 return NS_FINDBROADCASTER_NOT_FOUND
;
4255 return NS_FINDBROADCASTER_NOT_FOUND
;
4259 if (NS_FAILED(CallQueryInterface(aElement
, aListener
)))
4260 *aListener
= nsnull
;
4262 aAttribute
.AssignLiteral("*");
4265 // Make sure we got a valid listener.
4266 NS_ENSURE_TRUE(*aListener
, NS_ERROR_UNEXPECTED
);
4268 // Try to find the broadcaster element in the document.
4269 rv
= GetElementById(aBroadcasterID
, aBroadcaster
);
4270 if (NS_FAILED(rv
)) return rv
;
4272 // If we can't find the broadcaster, then we'll need to defer the
4273 // hookup. We may need to resolve some of the other overlays
4275 if (! *aBroadcaster
) {
4276 return NS_FINDBROADCASTER_AWAIT_OVERLAYS
;
4279 return NS_FINDBROADCASTER_FOUND
;
4283 nsXULDocument::CheckBroadcasterHookup(nsIContent
* aElement
,
4284 PRBool
* aNeedsHookup
,
4285 PRBool
* aDidResolve
)
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
= PR_FALSE
;
4294 nsCOMPtr
<nsIDOMElement
> listener
;
4295 nsAutoString broadcasterID
;
4296 nsAutoString attribute
;
4297 nsCOMPtr
<nsIDOMElement
> broadcaster
;
4299 rv
= FindBroadcaster(aElement
, getter_AddRefs(listener
),
4300 broadcasterID
, attribute
, getter_AddRefs(broadcaster
));
4302 case NS_FINDBROADCASTER_NOT_FOUND
:
4303 *aNeedsHookup
= PR_FALSE
;
4305 case NS_FINDBROADCASTER_AWAIT_OVERLAYS
:
4306 *aNeedsHookup
= PR_TRUE
;
4308 case NS_FINDBROADCASTER_FOUND
:
4314 rv
= AddBroadcastListenerFor(broadcaster
, listener
, attribute
);
4315 if (NS_FAILED(rv
)) return rv
;
4318 // Tell the world we succeeded
4319 if (PR_LOG_TEST(gXULLog
, PR_LOG_NOTICE
)) {
4320 nsCOMPtr
<nsIContent
> content
=
4321 do_QueryInterface(listener
);
4323 NS_ASSERTION(content
!= nsnull
, "not an nsIContent");
4327 nsAutoString tagStr
;
4328 rv
= content
->Tag()->ToString(tagStr
);
4329 if (NS_FAILED(rv
)) return rv
;
4331 nsCAutoString tagstrC
, attributeC
,broadcasteridC
;
4332 tagstrC
.AssignWithConversion(tagStr
);
4333 attributeC
.AssignWithConversion(attribute
);
4334 broadcasteridC
.AssignWithConversion(broadcasterID
);
4335 PR_LOG(gXULLog
, PR_LOG_NOTICE
,
4336 ("xul: broadcaster hookup <%s attribute='%s'> to %s",
4339 broadcasteridC
.get()));
4343 *aNeedsHookup
= PR_FALSE
;
4344 *aDidResolve
= PR_TRUE
;
4349 nsXULDocument::InsertElement(nsIContent
* aParent
, nsIContent
* aChild
, PRBool aNotify
)
4351 // Insert aChild appropriately into aParent, accounting for a
4352 // 'pos' attribute set on aChild.
4355 nsAutoString posStr
;
4356 PRBool wasInserted
= PR_FALSE
;
4358 // insert after an element of a given id
4359 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::insertafter
, posStr
);
4360 PRBool isInsertAfter
= PR_TRUE
;
4362 if (posStr
.IsEmpty()) {
4363 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::insertbefore
, posStr
);
4364 isInsertAfter
= PR_FALSE
;
4367 if (!posStr
.IsEmpty()) {
4368 nsCOMPtr
<nsIDOMDocument
> domDocument(
4369 do_QueryInterface(aParent
->GetDocument()));
4370 nsCOMPtr
<nsIDOMElement
> domElement
;
4372 char* str
= ToNewCString(posStr
);
4374 char* token
= nsCRT::strtok(str
, ", ", &rest
);
4377 rv
= domDocument
->GetElementById(NS_ConvertASCIItoUTF16(token
),
4378 getter_AddRefs(domElement
));
4382 token
= nsCRT::strtok(rest
, ", ", &rest
);
4384 nsMemory::Free(str
);
4389 nsCOMPtr
<nsIContent
> content(do_QueryInterface(domElement
));
4390 NS_ASSERTION(content
!= nsnull
, "null ptr");
4392 return NS_ERROR_UNEXPECTED
;
4394 PRInt32 pos
= aParent
->IndexOf(content
);
4397 pos
= isInsertAfter
? pos
+ 1 : pos
;
4398 rv
= aParent
->InsertChildAt(aChild
, pos
, aNotify
);
4402 wasInserted
= PR_TRUE
;
4409 aChild
->GetAttr(kNameSpaceID_None
, nsGkAtoms::position
, posStr
);
4410 if (!posStr
.IsEmpty()) {
4411 // Positions are one-indexed.
4412 PRInt32 pos
= posStr
.ToInteger(reinterpret_cast<PRInt32
*>(&rv
));
4413 // Note: if the insertion index (which is |pos - 1|) would be less
4414 // than 0 or greater than the number of children aParent has, then
4415 // don't insert, since the position is bogus. Just skip on to
4417 if (NS_SUCCEEDED(rv
) && pos
> 0 &&
4418 PRUint32(pos
- 1) <= aParent
->GetChildCount()) {
4419 rv
= aParent
->InsertChildAt(aChild
, pos
- 1, aNotify
);
4420 if (NS_SUCCEEDED(rv
))
4421 wasInserted
= PR_TRUE
;
4422 // If the insertion fails, then we should still
4423 // attempt an append. Thus, rather than returning rv
4424 // immediately, we fall through to the final
4425 // "catch-all" case that just does an AppendChildTo.
4430 if (! wasInserted
) {
4431 rv
= aParent
->AppendChildTo(aChild
, aNotify
);
4432 if (NS_FAILED(rv
)) return rv
;
4438 nsXULDocument::RemoveElement(nsIContent
* aParent
, nsIContent
* aChild
)
4440 PRInt32 nodeOffset
= aParent
->IndexOf(aChild
);
4442 return aParent
->RemoveChildAt(nodeOffset
, PR_TRUE
);
4445 //----------------------------------------------------------------------
4447 // CachedChromeStreamListener
4450 nsXULDocument::CachedChromeStreamListener::CachedChromeStreamListener(nsXULDocument
* aDocument
, PRBool aProtoLoaded
)
4451 : mDocument(aDocument
),
4452 mProtoLoaded(aProtoLoaded
)
4454 NS_ADDREF(mDocument
);
4458 nsXULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
4460 NS_RELEASE(mDocument
);
4464 NS_IMPL_ISUPPORTS2(nsXULDocument::CachedChromeStreamListener
,
4465 nsIRequestObserver
, nsIStreamListener
)
4468 nsXULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest
*request
,
4469 nsISupports
* acontext
)
4476 nsXULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest
*request
,
4477 nsISupports
* aContext
,
4483 return mDocument
->OnPrototypeLoadDone(PR_TRUE
);
4488 nsXULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest
*request
,
4489 nsISupports
* aContext
,
4490 nsIInputStream
* aInStr
,
4491 PRUint32 aSourceOffset
,
4494 NS_NOTREACHED("CachedChromeStream doesn't receive data");
4495 return NS_ERROR_UNEXPECTED
;
4498 //----------------------------------------------------------------------
4503 nsXULDocument::ParserObserver::ParserObserver(nsXULDocument
* aDocument
,
4504 nsXULPrototypeDocument
* aPrototype
)
4505 : mDocument(aDocument
), mPrototype(aPrototype
)
4509 nsXULDocument::ParserObserver::~ParserObserver()
4513 NS_IMPL_ISUPPORTS1(nsXULDocument::ParserObserver
, nsIRequestObserver
)
4516 nsXULDocument::ParserObserver::OnStartRequest(nsIRequest
*request
,
4517 nsISupports
* aContext
)
4519 // Guard against buggy channels calling OnStartRequest multiple times.
4521 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
4522 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
4523 if (channel
&& secMan
) {
4524 nsCOMPtr
<nsIPrincipal
> principal
;
4525 secMan
->GetChannelPrincipal(channel
, getter_AddRefs(principal
));
4527 // Failure there is ok -- it'll just set a (safe) null principal
4528 mPrototype
->SetDocumentPrincipal(principal
);
4531 // Make sure to avoid cycles
4532 mPrototype
= nsnull
;
4539 nsXULDocument::ParserObserver::OnStopRequest(nsIRequest
*request
,
4540 nsISupports
* aContext
,
4543 nsresult rv
= NS_OK
;
4545 if (NS_FAILED(aStatus
)) {
4546 // If an overlay load fails, we need to nudge the prototype
4548 nsCOMPtr
<nsIChannel
> aChannel
= do_QueryInterface(request
);
4550 nsCOMPtr
<nsIURI
> uri
;
4551 aChannel
->GetOriginalURI(getter_AddRefs(uri
));
4553 mDocument
->ReportMissingOverlay(uri
);
4557 rv
= mDocument
->ResumeWalk();
4560 // Drop the reference to the document to break cycle between the
4561 // document, the parser, the content sink, and the parser
4569 nsXULDocument::GetFocusController(nsIFocusController
** aFocusController
)
4571 nsCOMPtr
<nsIInterfaceRequestor
> ir
= do_QueryReferent(mDocumentContainer
);
4572 nsCOMPtr
<nsPIDOMWindow
> windowPrivate
= do_GetInterface(ir
);
4573 if (windowPrivate
) {
4574 NS_IF_ADDREF(*aFocusController
= windowPrivate
->GetRootFocusController());
4576 *aFocusController
= nsnull
;