1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla Communicator client code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* Chris Waterson <waterson@netscape.com>
24
* Ben Goodger <ben@netscape.com>
25
* Pete Collins <petejc@collab.net>
26
* Dan Rosen <dr@netscape.com>
27
* Johnny Stenback <jst@netscape.com>
30
* Alternatively, the contents of this file may be used under the terms of
31
* either the GNU General Public License Version 2 or later (the "GPL"), or
32
* 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 NPL, 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 NPL, 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".
59
2. The "element map" maps an RDF resource to the elements whose 'id'
60
or 'ref' attributes refer to that resource. We re-use the element
61
map to support the HTML-like 'getElementById()' method.
65
// Note the ALPHABETICAL ORDERING
66
#include "nsXULDocument.h"
68
#include "nsDOMError.h"
69
#include "nsIBoxObject.h"
70
#include "nsIChromeRegistry.h"
71
#include "nsIContentSink.h" // for NS_CONTENT_ID_COUNTER_BASE
72
#include "nsIScrollableView.h"
73
#include "nsIContentViewer.h"
74
#include "nsGUIEvent.h"
75
#include "nsIDOMXULElement.h"
76
#include "nsIElementFactory.h"
77
#include "nsIPrincipal.h"
78
#include "nsIPrivateDOMEvent.h"
79
#include "nsIRDFNode.h"
80
#include "nsIRDFRemoteDataSource.h"
81
#include "nsIRDFService.h"
82
#include "nsIStreamListener.h"
83
#include "nsITextContent.h"
85
#include "nsIDocShell.h"
86
#include "nsXULAtoms.h"
87
#include "nsIXULContent.h"
88
#include "nsIXULContentSink.h"
89
#include "nsXULContentUtils.h"
90
#include "nsIXULOverlayProvider.h"
91
#include "nsIXULPrototypeCache.h"
92
#include "nsNetUtil.h"
93
#include "nsParserCIID.h"
94
#include "nsPIBoxObject.h"
96
#include "nsILocalStore.h"
97
#include "nsRDFDOMNodeList.h"
98
#include "nsXPIDLString.h"
99
#include "nsPIDOMWindow.h"
100
#include "nsXULCommandDispatcher.h"
101
#include "nsXULDocument.h"
102
#include "nsXULElement.h"
105
#include "nsIFrame.h"
106
#include "nsIXBLService.h"
107
#include "nsCExternalHandlerService.h"
108
#include "nsIMIMEService.h"
109
#include "nsMimeTypes.h"
110
#include "nsIFastLoadService.h"
111
#include "nsIObjectInputStream.h"
112
#include "nsIObjectOutputStream.h"
113
#include "nsIFocusController.h"
114
#include "nsContentList.h"
115
#include "nsIScriptGlobalObject.h"
116
#include "nsIScriptGlobalObjectOwner.h"
117
#include "nsIScriptSecurityManager.h"
118
#include "nsContentUtils.h"
119
#include "nsIParser.h"
120
#include "nsICSSStyleSheet.h"
122
//----------------------------------------------------------------------
127
static NS_DEFINE_CID(kHTMLElementFactoryCID, NS_HTML_ELEMENT_FACTORY_CID);
128
static NS_DEFINE_CID(kLocalStoreCID, NS_LOCALSTORE_CID);
129
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
130
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
131
static NS_DEFINE_CID(kXMLElementFactoryCID, NS_XML_ELEMENT_FACTORY_CID);
132
static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID);
134
static PRBool IsChromeURI(nsIURI* aURI)
136
// why is this check a member function of nsXULDocument? -gagan
137
PRBool isChrome = PR_FALSE;
138
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome)
143
//----------------------------------------------------------------------
145
// Miscellaneous Constants
148
const nsForwardReference::Phase nsForwardReference::kPasses[] = {
149
nsForwardReference::eConstruction,
150
nsForwardReference::eHookup,
151
nsForwardReference::eDone
155
//----------------------------------------------------------------------
160
PRInt32 nsXULDocument::gRefCnt = 0;
162
nsIRDFService* nsXULDocument::gRDFService;
163
nsIRDFResource* nsXULDocument::kNC_persist;
164
nsIRDFResource* nsXULDocument::kNC_attribute;
165
nsIRDFResource* nsXULDocument::kNC_value;
167
nsIElementFactory* nsXULDocument::gHTMLElementFactory;
168
nsIElementFactory* nsXULDocument::gXMLElementFactory;
170
nsIXULPrototypeCache* nsXULDocument::gXULCache;
172
PRLogModuleInfo* nsXULDocument::gXULLog;
174
class nsProxyLoadStream : public nsIInputStream
182
nsProxyLoadStream(void) : mBuffer(nsnull)
186
virtual ~nsProxyLoadStream(void)
194
NS_IMETHOD Close(void)
200
NS_IMETHOD Available(PRUint32 *aLength)
202
*aLength = mSize - mIndex;
206
NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount)
208
PRUint32 readCount = 0;
209
while (mIndex < mSize && aCount > 0) {
210
*aBuf = mBuffer[mIndex];
216
*aReadCount = readCount;
220
NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure,
221
PRUint32 count, PRUint32 *_retval)
223
NS_NOTREACHED("ReadSegments");
224
return NS_ERROR_NOT_IMPLEMENTED;
227
NS_IMETHOD IsNonBlocking(PRBool *aNonBlocking)
229
*aNonBlocking = PR_TRUE;
234
void SetBuffer(const char* aBuffer, PRUint32 aSize)
242
NS_IMPL_ISUPPORTS1(nsProxyLoadStream, nsIInputStream)
244
//----------------------------------------------------------------------
246
// PlaceholderRequest
248
// This is a dummy request implementation that we add to the load
249
// group. It ensures that EndDocumentLoad() in the docshell doesn't
250
// fire before we've finished building the complete document content
254
class PlaceHolderRequest : public nsIChannel
257
PlaceHolderRequest();
258
virtual ~PlaceHolderRequest();
260
static PRInt32 gRefCnt;
263
nsCOMPtr<nsILoadGroup> mLoadGroup;
267
Create(nsIRequest** aResult);
272
NS_IMETHOD GetName(nsACString &result) {
273
return NS_ERROR_NOT_IMPLEMENTED;
275
NS_IMETHOD IsPending(PRBool *_retval) { *_retval = PR_TRUE; return NS_OK; }
276
NS_IMETHOD GetStatus(nsresult *status) { *status = NS_OK; return NS_OK; }
277
NS_IMETHOD Cancel(nsresult status) { return NS_OK; }
278
NS_IMETHOD Suspend(void) { return NS_OK; }
279
NS_IMETHOD Resume(void) { return NS_OK; }
280
NS_IMETHOD GetLoadGroup(nsILoadGroup * *aLoadGroup) { *aLoadGroup = mLoadGroup; NS_IF_ADDREF(*aLoadGroup); return NS_OK; }
281
NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup) { mLoadGroup = aLoadGroup; return NS_OK; }
282
NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_NORMAL; return NS_OK; }
283
NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
286
NS_IMETHOD GetOriginalURI(nsIURI* *aOriginalURI) { *aOriginalURI = gURI; NS_ADDREF(*aOriginalURI); return NS_OK; }
287
NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI) { gURI = aOriginalURI; NS_ADDREF(gURI); return NS_OK; }
288
NS_IMETHOD GetURI(nsIURI* *aURI) { *aURI = gURI; NS_ADDREF(*aURI); return NS_OK; }
289
NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; }
290
NS_IMETHOD Open(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; }
291
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; }
292
NS_IMETHOD GetOwner(nsISupports * *aOwner) { *aOwner = nsnull; return NS_OK; }
293
NS_IMETHOD SetOwner(nsISupports * aOwner) { return NS_OK; }
294
NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) { *aNotificationCallbacks = nsnull; return NS_OK; }
295
NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks) { return NS_OK; }
296
NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) { *aSecurityInfo = nsnull; return NS_OK; }
297
NS_IMETHOD GetContentType(nsACString &aContentType) { aContentType.Truncate(); return NS_OK; }
298
NS_IMETHOD SetContentType(const nsACString &aContentType) { return NS_OK; }
299
NS_IMETHOD GetContentCharset(nsACString &aContentCharset) { aContentCharset.Truncate(); return NS_OK; }
300
NS_IMETHOD SetContentCharset(const nsACString &aContentCharset) { return NS_OK; }
301
NS_IMETHOD GetContentLength(PRInt32 *aContentLength) { return NS_OK; }
302
NS_IMETHOD SetContentLength(PRInt32 aContentLength) { return NS_OK; }
305
PRInt32 PlaceHolderRequest::gRefCnt;
306
nsIURI* PlaceHolderRequest::gURI;
308
NS_IMPL_ADDREF(PlaceHolderRequest)
309
NS_IMPL_RELEASE(PlaceHolderRequest)
310
NS_IMPL_QUERY_INTERFACE2(PlaceHolderRequest, nsIRequest, nsIChannel)
313
PlaceHolderRequest::Create(nsIRequest** aResult)
315
PlaceHolderRequest* request = new PlaceHolderRequest();
317
return NS_ERROR_OUT_OF_MEMORY;
325
PlaceHolderRequest::PlaceHolderRequest()
328
if (gRefCnt++ == 0) {
330
rv = NS_NewURI(&gURI, NS_LITERAL_CSTRING("about:xul-master-placeholder"), nsnull);
331
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create about:xul-master-placeholder");
336
PlaceHolderRequest::~PlaceHolderRequest()
338
if (--gRefCnt == 0) {
343
//----------------------------------------------------------------------
345
struct BroadcasterMapEntry : public PLDHashEntryHdr {
346
nsIDOMElement* mBroadcaster; // [WEAK]
347
nsSmallVoidArray mListeners; // [OWNING] of BroadcastListener objects
350
struct BroadcastListener {
351
nsIDOMElement* mListener; // [WEAK] XXXwaterson crash waiting to happen!
352
nsCOMPtr<nsIAtom> mAttribute;
355
//----------------------------------------------------------------------
360
// NOTE! nsDocument::operator new() zeroes out all members, so
361
// don't bother initializing members to 0.
363
nsXULDocument::nsXULDocument(void)
364
: mResolutionPhase(nsForwardReference::eStart),
365
mState(eState_Master)
368
// NOTE! nsDocument::operator new() zeroes out all members, so don't
369
// bother initializing members to 0.
371
// Override the default in nsDocument
372
mCharacterSet.Assign(NS_LITERAL_CSTRING("UTF-8"));
375
nsXULDocument::~nsXULDocument()
377
NS_ASSERTION(mNextSrcLoadWaiter == nsnull,
378
"unreferenced document still waiting for script source to load?");
380
// Notify our observers here, we can't let the nsDocument
381
// destructor do that for us since some of the observers are
382
// deleted by the time we get there.
385
for (i = mObservers.Count() - 1; i >= 0; --i) {
386
// XXX Should this be a kungfudeathgrip?!!!!
387
nsIDocumentObserver* observer =
388
NS_STATIC_CAST(nsIDocumentObserver *, mObservers.ElementAt(i));
390
observer->DocumentWillBeDestroyed(this);
395
// In case we failed somewhere early on and the forward observer
396
// decls never got resolved.
397
DestroyForwardReferences();
399
// Destroy our broadcaster map.
400
if (mBroadcasterMap) {
401
PL_DHashTableDestroy(mBroadcasterMap);
405
nsCOMPtr<nsIRDFRemoteDataSource> remote =
406
do_QueryInterface(mLocalStore);
411
delete mTemplateBuilderTable;
413
if (--gRefCnt == 0) {
414
NS_IF_RELEASE(gRDFService);
416
NS_IF_RELEASE(kNC_persist);
417
NS_IF_RELEASE(kNC_attribute);
418
NS_IF_RELEASE(kNC_value);
420
NS_IF_RELEASE(gHTMLElementFactory);
421
NS_IF_RELEASE(gXMLElementFactory);
424
// Remove the current document here from the FastLoad table in
425
// case the document did not make it past StartLayout in
426
// ResumeWalk. The FastLoad table must be clear of entries so
427
// that the FastLoad file footer can be properly written.
429
gXULCache->RemoveFromFastLoadSet(mDocumentURI);
431
NS_RELEASE(gXULCache);
435
// The destructor of nsDocument will delete references to style
436
// sheets, but we don't want that if we're a popup document, so
437
// then we'll clear the stylesheets array here to prevent that
440
mStyleSheets.Clear();
443
// This is done in nsDocument::~nsDocument() too, but since this
444
// call ends up calling back into the document through virtual
445
// methods (nsIDocument::GetPrincipal()) we must do it here before
446
// we go out of nsXULDocument's destructor.
447
if (mNodeInfoManager) {
448
mNodeInfoManager->DropDocumentReference();
453
NS_NewXULDocument(nsIXULDocument** result)
455
NS_PRECONDITION(result != nsnull, "null ptr");
457
return NS_ERROR_NULL_POINTER;
459
nsXULDocument* doc = new nsXULDocument();
461
return NS_ERROR_OUT_OF_MEMORY;
466
if (NS_FAILED(rv = doc->Init())) {
476
//----------------------------------------------------------------------
478
// nsISupports interface
481
NS_IMPL_ADDREF_INHERITED(nsXULDocument, nsXMLDocument)
482
NS_IMPL_RELEASE_INHERITED(nsXULDocument, nsXMLDocument)
485
// QueryInterface implementation for nsXULDocument
486
NS_INTERFACE_MAP_BEGIN(nsXULDocument)
487
NS_INTERFACE_MAP_ENTRY(nsIXULDocument)
488
NS_INTERFACE_MAP_ENTRY(nsIDOMXULDocument)
489
NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
490
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XULDocument)
491
NS_INTERFACE_MAP_END_INHERITING(nsXMLDocument)
494
//----------------------------------------------------------------------
496
// nsIDocument interface
500
nsXULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
502
NS_NOTREACHED("Reset");
506
nsXULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup)
508
NS_NOTREACHED("ResetToURI");
511
// Override the nsDocument.cpp method to keep from returning the
512
// "cached XUL" type which is completely internal and may confuse
515
nsXULDocument::GetContentType(nsAString& aContentType)
517
aContentType.Assign(NS_LITERAL_STRING("application/vnd.mozilla.xul+xml"));
522
nsXULDocument::SetContentType(const nsAString& aContentType)
524
NS_ASSERTION(aContentType.Equals(NS_LITERAL_STRING("application/vnd.mozilla.xul+xml")),
525
"xul-documents always has content-type application/vnd.mozilla.xul+xml");
526
// Don't do anything, xul always has the mimetype
527
// application/vnd.mozilla.xul+xml
531
nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
532
nsILoadGroup* aLoadGroup,
533
nsISupports* aContainer,
534
nsIStreamListener **aDocListener,
535
PRBool aReset, nsIContentSink* aSink)
537
mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
539
mDocumentTitle.Truncate();
541
nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mDocumentURI));
542
NS_ENSURE_SUCCESS(rv, rv);
544
// XXXbz this code is repeated from nsDocument::Reset; we
545
// really need to refactor this part better.
546
PRBool isChrome = PR_FALSE;
547
PRBool isRes = PR_FALSE;
548
rv = mDocumentURI->SchemeIs("chrome", &isChrome);
549
rv |= mDocumentURI->SchemeIs("resource", &isRes);
551
if (NS_SUCCEEDED(rv) && !isChrome && !isRes) {
552
rv = aChannel->GetURI(getter_AddRefs(mDocumentURI));
553
NS_ENSURE_SUCCESS(rv, rv);
556
rv = ResetStylesheetsToURI(mDocumentURI);
557
if (NS_FAILED(rv)) return rv;
559
RetrieveRelevantHeaders(aChannel);
561
// Look in the chrome cache: we've got this puppy loaded
563
nsCOMPtr<nsIXULPrototypeDocument> proto;
564
if (IsChromeURI(mDocumentURI))
565
gXULCache->GetPrototype(mDocumentURI, getter_AddRefs(proto));
567
// Same comment as nsChromeProtocolHandler::NewChannel and
568
// nsXULDocument::ResumeWalk
571
// We don't abort on failure here because there are too many valid
572
// cases that can return failure, and the null-ness of |proto| is enough
573
// to trigger the fail-safe parse-from-disk solution. Example failure cases
574
// (for reference) include:
576
// NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
578
// other: the FastLoad cache file, XUL.mfl, could not be found, probably
579
// due to being accessed before a profile has been selected (e.g.
580
// loading chrome for the profile manager itself). This must be
584
// If we're racing with another document to load proto, wait till the
585
// load has finished loading before trying to add cloned style sheets.
586
// nsXULDocument::EndLoad will call proto->NotifyLoadDone, which will
587
// find all racing documents and notify them via OnPrototypeLoadDone,
588
// which will add style sheet clones to each document.
590
rv = proto->AwaitLoadDone(this, &loaded);
591
if (NS_FAILED(rv)) return rv;
593
mMasterPrototype = mCurrentPrototype = proto;
595
// Add cloned style sheet references only if the prototype has in
596
// fact already loaded. It may still be loading when we hit the XUL
599
rv = AddPrototypeSheets();
600
if (NS_FAILED(rv)) return rv;
603
// We need a listener, even if proto is not yet loaded, in which
604
// event the listener's OnStopRequest method does nothing, and all
605
// the interesting work happens below nsXULDocument::EndLoad, from
606
// the call there to mCurrentPrototype->NotifyLoadDone().
607
*aDocListener = new CachedChromeStreamListener(this, loaded);
609
return NS_ERROR_OUT_OF_MEMORY;
613
gXULCache->GetEnabled(&useXULCache);
614
PRBool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
617
// It's just a vanilla document load. Create a parser to deal
618
// with the stream n' stuff.
620
nsCOMPtr<nsIParser> parser;
621
rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
622
getter_AddRefs(parser));
623
if (NS_FAILED(rv)) return rv;
625
// Predicate mIsWritingFastLoad on the XUL cache being enabled,
626
// so we don't have to re-check whether the cache is enabled all
628
mIsWritingFastLoad = useXULCache;
630
nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
631
NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
632
if (NS_FAILED(rv)) return rv;
634
*aDocListener = listener;
636
parser->Parse(mDocumentURI);
638
// Put the current prototype, created under PrepareToLoad, into the
639
// XUL prototype cache now. We can't do this under PrepareToLoad or
640
// overlay loading will break; search for PutPrototype in ResumeWalk
641
// and see the comment there.
643
rv = gXULCache->PutPrototype(mCurrentPrototype);
644
if (NS_FAILED(rv)) return rv;
648
NS_IF_ADDREF(*aDocListener);
653
nsXULDocument::GetPrincipal()
655
NS_ASSERTION(mMasterPrototype, "Missing master prototype. See bug 169036");
656
NS_ENSURE_TRUE(mMasterPrototype, nsnull);
658
return mMasterPrototype->GetDocumentPrincipal();
662
nsXULDocument::SetPrincipal(nsIPrincipal *aPrincipal)
664
NS_NOTREACHED("SetPrincipal");
669
nsXULDocument::EndLoad()
673
// Whack the prototype document into the cache so that the next
674
// time somebody asks for it, they don't need to load it by hand.
676
nsCOMPtr<nsIURI> uri;
677
rv = mCurrentPrototype->GetURI(getter_AddRefs(uri));
678
if (NS_FAILED(rv)) return;
680
PRBool isChrome = IsChromeURI(uri);
682
// Remember if the XUL cache is on
684
gXULCache->GetEnabled(&useXULCache);
686
// If the current prototype is an overlay document (non-master prototype)
687
// and we're filling the FastLoad disk cache, tell the cache we're done
688
// loading it, and write the prototype.
689
if (useXULCache && mIsWritingFastLoad &&
690
mMasterPrototype != mCurrentPrototype &&
692
gXULCache->WritePrototype(mCurrentPrototype);
695
nsCOMPtr<nsIXULOverlayProvider> reg =
696
do_GetService(NS_CHROMEREGISTRY_CONTRACTID);
697
nsCOMPtr<nsICSSLoader> cssLoader = GetCSSLoader();
699
if (reg && cssLoader) {
700
nsCOMPtr<nsISimpleEnumerator> overlays;
701
reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
704
nsCOMPtr<nsISupports> next;
705
nsCOMPtr<nsIURI> sheetURI;
706
nsCOMPtr<nsICSSStyleSheet> sheet;
708
while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
710
overlays->GetNext(getter_AddRefs(next));
712
sheetURI = do_QueryInterface(next);
714
NS_ERROR("Chrome registry handed me a non-nsIURI object!");
718
if (useXULCache && IsChromeURI(sheetURI)) {
719
mCurrentPrototype->AddStyleSheetReference(sheetURI);
722
cssLoader->LoadAgentSheet(sheetURI, getter_AddRefs(sheet));
724
NS_WARNING("Couldn't load chrome style overlay.");
728
AddStyleSheet(sheet, 0);
733
// If it's a 'chrome:' prototype document, then notify any
734
// documents that raced to load the prototype, and awaited
735
// its load completion via proto->AwaitLoadDone().
736
rv = mCurrentPrototype->NotifyLoadDone();
737
if (NS_FAILED(rv)) return;
741
// Now walk the prototype to build content.
742
rv = PrepareToWalk();
743
if (NS_FAILED(rv)) return;
748
// Called back from nsXULPrototypeDocument::NotifyLoadDone for each XUL
749
// document that raced to start the same prototype document load, lost
750
// the race, but hit the XUL prototype cache because the winner filled
751
// the cache with the not-yet-loaded prototype object.
753
nsXULDocument::OnPrototypeLoadDone()
757
// Need to clone style sheet references now, as we couldn't do that
758
// in StartDocumentLoad, because the prototype may not have finished
759
// loading at that point.
760
rv = AddPrototypeSheets();
761
if (NS_FAILED(rv)) return rv;
763
// Now we must do for each secondary or later XUL document (those that
764
// lost the race to start the prototype document load) what is done by
765
// nsCachedChromeStreamListener::OnStopRequest for the primary document
766
// (the one that won the race to start the prototype load).
767
rv = PrepareToWalk();
768
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
769
if (NS_FAILED(rv)) return rv;
775
PR_STATIC_CALLBACK(PRBool)
776
ClearPresentationStuff(nsHashKey *aKey, void *aData, void* aClosure)
778
nsISupports *supp = NS_STATIC_CAST(nsISupports *, aData);
779
nsCOMPtr<nsPIBoxObject> boxObject(do_QueryInterface(supp));
782
boxObject->InvalidatePresentationStuff();
789
nsXULDocument::OnHide()
791
if (mBoxObjectTable) {
792
mBoxObjectTable->Enumerate(ClearPresentationStuff, nsnull);
798
PR_STATIC_CALLBACK(void)
799
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
801
BroadcasterMapEntry* entry =
802
NS_STATIC_CAST(BroadcasterMapEntry*, aEntry);
803
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
804
delete (BroadcastListener*)entry->mListeners[i];
807
// N.B. that we need to manually run the dtor because we
808
// constructed the nsSmallVoidArray object in-place.
809
entry->mListeners.~nsSmallVoidArray();
813
CanBroadcast(PRInt32 aNameSpaceID, nsIAtom* aAttribute)
815
// Don't push changes to the |id|, |ref|, or |persist| attribute.
816
if (aNameSpaceID == kNameSpaceID_None) {
817
if ((aAttribute == nsXULAtoms::id) ||
818
(aAttribute == nsXULAtoms::ref) ||
819
(aAttribute == nsXULAtoms::persist)) {
827
nsXULDocument::SynchronizeBroadcastListener(nsIDOMElement *aBroadcaster,
828
nsIDOMElement *aListener,
829
const nsAString &aAttr)
831
nsCOMPtr<nsIContent> broadcaster = do_QueryInterface(aBroadcaster);
832
nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
834
if (aAttr.Equals(NS_LITERAL_STRING("*"))) {
835
PRUint32 count = broadcaster->GetAttrCount();
836
while (count-- > 0) {
838
nsCOMPtr<nsIAtom> name;
839
nsCOMPtr<nsIAtom> prefix;
840
broadcaster->GetAttrNameAt(count, &nameSpaceID,
841
getter_AddRefs(name),
842
getter_AddRefs(prefix));
844
// _Don't_ push the |id|, |ref|, or |persist| attribute's value!
845
if (! CanBroadcast(nameSpaceID, name))
849
broadcaster->GetAttr(nameSpaceID, name, value);
850
listener->SetAttr(nameSpaceID, name, prefix, value, PR_FALSE);
853
// XXX we don't fire the |onbroadcast| handler during
854
// initial hookup: doing so would potentially run the
855
// |onbroadcast| handler before the |onload| handler,
856
// which could define JS properties that mask XBL
858
ExecuteOnBroadcastHandlerFor(broadcaster, aListener, name);
863
// Find out if the attribute is even present at all.
864
nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr);
867
nsresult rv = broadcaster->GetAttr(kNameSpaceID_None, name, value);
869
if (rv == NS_CONTENT_ATTR_NO_VALUE ||
870
rv == NS_CONTENT_ATTR_HAS_VALUE) {
871
listener->SetAttr(kNameSpaceID_None, name, value, PR_FALSE);
874
listener->UnsetAttr(kNameSpaceID_None, name, PR_FALSE);
878
// XXX we don't fire the |onbroadcast| handler during initial
879
// hookup: doing so would potentially run the |onbroadcast|
880
// handler before the |onload| handler, which could define JS
881
// properties that mask XBL properties, etc.
882
ExecuteOnBroadcastHandlerFor(broadcaster, aListener, name);
888
nsXULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
889
nsIDOMElement* aListener,
890
const nsAString& aAttr)
892
NS_ENSURE_ARG(aBroadcaster && aListener);
895
nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
902
rv = nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
909
static PLDHashTableOps gOps = {
913
PL_DHashVoidPtrKeyStub,
914
PL_DHashMatchEntryStub,
915
PL_DHashMoveEntryStub,
916
ClearBroadcasterMapEntry,
917
PL_DHashFinalizeStub,
921
if (! mBroadcasterMap) {
923
PL_NewDHashTable(&gOps, nsnull, sizeof(BroadcasterMapEntry),
926
if (! mBroadcasterMap)
927
return NS_ERROR_OUT_OF_MEMORY;
930
BroadcasterMapEntry* entry =
931
NS_STATIC_CAST(BroadcasterMapEntry*,
932
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
935
if (PL_DHASH_ENTRY_IS_FREE(entry)) {
937
NS_STATIC_CAST(BroadcasterMapEntry*,
938
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
942
return NS_ERROR_OUT_OF_MEMORY;
944
entry->mBroadcaster = aBroadcaster;
946
// N.B. placement new to construct the nsSmallVoidArray object
948
new (&entry->mListeners) nsSmallVoidArray();
951
// Only add the listener if it's not there already!
952
nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
954
BroadcastListener* bl;
955
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
956
bl = NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
958
if ((bl->mListener == aListener) && (bl->mAttribute == attr))
962
bl = new BroadcastListener;
964
return NS_ERROR_OUT_OF_MEMORY;
966
bl->mListener = aListener;
967
bl->mAttribute = attr;
969
entry->mListeners.AppendElement(bl);
971
SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
976
nsXULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
977
nsIDOMElement* aListener,
978
const nsAString& aAttr)
980
// If we haven't added any broadcast listeners, then there sure
981
// aren't any to remove.
982
if (! mBroadcasterMap)
985
BroadcasterMapEntry* entry =
986
NS_STATIC_CAST(BroadcasterMapEntry*,
987
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
990
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
991
nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
992
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
993
BroadcastListener* bl =
994
NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
996
if ((bl->mListener == aListener) && (bl->mAttribute == attr)) {
997
entry->mListeners.RemoveElementAt(i);
1000
if (entry->mListeners.Count() == 0)
1001
PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
1004
SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
1015
nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
1016
nsIDOMElement* aListener,
1019
// Now we execute the onchange handler in the context of the
1020
// observer. We need to find the observer in order to
1021
// execute the handler.
1023
nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
1024
PRUint32 count = listener->GetChildCount();
1025
for (PRUint32 i = 0; i < count; ++i) {
1026
// Look for an <observes> element beneath the listener. This
1027
// ought to have an |element| attribute that refers to
1028
// aBroadcaster, and an |attribute| element that tells us what
1029
// attriubtes we're listening for.
1030
nsIContent *child = listener->GetChildAt(i);
1032
nsINodeInfo *ni = child->GetNodeInfo();
1033
if (!ni || !ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL))
1036
// Is this the element that was listening to us?
1037
nsAutoString listeningToID;
1038
child->GetAttr(kNameSpaceID_None, nsXULAtoms::element, listeningToID);
1040
nsAutoString broadcasterID;
1041
aBroadcaster->GetAttr(kNameSpaceID_None, nsXULAtoms::id, broadcasterID);
1043
if (listeningToID != broadcasterID)
1046
// We are observing the broadcaster, but is this the right
1048
nsAutoString listeningToAttribute;
1049
child->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute,
1050
listeningToAttribute);
1052
if (!aAttr->Equals(listeningToAttribute) &&
1053
listeningToAttribute != NS_LITERAL_STRING("*")) {
1057
// This is the right <observes> element. Execute the
1058
// |onbroadcast| event handler
1059
nsEvent event(NS_XUL_BROADCAST);
1061
PRInt32 j = mPresShells.Count();
1063
nsCOMPtr<nsIPresShell> shell =
1064
NS_STATIC_CAST(nsIPresShell*, mPresShells[j]);
1066
nsCOMPtr<nsIPresContext> aPresContext;
1067
shell->GetPresContext(getter_AddRefs(aPresContext));
1069
// Handle the DOM event
1070
nsEventStatus status = nsEventStatus_eIgnore;
1071
child->HandleDOMEvent(aPresContext, &event, nsnull,
1072
NS_EVENT_FLAG_INIT, &status);
1080
nsXULDocument::AttributeChanged(nsIContent* aElement, PRInt32 aNameSpaceID,
1081
nsIAtom* aAttribute, PRInt32 aModType)
1085
// First see if we need to update our element map.
1086
if ((aAttribute == nsXULAtoms::id) || (aAttribute == nsXULAtoms::ref)) {
1088
rv = mElementMap.Enumerate(RemoveElementsFromMapByContent, aElement);
1089
if (NS_FAILED(rv)) return;
1091
// That'll have removed _both_ the 'ref' and 'id' entries from
1092
// the map. So add 'em back now.
1093
rv = AddElementToMap(aElement);
1094
if (NS_FAILED(rv)) return;
1097
// Synchronize broadcast listeners
1098
if (mBroadcasterMap && CanBroadcast(aNameSpaceID, aAttribute)) {
1099
nsCOMPtr<nsIDOMElement> domele = do_QueryInterface(aElement);
1100
BroadcasterMapEntry* entry =
1101
NS_STATIC_CAST(BroadcasterMapEntry*,
1102
PL_DHashTableOperate(mBroadcasterMap, domele.get(),
1105
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
1106
// We've got listeners: push the value.
1108
rv = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
1110
for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
1111
BroadcastListener* bl =
1112
NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
1114
if ((bl->mAttribute == aAttribute) ||
1115
(bl->mAttribute == nsXULAtoms::_star)) {
1116
nsCOMPtr<nsIContent> listener
1117
= do_QueryInterface(bl->mListener);
1119
if (rv == NS_CONTENT_ATTR_NO_VALUE ||
1120
rv == NS_CONTENT_ATTR_HAS_VALUE) {
1121
listener->SetAttr(kNameSpaceID_None, aAttribute, value,
1125
listener->UnsetAttr(kNameSpaceID_None, aAttribute,
1129
ExecuteOnBroadcastHandlerFor(aElement, bl->mListener,
1136
// Now notify external observers
1137
for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
1138
nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i];
1139
observer->AttributeChanged(this, aElement, aNameSpaceID, aAttribute,
1143
// See if there is anything we need to persist in the localstore.
1145
// XXX Namespace handling broken :-(
1146
nsAutoString persist;
1147
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist);
1148
if (NS_FAILED(rv)) return;
1150
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
1152
rv = aAttribute->ToString(attr);
1153
if (NS_FAILED(rv)) return;
1155
if (persist.Find(attr) >= 0) {
1156
rv = Persist(aElement, kNameSpaceID_None, aAttribute);
1157
if (NS_FAILED(rv)) return;
1163
nsXULDocument::ContentAppended(nsIContent* aContainer,
1164
PRInt32 aNewIndexInContainer)
1166
// First update our element map
1167
PRUint32 count = aContainer->GetChildCount();
1169
for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
1170
nsresult rv = AddSubtreeToDocument(aContainer->GetChildAt(i));
1175
nsXMLDocument::ContentAppended(aContainer, aNewIndexInContainer);
1179
nsXULDocument::ContentInserted(nsIContent* aContainer,
1181
PRInt32 aIndexInContainer)
1183
nsresult rv = AddSubtreeToDocument(aChild);
1187
nsXMLDocument::ContentInserted(aContainer, aChild,
1192
nsXULDocument::ContentReplaced(nsIContent* aContainer,
1193
nsIContent* aOldChild,
1194
nsIContent* aNewChild,
1195
PRInt32 aIndexInContainer)
1198
rv = RemoveSubtreeFromDocument(aOldChild);
1202
rv = AddSubtreeToDocument(aNewChild);
1206
nsXMLDocument::ContentReplaced(aContainer, aOldChild, aNewChild,
1211
nsXULDocument::ContentRemoved(nsIContent* aContainer,
1213
PRInt32 aIndexInContainer)
1216
rv = RemoveSubtreeFromDocument(aChild);
1220
nsXMLDocument::ContentRemoved(aContainer, aChild,
1225
nsXULDocument::HandleDOMEvent(nsIPresContext* aPresContext,
1227
nsIDOMEvent** aDOMEvent,
1229
nsEventStatus* aEventStatus)
1231
nsresult ret = NS_OK;
1232
nsIDOMEvent* domEvent = nsnull;
1233
PRBool externalDOMEvent = PR_FALSE;
1235
if (NS_EVENT_FLAG_INIT & aFlags) {
1238
externalDOMEvent = PR_TRUE;
1242
aDOMEvent = &domEvent;
1244
aEvent->flags |= aFlags;
1245
aFlags &= ~(NS_EVENT_FLAG_CANT_BUBBLE | NS_EVENT_FLAG_CANT_CANCEL);
1246
aFlags |= NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE;
1250
if (NS_EVENT_FLAG_CAPTURE & aFlags && mScriptGlobalObject) {
1251
mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
1252
aFlags & NS_EVENT_CAPTURE_MASK,
1256
// Local handling stage
1257
if (mListenerManager) {
1258
aEvent->flags |= aFlags;
1259
mListenerManager->HandleEvent(aPresContext, aEvent, aDOMEvent,
1260
this, aFlags, aEventStatus);
1261
aEvent->flags &= ~aFlags;
1265
if (NS_EVENT_FLAG_BUBBLE & aFlags && mScriptGlobalObject) {
1266
mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
1267
aFlags & NS_EVENT_BUBBLE_MASK,
1271
if (NS_EVENT_FLAG_INIT & aFlags) {
1272
// We're leaving the DOM event loop so if we created a DOM
1273
// event, release here.
1274
if (*aDOMEvent && !externalDOMEvent) {
1276
NS_RELEASE2(*aDOMEvent, rc);
1278
// Okay, so someone in the DOM loop (a listener, JS
1279
// object) still has a ref to the DOM Event but the
1280
// internal data hasn't been malloc'd. Force a copy
1281
// of the data here so the DOM Event is still valid.
1282
nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
1283
do_QueryInterface(*aDOMEvent);
1286
privateEvent->DuplicatePrivateData();
1297
//----------------------------------------------------------------------
1299
// nsIXULDocument interface
1303
nsXULDocument::AddElementForID(const nsAString& aID, nsIContent* aElement)
1305
NS_PRECONDITION(aElement != nsnull, "null ptr");
1307
return NS_ERROR_NULL_POINTER;
1309
mElementMap.Add(aID, aElement);
1315
nsXULDocument::RemoveElementForID(const nsAString& aID, nsIContent* aElement)
1317
NS_PRECONDITION(aElement != nsnull, "null ptr");
1319
return NS_ERROR_NULL_POINTER;
1321
mElementMap.Remove(aID, aElement);
1326
nsXULDocument::GetElementsForID(const nsAString& aID,
1327
nsISupportsArray* aElements)
1329
NS_PRECONDITION(aElements != nsnull, "null ptr");
1331
return NS_ERROR_NULL_POINTER;
1333
mElementMap.Find(aID, aElements);
1338
nsXULDocument::AddForwardReference(nsForwardReference* aRef)
1340
if (mResolutionPhase < aRef->GetPhase()) {
1341
mForwardReferences.AppendElement(aRef);
1344
NS_ERROR("forward references have already been resolved");
1353
nsXULDocument::ResolveForwardReferences()
1355
if (mResolutionPhase == nsForwardReference::eDone)
1358
// Resolve each outstanding 'forward' reference. We iterate
1359
// through the list of forward references until no more forward
1360
// references can be resolved. This annealing process is
1361
// guaranteed to converge because we've "closed the gate" to new
1362
// forward references.
1364
const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
1365
while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
1366
PRInt32 previous = 0;
1367
while (mForwardReferences.Count() && mForwardReferences.Count() != previous) {
1368
previous = mForwardReferences.Count();
1370
for (PRInt32 i = 0; i < mForwardReferences.Count(); ++i) {
1371
nsForwardReference* fwdref = NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
1373
if (fwdref->GetPhase() == *pass) {
1374
nsForwardReference::Result result = fwdref->Resolve();
1377
case nsForwardReference::eResolve_Succeeded:
1378
case nsForwardReference::eResolve_Error:
1379
mForwardReferences.RemoveElementAt(i);
1382
// fixup because we removed from list
1386
case nsForwardReference::eResolve_Later:
1387
// do nothing. we'll try again later
1397
DestroyForwardReferences();
1402
nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument)
1404
mMasterPrototype = aDocument;
1409
nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument)
1411
*aDocument = mMasterPrototype;
1412
NS_IF_ADDREF(*aDocument);
1417
nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument)
1419
mCurrentPrototype = aDocument;
1423
//----------------------------------------------------------------------
1425
// nsIDOMDocument interface
1429
nsXULDocument::GetElementsByAttribute(const nsAString& aAttribute,
1430
const nsAString& aValue,
1431
nsIDOMNodeList** aReturn)
1433
// XXX This should use nsContentList, but that does not support
1434
// _two_ strings being passed to the match func. Ah, the ability
1435
// to create real closures, where art thou?
1436
nsRDFDOMNodeList* elements = new nsRDFDOMNodeList();
1437
NS_ENSURE_TRUE(elements, NS_ERROR_OUT_OF_MEMORY);
1438
NS_ADDREF(elements);
1440
nsCOMPtr<nsIDOMNode> domRoot = do_QueryInterface(mRootContent);
1441
NS_ASSERTION(domRoot, "no doc root");
1443
nsresult rv = NS_OK;
1445
rv = GetElementsByAttribute(domRoot, aAttribute, aValue, elements);
1448
*aReturn = elements;
1455
nsXULDocument::Persist(const nsAString& aID,
1456
const nsAString& aAttr)
1458
// If we're currently reading persisted attributes out of the
1459
// localstore, _don't_ re-enter and try to set them again!
1460
if (mApplyingPersistedAttrs)
1465
nsCOMPtr<nsIDOMElement> domelement;
1466
rv = GetElementById(aID, getter_AddRefs(domelement));
1467
if (NS_FAILED(rv)) return rv;
1472
nsCOMPtr<nsIContent> element = do_QueryInterface(domelement);
1473
NS_ASSERTION(element != nsnull, "null ptr");
1475
return NS_ERROR_UNEXPECTED;
1477
nsCOMPtr<nsIAtom> tag;
1478
PRInt32 nameSpaceID;
1480
nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
1482
tag = ni->NameAtom();
1483
nameSpaceID = ni->NamespaceID();
1486
tag = do_GetAtom(aAttr);
1487
NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
1489
nameSpaceID = kNameSpaceID_None;
1492
rv = Persist(element, nameSpaceID, tag);
1493
if (NS_FAILED(rv)) return rv;
1500
nsXULDocument::Persist(nsIContent* aElement, PRInt32 aNameSpaceID,
1501
nsIAtom* aAttribute)
1503
// First make sure we _have_ a local store to stuff the persisted
1504
// information into. (We might not have one if profile information
1505
// hasn't been loaded yet...)
1511
nsCOMPtr<nsIRDFResource> element;
1512
rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
1513
if (NS_FAILED(rv)) return rv;
1515
// No ID, so nothing to persist.
1519
// Ick. Construct a property from the attribute. Punt on
1520
// namespaces for now.
1521
const char* attrstr;
1522
rv = aAttribute->GetUTF8String(&attrstr);
1523
if (NS_FAILED(rv)) return rv;
1525
nsCOMPtr<nsIRDFResource> attr;
1526
rv = gRDFService->GetResource(nsDependentCString(attrstr),
1527
getter_AddRefs(attr));
1528
if (NS_FAILED(rv)) return rv;
1530
// Turn the value into a literal
1531
nsAutoString valuestr;
1532
rv = aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
1533
if (NS_FAILED(rv)) return rv;
1535
PRBool novalue = (rv != NS_CONTENT_ATTR_HAS_VALUE);
1537
// See if there was an old value...
1538
nsCOMPtr<nsIRDFNode> oldvalue;
1539
rv = mLocalStore->GetTarget(element, attr, PR_TRUE, getter_AddRefs(oldvalue));
1540
if (NS_FAILED(rv)) return rv;
1542
if (oldvalue && novalue) {
1543
// ...there was an oldvalue, and they've removed it. XXXThis
1544
// handling isn't quite right...
1545
rv = mLocalStore->Unassert(element, attr, oldvalue);
1548
// Now either 'change' or 'assert' based on whether there was
1550
nsCOMPtr<nsIRDFLiteral> newvalue;
1551
rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
1552
if (NS_FAILED(rv)) return rv;
1555
if (oldvalue != newvalue)
1556
rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
1561
rv = mLocalStore->Assert(element, attr, newvalue, PR_TRUE);
1565
if (NS_FAILED(rv)) return rv;
1567
// Add it to the persisted set for this document (if it's not
1570
nsCAutoString docurl;
1571
rv = mDocumentURI->GetSpec(docurl);
1572
if (NS_FAILED(rv)) return rv;
1574
nsCOMPtr<nsIRDFResource> doc;
1575
rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
1576
if (NS_FAILED(rv)) return rv;
1578
PRBool hasAssertion;
1579
rv = mLocalStore->HasAssertion(doc, kNC_persist, element, PR_TRUE, &hasAssertion);
1580
if (NS_FAILED(rv)) return rv;
1582
if (! hasAssertion) {
1583
rv = mLocalStore->Assert(doc, kNC_persist, element, PR_TRUE);
1584
if (NS_FAILED(rv)) return rv;
1594
nsXULDocument::DestroyForwardReferences()
1596
for (PRInt32 i = mForwardReferences.Count() - 1; i >= 0; --i) {
1597
nsForwardReference* fwdref =
1598
NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
1602
mForwardReferences.Clear();
1608
nsXULDocument::GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth,
1611
nsresult result = NS_OK;
1615
FlushPendingNotifications();
1617
result = aShell->GetPrimaryFrameFor(mRootContent, &frame);
1618
if (NS_SUCCEEDED(result) && frame) {
1619
nsIView* view = frame->GetView();
1620
// If we have a view check if it's scrollable. If not,
1621
// just use the view size itself
1623
nsIScrollableView* scrollableView;
1625
if (NS_SUCCEEDED(CallQueryInterface(view, &scrollableView))) {
1626
scrollableView->GetScrolledView(view);
1629
nsRect r = view->GetBounds();
1630
size.height = r.height;
1631
size.width = r.width;
1633
// If we don't have a view, use the frame size
1635
size = frame->GetSize();
1638
// Convert from twips to pixels
1639
nsCOMPtr<nsIPresContext> context;
1640
result = aShell->GetPresContext(getter_AddRefs(context));
1642
if (NS_SUCCEEDED(result)) {
1644
scale = context->TwipsToPixels();
1646
*aWidth = NSTwipsToIntPixels(size.width, scale);
1647
*aHeight = NSTwipsToIntPixels(size.height, scale);
1659
nsXULDocument::GetWidth(PRInt32* aWidth)
1661
NS_ENSURE_ARG_POINTER(aWidth);
1663
nsresult rv = NS_OK;
1665
// We make the assumption that the first presentation shell
1666
// is the one for which we need information.
1667
nsIPresShell *shell = GetShellAt(0);
1669
PRInt32 width, height;
1671
rv = GetPixelDimensions(shell, &width, &height);
1680
nsXULDocument::GetHeight(PRInt32* aHeight)
1682
NS_ENSURE_ARG_POINTER(aHeight);
1684
nsresult rv = NS_OK;
1686
// We make the assumption that the first presentation shell
1687
// is the one for which we need information.
1688
nsIPresShell *shell = GetShellAt(0);
1690
PRInt32 width, height;
1692
rv = GetPixelDimensions(shell, &width, &height);
1700
//----------------------------------------------------------------------
1702
// nsIDOMXULDocument interface
1706
nsXULDocument::GetPopupNode(nsIDOMNode** aNode)
1710
// get focus controller
1711
nsCOMPtr<nsIFocusController> focusController;
1712
GetFocusController(getter_AddRefs(focusController));
1713
NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
1715
rv = focusController->GetPopupNode(aNode); // addref happens here
1721
nsXULDocument::SetPopupNode(nsIDOMNode* aNode)
1725
// get focus controller
1726
nsCOMPtr<nsIFocusController> focusController;
1727
GetFocusController(getter_AddRefs(focusController));
1728
NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
1730
rv = focusController->SetPopupNode(aNode);
1736
nsXULDocument::GetTooltipNode(nsIDOMNode** aNode)
1738
*aNode = mTooltipNode;
1739
NS_IF_ADDREF(*aNode);
1744
nsXULDocument::SetTooltipNode(nsIDOMNode* aNode)
1746
mTooltipNode = aNode;
1752
nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
1754
*aTracker = mCommandDispatcher;
1755
NS_IF_ADDREF(*aTracker);
1760
nsXULDocument::GetElementById(const nsAString& aId,
1761
nsIDOMElement** aReturn)
1763
NS_ENSURE_ARG_POINTER(aReturn);
1766
NS_WARN_IF_FALSE(!aId.IsEmpty(),"getElementById(\"\"), fix caller?");
1772
nsCOMPtr<nsIContent> element;
1773
rv = mElementMap.FindFirst(aId, getter_AddRefs(element));
1774
if (NS_FAILED(rv)) return rv;
1777
rv = CallQueryInterface(element, aReturn);
1784
nsXULDocument::AddElementToDocumentPre(nsIContent* aElement)
1786
// Do a bunch of work that's necessary when an element gets added
1787
// to the XUL Document.
1790
// 1. Add the element to the resource-to-element map
1791
rv = AddElementToMap(aElement);
1792
if (NS_FAILED(rv)) return rv;
1794
// 2. If the element is a 'command updater' (i.e., has a
1795
// "commandupdater='true'" attribute), then add the element to the
1796
// document's command dispatcher
1798
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
1800
if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
1801
value == NS_LITERAL_STRING("true")) {
1802
rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
1803
if (NS_FAILED(rv)) return rv;
1806
// 3. Check for a broadcaster hookup attribute, in which case
1807
// we'll hook the node up as a listener on a broadcaster.
1808
PRBool listener, resolved;
1809
rv = CheckBroadcasterHookup(this, aElement, &listener, &resolved);
1810
if (NS_FAILED(rv)) return rv;
1812
// If it's not there yet, we may be able to defer hookup until
1814
if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
1815
BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
1817
return NS_ERROR_OUT_OF_MEMORY;
1819
rv = AddForwardReference(hookup);
1820
if (NS_FAILED(rv)) return rv;
1827
nsXULDocument::AddElementToDocumentPost(nsIContent* aElement)
1829
nsINodeInfo *ni = aElement->GetNodeInfo();
1831
// We need to pay special attention to the keyset tag to set up a listener
1832
if (ni && ni->Equals(nsXULAtoms::keyset, kNameSpaceID_XUL)) {
1833
// Create our XUL key listener and hook it up.
1834
nsCOMPtr<nsIXBLService> xblService(do_GetService("@mozilla.org/xbl;1"));
1836
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(aElement));
1837
xblService->AttachGlobalKeyHandler(rec);
1841
// See if we need to attach a XUL template to this node
1843
nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
1848
if (mResolutionPhase == nsForwardReference::eDone) {
1849
rv = CreateTemplateBuilder(aElement);
1854
TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
1856
return NS_ERROR_OUT_OF_MEMORY;
1858
rv = AddForwardReference(hookup);
1868
nsXULDocument::AddSubtreeToDocument(nsIContent* aElement)
1872
// Do pre-order addition magic
1873
rv = AddElementToDocumentPre(aElement);
1874
if (NS_FAILED(rv)) return rv;
1876
// Recurse to children
1877
nsCOMPtr<nsIXULContent> xulcontent = do_QueryInterface(aElement);
1880
xulcontent ? xulcontent->PeekChildCount() : aElement->GetChildCount();
1882
while (count-- > 0) {
1883
rv = AddSubtreeToDocument(aElement->GetChildAt(count));
1888
// Do post-order addition magic
1889
return AddElementToDocumentPost(aElement);
1893
nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement)
1895
// Do a bunch of cleanup to remove an element from the XUL
1899
// 1. Remove any children from the document.
1900
PRUint32 count = aElement->GetChildCount();
1902
while (count-- > 0) {
1903
rv = RemoveSubtreeFromDocument(aElement->GetChildAt(count));
1908
// 2. Remove the element from the resource-to-element map
1909
rv = RemoveElementFromMap(aElement);
1910
if (NS_FAILED(rv)) return rv;
1912
// 3. If the element is a 'command updater', then remove the
1913
// element from the document's command dispatcher.
1915
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
1917
if (rv == NS_CONTENT_ATTR_HAS_VALUE &&
1918
value == NS_LITERAL_STRING("true")) {
1919
nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
1920
NS_ASSERTION(domelement != nsnull, "not a DOM element");
1922
return NS_ERROR_UNEXPECTED;
1924
rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
1925
if (NS_FAILED(rv)) return rv;
1928
// 4. Remove the element from our broadcaster map, since it is no longer
1930
// Do a getElementById to retrieve the broadcaster
1931
nsCOMPtr<nsIDOMElement> broadcaster;
1932
nsAutoString observesVal;
1934
if (aElement->HasAttr(kNameSpaceID_None, nsXULAtoms::observes)) {
1935
aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, observesVal);
1936
if (!observesVal.IsEmpty()) {
1937
GetElementById(observesVal, getter_AddRefs(broadcaster));
1939
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aElement));
1940
RemoveBroadcastListenerFor(broadcaster, elt,
1941
NS_LITERAL_STRING("*"));
1946
if (aElement->HasAttr(kNameSpaceID_None, nsXULAtoms::command)) {
1947
aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::command, observesVal);
1948
if (!observesVal.IsEmpty()) {
1949
GetElementById(observesVal, getter_AddRefs(broadcaster));
1951
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aElement));
1952
RemoveBroadcastListenerFor(broadcaster, elt,
1953
NS_LITERAL_STRING("*"));
1961
nsXULDocument::SetTemplateBuilderFor(nsIContent* aContent,
1962
nsIXULTemplateBuilder* aBuilder)
1964
if (! mTemplateBuilderTable) {
1965
mTemplateBuilderTable = new nsSupportsHashtable();
1966
if (! mTemplateBuilderTable)
1967
return NS_ERROR_OUT_OF_MEMORY;
1970
nsISupportsKey key(aContent);
1973
mTemplateBuilderTable->Put(&key, aBuilder);
1976
mTemplateBuilderTable->Remove(&key);
1983
nsXULDocument::GetTemplateBuilderFor(nsIContent* aContent,
1984
nsIXULTemplateBuilder** aResult)
1986
if (mTemplateBuilderTable) {
1987
nsISupportsKey key(aContent);
1988
*aResult = NS_STATIC_CAST(nsIXULTemplateBuilder*,
1989
mTemplateBuilderTable->Get(&key));
1997
// Attributes that are used with getElementById() and the
1998
// resource-to-element map.
1999
nsIAtom** nsXULDocument::kIdentityAttrs[] =
2007
nsXULDocument::AddElementToMap(nsIContent* aElement)
2009
// Look at the element's 'id' and 'ref' attributes, and if set,
2010
// add pointers in the resource-to-element map to the element.
2013
for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
2015
rv = aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value);
2016
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get attribute");
2017
if (NS_FAILED(rv)) return rv;
2019
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
2020
rv = mElementMap.Add(value, aElement);
2021
if (NS_FAILED(rv)) return rv;
2030
nsXULDocument::RemoveElementFromMap(nsIContent* aElement)
2032
// Remove the element from the resource-to-element map.
2035
for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
2037
rv = aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value);
2038
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get attribute");
2039
if (NS_FAILED(rv)) return rv;
2041
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
2042
rv = mElementMap.Remove(value, aElement);
2043
if (NS_FAILED(rv)) return rv;
2052
nsXULDocument::RemoveElementsFromMapByContent(const PRUnichar* aID,
2053
nsIContent* aElement,
2056
nsIContent* content = NS_REINTERPRET_CAST(nsIContent*, aClosure);
2057
return (aElement == content) ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;
2062
//----------------------------------------------------------------------
2064
// nsIDOMNode interface
2068
nsXULDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
2070
// We don't allow cloning of a document
2072
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
2076
//----------------------------------------------------------------------
2078
// Implementation methods
2082
nsXULDocument::Init()
2084
nsresult rv = nsXMLDocument::Init();
2085
NS_ENSURE_SUCCESS(rv, rv);
2087
// Create our command dispatcher and hook it up.
2088
rv = nsXULCommandDispatcher::Create(this,
2089
getter_AddRefs(mCommandDispatcher));
2090
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a focus tracker");
2091
if (NS_FAILED(rv)) return rv;
2093
// this _could_ fail; e.g., if we've tried to grab the local store
2094
// before profiles have initialized. If so, no big deal; nothing
2096
mLocalStore = do_GetService(kLocalStoreCID);
2098
// Create a new nsISupportsArray for dealing with overlay references
2099
rv = NS_NewISupportsArray(getter_AddRefs(mUnloadedOverlays));
2100
if (NS_FAILED(rv)) return rv;
2102
if (gRefCnt++ == 0) {
2103
// Keep the RDF service cached in a member variable to make using
2104
// it a bit less painful
2105
rv = CallGetService(kRDFServiceCID, &gRDFService);
2106
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
2107
if (NS_FAILED(rv)) return rv;
2109
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
2111
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
2113
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
2116
rv = CallGetService(kHTMLElementFactoryCID, &gHTMLElementFactory);
2117
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get HTML element factory");
2118
if (NS_FAILED(rv)) return rv;
2120
rv = CallGetService(kXMLElementFactoryCID, &gXMLElementFactory);
2121
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get XML element factory");
2122
if (NS_FAILED(rv)) return rv;
2124
rv = CallGetService(kXULPrototypeCacheCID, &gXULCache);
2125
if (NS_FAILED(rv)) return rv;
2130
gXULLog = PR_NewLogModule("nsXULDocument");
2138
nsXULDocument::StartLayout(void)
2140
if (!mRootContent) {
2142
nsCAutoString urlspec;
2143
mDocumentURI->GetSpec(urlspec);
2145
PR_LOG(gXULLog, PR_LOG_ALWAYS,
2146
("xul: unable to layout '%s'; no root content", urlspec.get()));
2151
PRUint32 count = GetNumberOfShells();
2152
for (PRUint32 i = 0; i < count; ++i) {
2153
nsIPresShell *shell = GetShellAt(i);
2155
// Resize-reflow this time
2156
nsCOMPtr<nsIPresContext> cx;
2157
shell->GetPresContext(getter_AddRefs(cx));
2158
NS_ASSERTION(cx != nsnull, "no pres context");
2160
return NS_ERROR_UNEXPECTED;
2162
nsCOMPtr<nsISupports> container = cx->GetContainer();
2163
NS_ASSERTION(container != nsnull, "pres context has no container");
2165
return NS_ERROR_UNEXPECTED;
2167
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
2168
NS_ASSERTION(docShell != nsnull, "container is not a docshell");
2170
return NS_ERROR_UNEXPECTED;
2172
nsRect r = cx->GetVisibleArea();
2174
// Trigger a refresh before the call to InitialReflow(),
2175
// because the view manager's UpdateView() function is
2176
// dropping dirty rects if refresh is disabled rather than
2177
// accumulating them until refresh is enabled and then
2178
// triggering a repaint...
2179
nsIViewManager* vm = shell->GetViewManager();
2181
nsCOMPtr<nsIContentViewer> contentViewer;
2182
nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
2183
if (NS_SUCCEEDED(rv) && (contentViewer != nsnull)) {
2185
contentViewer->GetEnableRendering(&enabled);
2187
vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
2192
shell->InitialReflow(r.width, r.height);
2194
// This is done because iframes don't load their subdocs until
2195
// they get reflowed. If we reflow asynchronously, our onload
2196
// will fire too early. -- hyatt
2197
FlushPendingNotifications();
2199
// Start observing the document _after_ we do the initial
2200
// reflow. Otherwise, we'll get into an trouble trying to
2201
// create kids before the root frame is established.
2202
shell->BeginObservingDocument();
2210
nsXULDocument::GetElementsByAttribute(nsIDOMNode* aNode,
2211
const nsAString& aAttribute,
2212
const nsAString& aValue,
2213
nsRDFDOMNodeList* aElements)
2217
nsCOMPtr<nsIDOMElement> element;
2218
element = do_QueryInterface(aNode);
2222
nsAutoString attrValue;
2223
if (NS_FAILED(rv = element->GetAttribute(aAttribute, attrValue))) {
2224
NS_ERROR("unable to get attribute value");
2228
if ((attrValue.Equals(aValue)) ||
2229
(!attrValue.IsEmpty() && aValue.Equals(NS_LITERAL_STRING("*")))) {
2230
if (NS_FAILED(rv = aElements->AppendNode(aNode))) {
2231
NS_ERROR("unable to append element to node list");
2236
nsCOMPtr<nsIDOMNodeList> children;
2237
if (NS_FAILED(rv = aNode->GetChildNodes( getter_AddRefs(children) ))) {
2238
NS_ERROR("unable to get node's children");
2242
// no kids: terminate the recursion
2247
if (NS_FAILED(children->GetLength(&length))) {
2248
NS_ERROR("unable to get node list's length");
2252
for (PRUint32 i = 0; i < length; ++i) {
2253
nsCOMPtr<nsIDOMNode> child;
2254
if (NS_FAILED(rv = children->Item(i, getter_AddRefs(child) ))) {
2255
NS_ERROR("unable to get child from list");
2259
if (NS_FAILED(rv = GetElementsByAttribute(child, aAttribute, aValue,
2261
NS_ERROR("unable to recursively get elements by attribute");
2271
nsXULDocument::PrepareToLoad(nsISupports* aContainer,
2272
const char* aCommand,
2273
nsIChannel* aChannel,
2274
nsILoadGroup* aLoadGroup,
2275
nsIParser** aResult)
2279
// Get the document's principal
2280
nsCOMPtr<nsISupports> owner;
2281
rv = aChannel->GetOwner(getter_AddRefs(owner));
2282
if (NS_FAILED(rv)) return rv;
2284
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
2286
return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
2291
nsXULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
2292
nsIPrincipal* aDocumentPrincipal,
2293
nsIParser** aResult)
2297
// Create a new prototype document.
2298
rv = NS_NewXULPrototypeDocument(nsnull,
2299
NS_GET_IID(nsIXULPrototypeDocument),
2300
getter_AddRefs(mCurrentPrototype));
2301
if (NS_FAILED(rv)) return rv;
2303
// Bootstrap the master document prototype.
2304
if (! mMasterPrototype) {
2305
mMasterPrototype = mCurrentPrototype;
2306
mMasterPrototype->SetDocumentPrincipal(aDocumentPrincipal);
2309
rv = mCurrentPrototype->SetURI(aURI);
2310
if (NS_FAILED(rv)) return rv;
2312
// Create a XUL content sink, a parser, and kick off a load for
2314
nsCOMPtr<nsIXULContentSink> sink;
2315
rv = NS_NewXULContentSink(getter_AddRefs(sink));
2316
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create XUL content sink");
2317
if (NS_FAILED(rv)) return rv;
2319
rv = sink->Init(this, mCurrentPrototype);
2320
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
2321
if (NS_FAILED(rv)) return rv;
2323
nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
2324
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
2325
if (NS_FAILED(rv)) return rv;
2327
parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
2330
parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
2331
kCharsetFromDocTypeDefault);
2332
parser->SetContentSink(sink); // grabs a reference to the parser
2335
NS_ADDREF(*aResult);
2341
nsXULDocument::ApplyPersistentAttributes()
2343
// Add all of the 'persisted' attributes into the content
2348
mApplyingPersistedAttrs = PR_TRUE;
2351
nsCOMPtr<nsISupportsArray> elements;
2352
rv = NS_NewISupportsArray(getter_AddRefs(elements));
2353
if (NS_FAILED(rv)) return rv;
2355
nsCAutoString docurl;
2356
mDocumentURI->GetSpec(docurl);
2358
nsCOMPtr<nsIRDFResource> doc;
2359
gRDFService->GetResource(docurl, getter_AddRefs(doc));
2361
nsCOMPtr<nsISimpleEnumerator> persisted;
2362
mLocalStore->GetTargets(doc, kNC_persist, PR_TRUE, getter_AddRefs(persisted));
2365
PRBool hasmore = PR_FALSE;
2366
persisted->HasMoreElements(&hasmore);
2370
nsCOMPtr<nsISupports> isupports;
2371
persisted->GetNext(getter_AddRefs(isupports));
2373
nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
2375
NS_WARNING("expected element to be a resource");
2380
resource->GetValueConst(&uri);
2385
nsXULContentUtils::MakeElementID(this, NS_ConvertASCIItoUCS2(uri), id);
2387
// This will clear the array if there are no elements.
2388
GetElementsForID(id, elements);
2391
elements->Count(&cnt);
2395
ApplyPersistentAttributesToElements(resource, elements);
2398
mApplyingPersistedAttrs = PR_FALSE;
2405
nsXULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
2406
nsISupportsArray* aElements)
2410
nsCOMPtr<nsISimpleEnumerator> attrs;
2411
rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
2412
if (NS_FAILED(rv)) return rv;
2416
rv = attrs->HasMoreElements(&hasmore);
2417
if (NS_FAILED(rv)) return rv;
2422
nsCOMPtr<nsISupports> isupports;
2423
rv = attrs->GetNext(getter_AddRefs(isupports));
2424
if (NS_FAILED(rv)) return rv;
2426
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
2428
NS_WARNING("expected a resource");
2432
const char* attrname;
2433
rv = property->GetValueConst(&attrname);
2434
if (NS_FAILED(rv)) return rv;
2436
nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
2438
return NS_ERROR_OUT_OF_MEMORY;
2440
// XXX could hang namespace off here, as well...
2442
nsCOMPtr<nsIRDFNode> node;
2443
rv = mLocalStore->GetTarget(aResource, property, PR_TRUE,
2444
getter_AddRefs(node));
2445
if (NS_FAILED(rv)) return rv;
2447
nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
2449
NS_WARNING("expected a literal");
2453
const PRUnichar* value;
2454
rv = literal->GetValueConst(&value);
2455
if (NS_FAILED(rv)) return rv;
2457
nsDependentString wrapper(value);
2460
rv = aElements->Count(&cnt);
2461
if (NS_FAILED(rv)) return rv;
2463
for (PRInt32 i = PRInt32(cnt) - 1; i >= 0; --i) {
2464
nsISupports* isupports2 = aElements->ElementAt(i);
2468
nsCOMPtr<nsIContent> element = do_QueryInterface(isupports2);
2469
NS_RELEASE(isupports2);
2471
rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
2481
//----------------------------------------------------------------------
2483
// nsXULDocument::ContextStack
2486
nsXULDocument::ContextStack::ContextStack()
2487
: mTop(nsnull), mDepth(0)
2491
nsXULDocument::ContextStack::~ContextStack()
2494
Entry* doomed = mTop;
2496
NS_IF_RELEASE(doomed->mElement);
2502
nsXULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
2503
nsIContent* aElement)
2505
Entry* entry = new Entry;
2507
return NS_ERROR_OUT_OF_MEMORY;
2509
entry->mPrototype = aPrototype;
2510
entry->mElement = aElement;
2511
NS_IF_ADDREF(entry->mElement);
2514
entry->mNext = mTop;
2522
nsXULDocument::ContextStack::Pop()
2525
return NS_ERROR_UNEXPECTED;
2527
Entry* doomed = mTop;
2531
NS_IF_RELEASE(doomed->mElement);
2537
nsXULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
2538
nsIContent** aElement,
2542
return NS_ERROR_UNEXPECTED;
2544
*aPrototype = mTop->mPrototype;
2545
*aElement = mTop->mElement;
2546
NS_IF_ADDREF(*aElement);
2547
*aIndex = mTop->mIndex;
2554
nsXULDocument::ContextStack::SetTopIndex(PRInt32 aIndex)
2557
return NS_ERROR_UNEXPECTED;
2559
mTop->mIndex = aIndex;
2565
nsXULDocument::ContextStack::IsInsideXULTemplate()
2568
for (nsIContent* element = mTop->mElement; element;
2569
element = element->GetParent()) {
2571
nsINodeInfo *ni = element->GetNodeInfo();
2573
if (ni && ni->Equals(nsXULAtoms::Template, kNameSpaceID_XUL)) {
2582
//----------------------------------------------------------------------
2584
// Content model walking routines
2588
nsXULDocument::PrepareToWalk()
2590
// Prepare to walk the mCurrentPrototype
2593
// Keep an owning reference to the prototype document so that its
2594
// elements aren't yanked from beneath us.
2595
mPrototypes.AppendObject(mCurrentPrototype);
2597
// Push the overlay references onto our overlay processing
2598
// stack. GetOverlayReferences() will return an ordered array of
2599
// overlay references...
2600
nsCOMPtr<nsISupportsArray> overlays;
2601
rv = mCurrentPrototype->GetOverlayReferences(getter_AddRefs(overlays));
2602
if (NS_FAILED(rv)) return rv;
2604
// ...and we preserve this ordering by appending to our
2605
// mUnloadedOverlays array in reverse order
2607
overlays->Count(&count);
2608
for (PRInt32 i = count - 1; i >= 0; --i) {
2609
nsISupports* isupports = overlays->ElementAt(i);
2610
mUnloadedOverlays->AppendElement(isupports);
2611
NS_IF_RELEASE(isupports);
2615
// Now check the chrome registry for any additional overlays.
2616
rv = AddChromeOverlays();
2618
// Get the prototype's root element and initialize the context
2619
// stack for the prototype walk.
2620
nsXULPrototypeElement* proto;
2621
rv = mCurrentPrototype->GetRootElement(&proto);
2622
if (NS_FAILED(rv)) return rv;
2627
nsCOMPtr<nsIURI> url;
2628
rv = mCurrentPrototype->GetURI(getter_AddRefs(url));
2629
if (NS_FAILED(rv)) return rv;
2631
nsCAutoString urlspec;
2632
rv = url->GetSpec(urlspec);
2633
if (NS_FAILED(rv)) return rv;
2635
PR_LOG(gXULLog, PR_LOG_ALWAYS,
2636
("xul: error parsing '%s'", urlspec.get()));
2642
// Do one-time initialization if we're preparing to walk the
2643
// master document's prototype.
2644
nsCOMPtr<nsIContent> root;
2646
if (mState == eState_Master) {
2647
rv = CreateElementFromPrototype(proto, getter_AddRefs(root));
2648
if (NS_FAILED(rv)) return rv;
2650
SetRootContent(root);
2652
// Add the root element to the XUL document's ID-to-element map.
2653
rv = AddElementToMap(root);
2654
if (NS_FAILED(rv)) return rv;
2656
// Add a dummy channel to the load group as a placeholder for the document
2658
rv = PlaceHolderRequest::Create(getter_AddRefs(mPlaceHolderRequest));
2659
if (NS_FAILED(rv)) return rv;
2661
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
2664
rv = mPlaceHolderRequest->SetLoadGroup(group);
2665
if (NS_FAILED(rv)) return rv;
2666
rv = group->AddRequest(mPlaceHolderRequest, nsnull);
2667
if (NS_FAILED(rv)) return rv;
2671
// There'd better not be anything on the context stack at this
2672
// point! This is the basis case for our "induction" in
2673
// ResumeWalk(), below, which'll assume that there's always a
2674
// content element on the context stack if either 1) we're in the
2675
// "master" document, or 2) we're in an overlay, and we've got
2676
// more than one prototype element (the single, root "overlay"
2677
// element) on the stack.
2678
NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
2679
if (mContextStack.Depth() != 0)
2680
return NS_ERROR_UNEXPECTED;
2682
rv = mContextStack.Push(proto, root);
2683
if (NS_FAILED(rv)) return rv;
2690
nsXULDocument::AddChromeOverlays()
2694
nsCOMPtr<nsIURI> docUri;
2695
rv = mCurrentPrototype->GetURI(getter_AddRefs(docUri));
2696
NS_ENSURE_SUCCESS(rv, rv);
2698
/* overlays only apply to chrome, skip all content URIs */
2699
if (!IsChromeURI(docUri)) return NS_OK;
2701
nsCOMPtr<nsIXULOverlayProvider> chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
2702
// In embedding situations, the chrome registry may not provide overlays,
2703
// or even exist at all; that's OK.
2704
NS_ENSURE_TRUE(chromeReg, NS_OK);
2706
nsCOMPtr<nsISimpleEnumerator> overlays;
2707
rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
2708
NS_ENSURE_SUCCESS(rv, rv);
2710
PRBool moreOverlays;
2711
nsCOMPtr<nsISupports> next;
2712
nsCOMPtr<nsIURI> uri;
2714
while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
2716
rv = overlays->GetNext(getter_AddRefs(next));
2717
if (NS_FAILED(rv) || !next) continue;
2719
uri = do_QueryInterface(next);
2721
NS_ERROR("Chrome registry handed me a non-nsIURI object!");
2725
mUnloadedOverlays->AppendElement(uri);
2732
nsXULDocument::ResumeWalk()
2734
// Walk the prototype and build the delegate content model. The
2735
// walk is performed in a top-down, left-to-right fashion. That
2736
// is, a parent is built before any of its children; a node is
2737
// only built after all of its siblings to the left are fully
2740
// It is interruptable so that transcluded documents (e.g.,
2741
// <html:script src="..." />) can be properly re-loaded if the
2742
// cached copy of the document becomes stale.
2744
nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
2745
NS_ENSURE_SUCCESS(rv, rv);
2748
// Begin (or resume) walking the current prototype.
2750
while (mContextStack.Depth() > 0) {
2751
// Look at the top of the stack to determine what we're
2752
// currently working on.
2753
nsXULPrototypeElement* proto;
2754
nsCOMPtr<nsIContent> element;
2756
rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
2757
if (NS_FAILED(rv)) return rv;
2759
if (indx >= (PRInt32)proto->mNumChildren) {
2760
// We've processed all of the prototype's children. If
2761
// we're in the master prototype, do post-order
2762
// document-level hookup. (An overlay will get its
2763
// document hookup done when it's successfully
2765
if (element && (mState == eState_Master))
2766
AddElementToDocumentPost(element);
2768
// Now pop the context stack back up to the parent
2769
// element and continue the prototype walk.
2770
mContextStack.Pop();
2774
// Grab the next child, and advance the current context stack
2775
// to the next sibling to our right.
2776
nsXULPrototypeNode* childproto = proto->mChildren[indx];
2777
mContextStack.SetTopIndex(++indx);
2779
switch (childproto->mType) {
2780
case nsXULPrototypeNode::eType_Element: {
2781
// An 'element', which may contain more content.
2782
nsXULPrototypeElement* protoele =
2783
NS_REINTERPRET_CAST(nsXULPrototypeElement*, childproto);
2785
nsCOMPtr<nsIContent> child;
2787
if ((mState == eState_Master) || (mContextStack.Depth() > 1)) {
2788
// We're in the master document -or -we're in an
2789
// overlay, and far enough down into the overlay's
2790
// content that we can simply build the delegates
2791
// and attach them to the parent node.
2792
NS_ASSERTION(element != nsnull, "no element on context stack");
2794
rv = CreateElementFromPrototype(protoele,
2795
getter_AddRefs(child));
2796
if (NS_FAILED(rv)) return rv;
2798
// ...and append it to the content model.
2799
rv = element->AppendChildTo(child, PR_FALSE, PR_FALSE);
2800
if (NS_FAILED(rv)) return rv;
2802
// do pre-order document-level hookup, but only if
2803
// we're in the master document. For an overlay,
2804
// this will happen when the overlay is
2805
// successfully resolved.
2806
if (mState == eState_Master)
2807
AddElementToDocumentPre(child);
2810
// We're in the "first ply" of an overlay: the
2811
// "hookup" nodes. Create an 'overlay' element so
2812
// that we can continue to build content, and
2813
// enter a forward reference so we can hook it up
2815
rv = CreateOverlayElement(protoele, getter_AddRefs(child));
2816
if (NS_FAILED(rv)) return rv;
2819
// If it has children, push the element onto the context
2820
// stack and begin to process them.
2821
if (protoele->mNumChildren > 0) {
2822
rv = mContextStack.Push(protoele, child);
2823
if (NS_FAILED(rv)) return rv;
2825
else if (mState == eState_Master) {
2826
// If there are no children, and we're in the
2827
// master document, do post-order document hookup
2829
AddElementToDocumentPost(child);
2834
case nsXULPrototypeNode::eType_Script: {
2835
// A script reference. Execute the script immediately;
2836
// this may have side effects in the content model.
2837
nsXULPrototypeScript* scriptproto =
2838
NS_REINTERPRET_CAST(nsXULPrototypeScript*, childproto);
2840
if (scriptproto->mSrcURI) {
2841
// A transcluded script reference; this may
2842
// "block" our prototype walk if the script isn't
2843
// cached, or the cached copy of the script is
2844
// stale and must be reloaded.
2846
rv = LoadScript(scriptproto, &blocked);
2847
if (NS_FAILED(rv)) return rv;
2852
else if (scriptproto->mJSObject) {
2854
rv = ExecuteScript(scriptproto->mJSObject);
2855
if (NS_FAILED(rv)) return rv;
2860
case nsXULPrototypeNode::eType_Text: {
2861
// A simple text node.
2863
if ((mState == eState_Master) || (mContextStack.Depth() > 1)) {
2864
// We're in the master document -or -we're in an
2865
// overlay, and far enough down into the overlay's
2866
// content that we can simply build the delegates
2867
// and attach them to the parent node.
2868
NS_ASSERTION(element, "no element on context stack");
2870
nsCOMPtr<nsITextContent> text;
2871
rv = NS_NewTextNode(getter_AddRefs(text));
2872
NS_ENSURE_SUCCESS(rv, rv);
2874
nsXULPrototypeText* textproto =
2875
NS_REINTERPRET_CAST(nsXULPrototypeText*, childproto);
2876
rv = text->SetText(textproto->mValue.get(),
2877
textproto->mValue.Length(),
2880
if (NS_FAILED(rv)) return rv;
2882
nsCOMPtr<nsIContent> child = do_QueryInterface(text);
2884
return NS_ERROR_UNEXPECTED;
2886
rv = element->AppendChildTo(child, PR_FALSE, PR_FALSE);
2887
if (NS_FAILED(rv)) return rv;
2894
// Once we get here, the context stack will have been
2895
// depleted. That means that the entire prototype has been
2896
// walked and content has been constructed.
2898
// If we're not already, mark us as now processing overlays.
2899
mState = eState_Overlay;
2902
mUnloadedOverlays->Count(&count);
2904
// If there are no overlay URIs, then we're done.
2908
nsCOMPtr<nsIURI> uri =
2909
dont_AddRef(NS_REINTERPRET_CAST(nsIURI*, mUnloadedOverlays->ElementAt(count - 1)));
2911
mUnloadedOverlays->RemoveElementAt(count - 1);
2914
if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
2915
nsCAutoString urlspec;
2916
uri->GetSpec(urlspec);
2918
PR_LOG(gXULLog, PR_LOG_DEBUG,
2919
("xul: loading overlay %s", urlspec.get()));
2923
// Chrome documents are allowed to load overlays from anywhere.
2924
// Also, any document may load a chrome:// overlay.
2925
// In all other cases, the overlay is only allowed to load if
2926
// the master document and prototype document have the same origin.
2928
PRBool overlayIsChrome = IsChromeURI(uri);
2929
if (!IsChromeURI(mDocumentURI) && !overlayIsChrome) {
2930
// Make sure we're allowed to load this overlay.
2931
rv = secMan->CheckSameOriginURI(mDocumentURI, uri);
2932
if (NS_FAILED(rv)) {
2933
// move on to the next overlay
2938
// Look in the prototype cache for the prototype document with
2939
// the specified overlay URI.
2940
if (overlayIsChrome)
2941
gXULCache->GetPrototype(uri, getter_AddRefs(mCurrentPrototype));
2943
mCurrentPrototype = nsnull;
2945
// Same comment as nsChromeProtocolHandler::NewChannel and
2946
// nsXULDocument::StartDocumentLoad
2949
// We don't abort on failure here because there are too many valid
2950
// cases that can return failure, and the null-ness of |proto| is
2951
// enough to trigger the fail-safe parse-from-disk solution.
2952
// Example failure cases (for reference) include:
2954
// NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
2956
// other: the FastLoad file, XUL.mfl, could not be found, probably
2957
// due to being accessed before a profile has been selected
2958
// (e.g. loading chrome for the profile manager itself).
2959
// The .xul file must be parsed from disk.
2962
gXULCache->GetEnabled(&useXULCache);
2964
if (useXULCache && mCurrentPrototype) {
2966
rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
2967
if (NS_FAILED(rv)) return rv;
2970
// Return to the main event loop and eagerly await the
2971
// prototype overlay load's completion. When the content
2972
// sink completes, it will trigger an EndLoad(), which'll
2973
// wind us back up here, in ResumeWalk().
2977
// Found the overlay's prototype in the cache, fully loaded.
2978
rv = AddPrototypeSheets();
2979
if (NS_FAILED(rv)) return rv;
2981
// Now prepare to walk the prototype to create its content
2982
rv = PrepareToWalk();
2983
if (NS_FAILED(rv)) return rv;
2985
PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
2988
// Not there. Initiate a load.
2989
PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
2991
nsCOMPtr<nsIParser> parser;
2992
rv = PrepareToLoadPrototype(uri, "view", nsnull, getter_AddRefs(parser));
2993
if (NS_FAILED(rv)) return rv;
2995
// Predicate mIsWritingFastLoad on the XUL cache being enabled,
2996
// so we don't have to re-check whether the cache is enabled all
2998
mIsWritingFastLoad = useXULCache;
3000
nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
3002
return NS_ERROR_UNEXPECTED;
3004
// Add an observer to the parser; this'll get called when
3005
// Necko fires its On[Start|Stop]Request() notifications,
3006
// and will let us recover from a missing overlay.
3007
ParserObserver* parserObserver = new ParserObserver(this);
3008
if (! parserObserver)
3009
return NS_ERROR_OUT_OF_MEMORY;
3011
NS_ADDREF(parserObserver);
3012
parser->Parse(uri, parserObserver);
3013
NS_RELEASE(parserObserver);
3015
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
3016
rv = NS_OpenURI(listener, nsnull, uri, nsnull, group);
3017
if (NS_FAILED(rv)) return rv;
3019
// If it's a 'chrome:' prototype document, then put it into
3020
// the prototype cache; other XUL documents will be reloaded
3021
// each time. We must do this after NS_OpenURI and AsyncOpen,
3022
// or chrome code will wrongly create a cached chrome channel
3023
// instead of a real one.
3024
if (useXULCache && overlayIsChrome) {
3025
rv = gXULCache->PutPrototype(mCurrentPrototype);
3026
if (NS_FAILED(rv)) return rv;
3029
// Return to the main event loop and eagerly await the
3030
// overlay load's completion. When the content sink
3031
// completes, it will trigger an EndLoad(), which'll wind
3032
// us back up here, in ResumeWalk().
3037
// If we get here, there is nothing left for us to walk. The content
3038
// model is built and ready for layout.
3039
rv = ResolveForwardReferences();
3040
if (NS_FAILED(rv)) return rv;
3042
rv = ApplyPersistentAttributes();
3043
if (NS_FAILED(rv)) return rv;
3045
// Everything after this point we only want to do once we're
3046
// certain that we've been embedded in a presentation shell.
3050
if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
3051
gXULCache->WritePrototype(mMasterPrototype);
3053
for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
3054
nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i];
3055
observer->EndLoad(this);
3057
NS_ASSERTION(mPlaceHolderRequest, "Bug 119310, perhaps overlayinfo referenced a overlay that doesn't exist");
3058
if (mPlaceHolderRequest) {
3059
// Remove the placeholder channel; if we're the last channel in the
3060
// load group, this will fire the OnEndDocumentLoad() method in the
3061
// docshell, and run the onload handlers, etc.
3062
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
3064
rv = group->RemoveRequest(mPlaceHolderRequest, nsnull, NS_OK);
3065
if (NS_FAILED(rv)) return rv;
3067
mPlaceHolderRequest = nsnull;
3075
nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock)
3077
// Load a transcluded script
3080
if (aScriptProto->mJSObject) {
3081
rv = ExecuteScript(aScriptProto->mJSObject);
3083
// Ignore return value from execution, and don't block
3088
// Try the XUL script cache, in case two XUL documents source the same
3089
// .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
3090
// XXXbe the cache relies on aScriptProto's GC root!
3092
gXULCache->GetEnabled(&useXULCache);
3095
gXULCache->GetScript(aScriptProto->mSrcURI,
3096
NS_REINTERPRET_CAST(void**, &aScriptProto->mJSObject));
3098
if (aScriptProto->mJSObject) {
3099
rv = ExecuteScript(aScriptProto->mJSObject);
3101
// Ignore return value from execution, and don't block
3107
// Set the current script prototype so that OnStreamComplete can report
3108
// the right file if there are errors in the script.
3109
NS_ASSERTION(!mCurrentScriptProto,
3110
"still loading a script when starting another load?");
3111
mCurrentScriptProto = aScriptProto;
3113
if (aScriptProto->mSrcLoading) {
3114
// Another XULDocument load has started, which is still in progress.
3115
// Remember to ResumeWalk this document when the load completes.
3116
mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
3117
aScriptProto->mSrcLoadWaiters = this;
3121
// Set mSrcLoading *before* calling NS_NewStreamLoader, in case the
3122
// stream completes (probably due to an error) within the activation
3123
// of NS_NewStreamLoader.
3124
aScriptProto->mSrcLoading = PR_TRUE;
3126
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
3128
// N.B., the loader will be released in OnStreamComplete
3129
nsIStreamLoader* loader;
3130
rv = NS_NewStreamLoader(&loader, aScriptProto->mSrcURI, this, nsnull, group);
3131
if (NS_FAILED(rv)) return rv;
3134
// Block until OnStreamComplete resumes us.
3141
nsXULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
3142
nsISupports* context,
3148
// print a load error on bad status
3149
if (NS_FAILED(aStatus)) {
3150
nsCOMPtr<nsIRequest> request;
3151
aLoader->GetRequest(getter_AddRefs(request));
3152
nsCOMPtr<nsIChannel> channel;
3153
channel = do_QueryInterface(request);
3155
nsCOMPtr<nsIURI> uri;
3156
channel->GetURI(getter_AddRefs(uri));
3158
nsCAutoString uriSpec;
3159
uri->GetSpec(uriSpec);
3160
printf("Failed to load %s\n", uriSpec.get());
3166
// This is the completion routine that will be called when a
3167
// transcluded script completes. Compile and execute the script
3168
// if the load was successful, then continue building content
3169
// from the prototype.
3172
NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
3173
"script source not loading on unichar stream complete?");
3175
// Clear mCurrentScriptProto now, but save it first for use below in
3176
// the compile/execute code, and in the while loop that resumes walks
3177
// of other documents that raced to load this script
3178
nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
3179
mCurrentScriptProto = nsnull;
3181
// Clear the prototype's loading flag before executing the script or
3182
// resuming document walks, in case any of those control flows starts a
3184
scriptProto->mSrcLoading = PR_FALSE;
3186
if (NS_SUCCEEDED(aStatus)) {
3187
// If the including XUL document is a FastLoad document, and we're
3188
// compiling an out-of-line script (one with src=...), then we must
3189
// be writing a new FastLoad file. If we were reading this script
3190
// from the FastLoad file, XULContentSinkImpl::OpenScript (over in
3191
// nsXULContentSink.cpp) would have already deserialized a non-null
3192
// script->mJSObject, causing control flow at the top of LoadScript
3193
// not to reach here.
3194
nsCOMPtr<nsIURI> uri = scriptProto->mSrcURI;
3196
nsString stringStr; stringStr.AssignWithConversion(string, stringLen);
3197
rv = scriptProto->Compile(stringStr.get(), stringLen, uri, 1, this,
3201
if (NS_SUCCEEDED(rv) && scriptProto->mJSObject) {
3202
rv = ExecuteScript(scriptProto->mJSObject);
3204
// If the XUL cache is enabled, save the script object there in
3205
// case different XUL documents source the same script.
3207
// But don't save the script in the cache unless the master XUL
3208
// document URL is a chrome: URL. It is valid for a URL such as
3209
// about:config to translate into a master document URL, whose
3210
// prototype document nodes -- including prototype scripts that
3211
// hold GC roots protecting their mJSObject pointers -- are not
3212
// cached in the XUL prototype cache. See StartDocumentLoad,
3213
// the fillXULCache logic.
3215
// A document such as about:config is free to load a script via
3216
// a URL such as chrome://global/content/config.js, and we must
3217
// not cache that script object without a prototype cache entry
3218
// containing a companion nsXULPrototypeScript node that owns a
3219
// GC root protecting the script object. Otherwise, the script
3220
// cache entry will dangle once the uncached prototype document
3221
// is released when its owning nsXULDocument is unloaded.
3223
// (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
3224
// the true crime story.)
3226
gXULCache->GetEnabled(&useXULCache);
3228
if (useXULCache && IsChromeURI(mDocumentURI)) {
3229
gXULCache->PutScript(scriptProto->mSrcURI,
3230
NS_REINTERPRET_CAST(void*, scriptProto->mJSObject));
3233
if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
3234
// If we are loading an overlay script, try to serialize
3235
// it to the FastLoad file here. Master scripts will be
3236
// serialized when the master prototype document gets
3237
// written, at the bottom of ResumeWalk. That way, master
3238
// out-of-line scripts are serialized in the same order that
3239
// they'll be read, in the FastLoad file, which reduces the
3240
// number of seeks that dump the underlying stream's buffer.
3242
// Ignore the return value, as we don't need to propagate
3243
// a failure to write to the FastLoad file, because this
3244
// method aborts that whole process on error.
3245
nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner
3246
= do_QueryInterface(mCurrentPrototype);
3247
nsCOMPtr<nsIScriptGlobalObject> global;
3248
globalOwner->GetScriptGlobalObject(getter_AddRefs(global));
3250
NS_ASSERTION(global != nsnull, "master prototype w/o global?!");
3252
nsIScriptContext *scriptContext = global->GetContext();
3254
scriptProto->SerializeOutOfLine(nsnull, scriptContext);
3258
// ignore any evaluation errors
3261
// balance the addref we added in LoadScript()
3262
NS_RELEASE(aLoader);
3266
// Load a pointer to the prototype-script's list of nsXULDocuments who
3267
// raced to load the same script
3268
nsXULDocument** docp = &scriptProto->mSrcLoadWaiters;
3270
// Resume walking other documents that waited for this one's load, first
3271
// executing the script we just compiled, in each doc's script context
3273
while ((doc = *docp) != nsnull) {
3274
NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
3275
"waiting for wrong script to load?");
3276
doc->mCurrentScriptProto = nsnull;
3278
// Unlink doc from scriptProto's list before executing and resuming
3279
*docp = doc->mNextSrcLoadWaiter;
3280
doc->mNextSrcLoadWaiter = nsnull;
3282
// Execute only if we loaded and compiled successfully, then resume
3283
if (NS_SUCCEEDED(aStatus) && scriptProto->mJSObject) {
3284
doc->ExecuteScript(scriptProto->mJSObject);
3295
nsXULDocument::ExecuteScript(JSObject* aScriptObject)
3297
NS_PRECONDITION(aScriptObject != nsnull, "null ptr");
3298
if (! aScriptObject)
3299
return NS_ERROR_NULL_POINTER;
3301
// Execute the precompiled script with the given version
3302
nsresult rv = NS_ERROR_UNEXPECTED;
3304
NS_ASSERTION(mScriptGlobalObject != nsnull, "no script global object");
3306
nsIScriptContext *context;
3307
if (mScriptGlobalObject && (context = mScriptGlobalObject->GetContext()))
3308
rv = context->ExecuteScript(aScriptObject, nsnull, nsnull, nsnull);
3315
nsXULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
3316
nsIContent** aResult)
3318
// Create a content model element from a prototype element.
3319
NS_PRECONDITION(aPrototype != nsnull, "null ptr");
3321
return NS_ERROR_NULL_POINTER;
3324
nsresult rv = NS_OK;
3327
if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS)) {
3328
nsAutoString tagstr;
3329
aPrototype->mNodeInfo->GetQualifiedName(tagstr);
3331
nsCAutoString tagstrC;
3332
tagstrC.AssignWithConversion(tagstr);
3333
PR_LOG(gXULLog, PR_LOG_ALWAYS,
3334
("xul: creating <%s> from prototype",
3339
nsCOMPtr<nsIContent> result;
3341
if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
3342
// If it's a XUL element, it'll be lightweight until somebody
3344
rv = nsXULElement::Create(aPrototype, this, PR_TRUE, getter_AddRefs(result));
3345
if (NS_FAILED(rv)) return rv;
3347
else if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
3348
// If it's an HTML element, it's gonna be heavyweight no matter
3349
// what. So we need to copy everything out of the prototype
3350
// into the element.
3352
gHTMLElementFactory->CreateInstanceByTag(aPrototype->mNodeInfo,
3353
getter_AddRefs(result));
3354
if (NS_FAILED(rv)) return rv;
3357
return NS_ERROR_UNEXPECTED;
3359
result->SetDocument(this, PR_FALSE, PR_TRUE);
3361
rv = AddAttributes(aPrototype, result);
3362
if (NS_FAILED(rv)) return rv;
3365
// If it's not a XUL element, it's gonna be heavyweight no matter
3366
// what. So we need to copy everything out of the prototype
3367
// into the element.
3369
nsCOMPtr<nsIElementFactory> elementFactory;
3370
GetElementFactory(aPrototype->mNodeInfo->NamespaceID(),
3371
getter_AddRefs(elementFactory));
3372
rv = elementFactory->CreateInstanceByTag(aPrototype->mNodeInfo,
3373
getter_AddRefs(result));
3374
if (NS_FAILED(rv)) return rv;
3377
return NS_ERROR_UNEXPECTED;
3379
result->SetDocument(this, PR_FALSE, PR_TRUE);
3381
rv = AddAttributes(aPrototype, result);
3382
if (NS_FAILED(rv)) return rv;
3385
result->SetContentID(mNextContentID++);
3388
NS_ADDREF(*aResult);
3393
nsXULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
3394
nsIContent** aResult)
3398
// This doesn't really do anything except create a placeholder
3399
// element. I'd use an XML element, but it gets its knickers in a
3400
// knot with DOM ranges when you try to remove its children.
3401
nsCOMPtr<nsIContent> element;
3402
rv = nsXULElement::Create(aPrototype, this, PR_FALSE,
3403
getter_AddRefs(element));
3404
if (NS_FAILED(rv)) return rv;
3406
OverlayForwardReference* fwdref =
3407
new OverlayForwardReference(this, element);
3409
return NS_ERROR_OUT_OF_MEMORY;
3411
// transferring ownership to ya...
3412
rv = AddForwardReference(fwdref);
3413
if (NS_FAILED(rv)) return rv;
3416
NS_ADDREF(*aResult);
3421
nsXULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
3422
nsIContent* aElement)
3426
for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i) {
3427
nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
3428
nsAutoString valueStr;
3429
protoattr->mValue.ToString(valueStr);
3431
rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
3432
protoattr->mName.LocalName(),
3433
protoattr->mName.GetPrefix(),
3436
if (NS_FAILED(rv)) return rv;
3444
nsXULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
3445
PRBool* aNeedsHookup)
3447
// See if the element already has a `database' attribute. If it
3448
// does, then the template builder has already been created.
3450
// XXX This approach will crash and burn (well, maybe not _that_
3451
// bad) if aElement is not a XUL element.
3453
// XXXvarga Do we still want to support non XUL content?
3454
nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
3456
nsCOMPtr<nsIRDFCompositeDataSource> ds;
3457
xulElement->GetDatabase(getter_AddRefs(ds));
3459
*aNeedsHookup = PR_FALSE;
3464
// Check aElement for a 'datasources' attribute, if it has
3465
// one a XUL template builder needs to be hooked up.
3466
*aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
3467
nsXULAtoms::datasources);
3472
nsXULDocument::CreateTemplateBuilder(nsIContent* aElement)
3474
// Check if need to construct a tree builder or content builder.
3475
PRBool isTreeBuilder = PR_FALSE;
3477
PRInt32 nameSpaceID;
3478
nsCOMPtr<nsIAtom> baseTag;
3480
nsCOMPtr<nsIXBLService> xblService = do_GetService("@mozilla.org/xbl;1");
3482
xblService->ResolveTag(aElement, &nameSpaceID, getter_AddRefs(baseTag));
3485
nsINodeInfo *ni = aElement->GetNodeInfo();
3486
nameSpaceID = ni->NamespaceID();
3487
baseTag = ni->NameAtom();
3490
if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsXULAtoms::tree)) {
3491
// By default, we build content for a tree and then we attach
3492
// the tree content view. However, if the `dont-build-content'
3493
// flag is set, then we we'll attach a tree builder which
3494
// directly implements the tree view.
3497
aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::flags, flags);
3498
if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
3499
isTreeBuilder = PR_TRUE;
3503
if (isTreeBuilder) {
3504
// Create and initialize a tree builder.
3505
nsCOMPtr<nsIXULTemplateBuilder> builder =
3506
do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
3509
return NS_ERROR_FAILURE;
3511
builder->Init(aElement);
3513
// Create a <treechildren> if one isn't there already.
3514
nsCOMPtr<nsIContent> bodyContent;
3515
nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
3516
nsXULAtoms::treechildren,
3517
getter_AddRefs(bodyContent));
3519
if (! bodyContent) {
3520
// Get the document.
3521
nsCOMPtr<nsIDOMDocument> domdoc =
3522
do_QueryInterface(aElement->GetDocument());
3523
NS_ASSERTION(domdoc, "no document");
3525
return NS_ERROR_UNEXPECTED;
3527
nsCOMPtr<nsIDOMElement> bodyElement;
3528
domdoc->CreateElement(NS_LITERAL_STRING("treechildren"),
3529
getter_AddRefs(bodyElement));
3531
bodyContent = do_QueryInterface(bodyElement);
3532
aElement->AppendChildTo(bodyContent, PR_FALSE, PR_TRUE);
3537
// Create and initialize a content builder.
3538
nsCOMPtr<nsIXULTemplateBuilder> builder
3539
= do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
3542
return NS_ERROR_FAILURE;
3544
builder->Init(aElement);
3546
nsCOMPtr<nsIXULContent> xulContent = do_QueryInterface(aElement);
3548
// Mark the XUL element as being lazy, so the template builder
3549
// will run when layout first asks for these nodes.
3550
xulContent->SetLazyState(nsIXULContent::eChildrenMustBeRebuilt);
3553
// Force construction of immediate template sub-content _now_.
3554
builder->CreateContents(aElement);
3563
nsXULDocument::AddPrototypeSheets()
3565
// Add mCurrentPrototype's style sheets to the document.
3568
nsCOMPtr<nsISupportsArray> sheets;
3569
rv = mCurrentPrototype->GetStyleSheetReferences(getter_AddRefs(sheets));
3570
if (NS_FAILED(rv)) return rv;
3573
sheets->Count(&count);
3574
for (PRUint32 i = 0; i < count; ++i) {
3575
nsISupports* isupports = sheets->ElementAt(i);
3576
nsCOMPtr<nsIURI> uri = do_QueryInterface(isupports);
3577
NS_IF_RELEASE(isupports);
3579
NS_ASSERTION(uri, "not a URI!!!");
3581
return NS_ERROR_UNEXPECTED;
3584
uri->GetAsciiSpec(spec);
3586
if (!IsChromeURI(uri)) {
3587
// These don't get to be in the prototype cache anyway...
3588
// and we can't load non-chrome sheets synchronously
3592
nsCOMPtr<nsICSSStyleSheet> sheet;
3594
// If the sheet is a chrome URL, then we can refetch the sheet
3595
// synchronously, since we know the sheet is local. It's not
3596
// too late! :) If we're lucky, the loader will just pull it
3597
// from the prototype cache anyway.
3598
// Otherwise we just bail. It shouldn't currently
3599
// be possible to get into this situation for any reason
3600
// other than a skin switch anyway (since skin switching is the
3601
// only system that partially invalidates the XUL cache).
3603
//XXXbz we hit this code from fastload all the time. Bug 183505.
3604
nsICSSLoader* loader = GetCSSLoader();
3605
NS_ENSURE_TRUE(loader, NS_ERROR_OUT_OF_MEMORY);
3606
rv = loader->LoadAgentSheet(uri, getter_AddRefs(sheet));
3607
// XXXldb We need to prevent bogus sheets from being held in the
3608
// prototype's list, but until then, don't propagate the failure
3609
// from LoadAgentSheet (and thus exit the loop).
3610
if (NS_SUCCEEDED(rv)) {
3611
AddStyleSheet(sheet, 0);
3619
//----------------------------------------------------------------------
3621
// nsXULDocument::OverlayForwardReference
3624
nsForwardReference::Result
3625
nsXULDocument::OverlayForwardReference::Resolve()
3627
// Resolve a forward reference from an overlay element; attempt to
3628
// hook it up into the main document.
3632
rv = mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
3633
if (NS_FAILED(rv)) return eResolve_Error;
3636
// overlay had no id, use the root element
3637
mDocument->InsertElement(mDocument->mRootContent, mOverlay);
3638
mResolved = PR_TRUE;
3639
return eResolve_Succeeded;
3642
nsCOMPtr<nsIDOMElement> domtarget;
3643
rv = mDocument->GetElementById(id, getter_AddRefs(domtarget));
3644
if (NS_FAILED(rv)) return eResolve_Error;
3646
// If we can't find the element in the document, defer the hookup
3649
return eResolve_Later;
3651
nsCOMPtr<nsIContent> target = do_QueryInterface(domtarget);
3652
NS_ASSERTION(target != nsnull, "not an nsIContent");
3654
return eResolve_Error;
3656
rv = Merge(target, mOverlay);
3657
if (NS_FAILED(rv)) return eResolve_Error;
3659
// Add child and any descendants to the element map
3660
rv = mDocument->AddSubtreeToDocument(target);
3661
if (NS_FAILED(rv)) return eResolve_Error;
3665
idC.AssignWithConversion(id);
3666
PR_LOG(gXULLog, PR_LOG_ALWAYS,
3667
("xul: overlay resolved '%s'",
3671
mResolved = PR_TRUE;
3672
return eResolve_Succeeded;
3678
nsXULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
3679
nsIContent* aOverlayNode)
3681
// This function is given:
3682
// aTargetNode: the node in the document whose 'id' attribute
3683
// matches a toplevel node in our overlay.
3684
// aOverlayNode: the node in the overlay document that matches
3685
// a node in the actual document.
3687
// This function merges the tree from the overlay into the tree in
3688
// the document, overwriting attributes and appending child content
3689
// nodes appropriately. (See XUL overlay reference for details)
3693
// Merge attributes from the overlay content node to that of the
3695
PRUint32 i, attrCount = aOverlayNode->GetAttrCount();
3697
for (i = 0; i < attrCount; ++i) {
3698
PRInt32 nameSpaceID;
3699
nsCOMPtr<nsIAtom> attr, prefix;
3700
rv = aOverlayNode->GetAttrNameAt(i, &nameSpaceID,
3701
getter_AddRefs(attr),
3702
getter_AddRefs(prefix));
3703
if (NS_FAILED(rv)) return rv;
3705
// We don't want to swap IDs, they should be the same.
3706
if (nameSpaceID == kNameSpaceID_None && attr.get() == nsXULAtoms::id)
3710
rv = aOverlayNode->GetAttr(nameSpaceID, attr, value);
3711
if (NS_FAILED(rv)) return rv;
3713
nsAutoString tempID;
3714
rv = aOverlayNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id, tempID);
3716
// Element in the overlay has the 'removeelement' attribute set
3717
// so remove it from the actual document.
3718
if (attr == nsXULAtoms::removeelement &&
3719
value.Equals(NS_LITERAL_STRING("true"))) {
3721
rv = RemoveElement(aTargetNode->GetParent(), aTargetNode);
3722
if (NS_FAILED(rv)) return rv;
3727
rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, PR_FALSE);
3728
if (NS_FAILED(rv)) return rv;
3732
// Walk our child nodes, looking for elements that have the 'id'
3733
// attribute set. If we find any, we must do a parent check in the
3734
// actual document to ensure that the structure matches that of
3735
// the actual document. If it does, we can call ourselves and attempt
3736
// to merge inside that subtree. If not, we just append the tree to
3737
// the parent like any other.
3739
PRUint32 childCount = aOverlayNode->GetChildCount();
3741
// This must be a strong reference since it will be the only
3742
// reference to a content object during part of this loop.
3743
nsCOMPtr<nsIContent> currContent;
3745
for (i = 0; i < childCount; ++i) {
3746
currContent = aOverlayNode->GetChildAt(0);
3749
rv = currContent->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
3750
if (NS_FAILED(rv)) return rv;
3752
nsCOMPtr<nsIDOMElement> nodeInDocument;
3753
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
3754
nsCOMPtr<nsIDOMDocument> domDocument(
3755
do_QueryInterface(aTargetNode->GetDocument()));
3756
if (!domDocument) return NS_ERROR_FAILURE;
3758
rv = domDocument->GetElementById(id, getter_AddRefs(nodeInDocument));
3759
if (NS_FAILED(rv)) return rv;
3762
// The item has an 'id' attribute set, and we need to check with
3763
// the actual document to see if an item with this id exists at
3764
// this locale. If so, we want to merge the subtree under that
3765
// node. Otherwise, we just do an append as if the element had
3767
if (nodeInDocument) {
3768
// Given two parents, aTargetNode and aOverlayNode, we want
3769
// to call merge on currContent if we find an associated
3770
// node in the document with the same id as currContent that
3771
// also has aTargetNode as its parent.
3773
nsAutoString documentParentID;
3774
rv = aTargetNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id,
3776
if (NS_FAILED(rv)) return rv;
3778
nsCOMPtr<nsIDOMNode> nodeParent;
3779
rv = nodeInDocument->GetParentNode(getter_AddRefs(nodeParent));
3780
if (NS_FAILED(rv)) return rv;
3781
nsCOMPtr<nsIDOMElement> elementParent(do_QueryInterface(nodeParent));
3783
nsAutoString parentID;
3784
elementParent->GetAttribute(NS_LITERAL_STRING("id"), parentID);
3785
if (parentID.Equals(documentParentID)) {
3786
// The element matches. "Go Deep!"
3787
nsCOMPtr<nsIContent> childDocumentContent(do_QueryInterface(nodeInDocument));
3788
rv = Merge(childDocumentContent, currContent);
3789
if (NS_FAILED(rv)) return rv;
3790
rv = aOverlayNode->RemoveChildAt(0, PR_FALSE);
3791
if (NS_FAILED(rv)) return rv;
3797
rv = aOverlayNode->RemoveChildAt(0, PR_FALSE);
3798
if (NS_FAILED(rv)) return rv;
3800
rv = InsertElement(aTargetNode, currContent);
3801
if (NS_FAILED(rv)) return rv;
3809
nsXULDocument::OverlayForwardReference::~OverlayForwardReference()
3812
if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS) && !mResolved) {
3814
mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
3817
idC.AssignWithConversion(id);
3818
PR_LOG(gXULLog, PR_LOG_ALWAYS,
3819
("xul: overlay failed to resolve '%s'",
3826
//----------------------------------------------------------------------
3828
// nsXULDocument::BroadcasterHookup
3831
nsForwardReference::Result
3832
nsXULDocument::BroadcasterHookup::Resolve()
3837
rv = CheckBroadcasterHookup(mDocument, mObservesElement, &listener, &mResolved);
3838
if (NS_FAILED(rv)) return eResolve_Error;
3840
return mResolved ? eResolve_Succeeded : eResolve_Later;
3844
nsXULDocument::BroadcasterHookup::~BroadcasterHookup()
3847
if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS) && !mResolved) {
3848
// Tell the world we failed
3851
nsIAtom *tag = mObservesElement->Tag();
3853
nsAutoString broadcasterID;
3854
nsAutoString attribute;
3856
if (tag == nsXULAtoms::observes) {
3857
rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID);
3858
if (NS_FAILED(rv)) return;
3860
rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute);
3861
if (NS_FAILED(rv)) return;
3864
rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID);
3865
if (NS_FAILED(rv)) return;
3867
attribute.Assign(NS_LITERAL_STRING("*"));
3870
nsAutoString tagStr;
3871
rv = tag->ToString(tagStr);
3872
if (NS_FAILED(rv)) return;
3874
nsCAutoString tagstrC, attributeC,broadcasteridC;
3875
tagstrC.AssignWithConversion(tagStr);
3876
attributeC.AssignWithConversion(attribute);
3877
broadcasteridC.AssignWithConversion(broadcasterID);
3878
PR_LOG(gXULLog, PR_LOG_ALWAYS,
3879
("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
3882
broadcasteridC.get()));
3888
//----------------------------------------------------------------------
3890
// nsXULDocument::TemplateBuilderHookup
3893
nsForwardReference::Result
3894
nsXULDocument::TemplateBuilderHookup::Resolve()
3897
nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
3899
return eResolve_Error;
3902
rv = CreateTemplateBuilder(mElement);
3904
return eResolve_Error;
3907
return eResolve_Succeeded;
3911
//----------------------------------------------------------------------
3915
nsXULDocument::CheckBroadcasterHookup(nsXULDocument* aDocument,
3916
nsIContent* aElement,
3917
PRBool* aNeedsHookup,
3918
PRBool* aDidResolve)
3920
// Resolve a broadcaster hookup. Look at the element that we're
3921
// trying to resolve: it could be an '<observes>' element, or just
3922
// a vanilla element with an 'observes' attribute on it.
3925
*aDidResolve = PR_FALSE;
3927
nsCOMPtr<nsIDOMElement> listener;
3928
nsAutoString broadcasterID;
3929
nsAutoString attribute;
3931
nsINodeInfo *ni = aElement->GetNodeInfo();
3933
if (ni && ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL)) {
3934
// It's an <observes> element, which means that the actual
3935
// listener is the _parent_ node. This element should have an
3936
// 'element' attribute that specifies the ID of the
3937
// broadcaster element, and an 'attribute' element, which
3938
// specifies the name of the attribute to observe.
3939
nsIContent* parent = aElement->GetParent();
3941
// If we're still parented by an 'overlay' tag, then we haven't
3942
// made it into the real document yet. Defer hookup.
3943
if (parent->GetNodeInfo()->Equals(nsXULAtoms::overlay, kNameSpaceID_XUL)) {
3944
*aNeedsHookup = PR_TRUE;
3948
listener = do_QueryInterface(parent);
3950
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID);
3951
if (NS_FAILED(rv)) return rv;
3953
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute);
3954
if (NS_FAILED(rv)) return rv;
3957
// It's a generic element, which means that we'll use the
3958
// value of the 'observes' attribute to determine the ID of
3959
// the broadcaster element, and we'll watch _all_ of its
3961
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID);
3962
if (NS_FAILED(rv)) return rv;
3964
// Bail if there's no broadcasterID
3965
if ((rv != NS_CONTENT_ATTR_HAS_VALUE) || (broadcasterID.IsEmpty())) {
3966
// Try the command attribute next.
3967
rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::command, broadcasterID);
3968
if (NS_FAILED(rv)) return rv;
3970
if (rv == NS_CONTENT_ATTR_HAS_VALUE && !broadcasterID.IsEmpty()) {
3971
// We've got something in the command attribute. We
3972
// only treat this as a normal broadcaster if we are
3973
// not a menuitem or a key.
3975
nsINodeInfo *ni = aElement->GetNodeInfo();
3976
if (ni->Equals(nsXULAtoms::menuitem, kNameSpaceID_XUL) ||
3977
ni->Equals(nsXULAtoms::key, kNameSpaceID_XUL)) {
3978
*aNeedsHookup = PR_FALSE;
3983
*aNeedsHookup = PR_FALSE;
3988
listener = do_QueryInterface(aElement);
3990
attribute.Assign(NS_LITERAL_STRING("*"));
3993
// Make sure we got a valid listener.
3994
NS_ASSERTION(listener != nsnull, "no listener");
3996
return NS_ERROR_UNEXPECTED;
3998
// Try to find the broadcaster element in the document.
3999
nsCOMPtr<nsIDOMElement> broadcaster;
4000
rv = aDocument->GetElementById(broadcasterID, getter_AddRefs(broadcaster));
4001
if (NS_FAILED(rv)) return rv;
4003
// If we can't find the broadcaster, then we'll need to defer the
4004
// hookup. We may need to resolve some of the other overlays
4006
if (! broadcaster) {
4007
*aNeedsHookup = PR_TRUE;
4011
rv = aDocument->AddBroadcastListenerFor(broadcaster, listener, attribute);
4012
if (NS_FAILED(rv)) return rv;
4015
// Tell the world we succeeded
4016
if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS)) {
4017
nsCOMPtr<nsIContent> content =
4018
do_QueryInterface(listener);
4020
NS_ASSERTION(content != nsnull, "not an nsIContent");
4024
nsAutoString tagStr;
4025
rv = content->Tag()->ToString(tagStr);
4026
if (NS_FAILED(rv)) return rv;
4028
nsCAutoString tagstrC, attributeC,broadcasteridC;
4029
tagstrC.AssignWithConversion(tagStr);
4030
attributeC.AssignWithConversion(attribute);
4031
broadcasteridC.AssignWithConversion(broadcasterID);
4032
PR_LOG(gXULLog, PR_LOG_ALWAYS,
4033
("xul: broadcaster hookup <%s attribute='%s'> to %s",
4036
broadcasteridC.get()));
4040
*aNeedsHookup = PR_FALSE;
4041
*aDidResolve = PR_TRUE;
4046
nsXULDocument::InsertElement(nsIContent* aParent, nsIContent* aChild)
4048
// Insert aChild appropriately into aParent, accounting for a
4049
// 'pos' attribute set on aChild.
4052
nsAutoString posStr;
4053
PRBool wasInserted = PR_FALSE;
4055
// insert after an element of a given id
4056
rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, posStr);
4057
if (NS_FAILED(rv)) return rv;
4058
PRBool isInsertAfter = PR_TRUE;
4060
if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
4061
rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore,
4063
if (NS_FAILED(rv)) return rv;
4064
isInsertAfter = PR_FALSE;
4067
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
4068
nsCOMPtr<nsIDOMDocument> domDocument(
4069
do_QueryInterface(aParent->GetDocument()));
4070
nsCOMPtr<nsIDOMElement> domElement;
4072
char* str = ToNewCString(posStr);
4074
char* token = nsCRT::strtok(str, ", ", &rest);
4077
rv = domDocument->GetElementById(NS_ConvertASCIItoUCS2(token),
4078
getter_AddRefs(domElement));
4082
token = nsCRT::strtok(rest, ", ", &rest);
4084
nsMemory::Free(str);
4089
nsCOMPtr<nsIContent> content(do_QueryInterface(domElement));
4090
NS_ASSERTION(content != nsnull, "null ptr");
4092
return NS_ERROR_UNEXPECTED;
4094
PRInt32 pos = aParent->IndexOf(content);
4097
pos = isInsertAfter ? pos + 1 : pos;
4098
rv = aParent->InsertChildAt(aChild, pos, PR_FALSE, PR_TRUE);
4102
wasInserted = PR_TRUE;
4109
rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::position, posStr);
4110
if (NS_FAILED(rv)) return rv;
4112
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
4113
// Positions are one-indexed.
4114
PRInt32 pos = posStr.ToInteger(NS_REINTERPRET_CAST(PRInt32*, &rv));
4115
if (NS_SUCCEEDED(rv)) {
4116
rv = aParent->InsertChildAt(aChild, pos - 1, PR_FALSE,
4118
if (NS_SUCCEEDED(rv))
4119
wasInserted = PR_TRUE;
4120
// If the insertion fails, then we should still
4121
// attempt an append. Thus, rather than returning rv
4122
// immediately, we fall through to the final
4123
// "catch-all" case that just does an AppendChildTo.
4128
if (! wasInserted) {
4129
rv = aParent->AppendChildTo(aChild, PR_FALSE, PR_TRUE);
4130
if (NS_FAILED(rv)) return rv;
4136
nsXULDocument::RemoveElement(nsIContent* aParent, nsIContent* aChild)
4138
PRInt32 nodeOffset = aParent->IndexOf(aChild);
4140
return aParent->RemoveChildAt(nodeOffset, PR_TRUE);
4144
nsXULDocument::GetElementFactory(PRInt32 aNameSpaceID,
4145
nsIElementFactory** aResult)
4147
// Retrieve the appropriate factory.
4148
nsContentUtils::GetNSManagerWeakRef()->GetElementFactory(aNameSpaceID,
4152
// Nothing found. Use generic XML element.
4153
*aResult = gXMLElementFactory;
4154
NS_IF_ADDREF(*aResult);
4158
//----------------------------------------------------------------------
4160
// CachedChromeStreamListener
4163
nsXULDocument::CachedChromeStreamListener::CachedChromeStreamListener(nsXULDocument* aDocument, PRBool aProtoLoaded)
4164
: mDocument(aDocument),
4165
mProtoLoaded(aProtoLoaded)
4167
NS_ADDREF(mDocument);
4171
nsXULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
4173
NS_RELEASE(mDocument);
4177
NS_IMPL_ISUPPORTS2(nsXULDocument::CachedChromeStreamListener,
4178
nsIRequestObserver, nsIStreamListener)
4181
nsXULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
4182
nsISupports* acontext)
4189
nsXULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
4190
nsISupports* aContext,
4197
rv = mDocument->PrepareToWalk();
4198
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
4199
if (NS_FAILED(rv)) return rv;
4201
return mDocument->ResumeWalk();
4206
nsXULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
4207
nsISupports* aContext,
4208
nsIInputStream* aInStr,
4209
PRUint32 aSourceOffset,
4212
NS_NOTREACHED("CachedChromeStream doesn't receive data");
4213
return NS_ERROR_UNEXPECTED;
4216
//----------------------------------------------------------------------
4221
nsXULDocument::ParserObserver::ParserObserver(nsXULDocument* aDocument)
4222
: mDocument(aDocument)
4224
NS_ADDREF(mDocument);
4227
nsXULDocument::ParserObserver::~ParserObserver()
4229
NS_IF_RELEASE(mDocument);
4232
NS_IMPL_ISUPPORTS1(nsXULDocument::ParserObserver, nsIRequestObserver)
4235
nsXULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
4236
nsISupports* aContext)
4242
nsXULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
4243
nsISupports* aContext,
4246
nsresult rv = NS_OK;
4248
if (NS_FAILED(aStatus)) {
4249
// If an overlay load fails, we need to nudge the prototype
4251
#define YELL_IF_MISSING_OVERLAY 1
4252
#if defined(DEBUG) || defined(YELL_IF_MISSING_OVERLAY)
4254
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
4255
if (!aChannel) return NS_ERROR_FAILURE;
4257
nsCOMPtr<nsIURI> uri;
4258
aChannel->GetOriginalURI(getter_AddRefs(uri));
4263
printf("*** Failed to load overlay %s\n", spec.get());
4266
rv = mDocument->ResumeWalk();
4269
// Drop the reference to the document to break cycle between the
4270
// document, the parser, the content sink, and the parser
4272
NS_RELEASE(mDocument);
4278
nsXULDocument::GetFocusController(nsIFocusController** aFocusController)
4280
nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryReferent(mDocumentContainer);
4281
nsCOMPtr<nsPIDOMWindow> windowPrivate = do_GetInterface(ir);
4282
if (windowPrivate) {
4283
windowPrivate->GetRootFocusController(aFocusController);
4285
*aFocusController = nsnull;
4288
//----------------------------------------------------------------------
4290
// The XUL element factory
4293
class XULElementFactoryImpl : public nsIElementFactory
4296
XULElementFactoryImpl();
4297
virtual ~XULElementFactoryImpl();
4302
NS_NewXULElementFactory(nsIElementFactory** aResult);
4304
// nsISupports interface
4307
// nsIElementFactory interface
4308
NS_IMETHOD CreateInstanceByTag(nsINodeInfo *aNodeInfo, nsIContent** aResult);
4311
XULElementFactoryImpl::XULElementFactoryImpl()
4315
XULElementFactoryImpl::~XULElementFactoryImpl()
4320
NS_IMPL_ISUPPORTS1(XULElementFactoryImpl, nsIElementFactory)
4324
NS_NewXULElementFactory(nsIElementFactory** aResult)
4326
NS_PRECONDITION(aResult != nsnull, "null ptr");
4328
return NS_ERROR_NULL_POINTER;
4330
XULElementFactoryImpl* result = new XULElementFactoryImpl();
4332
return NS_ERROR_OUT_OF_MEMORY;
4342
XULElementFactoryImpl::CreateInstanceByTag(nsINodeInfo *aNodeInfo,
4343
nsIContent** aResult)
4345
return nsXULElement::Create(aNodeInfo, aResult);