~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/content/xul/document/src/nsXULDocument.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
4
 *
 
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/
 
9
 *
 
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
 
13
 * License.
 
14
 *
 
15
 * The Original Code is Mozilla Communicator client code.
 
16
 *
 
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.
 
21
 *
 
22
 * Contributor(s):
 
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>
 
28
 *
 
29
 *
 
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.
 
41
 *
 
42
 * ***** END LICENSE BLOCK ***** */
 
43
 
 
44
/*
 
45
 
 
46
  An implementation for the XUL document. This implementation serves
 
47
  as the basis for generating an NGLayout content model.
 
48
 
 
49
  Notes
 
50
  -----
 
51
 
 
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".
 
58
 
 
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.
 
62
 
 
63
*/
 
64
 
 
65
// Note the ALPHABETICAL ORDERING
 
66
#include "nsXULDocument.h"
 
67
 
 
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"
 
84
#include "nsITimer.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"
 
95
#include "nsRDFCID.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"
 
103
#include "prlog.h"
 
104
#include "rdf.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"
 
121
 
 
122
//----------------------------------------------------------------------
 
123
//
 
124
// CIDs
 
125
//
 
126
 
 
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);
 
133
 
 
134
static PRBool IsChromeURI(nsIURI* aURI)
 
135
{
 
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)
 
139
        return PR_TRUE;
 
140
    return PR_FALSE;
 
141
}
 
142
 
 
143
//----------------------------------------------------------------------
 
144
//
 
145
// Miscellaneous Constants
 
146
//
 
147
 
 
148
const nsForwardReference::Phase nsForwardReference::kPasses[] = {
 
149
    nsForwardReference::eConstruction,
 
150
    nsForwardReference::eHookup,
 
151
    nsForwardReference::eDone
 
152
};
 
153
 
 
154
 
 
155
//----------------------------------------------------------------------
 
156
//
 
157
// Statics
 
158
//
 
159
 
 
160
PRInt32 nsXULDocument::gRefCnt = 0;
 
161
 
 
162
nsIRDFService* nsXULDocument::gRDFService;
 
163
nsIRDFResource* nsXULDocument::kNC_persist;
 
164
nsIRDFResource* nsXULDocument::kNC_attribute;
 
165
nsIRDFResource* nsXULDocument::kNC_value;
 
166
 
 
167
nsIElementFactory* nsXULDocument::gHTMLElementFactory;
 
168
nsIElementFactory*  nsXULDocument::gXMLElementFactory;
 
169
 
 
170
nsIXULPrototypeCache* nsXULDocument::gXULCache;
 
171
 
 
172
PRLogModuleInfo* nsXULDocument::gXULLog;
 
173
 
 
174
class nsProxyLoadStream : public nsIInputStream
 
175
{
 
176
private:
 
177
    const char* mBuffer;
 
178
    PRUint32    mSize;
 
179
    PRUint32    mIndex;
 
180
 
 
181
public:
 
182
    nsProxyLoadStream(void) : mBuffer(nsnull)
 
183
    {
 
184
    }
 
185
 
 
186
    virtual ~nsProxyLoadStream(void)
 
187
    {
 
188
    }
 
189
 
 
190
    // nsISupports
 
191
    NS_DECL_ISUPPORTS
 
192
 
 
193
    // nsIBaseStream
 
194
    NS_IMETHOD Close(void)
 
195
    {
 
196
        return NS_OK;
 
197
    }
 
198
 
 
199
    // nsIInputStream
 
200
    NS_IMETHOD Available(PRUint32 *aLength)
 
201
    {
 
202
        *aLength = mSize - mIndex;
 
203
        return NS_OK;
 
204
    }
 
205
 
 
206
    NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32 *aReadCount)
 
207
    {
 
208
        PRUint32 readCount = 0;
 
209
        while (mIndex < mSize && aCount > 0) {
 
210
            *aBuf = mBuffer[mIndex];
 
211
            ++aBuf;
 
212
            ++mIndex;
 
213
            readCount++;
 
214
            --aCount;
 
215
        }
 
216
        *aReadCount = readCount;
 
217
        return NS_OK;
 
218
    }
 
219
 
 
220
    NS_IMETHOD ReadSegments(nsWriteSegmentFun writer, void * closure,
 
221
                            PRUint32 count, PRUint32 *_retval)
 
222
    {
 
223
        NS_NOTREACHED("ReadSegments");
 
224
        return NS_ERROR_NOT_IMPLEMENTED;
 
225
    }
 
226
 
 
227
    NS_IMETHOD IsNonBlocking(PRBool *aNonBlocking)
 
228
    {
 
229
        *aNonBlocking = PR_TRUE;
 
230
        return NS_OK;
 
231
    }
 
232
 
 
233
    // Implementation
 
234
    void SetBuffer(const char* aBuffer, PRUint32 aSize)
 
235
    {
 
236
        mBuffer = aBuffer;
 
237
        mSize = aSize;
 
238
        mIndex = 0;
 
239
    }
 
240
};
 
241
 
 
242
NS_IMPL_ISUPPORTS1(nsProxyLoadStream, nsIInputStream)
 
243
 
 
244
//----------------------------------------------------------------------
 
245
//
 
246
// PlaceholderRequest
 
247
//
 
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
 
251
//   model.
 
252
//
 
253
 
 
254
class PlaceHolderRequest : public nsIChannel
 
255
{
 
256
protected:
 
257
    PlaceHolderRequest();
 
258
    virtual ~PlaceHolderRequest();
 
259
 
 
260
    static PRInt32 gRefCnt;
 
261
    static nsIURI* gURI;
 
262
 
 
263
    nsCOMPtr<nsILoadGroup> mLoadGroup;
 
264
 
 
265
public:
 
266
    static nsresult
 
267
    Create(nsIRequest** aResult);
 
268
 
 
269
    NS_DECL_ISUPPORTS
 
270
 
 
271
    // nsIRequest
 
272
    NS_IMETHOD GetName(nsACString &result) {
 
273
        return NS_ERROR_NOT_IMPLEMENTED;
 
274
    }
 
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; }
 
284
 
 
285
    // nsIChannel
 
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; }
 
303
};
 
304
 
 
305
PRInt32 PlaceHolderRequest::gRefCnt;
 
306
nsIURI* PlaceHolderRequest::gURI;
 
307
 
 
308
NS_IMPL_ADDREF(PlaceHolderRequest)
 
309
NS_IMPL_RELEASE(PlaceHolderRequest)
 
310
NS_IMPL_QUERY_INTERFACE2(PlaceHolderRequest, nsIRequest, nsIChannel)
 
311
 
 
312
nsresult
 
313
PlaceHolderRequest::Create(nsIRequest** aResult)
 
314
{
 
315
    PlaceHolderRequest* request = new PlaceHolderRequest();
 
316
    if (! request)
 
317
        return NS_ERROR_OUT_OF_MEMORY;
 
318
 
 
319
    *aResult = request;
 
320
    NS_ADDREF(*aResult);
 
321
    return NS_OK;
 
322
}
 
323
 
 
324
 
 
325
PlaceHolderRequest::PlaceHolderRequest()
 
326
{
 
327
 
 
328
    if (gRefCnt++ == 0) {
 
329
        nsresult rv;
 
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");
 
332
    }
 
333
}
 
334
 
 
335
 
 
336
PlaceHolderRequest::~PlaceHolderRequest()
 
337
{
 
338
    if (--gRefCnt == 0) {
 
339
        NS_IF_RELEASE(gURI);
 
340
    }
 
341
}
 
342
 
 
343
//----------------------------------------------------------------------
 
344
 
 
345
struct BroadcasterMapEntry : public PLDHashEntryHdr {
 
346
    nsIDOMElement*   mBroadcaster; // [WEAK]
 
347
    nsSmallVoidArray mListeners;   // [OWNING] of BroadcastListener objects
 
348
};
 
349
 
 
350
struct BroadcastListener {
 
351
    nsIDOMElement*    mListener; // [WEAK] XXXwaterson crash waiting to happen!
 
352
    nsCOMPtr<nsIAtom> mAttribute;
 
353
};
 
354
 
 
355
//----------------------------------------------------------------------
 
356
//
 
357
// ctors & dtors
 
358
//
 
359
 
 
360
    // NOTE! nsDocument::operator new() zeroes out all members, so
 
361
    // don't bother initializing members to 0.
 
362
 
 
363
nsXULDocument::nsXULDocument(void)
 
364
    : mResolutionPhase(nsForwardReference::eStart),
 
365
      mState(eState_Master)
 
366
{
 
367
 
 
368
    // NOTE! nsDocument::operator new() zeroes out all members, so don't
 
369
    // bother initializing members to 0.
 
370
 
 
371
    // Override the default in nsDocument
 
372
    mCharacterSet.Assign(NS_LITERAL_CSTRING("UTF-8"));
 
373
}
 
374
 
 
375
nsXULDocument::~nsXULDocument()
 
376
{
 
377
    NS_ASSERTION(mNextSrcLoadWaiter == nsnull,
 
378
        "unreferenced document still waiting for script source to load?");
 
379
 
 
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.
 
383
 
 
384
    PRInt32 i;
 
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));
 
389
 
 
390
        observer->DocumentWillBeDestroyed(this);
 
391
    }
 
392
 
 
393
    mObservers.Clear();
 
394
 
 
395
    // In case we failed somewhere early on and the forward observer
 
396
    // decls never got resolved.
 
397
    DestroyForwardReferences();
 
398
 
 
399
    // Destroy our broadcaster map.
 
400
    if (mBroadcasterMap) {
 
401
        PL_DHashTableDestroy(mBroadcasterMap);
 
402
    }
 
403
 
 
404
    if (mLocalStore) {
 
405
        nsCOMPtr<nsIRDFRemoteDataSource> remote =
 
406
            do_QueryInterface(mLocalStore);
 
407
        if (remote)
 
408
            remote->Flush();
 
409
    }
 
410
 
 
411
    delete mTemplateBuilderTable;
 
412
 
 
413
    if (--gRefCnt == 0) {
 
414
        NS_IF_RELEASE(gRDFService);
 
415
 
 
416
        NS_IF_RELEASE(kNC_persist);
 
417
        NS_IF_RELEASE(kNC_attribute);
 
418
        NS_IF_RELEASE(kNC_value);
 
419
 
 
420
        NS_IF_RELEASE(gHTMLElementFactory);
 
421
        NS_IF_RELEASE(gXMLElementFactory);
 
422
 
 
423
        if (gXULCache) {
 
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.
 
428
            if (mDocumentURI)
 
429
                gXULCache->RemoveFromFastLoadSet(mDocumentURI);
 
430
 
 
431
            NS_RELEASE(gXULCache);
 
432
        }
 
433
    }
 
434
 
 
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
 
438
    // from happening.
 
439
    if (mIsPopup) {
 
440
        mStyleSheets.Clear();
 
441
    }
 
442
 
 
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();
 
449
    }
 
450
}
 
451
 
 
452
nsresult
 
453
NS_NewXULDocument(nsIXULDocument** result)
 
454
{
 
455
    NS_PRECONDITION(result != nsnull, "null ptr");
 
456
    if (! result)
 
457
        return NS_ERROR_NULL_POINTER;
 
458
 
 
459
    nsXULDocument* doc = new nsXULDocument();
 
460
    if (! doc)
 
461
        return NS_ERROR_OUT_OF_MEMORY;
 
462
 
 
463
    NS_ADDREF(doc);
 
464
 
 
465
    nsresult rv;
 
466
    if (NS_FAILED(rv = doc->Init())) {
 
467
        NS_RELEASE(doc);
 
468
        return rv;
 
469
    }
 
470
 
 
471
    *result = doc;
 
472
    return NS_OK;
 
473
}
 
474
 
 
475
 
 
476
//----------------------------------------------------------------------
 
477
//
 
478
// nsISupports interface
 
479
//
 
480
 
 
481
NS_IMPL_ADDREF_INHERITED(nsXULDocument, nsXMLDocument)
 
482
NS_IMPL_RELEASE_INHERITED(nsXULDocument, nsXMLDocument)
 
483
 
 
484
 
 
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)
 
492
 
 
493
 
 
494
//----------------------------------------------------------------------
 
495
//
 
496
// nsIDocument interface
 
497
//
 
498
 
 
499
void
 
500
nsXULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
 
501
{
 
502
    NS_NOTREACHED("Reset");
 
503
}
 
504
 
 
505
void
 
506
nsXULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup)
 
507
{
 
508
    NS_NOTREACHED("ResetToURI");
 
509
}
 
510
 
 
511
// Override the nsDocument.cpp method to keep from returning the
 
512
// "cached XUL" type which is completely internal and may confuse
 
513
// people
 
514
NS_IMETHODIMP
 
515
nsXULDocument::GetContentType(nsAString& aContentType)
 
516
{
 
517
    aContentType.Assign(NS_LITERAL_STRING("application/vnd.mozilla.xul+xml"));
 
518
    return NS_OK;
 
519
}
 
520
 
 
521
void
 
522
nsXULDocument::SetContentType(const nsAString& aContentType)
 
523
{
 
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
 
528
}
 
529
 
 
530
nsresult
 
531
nsXULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
 
532
                                 nsILoadGroup* aLoadGroup,
 
533
                                 nsISupports* aContainer,
 
534
                                 nsIStreamListener **aDocListener,
 
535
                                 PRBool aReset, nsIContentSink* aSink)
 
536
{
 
537
    mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
 
538
 
 
539
    mDocumentTitle.Truncate();
 
540
 
 
541
    nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mDocumentURI));
 
542
    NS_ENSURE_SUCCESS(rv, rv);
 
543
    
 
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);
 
550
 
 
551
    if (NS_SUCCEEDED(rv) && !isChrome && !isRes) {
 
552
        rv = aChannel->GetURI(getter_AddRefs(mDocumentURI));
 
553
        NS_ENSURE_SUCCESS(rv, rv);
 
554
    }
 
555
 
 
556
    rv = ResetStylesheetsToURI(mDocumentURI);
 
557
    if (NS_FAILED(rv)) return rv;
 
558
 
 
559
    RetrieveRelevantHeaders(aChannel);
 
560
 
 
561
    // Look in the chrome cache: we've got this puppy loaded
 
562
    // already.
 
563
    nsCOMPtr<nsIXULPrototypeDocument> proto;
 
564
    if (IsChromeURI(mDocumentURI))
 
565
        gXULCache->GetPrototype(mDocumentURI, getter_AddRefs(proto));
 
566
 
 
567
    // Same comment as nsChromeProtocolHandler::NewChannel and
 
568
    // nsXULDocument::ResumeWalk
 
569
    // - Ben Goodger
 
570
    //
 
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:
 
575
    //
 
576
    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the FastLoad cache,
 
577
    //                         parse from disk
 
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
 
581
    //        parsed from disk.
 
582
 
 
583
    if (proto) {
 
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.
 
589
        PRBool loaded;
 
590
        rv = proto->AwaitLoadDone(this, &loaded);
 
591
        if (NS_FAILED(rv)) return rv;
 
592
 
 
593
        mMasterPrototype = mCurrentPrototype = proto;
 
594
 
 
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
 
597
        // prototype cache.
 
598
        if (loaded) {
 
599
            rv = AddPrototypeSheets();
 
600
            if (NS_FAILED(rv)) return rv;
 
601
        }
 
602
 
 
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);
 
608
        if (! *aDocListener)
 
609
            return NS_ERROR_OUT_OF_MEMORY;
 
610
    }
 
611
    else {
 
612
        PRBool useXULCache;
 
613
        gXULCache->GetEnabled(&useXULCache);
 
614
        PRBool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
 
615
 
 
616
 
 
617
        // It's just a vanilla document load. Create a parser to deal
 
618
        // with the stream n' stuff.
 
619
 
 
620
        nsCOMPtr<nsIParser> parser;
 
621
        rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
 
622
                           getter_AddRefs(parser));
 
623
        if (NS_FAILED(rv)) return rv;
 
624
 
 
625
        // Predicate mIsWritingFastLoad on the XUL cache being enabled,
 
626
        // so we don't have to re-check whether the cache is enabled all
 
627
        // the time.
 
628
        mIsWritingFastLoad = useXULCache;
 
629
 
 
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;
 
633
 
 
634
        *aDocListener = listener;
 
635
 
 
636
        parser->Parse(mDocumentURI);
 
637
 
 
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.
 
642
        if (fillXULCache) {
 
643
            rv = gXULCache->PutPrototype(mCurrentPrototype);
 
644
            if (NS_FAILED(rv)) return rv;
 
645
        }
 
646
    }
 
647
 
 
648
    NS_IF_ADDREF(*aDocListener);
 
649
    return NS_OK;
 
650
}
 
651
 
 
652
nsIPrincipal*
 
653
nsXULDocument::GetPrincipal()
 
654
{
 
655
    NS_ASSERTION(mMasterPrototype, "Missing master prototype. See bug 169036");
 
656
    NS_ENSURE_TRUE(mMasterPrototype, nsnull);
 
657
 
 
658
    return mMasterPrototype->GetDocumentPrincipal();
 
659
}
 
660
 
 
661
void
 
662
nsXULDocument::SetPrincipal(nsIPrincipal *aPrincipal)
 
663
{
 
664
    NS_NOTREACHED("SetPrincipal");
 
665
}
 
666
 
 
667
 
 
668
void
 
669
nsXULDocument::EndLoad()
 
670
{
 
671
    nsresult rv;
 
672
 
 
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.
 
675
 
 
676
    nsCOMPtr<nsIURI> uri;
 
677
    rv = mCurrentPrototype->GetURI(getter_AddRefs(uri));
 
678
    if (NS_FAILED(rv)) return;
 
679
 
 
680
    PRBool isChrome = IsChromeURI(uri);
 
681
 
 
682
    // Remember if the XUL cache is on
 
683
    PRBool useXULCache;
 
684
    gXULCache->GetEnabled(&useXULCache);
 
685
 
 
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 &&
 
691
        isChrome)
 
692
        gXULCache->WritePrototype(mCurrentPrototype);
 
693
 
 
694
    if (isChrome) {
 
695
        nsCOMPtr<nsIXULOverlayProvider> reg =
 
696
            do_GetService(NS_CHROMEREGISTRY_CONTRACTID);
 
697
        nsCOMPtr<nsICSSLoader> cssLoader = GetCSSLoader();
 
698
        
 
699
        if (reg && cssLoader) {
 
700
            nsCOMPtr<nsISimpleEnumerator> overlays;
 
701
            reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
 
702
 
 
703
            PRBool moreSheets;
 
704
            nsCOMPtr<nsISupports> next;
 
705
            nsCOMPtr<nsIURI> sheetURI;
 
706
            nsCOMPtr<nsICSSStyleSheet> sheet;
 
707
 
 
708
            while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
 
709
                   moreSheets) {
 
710
                overlays->GetNext(getter_AddRefs(next));
 
711
 
 
712
                sheetURI = do_QueryInterface(next);
 
713
                if (!uri) {
 
714
                    NS_ERROR("Chrome registry handed me a non-nsIURI object!");
 
715
                    continue;
 
716
                }
 
717
 
 
718
                if (useXULCache && IsChromeURI(sheetURI)) {
 
719
                    mCurrentPrototype->AddStyleSheetReference(sheetURI);
 
720
                }
 
721
 
 
722
                cssLoader->LoadAgentSheet(sheetURI, getter_AddRefs(sheet));
 
723
                if (!sheet) {
 
724
                    NS_WARNING("Couldn't load chrome style overlay.");
 
725
                    continue;
 
726
                }
 
727
 
 
728
                AddStyleSheet(sheet, 0);
 
729
            }
 
730
        }
 
731
 
 
732
        if (useXULCache) {
 
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;
 
738
        }
 
739
    }
 
740
 
 
741
    // Now walk the prototype to build content.
 
742
    rv = PrepareToWalk();
 
743
    if (NS_FAILED(rv)) return;
 
744
 
 
745
    ResumeWalk();
 
746
}
 
747
 
 
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.
 
752
NS_IMETHODIMP
 
753
nsXULDocument::OnPrototypeLoadDone()
 
754
{
 
755
    nsresult rv;
 
756
 
 
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;
 
762
 
 
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;
 
770
 
 
771
    return ResumeWalk();
 
772
}
 
773
 
 
774
 
 
775
PR_STATIC_CALLBACK(PRBool)
 
776
ClearPresentationStuff(nsHashKey *aKey, void *aData, void* aClosure)
 
777
{
 
778
    nsISupports *supp = NS_STATIC_CAST(nsISupports *, aData);
 
779
    nsCOMPtr<nsPIBoxObject> boxObject(do_QueryInterface(supp));
 
780
 
 
781
    if (boxObject) {
 
782
        boxObject->InvalidatePresentationStuff();
 
783
    }
 
784
 
 
785
    return PR_TRUE;
 
786
}
 
787
 
 
788
NS_IMETHODIMP
 
789
nsXULDocument::OnHide()
 
790
{
 
791
    if (mBoxObjectTable) {
 
792
        mBoxObjectTable->Enumerate(ClearPresentationStuff, nsnull);
 
793
    }
 
794
 
 
795
    return NS_OK;
 
796
}
 
797
 
 
798
PR_STATIC_CALLBACK(void)
 
799
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 
800
{
 
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];
 
805
    }
 
806
 
 
807
    // N.B. that we need to manually run the dtor because we
 
808
    // constructed the nsSmallVoidArray object in-place.
 
809
    entry->mListeners.~nsSmallVoidArray();
 
810
}
 
811
 
 
812
static PRBool
 
813
CanBroadcast(PRInt32 aNameSpaceID, nsIAtom* aAttribute)
 
814
{
 
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)) {
 
820
            return PR_FALSE;
 
821
        }
 
822
    }
 
823
    return PR_TRUE;
 
824
}
 
825
 
 
826
void
 
827
nsXULDocument::SynchronizeBroadcastListener(nsIDOMElement   *aBroadcaster,
 
828
                                            nsIDOMElement   *aListener,
 
829
                                            const nsAString &aAttr)
 
830
{
 
831
    nsCOMPtr<nsIContent> broadcaster = do_QueryInterface(aBroadcaster);
 
832
    nsCOMPtr<nsIContent> listener = do_QueryInterface(aListener);
 
833
 
 
834
    if (aAttr.Equals(NS_LITERAL_STRING("*"))) {
 
835
        PRUint32 count = broadcaster->GetAttrCount();
 
836
        while (count-- > 0) {
 
837
            PRInt32 nameSpaceID;
 
838
            nsCOMPtr<nsIAtom> name;
 
839
            nsCOMPtr<nsIAtom> prefix;
 
840
            broadcaster->GetAttrNameAt(count, &nameSpaceID,
 
841
                                       getter_AddRefs(name),
 
842
                                       getter_AddRefs(prefix));
 
843
 
 
844
            // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
 
845
            if (! CanBroadcast(nameSpaceID, name))
 
846
                continue;
 
847
 
 
848
            nsAutoString value;
 
849
            broadcaster->GetAttr(nameSpaceID, name, value);
 
850
            listener->SetAttr(nameSpaceID, name, prefix, value, PR_FALSE);
 
851
 
 
852
#if 0
 
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
 
857
            // properties, etc.
 
858
            ExecuteOnBroadcastHandlerFor(broadcaster, aListener, name);
 
859
#endif
 
860
        }
 
861
    }
 
862
    else {
 
863
        // Find out if the attribute is even present at all.
 
864
        nsCOMPtr<nsIAtom> name = do_GetAtom(aAttr);
 
865
 
 
866
        nsAutoString value;
 
867
        nsresult rv = broadcaster->GetAttr(kNameSpaceID_None, name, value);
 
868
 
 
869
        if (rv == NS_CONTENT_ATTR_NO_VALUE ||
 
870
            rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
871
            listener->SetAttr(kNameSpaceID_None, name, value, PR_FALSE);
 
872
        }
 
873
        else {
 
874
            listener->UnsetAttr(kNameSpaceID_None, name, PR_FALSE);
 
875
        }
 
876
 
 
877
#if 0
 
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);
 
883
#endif
 
884
    }
 
885
}
 
886
 
 
887
NS_IMETHODIMP
 
888
nsXULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
 
889
                                       nsIDOMElement* aListener,
 
890
                                       const nsAString& aAttr)
 
891
{
 
892
    NS_ENSURE_ARG(aBroadcaster && aListener);
 
893
    
 
894
    nsresult rv =
 
895
        nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
 
896
                                        aBroadcaster);
 
897
 
 
898
    if (NS_FAILED(rv)) {
 
899
        return rv;
 
900
    }
 
901
 
 
902
    rv = nsContentUtils::CheckSameOrigin(NS_STATIC_CAST(nsDocument *, this),
 
903
                                         aListener);
 
904
 
 
905
    if (NS_FAILED(rv)) {
 
906
        return rv;
 
907
    }
 
908
 
 
909
    static PLDHashTableOps gOps = {
 
910
        PL_DHashAllocTable,
 
911
        PL_DHashFreeTable,
 
912
        PL_DHashGetKeyStub,
 
913
        PL_DHashVoidPtrKeyStub,
 
914
        PL_DHashMatchEntryStub,
 
915
        PL_DHashMoveEntryStub,
 
916
        ClearBroadcasterMapEntry,
 
917
        PL_DHashFinalizeStub,
 
918
        nsnull
 
919
    };
 
920
 
 
921
    if (! mBroadcasterMap) {
 
922
        mBroadcasterMap =
 
923
            PL_NewDHashTable(&gOps, nsnull, sizeof(BroadcasterMapEntry),
 
924
                             PL_DHASH_MIN_SIZE);
 
925
 
 
926
        if (! mBroadcasterMap)
 
927
            return NS_ERROR_OUT_OF_MEMORY;
 
928
    }
 
929
 
 
930
    BroadcasterMapEntry* entry =
 
931
        NS_STATIC_CAST(BroadcasterMapEntry*,
 
932
                       PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
 
933
                                            PL_DHASH_LOOKUP));
 
934
 
 
935
    if (PL_DHASH_ENTRY_IS_FREE(entry)) {
 
936
        entry =
 
937
            NS_STATIC_CAST(BroadcasterMapEntry*,
 
938
                           PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
 
939
                                                PL_DHASH_ADD));
 
940
 
 
941
        if (! entry)
 
942
            return NS_ERROR_OUT_OF_MEMORY;
 
943
 
 
944
        entry->mBroadcaster = aBroadcaster;
 
945
 
 
946
        // N.B. placement new to construct the nsSmallVoidArray object
 
947
        // in-place
 
948
        new (&entry->mListeners) nsSmallVoidArray();
 
949
    }
 
950
 
 
951
    // Only add the listener if it's not there already!
 
952
    nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
 
953
 
 
954
    BroadcastListener* bl;
 
955
    for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
 
956
        bl = NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
 
957
 
 
958
        if ((bl->mListener == aListener) && (bl->mAttribute == attr))
 
959
            return NS_OK;
 
960
    }
 
961
 
 
962
    bl = new BroadcastListener;
 
963
    if (! bl)
 
964
        return NS_ERROR_OUT_OF_MEMORY;
 
965
 
 
966
    bl->mListener  = aListener;
 
967
    bl->mAttribute = attr;
 
968
 
 
969
    entry->mListeners.AppendElement(bl);
 
970
 
 
971
    SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
 
972
    return NS_OK;
 
973
}
 
974
 
 
975
NS_IMETHODIMP
 
976
nsXULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
 
977
                                          nsIDOMElement* aListener,
 
978
                                          const nsAString& aAttr)
 
979
{
 
980
    // If we haven't added any broadcast listeners, then there sure
 
981
    // aren't any to remove.
 
982
    if (! mBroadcasterMap)
 
983
        return NS_OK;
 
984
 
 
985
    BroadcasterMapEntry* entry =
 
986
        NS_STATIC_CAST(BroadcasterMapEntry*,
 
987
                       PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
 
988
                                            PL_DHASH_LOOKUP));
 
989
 
 
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]);
 
995
 
 
996
            if ((bl->mListener == aListener) && (bl->mAttribute == attr)) {
 
997
                entry->mListeners.RemoveElementAt(i);
 
998
                delete bl;
 
999
 
 
1000
                if (entry->mListeners.Count() == 0)
 
1001
                    PL_DHashTableOperate(mBroadcasterMap, aBroadcaster,
 
1002
                                         PL_DHASH_REMOVE);
 
1003
 
 
1004
                SynchronizeBroadcastListener(aBroadcaster, aListener, aAttr);
 
1005
 
 
1006
                break;
 
1007
            }
 
1008
        }
 
1009
    }
 
1010
 
 
1011
    return NS_OK;
 
1012
}
 
1013
 
 
1014
nsresult
 
1015
nsXULDocument::ExecuteOnBroadcastHandlerFor(nsIContent* aBroadcaster,
 
1016
                                            nsIDOMElement* aListener,
 
1017
                                            nsIAtom* aAttr)
 
1018
{
 
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.
 
1022
 
 
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);
 
1031
 
 
1032
        nsINodeInfo *ni = child->GetNodeInfo();
 
1033
        if (!ni || !ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL))
 
1034
            continue;
 
1035
 
 
1036
        // Is this the element that was listening to us?
 
1037
        nsAutoString listeningToID;
 
1038
        child->GetAttr(kNameSpaceID_None, nsXULAtoms::element, listeningToID);
 
1039
 
 
1040
        nsAutoString broadcasterID;
 
1041
        aBroadcaster->GetAttr(kNameSpaceID_None, nsXULAtoms::id, broadcasterID);
 
1042
 
 
1043
        if (listeningToID != broadcasterID)
 
1044
            continue;
 
1045
 
 
1046
        // We are observing the broadcaster, but is this the right
 
1047
        // attribute?
 
1048
        nsAutoString listeningToAttribute;
 
1049
        child->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute,
 
1050
                       listeningToAttribute);
 
1051
 
 
1052
        if (!aAttr->Equals(listeningToAttribute) &&
 
1053
            listeningToAttribute != NS_LITERAL_STRING("*")) {
 
1054
            continue;
 
1055
        }
 
1056
 
 
1057
        // This is the right <observes> element. Execute the
 
1058
        // |onbroadcast| event handler
 
1059
        nsEvent event(NS_XUL_BROADCAST);
 
1060
 
 
1061
        PRInt32 j = mPresShells.Count();
 
1062
        while (--j >= 0) {
 
1063
            nsCOMPtr<nsIPresShell> shell =
 
1064
                NS_STATIC_CAST(nsIPresShell*, mPresShells[j]);
 
1065
 
 
1066
            nsCOMPtr<nsIPresContext> aPresContext;
 
1067
            shell->GetPresContext(getter_AddRefs(aPresContext));
 
1068
 
 
1069
            // Handle the DOM event
 
1070
            nsEventStatus status = nsEventStatus_eIgnore;
 
1071
            child->HandleDOMEvent(aPresContext, &event, nsnull,
 
1072
                                  NS_EVENT_FLAG_INIT, &status);
 
1073
        }
 
1074
    }
 
1075
 
 
1076
    return NS_OK;
 
1077
}
 
1078
 
 
1079
void
 
1080
nsXULDocument::AttributeChanged(nsIContent* aElement, PRInt32 aNameSpaceID,
 
1081
                                nsIAtom* aAttribute, PRInt32 aModType)
 
1082
{
 
1083
    nsresult rv;
 
1084
 
 
1085
    // First see if we need to update our element map.
 
1086
    if ((aAttribute == nsXULAtoms::id) || (aAttribute == nsXULAtoms::ref)) {
 
1087
 
 
1088
        rv = mElementMap.Enumerate(RemoveElementsFromMapByContent, aElement);
 
1089
        if (NS_FAILED(rv)) return;
 
1090
 
 
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;
 
1095
    }
 
1096
 
 
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(),
 
1103
                                                PL_DHASH_LOOKUP));
 
1104
 
 
1105
        if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
 
1106
            // We've got listeners: push the value.
 
1107
            nsAutoString value;
 
1108
            rv = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
 
1109
 
 
1110
            for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) {
 
1111
                BroadcastListener* bl =
 
1112
                    NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]);
 
1113
 
 
1114
                if ((bl->mAttribute == aAttribute) ||
 
1115
                    (bl->mAttribute == nsXULAtoms::_star)) {
 
1116
                    nsCOMPtr<nsIContent> listener
 
1117
                        = do_QueryInterface(bl->mListener);
 
1118
 
 
1119
                    if (rv == NS_CONTENT_ATTR_NO_VALUE ||
 
1120
                        rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
1121
                        listener->SetAttr(kNameSpaceID_None, aAttribute, value,
 
1122
                                          PR_TRUE);
 
1123
                    }
 
1124
                    else {
 
1125
                        listener->UnsetAttr(kNameSpaceID_None, aAttribute,
 
1126
                                            PR_TRUE);
 
1127
                    }
 
1128
 
 
1129
                    ExecuteOnBroadcastHandlerFor(aElement, bl->mListener,
 
1130
                                                 aAttribute);
 
1131
                }
 
1132
            }
 
1133
        }
 
1134
    }
 
1135
 
 
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,
 
1140
                                   aModType);
 
1141
    }
 
1142
 
 
1143
    // See if there is anything we need to persist in the localstore.
 
1144
    //
 
1145
    // XXX Namespace handling broken :-(
 
1146
    nsAutoString persist;
 
1147
    rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist);
 
1148
    if (NS_FAILED(rv)) return;
 
1149
 
 
1150
    if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
1151
        nsAutoString attr;
 
1152
        rv = aAttribute->ToString(attr);
 
1153
        if (NS_FAILED(rv)) return;
 
1154
 
 
1155
        if (persist.Find(attr) >= 0) {
 
1156
            rv = Persist(aElement, kNameSpaceID_None, aAttribute);
 
1157
            if (NS_FAILED(rv)) return;
 
1158
        }
 
1159
    }
 
1160
}
 
1161
 
 
1162
void
 
1163
nsXULDocument::ContentAppended(nsIContent* aContainer,
 
1164
                               PRInt32 aNewIndexInContainer)
 
1165
{
 
1166
    // First update our element map
 
1167
    PRUint32 count = aContainer->GetChildCount();
 
1168
 
 
1169
    for (PRUint32 i = aNewIndexInContainer; i < count; ++i) {
 
1170
        nsresult rv = AddSubtreeToDocument(aContainer->GetChildAt(i));
 
1171
        if (NS_FAILED(rv))
 
1172
            return;
 
1173
    }
 
1174
 
 
1175
    nsXMLDocument::ContentAppended(aContainer, aNewIndexInContainer);
 
1176
}
 
1177
 
 
1178
void
 
1179
nsXULDocument::ContentInserted(nsIContent* aContainer,
 
1180
                               nsIContent* aChild,
 
1181
                               PRInt32 aIndexInContainer)
 
1182
{
 
1183
    nsresult rv = AddSubtreeToDocument(aChild);
 
1184
    if (NS_FAILED(rv))
 
1185
        return;
 
1186
 
 
1187
    nsXMLDocument::ContentInserted(aContainer, aChild,
 
1188
                                   aIndexInContainer);
 
1189
}
 
1190
 
 
1191
void
 
1192
nsXULDocument::ContentReplaced(nsIContent* aContainer,
 
1193
                               nsIContent* aOldChild,
 
1194
                               nsIContent* aNewChild,
 
1195
                               PRInt32 aIndexInContainer)
 
1196
{
 
1197
    nsresult rv;
 
1198
    rv = RemoveSubtreeFromDocument(aOldChild);
 
1199
    if (NS_FAILED(rv))
 
1200
        return;
 
1201
 
 
1202
    rv = AddSubtreeToDocument(aNewChild);
 
1203
    if (NS_FAILED(rv))
 
1204
        return;
 
1205
 
 
1206
    nsXMLDocument::ContentReplaced(aContainer, aOldChild, aNewChild,
 
1207
                                   aIndexInContainer);
 
1208
}
 
1209
 
 
1210
void
 
1211
nsXULDocument::ContentRemoved(nsIContent* aContainer,
 
1212
                              nsIContent* aChild,
 
1213
                              PRInt32 aIndexInContainer)
 
1214
{
 
1215
    nsresult rv;
 
1216
    rv = RemoveSubtreeFromDocument(aChild);
 
1217
    if (NS_FAILED(rv))
 
1218
        return;
 
1219
 
 
1220
    nsXMLDocument::ContentRemoved(aContainer, aChild,
 
1221
                                  aIndexInContainer);
 
1222
}
 
1223
 
 
1224
nsresult
 
1225
nsXULDocument::HandleDOMEvent(nsIPresContext* aPresContext,
 
1226
                            nsEvent* aEvent,
 
1227
                            nsIDOMEvent** aDOMEvent,
 
1228
                            PRUint32 aFlags,
 
1229
                            nsEventStatus* aEventStatus)
 
1230
{
 
1231
    nsresult ret = NS_OK;
 
1232
    nsIDOMEvent* domEvent = nsnull;
 
1233
    PRBool externalDOMEvent = PR_FALSE;
 
1234
 
 
1235
    if (NS_EVENT_FLAG_INIT & aFlags) {
 
1236
        if (aDOMEvent) {
 
1237
            if (*aDOMEvent) {
 
1238
                externalDOMEvent = PR_TRUE;
 
1239
            }
 
1240
        }
 
1241
        else {
 
1242
            aDOMEvent = &domEvent;
 
1243
        }
 
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;
 
1247
    }
 
1248
 
 
1249
    // Capturing stage
 
1250
    if (NS_EVENT_FLAG_CAPTURE & aFlags && mScriptGlobalObject) {
 
1251
        mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
 
1252
                                            aFlags & NS_EVENT_CAPTURE_MASK,
 
1253
                                            aEventStatus);
 
1254
    }
 
1255
 
 
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;
 
1262
    }
 
1263
 
 
1264
    // Bubbling stage
 
1265
    if (NS_EVENT_FLAG_BUBBLE & aFlags && mScriptGlobalObject) {
 
1266
        mScriptGlobalObject->HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
 
1267
                                            aFlags & NS_EVENT_BUBBLE_MASK,
 
1268
                                            aEventStatus);
 
1269
    }
 
1270
 
 
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) {
 
1275
            nsrefcnt rc;
 
1276
            NS_RELEASE2(*aDOMEvent, rc);
 
1277
            if (0 != 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);
 
1284
 
 
1285
                if (privateEvent) {
 
1286
                    privateEvent->DuplicatePrivateData();
 
1287
                }
 
1288
            }
 
1289
        }
 
1290
        aDOMEvent = nsnull;
 
1291
    }
 
1292
 
 
1293
    return ret;
 
1294
}
 
1295
 
 
1296
 
 
1297
//----------------------------------------------------------------------
 
1298
//
 
1299
// nsIXULDocument interface
 
1300
//
 
1301
 
 
1302
NS_IMETHODIMP
 
1303
nsXULDocument::AddElementForID(const nsAString& aID, nsIContent* aElement)
 
1304
{
 
1305
    NS_PRECONDITION(aElement != nsnull, "null ptr");
 
1306
    if (! aElement)
 
1307
        return NS_ERROR_NULL_POINTER;
 
1308
 
 
1309
    mElementMap.Add(aID, aElement);
 
1310
    return NS_OK;
 
1311
}
 
1312
 
 
1313
 
 
1314
NS_IMETHODIMP
 
1315
nsXULDocument::RemoveElementForID(const nsAString& aID, nsIContent* aElement)
 
1316
{
 
1317
    NS_PRECONDITION(aElement != nsnull, "null ptr");
 
1318
    if (! aElement)
 
1319
        return NS_ERROR_NULL_POINTER;
 
1320
 
 
1321
    mElementMap.Remove(aID, aElement);
 
1322
    return NS_OK;
 
1323
}
 
1324
 
 
1325
NS_IMETHODIMP
 
1326
nsXULDocument::GetElementsForID(const nsAString& aID,
 
1327
                                nsISupportsArray* aElements)
 
1328
{
 
1329
    NS_PRECONDITION(aElements != nsnull, "null ptr");
 
1330
    if (! aElements)
 
1331
        return NS_ERROR_NULL_POINTER;
 
1332
 
 
1333
    mElementMap.Find(aID, aElements);
 
1334
    return NS_OK;
 
1335
}
 
1336
 
 
1337
NS_IMETHODIMP
 
1338
nsXULDocument::AddForwardReference(nsForwardReference* aRef)
 
1339
{
 
1340
    if (mResolutionPhase < aRef->GetPhase()) {
 
1341
        mForwardReferences.AppendElement(aRef);
 
1342
    }
 
1343
    else {
 
1344
        NS_ERROR("forward references have already been resolved");
 
1345
        delete aRef;
 
1346
    }
 
1347
 
 
1348
    return NS_OK;
 
1349
}
 
1350
 
 
1351
 
 
1352
NS_IMETHODIMP
 
1353
nsXULDocument::ResolveForwardReferences()
 
1354
{
 
1355
    if (mResolutionPhase == nsForwardReference::eDone)
 
1356
        return NS_OK;
 
1357
 
 
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.
 
1363
 
 
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();
 
1369
 
 
1370
            for (PRInt32 i = 0; i < mForwardReferences.Count(); ++i) {
 
1371
                nsForwardReference* fwdref = NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
 
1372
 
 
1373
                if (fwdref->GetPhase() == *pass) {
 
1374
                    nsForwardReference::Result result = fwdref->Resolve();
 
1375
 
 
1376
                    switch (result) {
 
1377
                    case nsForwardReference::eResolve_Succeeded:
 
1378
                    case nsForwardReference::eResolve_Error:
 
1379
                        mForwardReferences.RemoveElementAt(i);
 
1380
                        delete fwdref;
 
1381
 
 
1382
                        // fixup because we removed from list
 
1383
                        --i;
 
1384
                        break;
 
1385
 
 
1386
                    case nsForwardReference::eResolve_Later:
 
1387
                        // do nothing. we'll try again later
 
1388
                        ;
 
1389
                    }
 
1390
                }
 
1391
            }
 
1392
        }
 
1393
 
 
1394
        ++pass;
 
1395
    }
 
1396
 
 
1397
    DestroyForwardReferences();
 
1398
    return NS_OK;
 
1399
}
 
1400
 
 
1401
NS_IMETHODIMP
 
1402
nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument)
 
1403
{
 
1404
    mMasterPrototype = aDocument;
 
1405
    return NS_OK;
 
1406
}
 
1407
 
 
1408
NS_IMETHODIMP
 
1409
nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument)
 
1410
{
 
1411
    *aDocument = mMasterPrototype;
 
1412
    NS_IF_ADDREF(*aDocument);
 
1413
    return NS_OK;
 
1414
}
 
1415
 
 
1416
NS_IMETHODIMP
 
1417
nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument)
 
1418
{
 
1419
    mCurrentPrototype = aDocument;
 
1420
    return NS_OK;
 
1421
}
 
1422
 
 
1423
//----------------------------------------------------------------------
 
1424
//
 
1425
// nsIDOMDocument interface
 
1426
//
 
1427
 
 
1428
NS_IMETHODIMP
 
1429
nsXULDocument::GetElementsByAttribute(const nsAString& aAttribute,
 
1430
                                      const nsAString& aValue,
 
1431
                                      nsIDOMNodeList** aReturn)
 
1432
{
 
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);
 
1439
 
 
1440
    nsCOMPtr<nsIDOMNode> domRoot = do_QueryInterface(mRootContent);
 
1441
    NS_ASSERTION(domRoot, "no doc root");
 
1442
 
 
1443
    nsresult rv = NS_OK;
 
1444
    if (domRoot) {
 
1445
        rv = GetElementsByAttribute(domRoot, aAttribute, aValue, elements);
 
1446
    }
 
1447
 
 
1448
    *aReturn = elements;
 
1449
 
 
1450
    return rv;
 
1451
}
 
1452
 
 
1453
 
 
1454
NS_IMETHODIMP
 
1455
nsXULDocument::Persist(const nsAString& aID,
 
1456
                       const nsAString& aAttr)
 
1457
{
 
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)
 
1461
        return NS_OK;
 
1462
 
 
1463
    nsresult rv;
 
1464
 
 
1465
    nsCOMPtr<nsIDOMElement> domelement;
 
1466
    rv = GetElementById(aID, getter_AddRefs(domelement));
 
1467
    if (NS_FAILED(rv)) return rv;
 
1468
 
 
1469
    if (! domelement)
 
1470
        return NS_OK;
 
1471
 
 
1472
    nsCOMPtr<nsIContent> element = do_QueryInterface(domelement);
 
1473
    NS_ASSERTION(element != nsnull, "null ptr");
 
1474
    if (! element)
 
1475
        return NS_ERROR_UNEXPECTED;
 
1476
 
 
1477
    nsCOMPtr<nsIAtom> tag;
 
1478
    PRInt32 nameSpaceID;
 
1479
 
 
1480
    nsCOMPtr<nsINodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
 
1481
    if (ni) {
 
1482
        tag = ni->NameAtom();
 
1483
        nameSpaceID = ni->NamespaceID();
 
1484
    }
 
1485
    else {
 
1486
        tag = do_GetAtom(aAttr);
 
1487
        NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
 
1488
 
 
1489
        nameSpaceID = kNameSpaceID_None;
 
1490
    }
 
1491
 
 
1492
    rv = Persist(element, nameSpaceID, tag);
 
1493
    if (NS_FAILED(rv)) return rv;
 
1494
 
 
1495
    return NS_OK;
 
1496
}
 
1497
 
 
1498
 
 
1499
nsresult
 
1500
nsXULDocument::Persist(nsIContent* aElement, PRInt32 aNameSpaceID,
 
1501
                       nsIAtom* aAttribute)
 
1502
{
 
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...)
 
1506
    if (! mLocalStore)
 
1507
        return NS_OK;
 
1508
 
 
1509
    nsresult rv;
 
1510
 
 
1511
    nsCOMPtr<nsIRDFResource> element;
 
1512
    rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
 
1513
    if (NS_FAILED(rv)) return rv;
 
1514
 
 
1515
    // No ID, so nothing to persist.
 
1516
    if (! element)
 
1517
        return NS_OK;
 
1518
 
 
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;
 
1524
 
 
1525
    nsCOMPtr<nsIRDFResource> attr;
 
1526
    rv = gRDFService->GetResource(nsDependentCString(attrstr),
 
1527
                                  getter_AddRefs(attr));
 
1528
    if (NS_FAILED(rv)) return rv;
 
1529
 
 
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;
 
1534
 
 
1535
    PRBool novalue = (rv != NS_CONTENT_ATTR_HAS_VALUE);
 
1536
 
 
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;
 
1541
 
 
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);
 
1546
    }
 
1547
    else {
 
1548
        // Now either 'change' or 'assert' based on whether there was
 
1549
        // an old value.
 
1550
        nsCOMPtr<nsIRDFLiteral> newvalue;
 
1551
        rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
 
1552
        if (NS_FAILED(rv)) return rv;
 
1553
 
 
1554
        if (oldvalue) {
 
1555
            if (oldvalue != newvalue)
 
1556
                rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
 
1557
            else
 
1558
                rv = NS_OK;
 
1559
        }
 
1560
        else {
 
1561
            rv = mLocalStore->Assert(element, attr, newvalue, PR_TRUE);
 
1562
        }
 
1563
    }
 
1564
 
 
1565
    if (NS_FAILED(rv)) return rv;
 
1566
 
 
1567
    // Add it to the persisted set for this document (if it's not
 
1568
    // there already).
 
1569
    {
 
1570
        nsCAutoString docurl;
 
1571
        rv = mDocumentURI->GetSpec(docurl);
 
1572
        if (NS_FAILED(rv)) return rv;
 
1573
 
 
1574
        nsCOMPtr<nsIRDFResource> doc;
 
1575
        rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
 
1576
        if (NS_FAILED(rv)) return rv;
 
1577
 
 
1578
        PRBool hasAssertion;
 
1579
        rv = mLocalStore->HasAssertion(doc, kNC_persist, element, PR_TRUE, &hasAssertion);
 
1580
        if (NS_FAILED(rv)) return rv;
 
1581
 
 
1582
        if (! hasAssertion) {
 
1583
            rv = mLocalStore->Assert(doc, kNC_persist, element, PR_TRUE);
 
1584
            if (NS_FAILED(rv)) return rv;
 
1585
        }
 
1586
    }
 
1587
 
 
1588
    return NS_OK;
 
1589
}
 
1590
 
 
1591
 
 
1592
 
 
1593
nsresult
 
1594
nsXULDocument::DestroyForwardReferences()
 
1595
{
 
1596
    for (PRInt32 i = mForwardReferences.Count() - 1; i >= 0; --i) {
 
1597
        nsForwardReference* fwdref =
 
1598
            NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]);
 
1599
        delete fwdref;
 
1600
    }
 
1601
 
 
1602
    mForwardReferences.Clear();
 
1603
    return NS_OK;
 
1604
}
 
1605
 
 
1606
 
 
1607
nsresult
 
1608
nsXULDocument::GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth,
 
1609
                                  PRInt32* aHeight)
 
1610
{
 
1611
    nsresult result = NS_OK;
 
1612
    nsSize size;
 
1613
    nsIFrame* frame;
 
1614
 
 
1615
    FlushPendingNotifications();
 
1616
 
 
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
 
1622
        if (view) {
 
1623
            nsIScrollableView* scrollableView;
 
1624
 
 
1625
            if (NS_SUCCEEDED(CallQueryInterface(view, &scrollableView))) {
 
1626
                scrollableView->GetScrolledView(view);
 
1627
            }
 
1628
 
 
1629
            nsRect r = view->GetBounds();
 
1630
            size.height = r.height;
 
1631
            size.width = r.width;
 
1632
        }
 
1633
        // If we don't have a view, use the frame size
 
1634
        else {
 
1635
            size = frame->GetSize();
 
1636
        }
 
1637
 
 
1638
        // Convert from twips to pixels
 
1639
        nsCOMPtr<nsIPresContext> context;
 
1640
        result = aShell->GetPresContext(getter_AddRefs(context));
 
1641
 
 
1642
        if (NS_SUCCEEDED(result)) {
 
1643
            float scale;
 
1644
            scale = context->TwipsToPixels();
 
1645
 
 
1646
            *aWidth = NSTwipsToIntPixels(size.width, scale);
 
1647
            *aHeight = NSTwipsToIntPixels(size.height, scale);
 
1648
        }
 
1649
    }
 
1650
    else {
 
1651
        *aWidth = 0;
 
1652
        *aHeight = 0;
 
1653
    }
 
1654
 
 
1655
    return result;
 
1656
}
 
1657
 
 
1658
NS_IMETHODIMP
 
1659
nsXULDocument::GetWidth(PRInt32* aWidth)
 
1660
{
 
1661
    NS_ENSURE_ARG_POINTER(aWidth);
 
1662
 
 
1663
    nsresult rv = NS_OK;
 
1664
 
 
1665
    // We make the assumption that the first presentation shell
 
1666
    // is the one for which we need information.
 
1667
    nsIPresShell *shell = GetShellAt(0);
 
1668
    if (shell) {
 
1669
        PRInt32 width, height;
 
1670
 
 
1671
        rv = GetPixelDimensions(shell, &width, &height);
 
1672
        *aWidth = width;
 
1673
    } else
 
1674
        *aWidth = 0;
 
1675
 
 
1676
    return rv;
 
1677
}
 
1678
 
 
1679
NS_IMETHODIMP
 
1680
nsXULDocument::GetHeight(PRInt32* aHeight)
 
1681
{
 
1682
    NS_ENSURE_ARG_POINTER(aHeight);
 
1683
 
 
1684
    nsresult rv = NS_OK;
 
1685
 
 
1686
    // We make the assumption that the first presentation shell
 
1687
    // is the one for which we need information.
 
1688
    nsIPresShell *shell = GetShellAt(0);
 
1689
    if (shell) {
 
1690
        PRInt32 width, height;
 
1691
 
 
1692
        rv = GetPixelDimensions(shell, &width, &height);
 
1693
        *aHeight = height;
 
1694
    } else
 
1695
        *aHeight = 0;
 
1696
 
 
1697
    return rv;
 
1698
}
 
1699
 
 
1700
//----------------------------------------------------------------------
 
1701
//
 
1702
// nsIDOMXULDocument interface
 
1703
//
 
1704
 
 
1705
NS_IMETHODIMP
 
1706
nsXULDocument::GetPopupNode(nsIDOMNode** aNode)
 
1707
{
 
1708
    nsresult rv;
 
1709
 
 
1710
    // get focus controller
 
1711
    nsCOMPtr<nsIFocusController> focusController;
 
1712
    GetFocusController(getter_AddRefs(focusController));
 
1713
    NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
 
1714
    // get popup node
 
1715
    rv = focusController->GetPopupNode(aNode); // addref happens here
 
1716
 
 
1717
    return rv;
 
1718
}
 
1719
 
 
1720
NS_IMETHODIMP
 
1721
nsXULDocument::SetPopupNode(nsIDOMNode* aNode)
 
1722
{
 
1723
    nsresult rv;
 
1724
 
 
1725
    // get focus controller
 
1726
    nsCOMPtr<nsIFocusController> focusController;
 
1727
    GetFocusController(getter_AddRefs(focusController));
 
1728
    NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE);
 
1729
    // set popup node
 
1730
    rv = focusController->SetPopupNode(aNode);
 
1731
 
 
1732
    return rv;
 
1733
}
 
1734
 
 
1735
NS_IMETHODIMP
 
1736
nsXULDocument::GetTooltipNode(nsIDOMNode** aNode)
 
1737
{
 
1738
    *aNode = mTooltipNode;
 
1739
    NS_IF_ADDREF(*aNode);
 
1740
    return NS_OK;
 
1741
}
 
1742
 
 
1743
NS_IMETHODIMP
 
1744
nsXULDocument::SetTooltipNode(nsIDOMNode* aNode)
 
1745
{
 
1746
    mTooltipNode = aNode;
 
1747
    return NS_OK;
 
1748
}
 
1749
 
 
1750
 
 
1751
NS_IMETHODIMP
 
1752
nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
 
1753
{
 
1754
    *aTracker = mCommandDispatcher;
 
1755
    NS_IF_ADDREF(*aTracker);
 
1756
    return NS_OK;
 
1757
}
 
1758
 
 
1759
NS_IMETHODIMP
 
1760
nsXULDocument::GetElementById(const nsAString& aId,
 
1761
                              nsIDOMElement** aReturn)
 
1762
{
 
1763
    NS_ENSURE_ARG_POINTER(aReturn);
 
1764
    *aReturn = nsnull;
 
1765
 
 
1766
    NS_WARN_IF_FALSE(!aId.IsEmpty(),"getElementById(\"\"), fix caller?");
 
1767
    if (aId.IsEmpty())
 
1768
      return NS_OK;
 
1769
 
 
1770
    nsresult rv;
 
1771
 
 
1772
    nsCOMPtr<nsIContent> element;
 
1773
    rv = mElementMap.FindFirst(aId, getter_AddRefs(element));
 
1774
    if (NS_FAILED(rv)) return rv;
 
1775
 
 
1776
    if (element) {
 
1777
        rv = CallQueryInterface(element, aReturn);
 
1778
    }
 
1779
 
 
1780
    return rv;
 
1781
}
 
1782
 
 
1783
nsresult
 
1784
nsXULDocument::AddElementToDocumentPre(nsIContent* aElement)
 
1785
{
 
1786
    // Do a bunch of work that's necessary when an element gets added
 
1787
    // to the XUL Document.
 
1788
    nsresult rv;
 
1789
 
 
1790
    // 1. Add the element to the resource-to-element map
 
1791
    rv = AddElementToMap(aElement);
 
1792
    if (NS_FAILED(rv)) return rv;
 
1793
 
 
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
 
1797
    nsAutoString value;
 
1798
    rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
 
1799
                           value);
 
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;
 
1804
    }
 
1805
 
 
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;
 
1811
 
 
1812
    // If it's not there yet, we may be able to defer hookup until
 
1813
    // later.
 
1814
    if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
 
1815
        BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
 
1816
        if (! hookup)
 
1817
            return NS_ERROR_OUT_OF_MEMORY;
 
1818
 
 
1819
        rv = AddForwardReference(hookup);
 
1820
        if (NS_FAILED(rv)) return rv;
 
1821
    }
 
1822
 
 
1823
    return NS_OK;
 
1824
}
 
1825
 
 
1826
nsresult
 
1827
nsXULDocument::AddElementToDocumentPost(nsIContent* aElement)
 
1828
{
 
1829
    nsINodeInfo *ni = aElement->GetNodeInfo();
 
1830
 
 
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"));
 
1835
        if (xblService) {
 
1836
            nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(aElement));
 
1837
            xblService->AttachGlobalKeyHandler(rec);
 
1838
        }
 
1839
    }
 
1840
 
 
1841
    // See if we need to attach a XUL template to this node
 
1842
    PRBool needsHookup;
 
1843
    nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
 
1844
    if (NS_FAILED(rv))
 
1845
        return rv;
 
1846
 
 
1847
    if (needsHookup) {
 
1848
        if (mResolutionPhase == nsForwardReference::eDone) {
 
1849
            rv = CreateTemplateBuilder(aElement);
 
1850
            if (NS_FAILED(rv))
 
1851
                return rv;
 
1852
        }
 
1853
        else {
 
1854
            TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
 
1855
            if (! hookup)
 
1856
                return NS_ERROR_OUT_OF_MEMORY;
 
1857
 
 
1858
            rv = AddForwardReference(hookup);
 
1859
            if (NS_FAILED(rv))
 
1860
                return rv;
 
1861
        }
 
1862
    }
 
1863
 
 
1864
    return NS_OK;
 
1865
}
 
1866
 
 
1867
NS_IMETHODIMP
 
1868
nsXULDocument::AddSubtreeToDocument(nsIContent* aElement)
 
1869
{
 
1870
    nsresult rv;
 
1871
 
 
1872
    // Do pre-order addition magic
 
1873
    rv = AddElementToDocumentPre(aElement);
 
1874
    if (NS_FAILED(rv)) return rv;
 
1875
 
 
1876
    // Recurse to children
 
1877
    nsCOMPtr<nsIXULContent> xulcontent = do_QueryInterface(aElement);
 
1878
 
 
1879
    PRUint32 count =
 
1880
        xulcontent ? xulcontent->PeekChildCount() : aElement->GetChildCount();
 
1881
 
 
1882
    while (count-- > 0) {
 
1883
        rv = AddSubtreeToDocument(aElement->GetChildAt(count));
 
1884
        if (NS_FAILED(rv))
 
1885
            return rv;
 
1886
    }
 
1887
 
 
1888
    // Do post-order addition magic
 
1889
    return AddElementToDocumentPost(aElement);
 
1890
}
 
1891
 
 
1892
NS_IMETHODIMP
 
1893
nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement)
 
1894
{
 
1895
    // Do a bunch of cleanup to remove an element from the XUL
 
1896
    // document.
 
1897
    nsresult rv;
 
1898
 
 
1899
    // 1. Remove any children from the document.
 
1900
    PRUint32 count = aElement->GetChildCount();
 
1901
 
 
1902
    while (count-- > 0) {
 
1903
        rv = RemoveSubtreeFromDocument(aElement->GetChildAt(count));
 
1904
        if (NS_FAILED(rv))
 
1905
            return rv;
 
1906
    }
 
1907
 
 
1908
    // 2. Remove the element from the resource-to-element map
 
1909
    rv = RemoveElementFromMap(aElement);
 
1910
    if (NS_FAILED(rv)) return rv;
 
1911
 
 
1912
    // 3. If the element is a 'command updater', then remove the
 
1913
    // element from the document's command dispatcher.
 
1914
    nsAutoString value;
 
1915
    rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::commandupdater,
 
1916
                           value);
 
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");
 
1921
        if (! domelement)
 
1922
            return NS_ERROR_UNEXPECTED;
 
1923
 
 
1924
        rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
 
1925
        if (NS_FAILED(rv)) return rv;
 
1926
    }
 
1927
 
 
1928
    // 4. Remove the element from our broadcaster map, since it is no longer
 
1929
    // in the document.
 
1930
    // Do a getElementById to retrieve the broadcaster
 
1931
    nsCOMPtr<nsIDOMElement> broadcaster;
 
1932
    nsAutoString observesVal;
 
1933
 
 
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));
 
1938
            if (broadcaster) {
 
1939
                nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aElement));
 
1940
                RemoveBroadcastListenerFor(broadcaster, elt,
 
1941
                                           NS_LITERAL_STRING("*"));
 
1942
            }
 
1943
        }
 
1944
    }
 
1945
 
 
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));
 
1950
            if (broadcaster) {
 
1951
                nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aElement));
 
1952
                RemoveBroadcastListenerFor(broadcaster, elt,
 
1953
                                           NS_LITERAL_STRING("*"));
 
1954
            }
 
1955
        }
 
1956
    }
 
1957
    return NS_OK;
 
1958
}
 
1959
 
 
1960
NS_IMETHODIMP
 
1961
nsXULDocument::SetTemplateBuilderFor(nsIContent* aContent,
 
1962
                                     nsIXULTemplateBuilder* aBuilder)
 
1963
{
 
1964
    if (! mTemplateBuilderTable) {
 
1965
        mTemplateBuilderTable = new nsSupportsHashtable();
 
1966
        if (! mTemplateBuilderTable)
 
1967
            return NS_ERROR_OUT_OF_MEMORY;
 
1968
    }
 
1969
 
 
1970
    nsISupportsKey key(aContent);
 
1971
 
 
1972
    if (aContent) {
 
1973
        mTemplateBuilderTable->Put(&key, aBuilder);
 
1974
    }
 
1975
    else {
 
1976
        mTemplateBuilderTable->Remove(&key);
 
1977
    }
 
1978
 
 
1979
    return NS_OK;
 
1980
}
 
1981
 
 
1982
NS_IMETHODIMP
 
1983
nsXULDocument::GetTemplateBuilderFor(nsIContent* aContent,
 
1984
                                     nsIXULTemplateBuilder** aResult)
 
1985
{
 
1986
    if (mTemplateBuilderTable) {
 
1987
        nsISupportsKey key(aContent);
 
1988
        *aResult = NS_STATIC_CAST(nsIXULTemplateBuilder*,
 
1989
                                  mTemplateBuilderTable->Get(&key));
 
1990
    }
 
1991
    else
 
1992
        *aResult = nsnull;
 
1993
 
 
1994
    return NS_OK;
 
1995
}
 
1996
 
 
1997
// Attributes that are used with getElementById() and the
 
1998
// resource-to-element map.
 
1999
nsIAtom** nsXULDocument::kIdentityAttrs[] =
 
2000
{
 
2001
    &nsXULAtoms::id,
 
2002
    &nsXULAtoms::ref,
 
2003
    nsnull
 
2004
};
 
2005
 
 
2006
nsresult
 
2007
nsXULDocument::AddElementToMap(nsIContent* aElement)
 
2008
{
 
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.
 
2011
    nsresult rv;
 
2012
 
 
2013
    for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
 
2014
        nsAutoString value;
 
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;
 
2018
 
 
2019
        if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
2020
            rv = mElementMap.Add(value, aElement);
 
2021
            if (NS_FAILED(rv)) return rv;
 
2022
        }
 
2023
    }
 
2024
 
 
2025
    return NS_OK;
 
2026
}
 
2027
 
 
2028
 
 
2029
nsresult
 
2030
nsXULDocument::RemoveElementFromMap(nsIContent* aElement)
 
2031
{
 
2032
    // Remove the element from the resource-to-element map.
 
2033
    nsresult rv;
 
2034
 
 
2035
    for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) {
 
2036
        nsAutoString value;
 
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;
 
2040
 
 
2041
        if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
2042
            rv = mElementMap.Remove(value, aElement);
 
2043
            if (NS_FAILED(rv)) return rv;
 
2044
        }
 
2045
    }
 
2046
 
 
2047
    return NS_OK;
 
2048
}
 
2049
 
 
2050
 
 
2051
PRIntn
 
2052
nsXULDocument::RemoveElementsFromMapByContent(const PRUnichar* aID,
 
2053
                                              nsIContent* aElement,
 
2054
                                              void* aClosure)
 
2055
{
 
2056
    nsIContent* content = NS_REINTERPRET_CAST(nsIContent*, aClosure);
 
2057
    return (aElement == content) ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;
 
2058
}
 
2059
 
 
2060
 
 
2061
 
 
2062
//----------------------------------------------------------------------
 
2063
//
 
2064
// nsIDOMNode interface
 
2065
//
 
2066
 
 
2067
NS_IMETHODIMP
 
2068
nsXULDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
 
2069
{
 
2070
    // We don't allow cloning of a document
 
2071
    *aReturn = nsnull;
 
2072
    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 
2073
}
 
2074
 
 
2075
 
 
2076
//----------------------------------------------------------------------
 
2077
//
 
2078
// Implementation methods
 
2079
//
 
2080
 
 
2081
nsresult
 
2082
nsXULDocument::Init()
 
2083
{
 
2084
    nsresult rv = nsXMLDocument::Init();
 
2085
    NS_ENSURE_SUCCESS(rv, rv);
 
2086
 
 
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;
 
2092
 
 
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
 
2095
    // will persist.
 
2096
    mLocalStore = do_GetService(kLocalStoreCID);
 
2097
 
 
2098
    // Create a new nsISupportsArray for dealing with overlay references
 
2099
    rv = NS_NewISupportsArray(getter_AddRefs(mUnloadedOverlays));
 
2100
    if (NS_FAILED(rv)) return rv;
 
2101
 
 
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;
 
2108
 
 
2109
        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
 
2110
                                 &kNC_persist);
 
2111
        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
 
2112
                                 &kNC_attribute);
 
2113
        gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
 
2114
                                 &kNC_value);
 
2115
 
 
2116
        rv = CallGetService(kHTMLElementFactoryCID, &gHTMLElementFactory);
 
2117
        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get HTML element factory");
 
2118
        if (NS_FAILED(rv)) return rv;
 
2119
 
 
2120
        rv = CallGetService(kXMLElementFactoryCID, &gXMLElementFactory);
 
2121
        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get XML element factory");
 
2122
        if (NS_FAILED(rv)) return rv;
 
2123
 
 
2124
        rv = CallGetService(kXULPrototypeCacheCID, &gXULCache);
 
2125
        if (NS_FAILED(rv)) return rv;
 
2126
    }
 
2127
 
 
2128
#ifdef PR_LOGGING
 
2129
    if (! gXULLog)
 
2130
        gXULLog = PR_NewLogModule("nsXULDocument");
 
2131
#endif
 
2132
 
 
2133
    return NS_OK;
 
2134
}
 
2135
 
 
2136
 
 
2137
nsresult
 
2138
nsXULDocument::StartLayout(void)
 
2139
{
 
2140
    if (!mRootContent) {
 
2141
#ifdef PR_LOGGING
 
2142
        nsCAutoString urlspec;
 
2143
        mDocumentURI->GetSpec(urlspec);
 
2144
 
 
2145
        PR_LOG(gXULLog, PR_LOG_ALWAYS,
 
2146
               ("xul: unable to layout '%s'; no root content", urlspec.get()));
 
2147
#endif
 
2148
        return NS_OK;
 
2149
    }
 
2150
 
 
2151
    PRUint32 count = GetNumberOfShells();
 
2152
    for (PRUint32 i = 0; i < count; ++i) {
 
2153
        nsIPresShell *shell = GetShellAt(i);
 
2154
 
 
2155
        // Resize-reflow this time
 
2156
        nsCOMPtr<nsIPresContext> cx;
 
2157
        shell->GetPresContext(getter_AddRefs(cx));
 
2158
        NS_ASSERTION(cx != nsnull, "no pres context");
 
2159
        if (! cx)
 
2160
            return NS_ERROR_UNEXPECTED;
 
2161
 
 
2162
        nsCOMPtr<nsISupports> container = cx->GetContainer();
 
2163
        NS_ASSERTION(container != nsnull, "pres context has no container");
 
2164
        if (! container)
 
2165
            return NS_ERROR_UNEXPECTED;
 
2166
 
 
2167
        nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
 
2168
        NS_ASSERTION(docShell != nsnull, "container is not a docshell");
 
2169
        if (! docShell)
 
2170
            return NS_ERROR_UNEXPECTED;
 
2171
 
 
2172
        nsRect r = cx->GetVisibleArea();
 
2173
 
 
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();
 
2180
        if (vm) {
 
2181
            nsCOMPtr<nsIContentViewer> contentViewer;
 
2182
            nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
 
2183
            if (NS_SUCCEEDED(rv) && (contentViewer != nsnull)) {
 
2184
                PRBool enabled;
 
2185
                contentViewer->GetEnableRendering(&enabled);
 
2186
                if (enabled) {
 
2187
                    vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
 
2188
                }
 
2189
            }
 
2190
        }
 
2191
 
 
2192
        shell->InitialReflow(r.width, r.height);
 
2193
 
 
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();
 
2198
 
 
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();
 
2203
    }
 
2204
 
 
2205
    return NS_OK;
 
2206
}
 
2207
 
 
2208
 
 
2209
nsresult
 
2210
nsXULDocument::GetElementsByAttribute(nsIDOMNode* aNode,
 
2211
                                        const nsAString& aAttribute,
 
2212
                                        const nsAString& aValue,
 
2213
                                        nsRDFDOMNodeList* aElements)
 
2214
{
 
2215
    nsresult rv;
 
2216
 
 
2217
    nsCOMPtr<nsIDOMElement> element;
 
2218
    element = do_QueryInterface(aNode);
 
2219
    if (!element)
 
2220
        return NS_OK;
 
2221
 
 
2222
    nsAutoString attrValue;
 
2223
    if (NS_FAILED(rv = element->GetAttribute(aAttribute, attrValue))) {
 
2224
        NS_ERROR("unable to get attribute value");
 
2225
        return rv;
 
2226
    }
 
2227
 
 
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");
 
2232
            return rv;
 
2233
        }
 
2234
    }
 
2235
 
 
2236
    nsCOMPtr<nsIDOMNodeList> children;
 
2237
    if (NS_FAILED(rv = aNode->GetChildNodes( getter_AddRefs(children) ))) {
 
2238
        NS_ERROR("unable to get node's children");
 
2239
        return rv;
 
2240
    }
 
2241
 
 
2242
    // no kids: terminate the recursion
 
2243
    if (! children)
 
2244
        return NS_OK;
 
2245
 
 
2246
    PRUint32 length;
 
2247
    if (NS_FAILED(children->GetLength(&length))) {
 
2248
        NS_ERROR("unable to get node list's length");
 
2249
        return rv;
 
2250
    }
 
2251
 
 
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");
 
2256
            return rv;
 
2257
        }
 
2258
 
 
2259
        if (NS_FAILED(rv = GetElementsByAttribute(child, aAttribute, aValue,
 
2260
                                                  aElements))) {
 
2261
            NS_ERROR("unable to recursively get elements by attribute");
 
2262
            return rv;
 
2263
        }
 
2264
    }
 
2265
 
 
2266
    return NS_OK;
 
2267
}
 
2268
 
 
2269
 
 
2270
nsresult
 
2271
nsXULDocument::PrepareToLoad(nsISupports* aContainer,
 
2272
                             const char* aCommand,
 
2273
                             nsIChannel* aChannel,
 
2274
                             nsILoadGroup* aLoadGroup,
 
2275
                             nsIParser** aResult)
 
2276
{
 
2277
    nsresult rv;
 
2278
 
 
2279
    // Get the document's principal
 
2280
    nsCOMPtr<nsISupports> owner;
 
2281
    rv = aChannel->GetOwner(getter_AddRefs(owner));
 
2282
    if (NS_FAILED(rv)) return rv;
 
2283
 
 
2284
    nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(owner);
 
2285
 
 
2286
    return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
 
2287
}
 
2288
 
 
2289
 
 
2290
nsresult
 
2291
nsXULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
 
2292
                                      nsIPrincipal* aDocumentPrincipal,
 
2293
                                      nsIParser** aResult)
 
2294
{
 
2295
    nsresult rv;
 
2296
 
 
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;
 
2302
 
 
2303
    // Bootstrap the master document prototype.
 
2304
    if (! mMasterPrototype) {
 
2305
        mMasterPrototype = mCurrentPrototype;
 
2306
        mMasterPrototype->SetDocumentPrincipal(aDocumentPrincipal);
 
2307
    }
 
2308
 
 
2309
    rv = mCurrentPrototype->SetURI(aURI);
 
2310
    if (NS_FAILED(rv)) return rv;
 
2311
 
 
2312
    // Create a XUL content sink, a parser, and kick off a load for
 
2313
    // the overlay.
 
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;
 
2318
 
 
2319
    rv = sink->Init(this, mCurrentPrototype);
 
2320
    NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
 
2321
    if (NS_FAILED(rv)) return rv;
 
2322
 
 
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;
 
2326
 
 
2327
    parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
 
2328
                       eViewSource);
 
2329
 
 
2330
    parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
 
2331
                               kCharsetFromDocTypeDefault);
 
2332
    parser->SetContentSink(sink); // grabs a reference to the parser
 
2333
 
 
2334
    *aResult = parser;
 
2335
    NS_ADDREF(*aResult);
 
2336
    return NS_OK;
 
2337
}
 
2338
 
 
2339
 
 
2340
nsresult
 
2341
nsXULDocument::ApplyPersistentAttributes()
 
2342
{
 
2343
    // Add all of the 'persisted' attributes into the content
 
2344
    // model.
 
2345
    if (! mLocalStore)
 
2346
        return NS_OK;
 
2347
 
 
2348
    mApplyingPersistedAttrs = PR_TRUE;
 
2349
 
 
2350
    nsresult rv;
 
2351
    nsCOMPtr<nsISupportsArray> elements;
 
2352
    rv = NS_NewISupportsArray(getter_AddRefs(elements));
 
2353
    if (NS_FAILED(rv)) return rv;
 
2354
 
 
2355
    nsCAutoString docurl;
 
2356
    mDocumentURI->GetSpec(docurl);
 
2357
 
 
2358
    nsCOMPtr<nsIRDFResource> doc;
 
2359
    gRDFService->GetResource(docurl, getter_AddRefs(doc));
 
2360
 
 
2361
    nsCOMPtr<nsISimpleEnumerator> persisted;
 
2362
    mLocalStore->GetTargets(doc, kNC_persist, PR_TRUE, getter_AddRefs(persisted));
 
2363
 
 
2364
    while (1) {
 
2365
        PRBool hasmore = PR_FALSE;
 
2366
        persisted->HasMoreElements(&hasmore);
 
2367
        if (! hasmore)
 
2368
            break;
 
2369
 
 
2370
        nsCOMPtr<nsISupports> isupports;
 
2371
        persisted->GetNext(getter_AddRefs(isupports));
 
2372
 
 
2373
        nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
 
2374
        if (! resource) {
 
2375
            NS_WARNING("expected element to be a resource");
 
2376
            continue;
 
2377
        }
 
2378
 
 
2379
        const char *uri;
 
2380
        resource->GetValueConst(&uri);
 
2381
        if (! uri)
 
2382
            continue;
 
2383
 
 
2384
        nsAutoString id;
 
2385
        nsXULContentUtils::MakeElementID(this, NS_ConvertASCIItoUCS2(uri), id);
 
2386
 
 
2387
        // This will clear the array if there are no elements.
 
2388
        GetElementsForID(id, elements);
 
2389
 
 
2390
        PRUint32 cnt = 0;
 
2391
        elements->Count(&cnt);
 
2392
        if (! cnt)
 
2393
            continue;
 
2394
 
 
2395
        ApplyPersistentAttributesToElements(resource, elements);
 
2396
    }
 
2397
 
 
2398
    mApplyingPersistedAttrs = PR_FALSE;
 
2399
 
 
2400
    return NS_OK;
 
2401
}
 
2402
 
 
2403
 
 
2404
nsresult
 
2405
nsXULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
 
2406
                                                   nsISupportsArray* aElements)
 
2407
{
 
2408
    nsresult rv;
 
2409
 
 
2410
    nsCOMPtr<nsISimpleEnumerator> attrs;
 
2411
    rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
 
2412
    if (NS_FAILED(rv)) return rv;
 
2413
 
 
2414
    while (1) {
 
2415
        PRBool hasmore;
 
2416
        rv = attrs->HasMoreElements(&hasmore);
 
2417
        if (NS_FAILED(rv)) return rv;
 
2418
 
 
2419
        if (! hasmore)
 
2420
            break;
 
2421
 
 
2422
        nsCOMPtr<nsISupports> isupports;
 
2423
        rv = attrs->GetNext(getter_AddRefs(isupports));
 
2424
        if (NS_FAILED(rv)) return rv;
 
2425
 
 
2426
        nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
 
2427
        if (! property) {
 
2428
            NS_WARNING("expected a resource");
 
2429
            continue;
 
2430
        }
 
2431
 
 
2432
        const char* attrname;
 
2433
        rv = property->GetValueConst(&attrname);
 
2434
        if (NS_FAILED(rv)) return rv;
 
2435
 
 
2436
        nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
 
2437
        if (! attr)
 
2438
            return NS_ERROR_OUT_OF_MEMORY;
 
2439
 
 
2440
        // XXX could hang namespace off here, as well...
 
2441
 
 
2442
        nsCOMPtr<nsIRDFNode> node;
 
2443
        rv = mLocalStore->GetTarget(aResource, property, PR_TRUE,
 
2444
                                    getter_AddRefs(node));
 
2445
        if (NS_FAILED(rv)) return rv;
 
2446
 
 
2447
        nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
 
2448
        if (! literal) {
 
2449
            NS_WARNING("expected a literal");
 
2450
            continue;
 
2451
        }
 
2452
 
 
2453
        const PRUnichar* value;
 
2454
        rv = literal->GetValueConst(&value);
 
2455
        if (NS_FAILED(rv)) return rv;
 
2456
 
 
2457
        nsDependentString wrapper(value);
 
2458
 
 
2459
        PRUint32 cnt;
 
2460
        rv = aElements->Count(&cnt);
 
2461
        if (NS_FAILED(rv)) return rv;
 
2462
 
 
2463
        for (PRInt32 i = PRInt32(cnt) - 1; i >= 0; --i) {
 
2464
            nsISupports* isupports2 = aElements->ElementAt(i);
 
2465
            if (! isupports2)
 
2466
                continue;
 
2467
 
 
2468
            nsCOMPtr<nsIContent> element = do_QueryInterface(isupports2);
 
2469
            NS_RELEASE(isupports2);
 
2470
 
 
2471
            rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
 
2472
                                  attr,
 
2473
                                  wrapper,
 
2474
                                  PR_TRUE);
 
2475
        }
 
2476
    }
 
2477
 
 
2478
    return NS_OK;
 
2479
}
 
2480
 
 
2481
//----------------------------------------------------------------------
 
2482
//
 
2483
// nsXULDocument::ContextStack
 
2484
//
 
2485
 
 
2486
nsXULDocument::ContextStack::ContextStack()
 
2487
    : mTop(nsnull), mDepth(0)
 
2488
{
 
2489
}
 
2490
 
 
2491
nsXULDocument::ContextStack::~ContextStack()
 
2492
{
 
2493
    while (mTop) {
 
2494
        Entry* doomed = mTop;
 
2495
        mTop = mTop->mNext;
 
2496
        NS_IF_RELEASE(doomed->mElement);
 
2497
        delete doomed;
 
2498
    }
 
2499
}
 
2500
 
 
2501
nsresult
 
2502
nsXULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
 
2503
                                  nsIContent* aElement)
 
2504
{
 
2505
    Entry* entry = new Entry;
 
2506
    if (! entry)
 
2507
        return NS_ERROR_OUT_OF_MEMORY;
 
2508
 
 
2509
    entry->mPrototype = aPrototype;
 
2510
    entry->mElement   = aElement;
 
2511
    NS_IF_ADDREF(entry->mElement);
 
2512
    entry->mIndex     = 0;
 
2513
 
 
2514
    entry->mNext = mTop;
 
2515
    mTop = entry;
 
2516
 
 
2517
    ++mDepth;
 
2518
    return NS_OK;
 
2519
}
 
2520
 
 
2521
nsresult
 
2522
nsXULDocument::ContextStack::Pop()
 
2523
{
 
2524
    if (mDepth == 0)
 
2525
        return NS_ERROR_UNEXPECTED;
 
2526
 
 
2527
    Entry* doomed = mTop;
 
2528
    mTop = mTop->mNext;
 
2529
    --mDepth;
 
2530
 
 
2531
    NS_IF_RELEASE(doomed->mElement);
 
2532
    delete doomed;
 
2533
    return NS_OK;
 
2534
}
 
2535
 
 
2536
nsresult
 
2537
nsXULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
 
2538
                                           nsIContent** aElement,
 
2539
                                           PRInt32* aIndex)
 
2540
{
 
2541
    if (mDepth == 0)
 
2542
        return NS_ERROR_UNEXPECTED;
 
2543
 
 
2544
    *aPrototype = mTop->mPrototype;
 
2545
    *aElement   = mTop->mElement;
 
2546
    NS_IF_ADDREF(*aElement);
 
2547
    *aIndex     = mTop->mIndex;
 
2548
 
 
2549
    return NS_OK;
 
2550
}
 
2551
 
 
2552
 
 
2553
nsresult
 
2554
nsXULDocument::ContextStack::SetTopIndex(PRInt32 aIndex)
 
2555
{
 
2556
    if (mDepth == 0)
 
2557
        return NS_ERROR_UNEXPECTED;
 
2558
 
 
2559
    mTop->mIndex = aIndex;
 
2560
    return NS_OK;
 
2561
}
 
2562
 
 
2563
 
 
2564
PRBool
 
2565
nsXULDocument::ContextStack::IsInsideXULTemplate()
 
2566
{
 
2567
    if (mDepth) {
 
2568
        for (nsIContent* element = mTop->mElement; element;
 
2569
             element = element->GetParent()) {
 
2570
 
 
2571
            nsINodeInfo *ni = element->GetNodeInfo();
 
2572
 
 
2573
            if (ni && ni->Equals(nsXULAtoms::Template, kNameSpaceID_XUL)) {
 
2574
                return PR_TRUE;
 
2575
            }
 
2576
        }
 
2577
    }
 
2578
    return PR_FALSE;
 
2579
}
 
2580
 
 
2581
 
 
2582
//----------------------------------------------------------------------
 
2583
//
 
2584
// Content model walking routines
 
2585
//
 
2586
 
 
2587
nsresult
 
2588
nsXULDocument::PrepareToWalk()
 
2589
{
 
2590
    // Prepare to walk the mCurrentPrototype
 
2591
    nsresult rv;
 
2592
 
 
2593
    // Keep an owning reference to the prototype document so that its
 
2594
    // elements aren't yanked from beneath us.
 
2595
    mPrototypes.AppendObject(mCurrentPrototype);
 
2596
 
 
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;
 
2603
 
 
2604
    // ...and we preserve this ordering by appending to our
 
2605
    // mUnloadedOverlays array in reverse order
 
2606
    PRUint32 count;
 
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);
 
2612
    }
 
2613
 
 
2614
 
 
2615
    // Now check the chrome registry for any additional overlays.
 
2616
    rv = AddChromeOverlays();
 
2617
 
 
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;
 
2623
 
 
2624
 
 
2625
    if (! proto) {
 
2626
#ifdef PR_LOGGING
 
2627
        nsCOMPtr<nsIURI> url;
 
2628
        rv = mCurrentPrototype->GetURI(getter_AddRefs(url));
 
2629
        if (NS_FAILED(rv)) return rv;
 
2630
 
 
2631
        nsCAutoString urlspec;
 
2632
        rv = url->GetSpec(urlspec);
 
2633
        if (NS_FAILED(rv)) return rv;
 
2634
 
 
2635
        PR_LOG(gXULLog, PR_LOG_ALWAYS,
 
2636
               ("xul: error parsing '%s'", urlspec.get()));
 
2637
#endif
 
2638
 
 
2639
        return NS_OK;
 
2640
    }
 
2641
 
 
2642
    // Do one-time initialization if we're preparing to walk the
 
2643
    // master document's prototype.
 
2644
    nsCOMPtr<nsIContent> root;
 
2645
 
 
2646
    if (mState == eState_Master) {
 
2647
        rv = CreateElementFromPrototype(proto, getter_AddRefs(root));
 
2648
        if (NS_FAILED(rv)) return rv;
 
2649
 
 
2650
        SetRootContent(root);
 
2651
 
 
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;
 
2655
 
 
2656
        // Add a dummy channel to the load group as a placeholder for the document
 
2657
        // load
 
2658
        rv = PlaceHolderRequest::Create(getter_AddRefs(mPlaceHolderRequest));
 
2659
        if (NS_FAILED(rv)) return rv;
 
2660
 
 
2661
        nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
 
2662
 
 
2663
        if (group) {
 
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;
 
2668
        }
 
2669
    }
 
2670
 
 
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;
 
2681
 
 
2682
    rv = mContextStack.Push(proto, root);
 
2683
    if (NS_FAILED(rv)) return rv;
 
2684
 
 
2685
    return NS_OK;
 
2686
}
 
2687
 
 
2688
 
 
2689
nsresult
 
2690
nsXULDocument::AddChromeOverlays()
 
2691
{
 
2692
    nsresult rv;
 
2693
 
 
2694
    nsCOMPtr<nsIURI> docUri;
 
2695
    rv = mCurrentPrototype->GetURI(getter_AddRefs(docUri));
 
2696
    NS_ENSURE_SUCCESS(rv, rv);
 
2697
 
 
2698
    /* overlays only apply to chrome, skip all content URIs */
 
2699
    if (!IsChromeURI(docUri)) return NS_OK;
 
2700
 
 
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);
 
2705
 
 
2706
    nsCOMPtr<nsISimpleEnumerator> overlays;
 
2707
    rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
 
2708
    NS_ENSURE_SUCCESS(rv, rv);
 
2709
 
 
2710
    PRBool moreOverlays;
 
2711
    nsCOMPtr<nsISupports> next;
 
2712
    nsCOMPtr<nsIURI> uri;
 
2713
 
 
2714
    while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
 
2715
           moreOverlays) {
 
2716
        rv = overlays->GetNext(getter_AddRefs(next));
 
2717
        if (NS_FAILED(rv) || !next) continue;
 
2718
 
 
2719
        uri = do_QueryInterface(next);
 
2720
        if (!uri) {
 
2721
            NS_ERROR("Chrome registry handed me a non-nsIURI object!");
 
2722
            continue;
 
2723
        }
 
2724
 
 
2725
        mUnloadedOverlays->AppendElement(uri);
 
2726
    }
 
2727
 
 
2728
    return NS_OK;
 
2729
}
 
2730
 
 
2731
nsresult
 
2732
nsXULDocument::ResumeWalk()
 
2733
{
 
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
 
2738
    // constructed.
 
2739
    //
 
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.
 
2743
    nsresult rv;
 
2744
    nsCOMPtr<nsIScriptSecurityManager> secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
 
2745
    NS_ENSURE_SUCCESS(rv, rv);
 
2746
 
 
2747
    while (1) {
 
2748
        // Begin (or resume) walking the current prototype.
 
2749
 
 
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;
 
2755
            PRInt32 indx;
 
2756
            rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
 
2757
            if (NS_FAILED(rv)) return rv;
 
2758
 
 
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
 
2764
                // resolved.)
 
2765
                if (element && (mState == eState_Master))
 
2766
                    AddElementToDocumentPost(element);
 
2767
 
 
2768
                // Now pop the context stack back up to the parent
 
2769
                // element and continue the prototype walk.
 
2770
                mContextStack.Pop();
 
2771
                continue;
 
2772
            }
 
2773
 
 
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);
 
2778
 
 
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);
 
2784
 
 
2785
                nsCOMPtr<nsIContent> child;
 
2786
 
 
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");
 
2793
 
 
2794
                    rv = CreateElementFromPrototype(protoele,
 
2795
                                                    getter_AddRefs(child));
 
2796
                    if (NS_FAILED(rv)) return rv;
 
2797
 
 
2798
                    // ...and append it to the content model.
 
2799
                    rv = element->AppendChildTo(child, PR_FALSE, PR_FALSE);
 
2800
                    if (NS_FAILED(rv)) return rv;
 
2801
 
 
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);
 
2808
                }
 
2809
                else {
 
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
 
2814
                    // later.
 
2815
                    rv = CreateOverlayElement(protoele, getter_AddRefs(child));
 
2816
                    if (NS_FAILED(rv)) return rv;
 
2817
                }
 
2818
 
 
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;
 
2824
                }
 
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
 
2828
                    // immediately.
 
2829
                    AddElementToDocumentPost(child);
 
2830
                }
 
2831
            }
 
2832
            break;
 
2833
 
 
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);
 
2839
 
 
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.
 
2845
                    PRBool blocked;
 
2846
                    rv = LoadScript(scriptproto, &blocked);
 
2847
                    if (NS_FAILED(rv)) return rv;
 
2848
 
 
2849
                    if (blocked)
 
2850
                        return NS_OK;
 
2851
                }
 
2852
                else if (scriptproto->mJSObject) {
 
2853
                    // An inline script
 
2854
                    rv = ExecuteScript(scriptproto->mJSObject);
 
2855
                    if (NS_FAILED(rv)) return rv;
 
2856
                }
 
2857
            }
 
2858
            break;
 
2859
 
 
2860
            case nsXULPrototypeNode::eType_Text: {
 
2861
                // A simple text node.
 
2862
 
 
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");
 
2869
 
 
2870
                    nsCOMPtr<nsITextContent> text;
 
2871
                    rv = NS_NewTextNode(getter_AddRefs(text));
 
2872
                    NS_ENSURE_SUCCESS(rv, rv);
 
2873
 
 
2874
                    nsXULPrototypeText* textproto =
 
2875
                        NS_REINTERPRET_CAST(nsXULPrototypeText*, childproto);
 
2876
                    rv = text->SetText(textproto->mValue.get(),
 
2877
                                       textproto->mValue.Length(),
 
2878
                                       PR_FALSE);
 
2879
 
 
2880
                    if (NS_FAILED(rv)) return rv;
 
2881
 
 
2882
                    nsCOMPtr<nsIContent> child = do_QueryInterface(text);
 
2883
                    if (! child)
 
2884
                        return NS_ERROR_UNEXPECTED;
 
2885
 
 
2886
                    rv = element->AppendChildTo(child, PR_FALSE, PR_FALSE);
 
2887
                    if (NS_FAILED(rv)) return rv;
 
2888
                }
 
2889
            }
 
2890
            break;
 
2891
            }
 
2892
        }
 
2893
 
 
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.
 
2897
 
 
2898
        // If we're not already, mark us as now processing overlays.
 
2899
        mState = eState_Overlay;
 
2900
 
 
2901
        PRUint32 count;
 
2902
        mUnloadedOverlays->Count(&count);
 
2903
 
 
2904
        // If there are no overlay URIs, then we're done.
 
2905
        if (! count)
 
2906
            break;
 
2907
 
 
2908
        nsCOMPtr<nsIURI> uri =
 
2909
            dont_AddRef(NS_REINTERPRET_CAST(nsIURI*, mUnloadedOverlays->ElementAt(count - 1)));
 
2910
 
 
2911
        mUnloadedOverlays->RemoveElementAt(count - 1);
 
2912
 
 
2913
#ifdef PR_LOGGING
 
2914
        if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) {
 
2915
            nsCAutoString urlspec;
 
2916
            uri->GetSpec(urlspec);
 
2917
 
 
2918
            PR_LOG(gXULLog, PR_LOG_DEBUG,
 
2919
                   ("xul: loading overlay %s", urlspec.get()));
 
2920
        }
 
2921
#endif
 
2922
 
 
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.
 
2927
 
 
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
 
2934
                continue;
 
2935
            }
 
2936
        }
 
2937
 
 
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));
 
2942
        else
 
2943
            mCurrentPrototype = nsnull;
 
2944
 
 
2945
        // Same comment as nsChromeProtocolHandler::NewChannel and
 
2946
        // nsXULDocument::StartDocumentLoad
 
2947
        // - Ben Goodger
 
2948
        //
 
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:
 
2953
        //
 
2954
        // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
 
2955
        //                         parse from disk
 
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.
 
2960
 
 
2961
        PRBool useXULCache;
 
2962
        gXULCache->GetEnabled(&useXULCache);
 
2963
 
 
2964
        if (useXULCache && mCurrentPrototype) {
 
2965
            PRBool loaded;
 
2966
            rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
 
2967
            if (NS_FAILED(rv)) return rv;
 
2968
 
 
2969
            if (! loaded) {
 
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().
 
2974
                return NS_OK;
 
2975
            }
 
2976
 
 
2977
            // Found the overlay's prototype in the cache, fully loaded.
 
2978
            rv = AddPrototypeSheets();
 
2979
            if (NS_FAILED(rv)) return rv;
 
2980
 
 
2981
            // Now prepare to walk the prototype to create its content
 
2982
            rv = PrepareToWalk();
 
2983
            if (NS_FAILED(rv)) return rv;
 
2984
 
 
2985
            PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached"));
 
2986
        }
 
2987
        else {
 
2988
            // Not there. Initiate a load.
 
2989
            PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached"));
 
2990
 
 
2991
            nsCOMPtr<nsIParser> parser;
 
2992
            rv = PrepareToLoadPrototype(uri, "view", nsnull, getter_AddRefs(parser));
 
2993
            if (NS_FAILED(rv)) return rv;
 
2994
 
 
2995
            // Predicate mIsWritingFastLoad on the XUL cache being enabled,
 
2996
            // so we don't have to re-check whether the cache is enabled all
 
2997
            // the time.
 
2998
            mIsWritingFastLoad = useXULCache;
 
2999
 
 
3000
            nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
 
3001
            if (! listener)
 
3002
                return NS_ERROR_UNEXPECTED;
 
3003
 
 
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;
 
3010
 
 
3011
            NS_ADDREF(parserObserver);
 
3012
            parser->Parse(uri, parserObserver);
 
3013
            NS_RELEASE(parserObserver);
 
3014
 
 
3015
            nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
 
3016
            rv = NS_OpenURI(listener, nsnull, uri, nsnull, group);
 
3017
            if (NS_FAILED(rv)) return rv;
 
3018
 
 
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;
 
3027
            }
 
3028
 
 
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().
 
3033
            return NS_OK;
 
3034
        }
 
3035
    }
 
3036
 
 
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;
 
3041
 
 
3042
    rv = ApplyPersistentAttributes();
 
3043
    if (NS_FAILED(rv)) return rv;
 
3044
 
 
3045
    // Everything after this point we only want to do once we're
 
3046
    // certain that we've been embedded in a presentation shell.
 
3047
 
 
3048
    StartLayout();
 
3049
 
 
3050
    if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
 
3051
        gXULCache->WritePrototype(mMasterPrototype);
 
3052
 
 
3053
    for (PRInt32 i = mObservers.Count() - 1; i >= 0; --i) {
 
3054
        nsIDocumentObserver* observer = (nsIDocumentObserver*) mObservers[i];
 
3055
        observer->EndLoad(this);
 
3056
    }
 
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);
 
3063
        if (group) {
 
3064
            rv = group->RemoveRequest(mPlaceHolderRequest, nsnull, NS_OK);
 
3065
            if (NS_FAILED(rv)) return rv;
 
3066
 
 
3067
            mPlaceHolderRequest = nsnull;
 
3068
        }
 
3069
    }
 
3070
 
 
3071
    return rv;
 
3072
}
 
3073
 
 
3074
nsresult
 
3075
nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock)
 
3076
{
 
3077
    // Load a transcluded script
 
3078
    nsresult rv;
 
3079
 
 
3080
    if (aScriptProto->mJSObject) {
 
3081
        rv = ExecuteScript(aScriptProto->mJSObject);
 
3082
 
 
3083
        // Ignore return value from execution, and don't block
 
3084
        *aBlock = PR_FALSE;
 
3085
        return NS_OK;
 
3086
    }
 
3087
 
 
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!
 
3091
    PRBool useXULCache;
 
3092
    gXULCache->GetEnabled(&useXULCache);
 
3093
 
 
3094
    if (useXULCache) {
 
3095
        gXULCache->GetScript(aScriptProto->mSrcURI,
 
3096
                             NS_REINTERPRET_CAST(void**, &aScriptProto->mJSObject));
 
3097
 
 
3098
        if (aScriptProto->mJSObject) {
 
3099
            rv = ExecuteScript(aScriptProto->mJSObject);
 
3100
 
 
3101
            // Ignore return value from execution, and don't block
 
3102
            *aBlock = PR_FALSE;
 
3103
            return NS_OK;
 
3104
        }
 
3105
    }
 
3106
 
 
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;
 
3112
 
 
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;
 
3118
        NS_ADDREF_THIS();
 
3119
    }
 
3120
    else {
 
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;
 
3125
 
 
3126
        nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
 
3127
 
 
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;
 
3132
    }
 
3133
 
 
3134
    // Block until OnStreamComplete resumes us.
 
3135
    *aBlock = PR_TRUE;
 
3136
    return NS_OK;
 
3137
}
 
3138
 
 
3139
 
 
3140
NS_IMETHODIMP
 
3141
nsXULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
 
3142
                                nsISupports* context,
 
3143
                                nsresult aStatus,
 
3144
                                PRUint32 stringLen,
 
3145
                                const char* string)
 
3146
{
 
3147
#ifdef DEBUG
 
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);
 
3154
        if (channel) {
 
3155
            nsCOMPtr<nsIURI> uri;
 
3156
            channel->GetURI(getter_AddRefs(uri));
 
3157
            if (uri) {
 
3158
                nsCAutoString uriSpec;
 
3159
                uri->GetSpec(uriSpec);
 
3160
                printf("Failed to load %s\n", uriSpec.get());
 
3161
            }
 
3162
        }
 
3163
    }
 
3164
#endif
 
3165
 
 
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.
 
3170
    nsresult rv;
 
3171
 
 
3172
    NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
 
3173
                 "script source not loading on unichar stream complete?");
 
3174
 
 
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;
 
3180
 
 
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
 
3183
    // new script load.
 
3184
    scriptProto->mSrcLoading = PR_FALSE;
 
3185
 
 
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;
 
3195
 
 
3196
        nsString stringStr; stringStr.AssignWithConversion(string, stringLen);
 
3197
        rv = scriptProto->Compile(stringStr.get(), stringLen, uri, 1, this,
 
3198
                                  mCurrentPrototype);
 
3199
 
 
3200
        aStatus = rv;
 
3201
        if (NS_SUCCEEDED(rv) && scriptProto->mJSObject) {
 
3202
            rv = ExecuteScript(scriptProto->mJSObject);
 
3203
 
 
3204
            // If the XUL cache is enabled, save the script object there in
 
3205
            // case different XUL documents source the same script.
 
3206
            //
 
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.
 
3214
            //
 
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.
 
3222
            //
 
3223
            // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
 
3224
            // the true crime story.)
 
3225
            PRBool useXULCache;
 
3226
            gXULCache->GetEnabled(&useXULCache);
 
3227
 
 
3228
            if (useXULCache && IsChromeURI(mDocumentURI)) {
 
3229
                gXULCache->PutScript(scriptProto->mSrcURI,
 
3230
                                     NS_REINTERPRET_CAST(void*, scriptProto->mJSObject));
 
3231
            }
 
3232
 
 
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.
 
3241
                //
 
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));
 
3249
 
 
3250
                NS_ASSERTION(global != nsnull, "master prototype w/o global?!");
 
3251
                if (global) {
 
3252
                    nsIScriptContext *scriptContext = global->GetContext();
 
3253
                    if (scriptContext)
 
3254
                        scriptProto->SerializeOutOfLine(nsnull, scriptContext);
 
3255
                }
 
3256
            }
 
3257
        }
 
3258
        // ignore any evaluation errors
 
3259
    }
 
3260
 
 
3261
    // balance the addref we added in LoadScript()
 
3262
    NS_RELEASE(aLoader);
 
3263
 
 
3264
    rv = ResumeWalk();
 
3265
 
 
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;
 
3269
 
 
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
 
3272
    nsXULDocument* doc;
 
3273
    while ((doc = *docp) != nsnull) {
 
3274
        NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
 
3275
                     "waiting for wrong script to load?");
 
3276
        doc->mCurrentScriptProto = nsnull;
 
3277
 
 
3278
        // Unlink doc from scriptProto's list before executing and resuming
 
3279
        *docp = doc->mNextSrcLoadWaiter;
 
3280
        doc->mNextSrcLoadWaiter = nsnull;
 
3281
 
 
3282
        // Execute only if we loaded and compiled successfully, then resume
 
3283
        if (NS_SUCCEEDED(aStatus) && scriptProto->mJSObject) {
 
3284
            doc->ExecuteScript(scriptProto->mJSObject);
 
3285
        }
 
3286
        doc->ResumeWalk();
 
3287
        NS_RELEASE(doc);
 
3288
    }
 
3289
 
 
3290
    return rv;
 
3291
}
 
3292
 
 
3293
 
 
3294
nsresult
 
3295
nsXULDocument::ExecuteScript(JSObject* aScriptObject)
 
3296
{
 
3297
    NS_PRECONDITION(aScriptObject != nsnull, "null ptr");
 
3298
    if (! aScriptObject)
 
3299
        return NS_ERROR_NULL_POINTER;
 
3300
 
 
3301
    // Execute the precompiled script with the given version
 
3302
    nsresult rv = NS_ERROR_UNEXPECTED;
 
3303
 
 
3304
    NS_ASSERTION(mScriptGlobalObject != nsnull, "no script global object");
 
3305
 
 
3306
    nsIScriptContext *context;
 
3307
    if (mScriptGlobalObject && (context = mScriptGlobalObject->GetContext()))
 
3308
        rv = context->ExecuteScript(aScriptObject, nsnull, nsnull, nsnull);
 
3309
 
 
3310
    return rv;
 
3311
}
 
3312
 
 
3313
 
 
3314
nsresult
 
3315
nsXULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
 
3316
                                          nsIContent** aResult)
 
3317
{
 
3318
    // Create a content model element from a prototype element.
 
3319
    NS_PRECONDITION(aPrototype != nsnull, "null ptr");
 
3320
    if (! aPrototype)
 
3321
        return NS_ERROR_NULL_POINTER;
 
3322
 
 
3323
    *aResult = nsnull;
 
3324
    nsresult rv = NS_OK;
 
3325
 
 
3326
#ifdef PR_LOGGING
 
3327
    if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS)) {
 
3328
        nsAutoString tagstr;
 
3329
        aPrototype->mNodeInfo->GetQualifiedName(tagstr);
 
3330
 
 
3331
        nsCAutoString tagstrC;
 
3332
        tagstrC.AssignWithConversion(tagstr);
 
3333
        PR_LOG(gXULLog, PR_LOG_ALWAYS,
 
3334
               ("xul: creating <%s> from prototype",
 
3335
                tagstrC.get()));
 
3336
    }
 
3337
#endif
 
3338
 
 
3339
    nsCOMPtr<nsIContent> result;
 
3340
 
 
3341
    if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
 
3342
        // If it's a XUL element, it'll be lightweight until somebody
 
3343
        // monkeys with it.
 
3344
        rv = nsXULElement::Create(aPrototype, this, PR_TRUE, getter_AddRefs(result));
 
3345
        if (NS_FAILED(rv)) return rv;
 
3346
    }
 
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.
 
3351
 
 
3352
        gHTMLElementFactory->CreateInstanceByTag(aPrototype->mNodeInfo,
 
3353
                                                 getter_AddRefs(result));
 
3354
        if (NS_FAILED(rv)) return rv;
 
3355
 
 
3356
        if (! result)
 
3357
            return NS_ERROR_UNEXPECTED;
 
3358
 
 
3359
        result->SetDocument(this, PR_FALSE, PR_TRUE);
 
3360
 
 
3361
        rv = AddAttributes(aPrototype, result);
 
3362
        if (NS_FAILED(rv)) return rv;
 
3363
    }
 
3364
    else {
 
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.
 
3368
 
 
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;
 
3375
 
 
3376
        if (! result)
 
3377
            return NS_ERROR_UNEXPECTED;
 
3378
 
 
3379
        result->SetDocument(this, PR_FALSE, PR_TRUE);
 
3380
 
 
3381
        rv = AddAttributes(aPrototype, result);
 
3382
        if (NS_FAILED(rv)) return rv;
 
3383
    }
 
3384
 
 
3385
    result->SetContentID(mNextContentID++);
 
3386
 
 
3387
    *aResult = result;
 
3388
    NS_ADDREF(*aResult);
 
3389
    return NS_OK;
 
3390
}
 
3391
 
 
3392
nsresult
 
3393
nsXULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
 
3394
                                    nsIContent** aResult)
 
3395
{
 
3396
    nsresult rv;
 
3397
 
 
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;
 
3405
 
 
3406
    OverlayForwardReference* fwdref =
 
3407
        new OverlayForwardReference(this, element);
 
3408
    if (! fwdref)
 
3409
        return NS_ERROR_OUT_OF_MEMORY;
 
3410
 
 
3411
    // transferring ownership to ya...
 
3412
    rv = AddForwardReference(fwdref);
 
3413
    if (NS_FAILED(rv)) return rv;
 
3414
 
 
3415
    *aResult = element;
 
3416
    NS_ADDREF(*aResult);
 
3417
    return NS_OK;
 
3418
}
 
3419
 
 
3420
nsresult
 
3421
nsXULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
 
3422
                             nsIContent* aElement)
 
3423
{
 
3424
    nsresult rv;
 
3425
 
 
3426
    for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i) {
 
3427
        nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
 
3428
        nsAutoString  valueStr;
 
3429
        protoattr->mValue.ToString(valueStr);
 
3430
 
 
3431
        rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
 
3432
                               protoattr->mName.LocalName(),
 
3433
                               protoattr->mName.GetPrefix(),
 
3434
                               valueStr,
 
3435
                               PR_FALSE);
 
3436
        if (NS_FAILED(rv)) return rv;
 
3437
    }
 
3438
 
 
3439
    return NS_OK;
 
3440
}
 
3441
 
 
3442
 
 
3443
nsresult
 
3444
nsXULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
 
3445
                                          PRBool* aNeedsHookup)
 
3446
{
 
3447
    // See if the element already has a `database' attribute. If it
 
3448
    // does, then the template builder has already been created.
 
3449
    //
 
3450
    // XXX This approach will crash and burn (well, maybe not _that_
 
3451
    // bad) if aElement is not a XUL element.
 
3452
    //
 
3453
    // XXXvarga Do we still want to support non XUL content?
 
3454
    nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
 
3455
    if (xulElement) {
 
3456
        nsCOMPtr<nsIRDFCompositeDataSource> ds;
 
3457
        xulElement->GetDatabase(getter_AddRefs(ds));
 
3458
        if (ds) {
 
3459
            *aNeedsHookup = PR_FALSE;
 
3460
            return NS_OK;
 
3461
        }
 
3462
    }
 
3463
 
 
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);
 
3468
    return NS_OK;
 
3469
}
 
3470
 
 
3471
nsresult
 
3472
nsXULDocument::CreateTemplateBuilder(nsIContent* aElement)
 
3473
{
 
3474
    // Check if need to construct a tree builder or content builder.
 
3475
    PRBool isTreeBuilder = PR_FALSE;
 
3476
 
 
3477
    PRInt32 nameSpaceID;
 
3478
    nsCOMPtr<nsIAtom> baseTag;
 
3479
 
 
3480
    nsCOMPtr<nsIXBLService> xblService = do_GetService("@mozilla.org/xbl;1");
 
3481
    if (xblService) {
 
3482
        xblService->ResolveTag(aElement, &nameSpaceID, getter_AddRefs(baseTag));
 
3483
    }
 
3484
    else {
 
3485
        nsINodeInfo *ni = aElement->GetNodeInfo();
 
3486
        nameSpaceID = ni->NamespaceID();
 
3487
        baseTag = ni->NameAtom();
 
3488
    }
 
3489
 
 
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.
 
3495
 
 
3496
        nsAutoString flags;
 
3497
        aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::flags, flags);
 
3498
        if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
 
3499
            isTreeBuilder = PR_TRUE;
 
3500
        }
 
3501
    }
 
3502
 
 
3503
    if (isTreeBuilder) {
 
3504
        // Create and initialize a tree builder.
 
3505
        nsCOMPtr<nsIXULTemplateBuilder> builder =
 
3506
            do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
 
3507
 
 
3508
        if (! builder)
 
3509
            return NS_ERROR_FAILURE;
 
3510
 
 
3511
        builder->Init(aElement);
 
3512
 
 
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));
 
3518
 
 
3519
        if (! bodyContent) {
 
3520
            // Get the document.
 
3521
            nsCOMPtr<nsIDOMDocument> domdoc =
 
3522
                do_QueryInterface(aElement->GetDocument());
 
3523
            NS_ASSERTION(domdoc, "no document");
 
3524
            if (! domdoc)
 
3525
                return NS_ERROR_UNEXPECTED;
 
3526
            if (domdoc) {
 
3527
                nsCOMPtr<nsIDOMElement> bodyElement;
 
3528
                domdoc->CreateElement(NS_LITERAL_STRING("treechildren"),
 
3529
                                      getter_AddRefs(bodyElement));
 
3530
 
 
3531
                bodyContent = do_QueryInterface(bodyElement);
 
3532
                aElement->AppendChildTo(bodyContent, PR_FALSE, PR_TRUE);
 
3533
            }
 
3534
        }
 
3535
    }
 
3536
    else {
 
3537
        // Create and initialize a content builder.
 
3538
        nsCOMPtr<nsIXULTemplateBuilder> builder
 
3539
            = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
 
3540
 
 
3541
        if (! builder)
 
3542
            return NS_ERROR_FAILURE;
 
3543
 
 
3544
        builder->Init(aElement);
 
3545
 
 
3546
        nsCOMPtr<nsIXULContent> xulContent = do_QueryInterface(aElement);
 
3547
        if (xulContent) {
 
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);
 
3551
        }
 
3552
        else {
 
3553
            // Force construction of immediate template sub-content _now_.
 
3554
            builder->CreateContents(aElement);
 
3555
        }
 
3556
    }
 
3557
 
 
3558
    return NS_OK;
 
3559
}
 
3560
 
 
3561
 
 
3562
nsresult
 
3563
nsXULDocument::AddPrototypeSheets()
 
3564
{
 
3565
    // Add mCurrentPrototype's style sheets to the document.
 
3566
    nsresult rv;
 
3567
 
 
3568
    nsCOMPtr<nsISupportsArray> sheets;
 
3569
    rv = mCurrentPrototype->GetStyleSheetReferences(getter_AddRefs(sheets));
 
3570
    if (NS_FAILED(rv)) return rv;
 
3571
 
 
3572
    PRUint32 count;
 
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);
 
3578
 
 
3579
        NS_ASSERTION(uri, "not a URI!!!");
 
3580
        if (! uri)
 
3581
            return NS_ERROR_UNEXPECTED;
 
3582
 
 
3583
        nsCAutoString spec;
 
3584
        uri->GetAsciiSpec(spec);
 
3585
 
 
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
 
3589
            continue;
 
3590
        }
 
3591
 
 
3592
        nsCOMPtr<nsICSSStyleSheet> sheet;
 
3593
 
 
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).
 
3602
        // - dwh
 
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);
 
3612
        }
 
3613
    }
 
3614
 
 
3615
    return NS_OK;
 
3616
}
 
3617
 
 
3618
 
 
3619
//----------------------------------------------------------------------
 
3620
//
 
3621
// nsXULDocument::OverlayForwardReference
 
3622
//
 
3623
 
 
3624
nsForwardReference::Result
 
3625
nsXULDocument::OverlayForwardReference::Resolve()
 
3626
{
 
3627
    // Resolve a forward reference from an overlay element; attempt to
 
3628
    // hook it up into the main document.
 
3629
    nsresult rv;
 
3630
 
 
3631
    nsAutoString id;
 
3632
    rv = mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
 
3633
    if (NS_FAILED(rv)) return eResolve_Error;
 
3634
 
 
3635
    if (id.IsEmpty()) {
 
3636
        // overlay had no id, use the root element
 
3637
        mDocument->InsertElement(mDocument->mRootContent, mOverlay);
 
3638
        mResolved = PR_TRUE;
 
3639
        return eResolve_Succeeded;
 
3640
    }
 
3641
 
 
3642
    nsCOMPtr<nsIDOMElement> domtarget;
 
3643
    rv = mDocument->GetElementById(id, getter_AddRefs(domtarget));
 
3644
    if (NS_FAILED(rv)) return eResolve_Error;
 
3645
 
 
3646
    // If we can't find the element in the document, defer the hookup
 
3647
    // until later.
 
3648
    if (! domtarget)
 
3649
        return eResolve_Later;
 
3650
 
 
3651
    nsCOMPtr<nsIContent> target = do_QueryInterface(domtarget);
 
3652
    NS_ASSERTION(target != nsnull, "not an nsIContent");
 
3653
    if (! target)
 
3654
        return eResolve_Error;
 
3655
 
 
3656
    rv = Merge(target, mOverlay);
 
3657
    if (NS_FAILED(rv)) return eResolve_Error;
 
3658
 
 
3659
    // Add child and any descendants to the element map
 
3660
    rv = mDocument->AddSubtreeToDocument(target);
 
3661
    if (NS_FAILED(rv)) return eResolve_Error;
 
3662
 
 
3663
#ifdef PR_LOGGING
 
3664
    nsCAutoString idC;
 
3665
    idC.AssignWithConversion(id);
 
3666
    PR_LOG(gXULLog, PR_LOG_ALWAYS,
 
3667
           ("xul: overlay resolved '%s'",
 
3668
            idC.get()));
 
3669
#endif
 
3670
 
 
3671
    mResolved = PR_TRUE;
 
3672
    return eResolve_Succeeded;
 
3673
}
 
3674
 
 
3675
 
 
3676
 
 
3677
nsresult
 
3678
nsXULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
 
3679
                                              nsIContent* aOverlayNode)
 
3680
{
 
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.
 
3686
    //
 
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)
 
3690
 
 
3691
    nsresult rv;
 
3692
 
 
3693
    // Merge attributes from the overlay content node to that of the
 
3694
    // actual document.
 
3695
    PRUint32 i, attrCount = aOverlayNode->GetAttrCount();
 
3696
 
 
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;
 
3704
 
 
3705
        // We don't want to swap IDs, they should be the same.
 
3706
        if (nameSpaceID == kNameSpaceID_None && attr.get() == nsXULAtoms::id)
 
3707
            continue;
 
3708
 
 
3709
        nsAutoString value;
 
3710
        rv = aOverlayNode->GetAttr(nameSpaceID, attr, value);
 
3711
        if (NS_FAILED(rv)) return rv;
 
3712
 
 
3713
        nsAutoString tempID;
 
3714
        rv = aOverlayNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id, tempID);
 
3715
 
 
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"))) {
 
3720
 
 
3721
            rv = RemoveElement(aTargetNode->GetParent(), aTargetNode);
 
3722
            if (NS_FAILED(rv)) return rv;
 
3723
 
 
3724
            return NS_OK;
 
3725
        }
 
3726
 
 
3727
        rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, PR_FALSE);
 
3728
        if (NS_FAILED(rv)) return rv;
 
3729
    }
 
3730
 
 
3731
 
 
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.
 
3738
 
 
3739
    PRUint32 childCount = aOverlayNode->GetChildCount();
 
3740
 
 
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;
 
3744
 
 
3745
    for (i = 0; i < childCount; ++i) {
 
3746
        currContent = aOverlayNode->GetChildAt(0);
 
3747
 
 
3748
        nsAutoString id;
 
3749
        rv = currContent->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
 
3750
        if (NS_FAILED(rv)) return rv;
 
3751
 
 
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;
 
3757
 
 
3758
            rv = domDocument->GetElementById(id, getter_AddRefs(nodeInDocument));
 
3759
            if (NS_FAILED(rv)) return rv;
 
3760
        }
 
3761
 
 
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
 
3766
        // no id attribute.
 
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.
 
3772
 
 
3773
            nsAutoString documentParentID;
 
3774
            rv = aTargetNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id,
 
3775
                                      documentParentID);
 
3776
            if (NS_FAILED(rv)) return rv;
 
3777
 
 
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));
 
3782
 
 
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;
 
3792
 
 
3793
                continue;
 
3794
            }
 
3795
        }
 
3796
 
 
3797
        rv = aOverlayNode->RemoveChildAt(0, PR_FALSE);
 
3798
        if (NS_FAILED(rv)) return rv;
 
3799
 
 
3800
        rv = InsertElement(aTargetNode, currContent);
 
3801
        if (NS_FAILED(rv)) return rv;
 
3802
    }
 
3803
 
 
3804
    return NS_OK;
 
3805
}
 
3806
 
 
3807
 
 
3808
 
 
3809
nsXULDocument::OverlayForwardReference::~OverlayForwardReference()
 
3810
{
 
3811
#ifdef PR_LOGGING
 
3812
    if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS) && !mResolved) {
 
3813
        nsAutoString id;
 
3814
        mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id);
 
3815
 
 
3816
        nsCAutoString idC;
 
3817
        idC.AssignWithConversion(id);
 
3818
        PR_LOG(gXULLog, PR_LOG_ALWAYS,
 
3819
               ("xul: overlay failed to resolve '%s'",
 
3820
                idC.get()));
 
3821
    }
 
3822
#endif
 
3823
}
 
3824
 
 
3825
 
 
3826
//----------------------------------------------------------------------
 
3827
//
 
3828
// nsXULDocument::BroadcasterHookup
 
3829
//
 
3830
 
 
3831
nsForwardReference::Result
 
3832
nsXULDocument::BroadcasterHookup::Resolve()
 
3833
{
 
3834
    nsresult rv;
 
3835
 
 
3836
    PRBool listener;
 
3837
    rv = CheckBroadcasterHookup(mDocument, mObservesElement, &listener, &mResolved);
 
3838
    if (NS_FAILED(rv)) return eResolve_Error;
 
3839
 
 
3840
    return mResolved ? eResolve_Succeeded : eResolve_Later;
 
3841
}
 
3842
 
 
3843
 
 
3844
nsXULDocument::BroadcasterHookup::~BroadcasterHookup()
 
3845
{
 
3846
#ifdef PR_LOGGING
 
3847
    if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS) && !mResolved) {
 
3848
        // Tell the world we failed
 
3849
        nsresult rv;
 
3850
 
 
3851
        nsIAtom *tag = mObservesElement->Tag();
 
3852
 
 
3853
        nsAutoString broadcasterID;
 
3854
        nsAutoString attribute;
 
3855
 
 
3856
        if (tag == nsXULAtoms::observes) {
 
3857
            rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID);
 
3858
            if (NS_FAILED(rv)) return;
 
3859
 
 
3860
            rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute);
 
3861
            if (NS_FAILED(rv)) return;
 
3862
        }
 
3863
        else {
 
3864
            rv = mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID);
 
3865
            if (NS_FAILED(rv)) return;
 
3866
 
 
3867
            attribute.Assign(NS_LITERAL_STRING("*"));
 
3868
        }
 
3869
 
 
3870
        nsAutoString tagStr;
 
3871
        rv = tag->ToString(tagStr);
 
3872
        if (NS_FAILED(rv)) return;
 
3873
 
 
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",
 
3880
                tagstrC.get(),
 
3881
                attributeC.get(),
 
3882
                broadcasteridC.get()));
 
3883
    }
 
3884
#endif
 
3885
}
 
3886
 
 
3887
 
 
3888
//----------------------------------------------------------------------
 
3889
//
 
3890
// nsXULDocument::TemplateBuilderHookup
 
3891
//
 
3892
 
 
3893
nsForwardReference::Result
 
3894
nsXULDocument::TemplateBuilderHookup::Resolve()
 
3895
{
 
3896
    PRBool needsHookup;
 
3897
    nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
 
3898
    if (NS_FAILED(rv))
 
3899
        return eResolve_Error;
 
3900
 
 
3901
    if (needsHookup) {
 
3902
        rv = CreateTemplateBuilder(mElement);
 
3903
        if (NS_FAILED(rv))
 
3904
            return eResolve_Error;
 
3905
    }
 
3906
 
 
3907
    return eResolve_Succeeded;
 
3908
}
 
3909
 
 
3910
 
 
3911
//----------------------------------------------------------------------
 
3912
 
 
3913
 
 
3914
nsresult
 
3915
nsXULDocument::CheckBroadcasterHookup(nsXULDocument* aDocument,
 
3916
                                      nsIContent* aElement,
 
3917
                                      PRBool* aNeedsHookup,
 
3918
                                      PRBool* aDidResolve)
 
3919
{
 
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.
 
3923
    nsresult rv;
 
3924
 
 
3925
    *aDidResolve = PR_FALSE;
 
3926
 
 
3927
    nsCOMPtr<nsIDOMElement> listener;
 
3928
    nsAutoString broadcasterID;
 
3929
    nsAutoString attribute;
 
3930
 
 
3931
    nsINodeInfo *ni = aElement->GetNodeInfo();
 
3932
 
 
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();
 
3940
 
 
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;
 
3945
            return NS_OK;
 
3946
        }
 
3947
 
 
3948
        listener = do_QueryInterface(parent);
 
3949
 
 
3950
        rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID);
 
3951
        if (NS_FAILED(rv)) return rv;
 
3952
 
 
3953
        rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute);
 
3954
        if (NS_FAILED(rv)) return rv;
 
3955
    }
 
3956
    else {
 
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
 
3960
        // values.
 
3961
        rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID);
 
3962
        if (NS_FAILED(rv)) return rv;
 
3963
 
 
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;
 
3969
 
 
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.
 
3974
 
 
3975
                nsINodeInfo *ni = aElement->GetNodeInfo();
 
3976
                if (ni->Equals(nsXULAtoms::menuitem, kNameSpaceID_XUL) ||
 
3977
                    ni->Equals(nsXULAtoms::key, kNameSpaceID_XUL)) {
 
3978
                *aNeedsHookup = PR_FALSE;
 
3979
                return NS_OK;
 
3980
              }
 
3981
            }
 
3982
            else {
 
3983
              *aNeedsHookup = PR_FALSE;
 
3984
              return NS_OK;
 
3985
            }
 
3986
        }
 
3987
 
 
3988
        listener = do_QueryInterface(aElement);
 
3989
 
 
3990
        attribute.Assign(NS_LITERAL_STRING("*"));
 
3991
    }
 
3992
 
 
3993
    // Make sure we got a valid listener.
 
3994
    NS_ASSERTION(listener != nsnull, "no listener");
 
3995
    if (! listener)
 
3996
        return NS_ERROR_UNEXPECTED;
 
3997
 
 
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;
 
4002
 
 
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
 
4005
    // first.
 
4006
    if (! broadcaster) {
 
4007
        *aNeedsHookup = PR_TRUE;
 
4008
        return NS_OK;
 
4009
    }
 
4010
 
 
4011
    rv = aDocument->AddBroadcastListenerFor(broadcaster, listener, attribute);
 
4012
    if (NS_FAILED(rv)) return rv;
 
4013
 
 
4014
#ifdef PR_LOGGING
 
4015
    // Tell the world we succeeded
 
4016
    if (PR_LOG_TEST(gXULLog, PR_LOG_ALWAYS)) {
 
4017
        nsCOMPtr<nsIContent> content =
 
4018
            do_QueryInterface(listener);
 
4019
 
 
4020
        NS_ASSERTION(content != nsnull, "not an nsIContent");
 
4021
        if (! content)
 
4022
            return rv;
 
4023
 
 
4024
        nsAutoString tagStr;
 
4025
        rv = content->Tag()->ToString(tagStr);
 
4026
        if (NS_FAILED(rv)) return rv;
 
4027
 
 
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",
 
4034
                tagstrC.get(),
 
4035
                attributeC.get(),
 
4036
                broadcasteridC.get()));
 
4037
    }
 
4038
#endif
 
4039
 
 
4040
    *aNeedsHookup = PR_FALSE;
 
4041
    *aDidResolve = PR_TRUE;
 
4042
    return NS_OK;
 
4043
}
 
4044
 
 
4045
nsresult
 
4046
nsXULDocument::InsertElement(nsIContent* aParent, nsIContent* aChild)
 
4047
{
 
4048
    // Insert aChild appropriately into aParent, accounting for a
 
4049
    // 'pos' attribute set on aChild.
 
4050
    nsresult rv;
 
4051
 
 
4052
    nsAutoString posStr;
 
4053
    PRBool wasInserted = PR_FALSE;
 
4054
 
 
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;
 
4059
 
 
4060
    if (rv != NS_CONTENT_ATTR_HAS_VALUE) {
 
4061
        rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore,
 
4062
                             posStr);
 
4063
        if (NS_FAILED(rv)) return rv;
 
4064
        isInsertAfter = PR_FALSE;
 
4065
    }
 
4066
 
 
4067
    if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
 
4068
        nsCOMPtr<nsIDOMDocument> domDocument(
 
4069
               do_QueryInterface(aParent->GetDocument()));
 
4070
        nsCOMPtr<nsIDOMElement> domElement;
 
4071
 
 
4072
        char* str = ToNewCString(posStr);
 
4073
        char* rest;
 
4074
        char* token = nsCRT::strtok(str, ", ", &rest);
 
4075
 
 
4076
        while (token) {
 
4077
            rv = domDocument->GetElementById(NS_ConvertASCIItoUCS2(token),
 
4078
                                             getter_AddRefs(domElement));
 
4079
            if (domElement)
 
4080
                break;
 
4081
 
 
4082
            token = nsCRT::strtok(rest, ", ", &rest);
 
4083
        }
 
4084
        nsMemory::Free(str);
 
4085
        if (NS_FAILED(rv))
 
4086
            return rv;
 
4087
 
 
4088
        if (domElement) {
 
4089
            nsCOMPtr<nsIContent> content(do_QueryInterface(domElement));
 
4090
            NS_ASSERTION(content != nsnull, "null ptr");
 
4091
            if (!content)
 
4092
                return NS_ERROR_UNEXPECTED;
 
4093
 
 
4094
            PRInt32 pos = aParent->IndexOf(content);
 
4095
 
 
4096
            if (pos != -1) {
 
4097
                pos = isInsertAfter ? pos + 1 : pos;
 
4098
                rv = aParent->InsertChildAt(aChild, pos, PR_FALSE, PR_TRUE);
 
4099
                if (NS_FAILED(rv))
 
4100
                    return rv;
 
4101
 
 
4102
                wasInserted = PR_TRUE;
 
4103
            }
 
4104
        }
 
4105
    }
 
4106
 
 
4107
    if (!wasInserted) {
 
4108
 
 
4109
        rv = aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::position, posStr);
 
4110
        if (NS_FAILED(rv)) return rv;
 
4111
 
 
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,
 
4117
                                            PR_TRUE);
 
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.
 
4124
            }
 
4125
        }
 
4126
    }
 
4127
 
 
4128
    if (! wasInserted) {
 
4129
        rv = aParent->AppendChildTo(aChild, PR_FALSE, PR_TRUE);
 
4130
        if (NS_FAILED(rv)) return rv;
 
4131
    }
 
4132
    return NS_OK;
 
4133
}
 
4134
 
 
4135
nsresult
 
4136
nsXULDocument::RemoveElement(nsIContent* aParent, nsIContent* aChild)
 
4137
{
 
4138
    PRInt32 nodeOffset = aParent->IndexOf(aChild);
 
4139
 
 
4140
    return aParent->RemoveChildAt(nodeOffset, PR_TRUE);
 
4141
}
 
4142
 
 
4143
void
 
4144
nsXULDocument::GetElementFactory(PRInt32 aNameSpaceID,
 
4145
                                 nsIElementFactory** aResult)
 
4146
{
 
4147
    // Retrieve the appropriate factory.
 
4148
    nsContentUtils::GetNSManagerWeakRef()->GetElementFactory(aNameSpaceID,
 
4149
                                                             aResult);
 
4150
 
 
4151
    if (!*aResult) {
 
4152
        // Nothing found. Use generic XML element.
 
4153
        *aResult = gXMLElementFactory;
 
4154
        NS_IF_ADDREF(*aResult);
 
4155
    }
 
4156
}
 
4157
 
 
4158
//----------------------------------------------------------------------
 
4159
//
 
4160
// CachedChromeStreamListener
 
4161
//
 
4162
 
 
4163
nsXULDocument::CachedChromeStreamListener::CachedChromeStreamListener(nsXULDocument* aDocument, PRBool aProtoLoaded)
 
4164
    : mDocument(aDocument),
 
4165
      mProtoLoaded(aProtoLoaded)
 
4166
{
 
4167
    NS_ADDREF(mDocument);
 
4168
}
 
4169
 
 
4170
 
 
4171
nsXULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
 
4172
{
 
4173
    NS_RELEASE(mDocument);
 
4174
}
 
4175
 
 
4176
 
 
4177
NS_IMPL_ISUPPORTS2(nsXULDocument::CachedChromeStreamListener,
 
4178
                   nsIRequestObserver, nsIStreamListener)
 
4179
 
 
4180
NS_IMETHODIMP
 
4181
nsXULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
 
4182
                                                          nsISupports* acontext)
 
4183
{
 
4184
    return NS_OK;
 
4185
}
 
4186
 
 
4187
 
 
4188
NS_IMETHODIMP
 
4189
nsXULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
 
4190
                                                         nsISupports* aContext,
 
4191
                                                         nsresult aStatus)
 
4192
{
 
4193
    if (! mProtoLoaded)
 
4194
        return NS_OK;
 
4195
 
 
4196
    nsresult rv;
 
4197
    rv = mDocument->PrepareToWalk();
 
4198
    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
 
4199
    if (NS_FAILED(rv)) return rv;
 
4200
 
 
4201
    return mDocument->ResumeWalk();
 
4202
}
 
4203
 
 
4204
 
 
4205
NS_IMETHODIMP
 
4206
nsXULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
 
4207
                                                           nsISupports* aContext,
 
4208
                                                           nsIInputStream* aInStr,
 
4209
                                                           PRUint32 aSourceOffset,
 
4210
                                                           PRUint32 aCount)
 
4211
{
 
4212
    NS_NOTREACHED("CachedChromeStream doesn't receive data");
 
4213
    return NS_ERROR_UNEXPECTED;
 
4214
}
 
4215
 
 
4216
//----------------------------------------------------------------------
 
4217
//
 
4218
// ParserObserver
 
4219
//
 
4220
 
 
4221
nsXULDocument::ParserObserver::ParserObserver(nsXULDocument* aDocument)
 
4222
    : mDocument(aDocument)
 
4223
{
 
4224
    NS_ADDREF(mDocument);
 
4225
}
 
4226
 
 
4227
nsXULDocument::ParserObserver::~ParserObserver()
 
4228
{
 
4229
    NS_IF_RELEASE(mDocument);
 
4230
}
 
4231
 
 
4232
NS_IMPL_ISUPPORTS1(nsXULDocument::ParserObserver, nsIRequestObserver)
 
4233
 
 
4234
NS_IMETHODIMP
 
4235
nsXULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
 
4236
                                              nsISupports* aContext)
 
4237
{
 
4238
    return NS_OK;
 
4239
}
 
4240
 
 
4241
NS_IMETHODIMP
 
4242
nsXULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
 
4243
                                             nsISupports* aContext,
 
4244
                                             nsresult aStatus)
 
4245
{
 
4246
    nsresult rv = NS_OK;
 
4247
 
 
4248
    if (NS_FAILED(aStatus)) {
 
4249
        // If an overlay load fails, we need to nudge the prototype
 
4250
        // walk along.
 
4251
#define YELL_IF_MISSING_OVERLAY 1
 
4252
#if defined(DEBUG) || defined(YELL_IF_MISSING_OVERLAY)
 
4253
 
 
4254
        nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
 
4255
        if (!aChannel) return NS_ERROR_FAILURE;
 
4256
 
 
4257
        nsCOMPtr<nsIURI> uri;
 
4258
        aChannel->GetOriginalURI(getter_AddRefs(uri));
 
4259
 
 
4260
        nsCAutoString spec;
 
4261
        uri->GetSpec(spec);
 
4262
 
 
4263
        printf("*** Failed to load overlay %s\n", spec.get());
 
4264
#endif
 
4265
 
 
4266
        rv = mDocument->ResumeWalk();
 
4267
    }
 
4268
 
 
4269
    // Drop the reference to the document to break cycle between the
 
4270
    // document, the parser, the content sink, and the parser
 
4271
    // observer.
 
4272
    NS_RELEASE(mDocument);
 
4273
 
 
4274
    return rv;
 
4275
}
 
4276
 
 
4277
void
 
4278
nsXULDocument::GetFocusController(nsIFocusController** aFocusController)
 
4279
{
 
4280
    nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryReferent(mDocumentContainer);
 
4281
    nsCOMPtr<nsPIDOMWindow> windowPrivate = do_GetInterface(ir);
 
4282
    if (windowPrivate) {
 
4283
        windowPrivate->GetRootFocusController(aFocusController);
 
4284
    } else
 
4285
        *aFocusController = nsnull;
 
4286
}
 
4287
 
 
4288
//----------------------------------------------------------------------
 
4289
//
 
4290
// The XUL element factory
 
4291
//
 
4292
 
 
4293
class XULElementFactoryImpl : public nsIElementFactory
 
4294
{
 
4295
protected:
 
4296
    XULElementFactoryImpl();
 
4297
    virtual ~XULElementFactoryImpl();
 
4298
 
 
4299
public:
 
4300
    friend
 
4301
    nsresult
 
4302
    NS_NewXULElementFactory(nsIElementFactory** aResult);
 
4303
 
 
4304
    // nsISupports interface
 
4305
    NS_DECL_ISUPPORTS
 
4306
 
 
4307
    // nsIElementFactory interface
 
4308
    NS_IMETHOD CreateInstanceByTag(nsINodeInfo *aNodeInfo, nsIContent** aResult);
 
4309
};
 
4310
 
 
4311
XULElementFactoryImpl::XULElementFactoryImpl()
 
4312
{
 
4313
}
 
4314
 
 
4315
XULElementFactoryImpl::~XULElementFactoryImpl()
 
4316
{
 
4317
}
 
4318
 
 
4319
 
 
4320
NS_IMPL_ISUPPORTS1(XULElementFactoryImpl, nsIElementFactory)
 
4321
 
 
4322
 
 
4323
nsresult
 
4324
NS_NewXULElementFactory(nsIElementFactory** aResult)
 
4325
{
 
4326
    NS_PRECONDITION(aResult != nsnull, "null ptr");
 
4327
    if (! aResult)
 
4328
        return NS_ERROR_NULL_POINTER;
 
4329
 
 
4330
    XULElementFactoryImpl* result = new XULElementFactoryImpl();
 
4331
    if (! result)
 
4332
        return NS_ERROR_OUT_OF_MEMORY;
 
4333
 
 
4334
    NS_ADDREF(result);
 
4335
    *aResult = result;
 
4336
    return NS_OK;
 
4337
}
 
4338
 
 
4339
 
 
4340
 
 
4341
NS_IMETHODIMP
 
4342
XULElementFactoryImpl::CreateInstanceByTag(nsINodeInfo *aNodeInfo,
 
4343
                                           nsIContent** aResult)
 
4344
{
 
4345
    return nsXULElement::Create(aNodeInfo, aResult);
 
4346
}