Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/xul/XULDocument.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim: set ts=4 sw=4 et tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
9
  An implementation for the XUL document. This implementation serves
10
  as the basis for generating an NGLayout content model.
11
12
  Notes
13
  -----
14
15
  1. We do some monkey business in the document observer methods to
16
     keep the element map in sync for HTML elements. Why don't we just
17
     do it for _all_ elements? Well, in the case of XUL elements,
18
     which may be lazily created during frame construction, the
19
     document observer methods will never be called because we'll be
20
     adding the XUL nodes into the content model "quietly".
21
22
*/
23
24
#include "mozilla/ArrayUtils.h"
25
26
#include "XULDocument.h"
27
28
#include "nsError.h"
29
#include "nsIBoxObject.h"
30
#include "nsIChromeRegistry.h"
31
#include "nsView.h"
32
#include "nsViewManager.h"
33
#include "nsIContentViewer.h"
34
#include "nsIStreamListener.h"
35
#include "nsITimer.h"
36
#include "nsDocShell.h"
37
#include "nsGkAtoms.h"
38
#include "nsXMLContentSink.h"
39
#include "nsXULContentSink.h"
40
#include "nsXULContentUtils.h"
41
#include "nsIStringEnumerator.h"
42
#include "nsDocElementCreatedNotificationRunner.h"
43
#include "nsNetUtil.h"
44
#include "nsParserCIID.h"
45
#include "nsPIBoxObject.h"
46
#include "mozilla/dom/BoxObject.h"
47
#include "nsString.h"
48
#include "nsPIDOMWindow.h"
49
#include "nsPIWindowRoot.h"
50
#include "nsXULElement.h"
51
#include "nsXULPrototypeCache.h"
52
#include "mozilla/Logging.h"
53
#include "nsIFrame.h"
54
#include "nsXBLService.h"
55
#include "nsCExternalHandlerService.h"
56
#include "nsMimeTypes.h"
57
#include "nsIObjectInputStream.h"
58
#include "nsIObjectOutputStream.h"
59
#include "nsContentList.h"
60
#include "nsISimpleEnumerator.h"
61
#include "nsIScriptGlobalObject.h"
62
#include "nsIScriptSecurityManager.h"
63
#include "nsNodeInfoManager.h"
64
#include "nsContentCreatorFunctions.h"
65
#include "nsContentUtils.h"
66
#include "nsIParser.h"
67
#include "nsCharsetSource.h"
68
#include "mozilla/StyleSheetInlines.h"
69
#include "mozilla/css/Loader.h"
70
#include "nsIScriptError.h"
71
#include "nsIStyleSheetLinkingElement.h"
72
#include "nsIObserverService.h"
73
#include "nsNodeUtils.h"
74
#include "nsIXULWindow.h"
75
#include "nsXULPopupManager.h"
76
#include "nsCCUncollectableMarker.h"
77
#include "nsURILoader.h"
78
#include "mozilla/BasicEvents.h"
79
#include "mozilla/dom/DocumentL10n.h"
80
#include "mozilla/dom/Element.h"
81
#include "mozilla/dom/NodeInfoInlines.h"
82
#include "mozilla/dom/ProcessingInstruction.h"
83
#include "mozilla/dom/ScriptSettings.h"
84
#include "mozilla/dom/XULDocumentBinding.h"
85
#include "mozilla/EventDispatcher.h"
86
#include "mozilla/LoadInfo.h"
87
#include "mozilla/Preferences.h"
88
#include "nsTextNode.h"
89
#include "nsJSUtils.h"
90
#include "js/CompilationAndEvaluation.h"
91
#include "js/SourceBufferHolder.h"
92
#include "mozilla/dom/URL.h"
93
#include "nsIContentPolicy.h"
94
#include "mozAutoDocUpdate.h"
95
#include "xpcpublic.h"
96
#include "mozilla/StyleSheet.h"
97
#include "mozilla/StyleSheetInlines.h"
98
#include "nsIConsoleService.h"
99
100
using namespace mozilla;
101
using namespace mozilla::dom;
102
103
//----------------------------------------------------------------------
104
//
105
// CIDs
106
//
107
108
static NS_DEFINE_CID(kParserCID,                 NS_PARSER_CID);
109
110
//----------------------------------------------------------------------
111
//
112
// Statics
113
//
114
115
int32_t XULDocument::gRefCnt = 0;
116
117
LazyLogModule XULDocument::gXULLog("XULDocument");
118
119
//----------------------------------------------------------------------
120
121
struct BroadcastListener {
122
    nsWeakPtr mListener;
123
    RefPtr<nsAtom> mAttribute;
124
};
125
126
struct BroadcasterMapEntry : public PLDHashEntryHdr
127
{
128
    Element* mBroadcaster;  // [WEAK]
129
    nsTArray<BroadcastListener*> mListeners;  // [OWNING] of BroadcastListener objects
130
};
131
132
//----------------------------------------------------------------------
133
//
134
// ctors & dtors
135
//
136
137
namespace mozilla {
138
namespace dom {
139
140
XULDocument::XULDocument(void)
141
    : XMLDocument("application/vnd.mozilla.xul+xml"),
142
      mNextSrcLoadWaiter(nullptr),
143
      mApplyingPersistedAttrs(false),
144
      mIsWritingFastLoad(false),
145
      mDocumentLoaded(false),
146
      mStillWalking(false),
147
      mPendingSheets(0),
148
      mCurrentScriptProto(nullptr),
149
      mOffThreadCompiling(false),
150
      mOffThreadCompileStringBuf(nullptr),
151
      mOffThreadCompileStringLength(0),
152
      mBroadcasterMap(nullptr),
153
      mInitialLayoutComplete(false),
154
      mHandlingDelayedAttrChange(false),
155
      mHandlingDelayedBroadcasters(false)
156
0
{
157
0
    // Override the default in nsDocument
158
0
    mCharacterSet = UTF_8_ENCODING;
159
0
160
0
    mDefaultElementType = kNameSpaceID_XUL;
161
0
    mType = eXUL;
162
0
163
0
    mDelayFrameLoaderInitialization = true;
164
0
165
0
    mAllowXULXBL = eTriTrue;
166
0
}
167
168
XULDocument::~XULDocument()
169
0
{
170
0
    NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
171
0
        "unreferenced document still waiting for script source to load?");
172
0
173
0
    // Destroy our broadcaster map.
174
0
    delete mBroadcasterMap;
175
0
176
0
    Preferences::UnregisterCallback(XULDocument::DirectionChanged,
177
0
                                    "intl.uidirection", this);
178
0
179
0
    if (mOffThreadCompileStringBuf) {
180
0
      js_free(mOffThreadCompileStringBuf);
181
0
    }
182
0
}
183
184
} // namespace dom
185
} // namespace mozilla
186
187
nsresult
188
NS_NewXULDocument(nsIDocument** result)
189
0
{
190
0
    MOZ_ASSERT(result != nullptr, "null ptr");
191
0
    if (! result)
192
0
        return NS_ERROR_NULL_POINTER;
193
0
194
0
    RefPtr<XULDocument> doc = new XULDocument();
195
0
196
0
    nsresult rv;
197
0
    if (NS_FAILED(rv = doc->Init())) {
198
0
        return rv;
199
0
    }
200
0
201
0
    doc.forget(result);
202
0
    return NS_OK;
203
0
}
204
205
206
namespace mozilla {
207
namespace dom {
208
209
//----------------------------------------------------------------------
210
//
211
// nsISupports interface
212
//
213
214
NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
215
216
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
217
0
    NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
218
0
                 "Shouldn't traverse XULDocument!");
219
0
    // XXX tmp->mContextStack?
220
0
221
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
222
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
223
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
224
0
225
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
226
227
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
228
0
    NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
229
0
    //XXX We should probably unlink all the objects we traverse.
230
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
231
232
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(XULDocument,
233
                                             XMLDocument,
234
                                             nsIStreamLoaderObserver,
235
                                             nsICSSLoaderObserver,
236
                                             nsIOffThreadScriptReceiver)
237
238
239
//----------------------------------------------------------------------
240
//
241
// nsIDocument interface
242
//
243
244
void
245
XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
246
0
{
247
0
    MOZ_ASSERT_UNREACHABLE("Reset");
248
0
}
249
250
void
251
XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
252
                        nsIPrincipal* aPrincipal)
253
0
{
254
0
    MOZ_ASSERT_UNREACHABLE("ResetToURI");
255
0
}
256
257
void
258
XULDocument::SetContentType(const nsAString& aContentType)
259
0
{
260
0
    NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
261
0
                 "xul-documents always has content-type application/vnd.mozilla.xul+xml");
262
0
    // Don't do anything, xul always has the mimetype
263
0
    // application/vnd.mozilla.xul+xml
264
0
}
265
266
// This is called when the master document begins loading, whether it's
267
// being cached or not.
268
nsresult
269
XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
270
                               nsILoadGroup* aLoadGroup,
271
                               nsISupports* aContainer,
272
                               nsIStreamListener **aDocListener,
273
                               bool aReset, nsIContentSink* aSink)
274
0
{
275
0
    if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
276
0
277
0
        nsCOMPtr<nsIURI> uri;
278
0
        nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
279
0
        if (NS_SUCCEEDED(rv)) {
280
0
            nsAutoCString urlspec;
281
0
            rv = uri->GetSpec(urlspec);
282
0
            if (NS_SUCCEEDED(rv)) {
283
0
                MOZ_LOG(gXULLog, LogLevel::Warning,
284
0
                       ("xul: load document '%s'", urlspec.get()));
285
0
            }
286
0
        }
287
0
    }
288
0
    // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
289
0
    // we'll possibly need to reset our content type afterwards.
290
0
    mStillWalking = true;
291
0
    mMayStartLayout = false;
292
0
    mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
293
0
294
0
    mChannel = aChannel;
295
0
296
0
    // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
297
0
    nsresult rv =
298
0
        NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
299
0
    NS_ENSURE_SUCCESS(rv, rv);
300
0
301
0
    mOriginalURI = mDocumentURI;
302
0
303
0
    // Get the document's principal
304
0
    nsCOMPtr<nsIPrincipal> principal;
305
0
    nsContentUtils::GetSecurityManager()->
306
0
        GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
307
0
    principal = MaybeDowngradePrincipal(principal);
308
0
309
0
    ResetStylesheetsToURI(mDocumentURI);
310
0
311
0
    RetrieveRelevantHeaders(aChannel);
312
0
313
0
    // Look in the chrome cache: we've got this puppy loaded
314
0
    // already.
315
0
    nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
316
0
            nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
317
0
            nullptr;
318
0
319
0
    // Same comment as nsChromeProtocolHandler::NewChannel and
320
0
    // XULDocument::ResumeWalk
321
0
    // - Ben Goodger
322
0
    //
323
0
    // We don't abort on failure here because there are too many valid
324
0
    // cases that can return failure, and the null-ness of |proto| is enough
325
0
    // to trigger the fail-safe parse-from-disk solution. Example failure cases
326
0
    // (for reference) include:
327
0
    //
328
0
    // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
329
0
    //                         parse from disk
330
0
    // other: the startup cache file could not be found, probably
331
0
    //        due to being accessed before a profile has been selected (e.g.
332
0
    //        loading chrome for the profile manager itself). This must be
333
0
    //        parsed from disk.
334
0
335
0
    if (proto) {
336
0
        // If we're racing with another document to load proto, wait till the
337
0
        // load has finished loading before trying to add cloned style sheets.
338
0
        // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
339
0
        // find all racing documents and notify them via OnPrototypeLoadDone,
340
0
        // which will add style sheet clones to each document.
341
0
        bool loaded;
342
0
        rv = proto->AwaitLoadDone(this, &loaded);
343
0
        if (NS_FAILED(rv)) return rv;
344
0
345
0
        mCurrentPrototype = proto;
346
0
347
0
        // Set up the right principal on ourselves.
348
0
        SetPrincipal(proto->DocumentPrincipal());
349
0
350
0
        // We need a listener, even if proto is not yet loaded, in which
351
0
        // event the listener's OnStopRequest method does nothing, and all
352
0
        // the interesting work happens below XULDocument::EndLoad, from
353
0
        // the call there to mCurrentPrototype->NotifyLoadDone().
354
0
        *aDocListener = new CachedChromeStreamListener(this, loaded);
355
0
    }
356
0
    else {
357
0
        bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
358
0
        bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
359
0
360
0
361
0
        // It's just a vanilla document load. Create a parser to deal
362
0
        // with the stream n' stuff.
363
0
364
0
        nsCOMPtr<nsIParser> parser;
365
0
        rv = PrepareToLoadPrototype(mDocumentURI, aCommand, principal,
366
0
                                    getter_AddRefs(parser));
367
0
        if (NS_FAILED(rv)) return rv;
368
0
369
0
        // Predicate mIsWritingFastLoad on the XUL cache being enabled,
370
0
        // so we don't have to re-check whether the cache is enabled all
371
0
        // the time.
372
0
        mIsWritingFastLoad = useXULCache;
373
0
374
0
        nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
375
0
        NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
376
0
        if (NS_FAILED(rv)) return rv;
377
0
378
0
        *aDocListener = listener;
379
0
380
0
        parser->Parse(mDocumentURI);
381
0
382
0
        // Put the current prototype, created under PrepareToLoad, into the
383
0
        // XUL prototype cache now.  We can't do this under PrepareToLoad or
384
0
        // overlay loading will break; search for PutPrototype in ResumeWalk
385
0
        // and see the comment there.
386
0
        if (fillXULCache) {
387
0
            nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
388
0
        }
389
0
    }
390
0
391
0
    NS_IF_ADDREF(*aDocListener);
392
0
    return NS_OK;
393
0
}
394
395
// This gets invoked after the prototype for this document is fully built in the
396
// content sink.
397
void
398
XULDocument::EndLoad()
399
0
{
400
0
    nsresult rv;
401
0
402
0
    // Whack the prototype document into the cache so that the next
403
0
    // time somebody asks for it, they don't need to load it by hand.
404
0
405
0
    nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
406
0
    bool isChrome = IsChromeURI(uri);
407
0
408
0
    // Remember if the XUL cache is on
409
0
    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
410
0
411
0
    if (isChrome && useXULCache) {
412
0
        // If it's a chrome prototype document, then notify any
413
0
        // documents that raced to load the prototype, and awaited
414
0
        // its load completion via proto->AwaitLoadDone().
415
0
        rv = mCurrentPrototype->NotifyLoadDone();
416
0
        if (NS_FAILED(rv)) return;
417
0
    }
418
0
419
0
    OnPrototypeLoadDone(true);
420
0
    if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
421
0
        nsAutoCString urlspec;
422
0
        rv = uri->GetSpec(urlspec);
423
0
        if (NS_SUCCEEDED(rv)) {
424
0
            MOZ_LOG(gXULLog, LogLevel::Warning,
425
0
                   ("xul: Finished loading document '%s'", urlspec.get()));
426
0
        }
427
0
    }
428
0
}
429
430
nsresult
431
XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
432
0
{
433
0
    nsresult rv;
434
0
435
0
    rv = PrepareToWalk();
436
0
    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
437
0
    if (NS_FAILED(rv)) return rv;
438
0
439
0
    if (aResumeWalk) {
440
0
        rv = ResumeWalk();
441
0
    }
442
0
    return rv;
443
0
}
444
445
static void
446
ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
447
0
{
448
0
    BroadcasterMapEntry* entry =
449
0
        static_cast<BroadcasterMapEntry*>(aEntry);
450
0
    for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
451
0
        delete entry->mListeners[i];
452
0
    }
453
0
    entry->mListeners.Clear();
454
0
455
0
    // N.B. that we need to manually run the dtor because we
456
0
    // constructed the nsTArray object in-place.
457
0
    entry->mListeners.~nsTArray<BroadcastListener*>();
458
0
}
459
460
static bool
461
CanBroadcast(int32_t aNameSpaceID, nsAtom* aAttribute)
462
0
{
463
0
    // Don't push changes to the |id|, |persist|, |command| or
464
0
    // |observes| attribute.
465
0
    if (aNameSpaceID == kNameSpaceID_None) {
466
0
        if ((aAttribute == nsGkAtoms::id) ||
467
0
            (aAttribute == nsGkAtoms::persist) ||
468
0
            (aAttribute == nsGkAtoms::command) ||
469
0
            (aAttribute == nsGkAtoms::observes)) {
470
0
            return false;
471
0
        }
472
0
    }
473
0
    return true;
474
0
}
475
476
struct nsAttrNameInfo
477
{
478
  nsAttrNameInfo(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix) :
479
0
    mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
480
  nsAttrNameInfo(const nsAttrNameInfo& aOther) :
481
    mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
482
0
    mPrefix(aOther.mPrefix) {}
483
  int32_t           mNamespaceID;
484
  RefPtr<nsAtom> mName;
485
  RefPtr<nsAtom> mPrefix;
486
};
487
488
void
489
XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
490
                                          Element *aListener,
491
                                          const nsAString &aAttr)
492
0
{
493
0
    if (!nsContentUtils::IsSafeToRunScript()) {
494
0
        nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
495
0
                                               aAttr);
496
0
        mDelayedBroadcasters.AppendElement(delayedUpdate);
497
0
        MaybeBroadcast();
498
0
        return;
499
0
    }
500
0
    bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
501
0
502
0
    if (aAttr.EqualsLiteral("*")) {
503
0
        uint32_t count = aBroadcaster->GetAttrCount();
504
0
        nsTArray<nsAttrNameInfo> attributes(count);
505
0
        for (uint32_t i = 0; i < count; ++i) {
506
0
            const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
507
0
            int32_t nameSpaceID = attrName->NamespaceID();
508
0
            nsAtom* name = attrName->LocalName();
509
0
510
0
            // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
511
0
            if (! CanBroadcast(nameSpaceID, name))
512
0
                continue;
513
0
514
0
            attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
515
0
                                                    attrName->GetPrefix()));
516
0
        }
517
0
518
0
        count = attributes.Length();
519
0
        while (count-- > 0) {
520
0
            int32_t nameSpaceID = attributes[count].mNamespaceID;
521
0
            nsAtom* name = attributes[count].mName;
522
0
            nsAutoString value;
523
0
            if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
524
0
              aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
525
0
                                 value, notify);
526
0
            }
527
0
528
#if 0
529
            // XXX we don't fire the |onbroadcast| handler during
530
            // initial hookup: doing so would potentially run the
531
            // |onbroadcast| handler before the |onload| handler,
532
            // which could define JS properties that mask XBL
533
            // properties, etc.
534
            ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
535
#endif
536
        }
537
0
    }
538
0
    else {
539
0
        // Find out if the attribute is even present at all.
540
0
        RefPtr<nsAtom> name = NS_Atomize(aAttr);
541
0
542
0
        nsAutoString value;
543
0
        if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
544
0
            aListener->SetAttr(kNameSpaceID_None, name, value, notify);
545
0
        } else {
546
0
            aListener->UnsetAttr(kNameSpaceID_None, name, notify);
547
0
        }
548
0
549
#if 0
550
        // XXX we don't fire the |onbroadcast| handler during initial
551
        // hookup: doing so would potentially run the |onbroadcast|
552
        // handler before the |onload| handler, which could define JS
553
        // properties that mask XBL properties, etc.
554
        ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
555
#endif
556
    }
557
0
}
558
559
void
560
XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
561
                                     const nsAString& aAttr, ErrorResult& aRv)
562
0
{
563
0
    nsresult rv =
564
0
        nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
565
0
566
0
    if (NS_FAILED(rv)) {
567
0
        aRv.Throw(rv);
568
0
        return;
569
0
    }
570
0
571
0
    rv = nsContentUtils::CheckSameOrigin(this, &aListener);
572
0
573
0
    if (NS_FAILED(rv)) {
574
0
        aRv.Throw(rv);
575
0
        return;
576
0
    }
577
0
578
0
    static const PLDHashTableOps gOps = {
579
0
        PLDHashTable::HashVoidPtrKeyStub,
580
0
        PLDHashTable::MatchEntryStub,
581
0
        PLDHashTable::MoveEntryStub,
582
0
        ClearBroadcasterMapEntry,
583
0
        nullptr
584
0
    };
585
0
586
0
    if (! mBroadcasterMap) {
587
0
        mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
588
0
    }
589
0
590
0
    auto entry = static_cast<BroadcasterMapEntry*>
591
0
                            (mBroadcasterMap->Search(&aBroadcaster));
592
0
    if (!entry) {
593
0
        entry = static_cast<BroadcasterMapEntry*>
594
0
                           (mBroadcasterMap->Add(&aBroadcaster, fallible));
595
0
596
0
        if (! entry) {
597
0
            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
598
0
            return;
599
0
        }
600
0
601
0
        entry->mBroadcaster = &aBroadcaster;
602
0
603
0
        // N.B. placement new to construct the nsTArray object in-place
604
0
        new (&entry->mListeners) nsTArray<BroadcastListener*>();
605
0
    }
606
0
607
0
    // Only add the listener if it's not there already!
608
0
    RefPtr<nsAtom> attr = NS_Atomize(aAttr);
609
0
610
0
    for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
611
0
        BroadcastListener* bl = entry->mListeners[i];
612
0
        nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
613
0
614
0
        if (blListener == &aListener && bl->mAttribute == attr)
615
0
            return;
616
0
    }
617
0
618
0
    BroadcastListener* bl = new BroadcastListener;
619
0
    bl->mListener  = do_GetWeakReference(&aListener);
620
0
    bl->mAttribute = attr;
621
0
622
0
    entry->mListeners.AppendElement(bl);
623
0
624
0
    SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
625
0
}
626
627
void
628
XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
629
                                        Element& aListener,
630
                                        const nsAString& aAttr)
631
0
{
632
0
    // If we haven't added any broadcast listeners, then there sure
633
0
    // aren't any to remove.
634
0
    if (! mBroadcasterMap)
635
0
        return;
636
0
637
0
    auto entry = static_cast<BroadcasterMapEntry*>
638
0
                            (mBroadcasterMap->Search(&aBroadcaster));
639
0
    if (entry) {
640
0
        RefPtr<nsAtom> attr = NS_Atomize(aAttr);
641
0
        for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
642
0
            BroadcastListener* bl = entry->mListeners[i];
643
0
            nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
644
0
645
0
            if (blListener == &aListener && bl->mAttribute == attr) {
646
0
                entry->mListeners.RemoveElementAt(i);
647
0
                delete bl;
648
0
649
0
                if (entry->mListeners.IsEmpty())
650
0
                    mBroadcasterMap->RemoveEntry(entry);
651
0
652
0
                break;
653
0
            }
654
0
        }
655
0
    }
656
0
}
657
658
nsresult
659
XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
660
                                          Element* aListener,
661
                                          nsAtom* aAttr)
662
0
{
663
0
    // Now we execute the onchange handler in the context of the
664
0
    // observer. We need to find the observer in order to
665
0
    // execute the handler.
666
0
667
0
    for (nsIContent* child = aListener->GetFirstChild();
668
0
         child;
669
0
         child = child->GetNextSibling()) {
670
0
671
0
        // Look for an <observes> element beneath the listener. This
672
0
        // ought to have an |element| attribute that refers to
673
0
        // aBroadcaster, and an |attribute| element that tells us what
674
0
        // attriubtes we're listening for.
675
0
        if (!child->IsXULElement(nsGkAtoms::observes))
676
0
            continue;
677
0
678
0
        // Is this the element that was listening to us?
679
0
        nsAutoString listeningToID;
680
0
        child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
681
0
682
0
        nsAutoString broadcasterID;
683
0
        aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
684
0
685
0
        if (listeningToID != broadcasterID)
686
0
            continue;
687
0
688
0
        // We are observing the broadcaster, but is this the right
689
0
        // attribute?
690
0
        nsAutoString listeningToAttribute;
691
0
        child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
692
0
                                    listeningToAttribute);
693
0
694
0
        if (!aAttr->Equals(listeningToAttribute) &&
695
0
            !listeningToAttribute.EqualsLiteral("*")) {
696
0
            continue;
697
0
        }
698
0
699
0
        // This is the right <observes> element. Execute the
700
0
        // |onbroadcast| event handler
701
0
        WidgetEvent event(true, eXULBroadcast);
702
0
703
0
        RefPtr<nsPresContext> presContext = GetPresContext();
704
0
        if (presContext) {
705
0
          // Handle the DOM event
706
0
          nsEventStatus status = nsEventStatus_eIgnore;
707
0
          EventDispatcher::Dispatch(child, presContext, &event, nullptr,
708
0
                                    &status);
709
0
        }
710
0
    }
711
0
712
0
    return NS_OK;
713
0
}
714
715
static bool
716
ShouldPersistAttribute(Element* aElement, nsAtom* aAttribute)
717
0
{
718
0
    if (aElement->IsXULElement(nsGkAtoms::window)) {
719
0
        // This is not an element of the top document, its owner is
720
0
        // not an nsXULWindow. Persist it.
721
0
        if (aElement->OwnerDoc()->GetParentDocument()) {
722
0
            return true;
723
0
        }
724
0
        // The following attributes of xul:window should be handled in
725
0
        // nsXULWindow::SavePersistentAttributes instead of here.
726
0
        if (aAttribute == nsGkAtoms::screenX ||
727
0
            aAttribute == nsGkAtoms::screenY ||
728
0
            aAttribute == nsGkAtoms::width ||
729
0
            aAttribute == nsGkAtoms::height ||
730
0
            aAttribute == nsGkAtoms::sizemode) {
731
0
            return false;
732
0
        }
733
0
    }
734
0
    return true;
735
0
}
736
737
void
738
XULDocument::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
739
                              nsAtom* aAttribute, int32_t aModType,
740
                              const nsAttrValue* aOldValue)
741
0
{
742
0
    NS_ASSERTION(aElement->OwnerDoc() == this, "unexpected doc");
743
0
744
0
    // Might not need this, but be safe for now.
745
0
    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
746
0
747
0
    // Synchronize broadcast listeners
748
0
    if (mBroadcasterMap &&
749
0
        CanBroadcast(aNameSpaceID, aAttribute)) {
750
0
        auto entry = static_cast<BroadcasterMapEntry*>
751
0
                                (mBroadcasterMap->Search(aElement));
752
0
753
0
        if (entry) {
754
0
            // We've got listeners: push the value.
755
0
            nsAutoString value;
756
0
            bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
757
0
758
0
            for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
759
0
                BroadcastListener* bl = entry->mListeners[i];
760
0
                if ((bl->mAttribute == aAttribute) ||
761
0
                    (bl->mAttribute == nsGkAtoms::_asterisk)) {
762
0
                    nsCOMPtr<Element> listenerEl
763
0
                        = do_QueryReferent(bl->mListener);
764
0
                    if (listenerEl) {
765
0
                        nsAutoString currentValue;
766
0
                        bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
767
0
                                                           aAttribute,
768
0
                                                           currentValue);
769
0
                        // We need to update listener only if we're
770
0
                        // (1) removing an existing attribute,
771
0
                        // (2) adding a new attribute or
772
0
                        // (3) changing the value of an attribute.
773
0
                        bool needsAttrChange =
774
0
                            attrSet != hasAttr || !value.Equals(currentValue);
775
0
                        nsDelayedBroadcastUpdate delayedUpdate(aElement,
776
0
                                                               listenerEl,
777
0
                                                               aAttribute,
778
0
                                                               value,
779
0
                                                               attrSet,
780
0
                                                               needsAttrChange);
781
0
782
0
                        size_t index =
783
0
                            mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
784
0
                                0, nsDelayedBroadcastUpdate::Comparator());
785
0
                        if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
786
0
                            if (mHandlingDelayedAttrChange) {
787
0
                                NS_WARNING("Broadcasting loop!");
788
0
                                continue;
789
0
                            }
790
0
                            mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
791
0
                        }
792
0
793
0
                        mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
794
0
                    }
795
0
                }
796
0
            }
797
0
        }
798
0
    }
799
0
800
0
    // checks for modifications in broadcasters
801
0
    CheckBroadcasterHookup(aElement);
802
0
803
0
    // See if there is anything we need to persist in the localstore.
804
0
    //
805
0
    // XXX Namespace handling broken :-(
806
0
    nsAutoString persist;
807
0
    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
808
0
    // Persistence of attributes of xul:window is handled in nsXULWindow.
809
0
    if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
810
0
        // XXXldb This should check that it's a token, not just a substring.
811
0
        persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
812
0
      nsContentUtils::AddScriptRunner(
813
0
        NewRunnableMethod<Element*, int32_t, nsAtom*>(
814
0
          "dom::XULDocument::Persist",
815
0
          this,
816
0
          &XULDocument::Persist,
817
0
          aElement,
818
0
          kNameSpaceID_None,
819
0
          aAttribute));
820
0
    }
821
0
}
822
823
void
824
XULDocument::ContentAppended(nsIContent* aFirstNewContent)
825
0
{
826
0
    NS_ASSERTION(aFirstNewContent->OwnerDoc() == this, "unexpected doc");
827
0
828
0
    // Might not need this, but be safe for now.
829
0
    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
830
0
831
0
    // Update our element map
832
0
    nsresult rv = NS_OK;
833
0
    for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
834
0
         cur = cur->GetNextSibling()) {
835
0
        rv = AddSubtreeToDocument(cur);
836
0
    }
837
0
}
838
839
void
840
XULDocument::ContentInserted(nsIContent* aChild)
841
0
{
842
0
    NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
843
0
844
0
    // Might not need this, but be safe for now.
845
0
    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
846
0
847
0
    AddSubtreeToDocument(aChild);
848
0
}
849
850
void
851
XULDocument::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
852
0
{
853
0
    NS_ASSERTION(aChild->OwnerDoc() == this, "unexpected doc");
854
0
855
0
    // Might not need this, but be safe for now.
856
0
    nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
857
0
858
0
    RemoveSubtreeFromDocument(aChild);
859
0
}
860
861
//----------------------------------------------------------------------
862
//
863
// nsIDocument interface
864
//
865
866
867
void
868
XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
869
                     nsAtom* aAttribute)
870
0
{
871
0
    // For non-chrome documents, persistance is simply broken
872
0
    if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
873
0
        return;
874
0
875
0
    if (!mLocalStore) {
876
0
        mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
877
0
        if (NS_WARN_IF(!mLocalStore)) {
878
0
            return;
879
0
        }
880
0
    }
881
0
882
0
    nsAutoString id;
883
0
884
0
    aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
885
0
    nsAtomString attrstr(aAttribute);
886
0
887
0
    nsAutoString valuestr;
888
0
    aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
889
0
890
0
    nsAutoCString utf8uri;
891
0
    nsresult rv = mDocumentURI->GetSpec(utf8uri);
892
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
893
0
        return;
894
0
    }
895
0
    NS_ConvertUTF8toUTF16 uri(utf8uri);
896
0
897
0
    bool hasAttr;
898
0
    rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
899
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
900
0
        return;
901
0
    }
902
0
903
0
    if (hasAttr && valuestr.IsEmpty()) {
904
0
        mLocalStore->RemoveValue(uri, id, attrstr);
905
0
        return;
906
0
    }
907
0
908
0
    // Persisting attributes to top level windows is handled by nsXULWindow.
909
0
    if (aElement->IsXULElement(nsGkAtoms::window)) {
910
0
        if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
911
0
           return;
912
0
        }
913
0
    }
914
0
915
0
    mLocalStore->SetValue(uri, id, attrstr, valuestr);
916
0
}
917
918
nsresult
919
XULDocument::AddElementToDocumentPre(Element* aElement)
920
0
{
921
0
    // Do a bunch of work that's necessary when an element gets added
922
0
    // to the XUL Document.
923
0
    nsresult rv;
924
0
925
0
    // 1. Add the element to the id map, since it seems this can be
926
0
    // called when creating elements from prototypes.
927
0
    nsAtom* id = aElement->GetID();
928
0
    if (id) {
929
0
        // FIXME: Shouldn't BindToTree take care of this?
930
0
        nsAutoScriptBlocker scriptBlocker;
931
0
        AddToIdTable(aElement, id);
932
0
    }
933
0
934
0
    // 2. Check for a broadcaster hookup attribute, in which case
935
0
    // we'll hook the node up as a listener on a broadcaster.
936
0
    rv = CheckBroadcasterHookup(aElement);
937
0
    if (NS_FAILED(rv)) return rv;
938
0
939
0
    return NS_OK;
940
0
}
941
942
nsresult
943
XULDocument::AddElementToDocumentPost(Element* aElement)
944
0
{
945
0
    if (aElement == GetRootElement()) {
946
0
        ResetDocumentDirection();
947
0
    }
948
0
949
0
    if (aElement->IsXULElement(nsGkAtoms::link)) {
950
0
        LocalizationLinkAdded(aElement);
951
0
    } else if (aElement->IsXULElement(nsGkAtoms::linkset)) {
952
0
        OnL10nResourceContainerParsed();
953
0
    }
954
0
955
0
    return NS_OK;
956
0
}
957
958
nsresult
959
XULDocument::AddSubtreeToDocument(nsIContent* aContent)
960
0
{
961
0
    NS_ASSERTION(aContent->GetUncomposedDoc() == this, "Element not in doc!");
962
0
    // From here on we only care about elements.
963
0
    Element* aElement = Element::FromNode(aContent);
964
0
    if (!aElement) {
965
0
        return NS_OK;
966
0
    }
967
0
968
0
    // Do pre-order addition magic
969
0
    nsresult rv = AddElementToDocumentPre(aElement);
970
0
    if (NS_FAILED(rv)) return rv;
971
0
972
0
    // Recurse to children
973
0
    for (nsIContent* child = aElement->GetLastChild();
974
0
         child;
975
0
         child = child->GetPreviousSibling()) {
976
0
977
0
        rv = AddSubtreeToDocument(child);
978
0
        if (NS_FAILED(rv))
979
0
            return rv;
980
0
    }
981
0
982
0
    // Do post-order addition magic
983
0
    return AddElementToDocumentPost(aElement);
984
0
}
985
986
nsresult
987
XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
988
0
{
989
0
    // From here on we only care about elements.
990
0
    Element* aElement = Element::FromNode(aContent);
991
0
    if (!aElement) {
992
0
        return NS_OK;
993
0
    }
994
0
995
0
    // Do a bunch of cleanup to remove an element from the XUL
996
0
    // document.
997
0
    nsresult rv;
998
0
999
0
    // Remove any children from the document.
1000
0
    for (nsIContent* child = aElement->GetLastChild();
1001
0
         child;
1002
0
         child = child->GetPreviousSibling()) {
1003
0
1004
0
        rv = RemoveSubtreeFromDocument(child);
1005
0
        if (NS_FAILED(rv))
1006
0
            return rv;
1007
0
    }
1008
0
1009
0
    // Remove the element from the id map, since we added it in
1010
0
    // AddElementToDocumentPre().
1011
0
    nsAtom* id = aElement->GetID();
1012
0
    if (id) {
1013
0
        // FIXME: Shouldn't UnbindFromTree take care of this?
1014
0
        nsAutoScriptBlocker scriptBlocker;
1015
0
        RemoveFromIdTable(aElement, id);
1016
0
    }
1017
0
1018
0
    // Remove the element from our broadcaster map, since it is no longer
1019
0
    // in the document.
1020
0
    nsCOMPtr<Element> broadcaster, listener;
1021
0
    nsAutoString attribute, broadcasterID;
1022
0
    rv = FindBroadcaster(aElement, getter_AddRefs(listener),
1023
0
                         broadcasterID, attribute, getter_AddRefs(broadcaster));
1024
0
    if (rv == NS_FINDBROADCASTER_FOUND) {
1025
0
        RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
1026
0
    }
1027
0
1028
0
    return NS_OK;
1029
0
}
1030
1031
//----------------------------------------------------------------------
1032
//
1033
// nsINode interface
1034
//
1035
1036
nsresult
1037
XULDocument::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) const
1038
0
{
1039
0
    // We don't allow cloning of a XUL document
1040
0
    *aResult = nullptr;
1041
0
    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
1042
0
}
1043
1044
1045
//----------------------------------------------------------------------
1046
//
1047
// Implementation methods
1048
//
1049
1050
nsresult
1051
XULDocument::Init()
1052
0
{
1053
0
    nsresult rv = XMLDocument::Init();
1054
0
    NS_ENSURE_SUCCESS(rv, rv);
1055
0
1056
0
    if (gRefCnt++ == 0) {
1057
0
        // ensure that the XUL prototype cache is instantiated successfully,
1058
0
        // so that we can use nsXULPrototypeCache::GetInstance() without
1059
0
        // null-checks in the rest of the class.
1060
0
        nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
1061
0
        if (!cache) {
1062
0
          NS_ERROR("Could not instantiate nsXULPrototypeCache");
1063
0
          return NS_ERROR_FAILURE;
1064
0
        }
1065
0
    }
1066
0
1067
0
    Preferences::RegisterCallback(XULDocument::DirectionChanged,
1068
0
                                  "intl.uidirection", this);
1069
0
1070
0
    return NS_OK;
1071
0
}
1072
1073
1074
nsresult
1075
XULDocument::StartLayout(void)
1076
0
{
1077
0
    mMayStartLayout = true;
1078
0
    nsCOMPtr<nsIPresShell> shell = GetShell();
1079
0
    if (shell) {
1080
0
        // Resize-reflow this time
1081
0
        nsPresContext *cx = shell->GetPresContext();
1082
0
        NS_ASSERTION(cx != nullptr, "no pres context");
1083
0
        if (! cx)
1084
0
            return NS_ERROR_UNEXPECTED;
1085
0
1086
0
        nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
1087
0
        NS_ASSERTION(docShell != nullptr, "container is not a docshell");
1088
0
        if (! docShell)
1089
0
            return NS_ERROR_UNEXPECTED;
1090
0
1091
0
        nsresult rv = shell->Initialize();
1092
0
        NS_ENSURE_SUCCESS(rv, rv);
1093
0
    }
1094
0
1095
0
    return NS_OK;
1096
0
}
1097
1098
nsresult
1099
XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
1100
                                    nsIPrincipal* aDocumentPrincipal,
1101
                                    nsIParser** aResult)
1102
0
{
1103
0
    nsresult rv;
1104
0
1105
0
    // Create a new prototype document.
1106
0
    rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
1107
0
    if (NS_FAILED(rv)) return rv;
1108
0
1109
0
    rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
1110
0
    if (NS_FAILED(rv)) {
1111
0
        mCurrentPrototype = nullptr;
1112
0
        return rv;
1113
0
    }
1114
0
1115
0
    SetPrincipal(aDocumentPrincipal);
1116
0
1117
0
    // Create a XUL content sink, a parser, and kick off a load for
1118
0
    // the document.
1119
0
    RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
1120
0
1121
0
    rv = sink->Init(this, mCurrentPrototype);
1122
0
    NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
1123
0
    if (NS_FAILED(rv)) return rv;
1124
0
1125
0
    nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
1126
0
    NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
1127
0
    if (NS_FAILED(rv)) return rv;
1128
0
1129
0
    parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
1130
0
                       eViewSource);
1131
0
1132
0
    parser->SetDocumentCharset(UTF_8_ENCODING,
1133
0
                               kCharsetFromDocTypeDefault);
1134
0
    parser->SetContentSink(sink); // grabs a reference to the parser
1135
0
1136
0
    parser.forget(aResult);
1137
0
    return NS_OK;
1138
0
}
1139
1140
1141
nsresult
1142
XULDocument::ApplyPersistentAttributes()
1143
0
{
1144
0
    // For non-chrome documents, persistance is simply broken
1145
0
    if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
1146
0
        return NS_ERROR_NOT_AVAILABLE;
1147
0
1148
0
    // Add all of the 'persisted' attributes into the content
1149
0
    // model.
1150
0
    if (!mLocalStore) {
1151
0
        mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
1152
0
        if (NS_WARN_IF(!mLocalStore)) {
1153
0
            return NS_ERROR_NOT_INITIALIZED;
1154
0
        }
1155
0
    }
1156
0
1157
0
    mApplyingPersistedAttrs = true;
1158
0
    ApplyPersistentAttributesInternal();
1159
0
    mApplyingPersistedAttrs = false;
1160
0
1161
0
    return NS_OK;
1162
0
}
1163
1164
1165
nsresult
1166
XULDocument::ApplyPersistentAttributesInternal()
1167
0
{
1168
0
    nsCOMArray<Element> elements;
1169
0
1170
0
    nsAutoCString utf8uri;
1171
0
    nsresult rv = mDocumentURI->GetSpec(utf8uri);
1172
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1173
0
        return rv;
1174
0
    }
1175
0
    NS_ConvertUTF8toUTF16 uri(utf8uri);
1176
0
1177
0
    // Get a list of element IDs for which persisted values are available
1178
0
    nsCOMPtr<nsIStringEnumerator> ids;
1179
0
    rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
1180
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1181
0
        return rv;
1182
0
    }
1183
0
1184
0
    while (1) {
1185
0
        bool hasmore = false;
1186
0
        ids->HasMore(&hasmore);
1187
0
        if (!hasmore) {
1188
0
            break;
1189
0
        }
1190
0
1191
0
        nsAutoString id;
1192
0
        ids->GetNext(id);
1193
0
1194
0
        nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(id);
1195
0
        if (!entry) {
1196
0
            continue;
1197
0
        }
1198
0
1199
0
        // We want to hold strong refs to the elements while applying
1200
0
        // persistent attributes, just in case.
1201
0
        elements.Clear();
1202
0
        elements.SetCapacity(entry->GetIdElements().Length());
1203
0
        for (Element* element : entry->GetIdElements()) {
1204
0
            elements.AppendObject(element);
1205
0
        }
1206
0
        if (elements.IsEmpty()) {
1207
0
            continue;
1208
0
        }
1209
0
1210
0
        rv = ApplyPersistentAttributesToElements(id, elements);
1211
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
1212
0
            return rv;
1213
0
        }
1214
0
    }
1215
0
1216
0
    return NS_OK;
1217
0
}
1218
1219
nsresult
1220
XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
1221
                                                 nsCOMArray<Element>& aElements)
1222
0
{
1223
0
    nsAutoCString utf8uri;
1224
0
    nsresult rv = mDocumentURI->GetSpec(utf8uri);
1225
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1226
0
        return rv;
1227
0
    }
1228
0
    NS_ConvertUTF8toUTF16 uri(utf8uri);
1229
0
1230
0
    // Get a list of attributes for which persisted values are available
1231
0
    nsCOMPtr<nsIStringEnumerator> attrs;
1232
0
    rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
1233
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1234
0
        return rv;
1235
0
    }
1236
0
1237
0
    while (1) {
1238
0
        bool hasmore = PR_FALSE;
1239
0
        attrs->HasMore(&hasmore);
1240
0
        if (!hasmore) {
1241
0
            break;
1242
0
        }
1243
0
1244
0
        nsAutoString attrstr;
1245
0
        attrs->GetNext(attrstr);
1246
0
1247
0
        nsAutoString value;
1248
0
        rv = mLocalStore->GetValue(uri, aID, attrstr, value);
1249
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
1250
0
            return rv;
1251
0
        }
1252
0
1253
0
        RefPtr<nsAtom> attr = NS_Atomize(attrstr);
1254
0
        if (NS_WARN_IF(!attr)) {
1255
0
            return NS_ERROR_OUT_OF_MEMORY;
1256
0
        }
1257
0
1258
0
        uint32_t cnt = aElements.Count();
1259
0
        for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
1260
0
            RefPtr<Element> element = aElements.SafeObjectAt(i);
1261
0
            if (!element) {
1262
0
                 continue;
1263
0
            }
1264
0
1265
0
            // Applying persistent attributes to top level windows is handled
1266
0
            // by nsXULWindow.
1267
0
            if (element->IsXULElement(nsGkAtoms::window)) {
1268
0
                if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
1269
0
                    continue;
1270
0
                }
1271
0
            }
1272
0
1273
0
            Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);
1274
0
        }
1275
0
    }
1276
0
1277
0
    return NS_OK;
1278
0
}
1279
1280
void
1281
XULDocument::TraceProtos(JSTracer* aTrc)
1282
0
{
1283
0
    uint32_t i, count = mPrototypes.Length();
1284
0
    for (i = 0; i < count; ++i) {
1285
0
        mPrototypes[i]->TraceProtos(aTrc);
1286
0
    }
1287
0
1288
0
    if (mCurrentPrototype) {
1289
0
        mCurrentPrototype->TraceProtos(aTrc);
1290
0
    }
1291
0
}
1292
1293
//----------------------------------------------------------------------
1294
//
1295
// XULDocument::ContextStack
1296
//
1297
1298
XULDocument::ContextStack::ContextStack()
1299
    : mTop(nullptr), mDepth(0)
1300
0
{
1301
0
}
1302
1303
XULDocument::ContextStack::~ContextStack()
1304
0
{
1305
0
    while (mTop) {
1306
0
        Entry* doomed = mTop;
1307
0
        mTop = mTop->mNext;
1308
0
        NS_IF_RELEASE(doomed->mElement);
1309
0
        delete doomed;
1310
0
    }
1311
0
}
1312
1313
nsresult
1314
XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
1315
                                nsIContent* aElement)
1316
0
{
1317
0
    Entry* entry = new Entry;
1318
0
    entry->mPrototype = aPrototype;
1319
0
    entry->mElement   = aElement;
1320
0
    NS_IF_ADDREF(entry->mElement);
1321
0
    entry->mIndex     = 0;
1322
0
1323
0
    entry->mNext = mTop;
1324
0
    mTop = entry;
1325
0
1326
0
    ++mDepth;
1327
0
    return NS_OK;
1328
0
}
1329
1330
nsresult
1331
XULDocument::ContextStack::Pop()
1332
0
{
1333
0
    if (mDepth == 0)
1334
0
        return NS_ERROR_UNEXPECTED;
1335
0
1336
0
    Entry* doomed = mTop;
1337
0
    mTop = mTop->mNext;
1338
0
    --mDepth;
1339
0
1340
0
    NS_IF_RELEASE(doomed->mElement);
1341
0
    delete doomed;
1342
0
    return NS_OK;
1343
0
}
1344
1345
nsresult
1346
XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
1347
                                nsIContent** aElement,
1348
                                int32_t* aIndex)
1349
0
{
1350
0
    if (mDepth == 0)
1351
0
        return NS_ERROR_UNEXPECTED;
1352
0
1353
0
    *aPrototype = mTop->mPrototype;
1354
0
    *aElement   = mTop->mElement;
1355
0
    NS_IF_ADDREF(*aElement);
1356
0
    *aIndex     = mTop->mIndex;
1357
0
1358
0
    return NS_OK;
1359
0
}
1360
1361
1362
nsresult
1363
XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
1364
0
{
1365
0
    if (mDepth == 0)
1366
0
        return NS_ERROR_UNEXPECTED;
1367
0
1368
0
    mTop->mIndex = aIndex;
1369
0
    return NS_OK;
1370
0
}
1371
1372
1373
//----------------------------------------------------------------------
1374
//
1375
// Content model walking routines
1376
//
1377
1378
nsresult
1379
XULDocument::PrepareToWalk()
1380
0
{
1381
0
    // Prepare to walk the mCurrentPrototype
1382
0
    nsresult rv;
1383
0
1384
0
    // Keep an owning reference to the prototype document so that its
1385
0
    // elements aren't yanked from beneath us.
1386
0
    mPrototypes.AppendElement(mCurrentPrototype);
1387
0
1388
0
    // Get the prototype's root element and initialize the context
1389
0
    // stack for the prototype walk.
1390
0
    nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
1391
0
1392
0
    if (! proto) {
1393
0
        if (MOZ_LOG_TEST(gXULLog, LogLevel::Error)) {
1394
0
            nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
1395
0
1396
0
            nsAutoCString urlspec;
1397
0
            rv = url->GetSpec(urlspec);
1398
0
            if (NS_FAILED(rv)) return rv;
1399
0
1400
0
            MOZ_LOG(gXULLog, LogLevel::Error,
1401
0
                   ("xul: error parsing '%s'", urlspec.get()));
1402
0
        }
1403
0
1404
0
        return NS_OK;
1405
0
    }
1406
0
1407
0
    nsINode* nodeToInsertBefore = nsINode::GetFirstChild();
1408
0
1409
0
    const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
1410
0
        mCurrentPrototype->GetProcessingInstructions();
1411
0
1412
0
    uint32_t total = processingInstructions.Length();
1413
0
    for (uint32_t i = 0; i < total; ++i) {
1414
0
        rv = CreateAndInsertPI(processingInstructions[i],
1415
0
                               this, nodeToInsertBefore);
1416
0
        if (NS_FAILED(rv)) return rv;
1417
0
    }
1418
0
1419
0
    // Do one-time initialization.
1420
0
    RefPtr<Element> root;
1421
0
1422
0
    // Add the root element
1423
0
    rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
1424
0
    if (NS_FAILED(rv)) return rv;
1425
0
1426
0
    rv = AppendChildTo(root, false);
1427
0
    if (NS_FAILED(rv)) return rv;
1428
0
1429
0
    // Block onload until we've finished building the complete
1430
0
    // document content model.
1431
0
    BlockOnload();
1432
0
1433
0
    nsContentUtils::AddScriptRunner(
1434
0
        new nsDocElementCreatedNotificationRunner(this));
1435
0
1436
0
    // There'd better not be anything on the context stack at this
1437
0
    // point! This is the basis case for our "induction" in
1438
0
    // ResumeWalk(), below, which'll assume that there's always a
1439
0
    // content element on the context stack if we're in the document.
1440
0
    NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
1441
0
    if (mContextStack.Depth() != 0)
1442
0
        return NS_ERROR_UNEXPECTED;
1443
0
1444
0
    rv = mContextStack.Push(proto, root);
1445
0
    if (NS_FAILED(rv)) return rv;
1446
0
1447
0
    return NS_OK;
1448
0
}
1449
1450
nsresult
1451
XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
1452
                               nsINode* aParent, nsINode* aBeforeThis)
1453
0
{
1454
0
    MOZ_ASSERT(aProtoPI, "null ptr");
1455
0
    MOZ_ASSERT(aParent, "null ptr");
1456
0
1457
0
    RefPtr<ProcessingInstruction> node =
1458
0
        NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
1459
0
                                       aProtoPI->mData);
1460
0
1461
0
    nsresult rv;
1462
0
    if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
1463
0
        rv = InsertXMLStylesheetPI(aProtoPI, aParent, aBeforeThis, node);
1464
0
    } else {
1465
0
        // No special processing, just add the PI to the document.
1466
0
        rv = aParent->InsertChildBefore(node->AsContent(),
1467
0
                                        aBeforeThis
1468
0
                                          ? aBeforeThis->AsContent() : nullptr,
1469
0
                                        false);
1470
0
    }
1471
0
1472
0
    return rv;
1473
0
}
1474
1475
nsresult
1476
XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
1477
                                   nsINode* aParent,
1478
                                   nsINode* aBeforeThis,
1479
                                   nsIContent* aPINode)
1480
0
{
1481
0
    nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
1482
0
    NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
1483
0
                       "implement nsIStyleSheetLinkingElement!");
1484
0
1485
0
    nsresult rv;
1486
0
1487
0
    ssle->InitStyleLinkElement(false);
1488
0
    // We want to be notified when the style sheet finishes loading, so
1489
0
    // disable style sheet loading for now.
1490
0
    ssle->SetEnableUpdates(false);
1491
0
    ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
1492
0
1493
0
    rv = aParent->InsertChildBefore(aPINode->AsContent(),
1494
0
                                    aBeforeThis
1495
0
                                      ? aBeforeThis->AsContent() : nullptr,
1496
0
                                    false);
1497
0
    if (NS_FAILED(rv)) return rv;
1498
0
1499
0
    ssle->SetEnableUpdates(true);
1500
0
1501
0
    // load the stylesheet if necessary, passing ourselves as
1502
0
    // nsICSSObserver
1503
0
    auto result = ssle->UpdateStyleSheet(this);
1504
0
    if (result.isErr()) {
1505
0
        // Ignore errors from UpdateStyleSheet; we don't want failure to
1506
0
        // do that to break the XUL document load.  But do propagate out
1507
0
        // NS_ERROR_OUT_OF_MEMORY.
1508
0
        if (result.unwrapErr() == NS_ERROR_OUT_OF_MEMORY) {
1509
0
            return result.unwrapErr();
1510
0
        }
1511
0
        return NS_OK;
1512
0
    }
1513
0
1514
0
    auto update = result.unwrap();
1515
0
    if (update.ShouldBlock()) {
1516
0
        ++mPendingSheets;
1517
0
    }
1518
0
1519
0
    return NS_OK;
1520
0
}
1521
1522
nsresult
1523
XULDocument::ResumeWalk()
1524
0
{
1525
0
    // Walk the prototype and build the delegate content model. The
1526
0
    // walk is performed in a top-down, left-to-right fashion. That
1527
0
    // is, a parent is built before any of its children; a node is
1528
0
    // only built after all of its siblings to the left are fully
1529
0
    // constructed.
1530
0
    //
1531
0
    // It is interruptable so that transcluded documents (e.g.,
1532
0
    // <html:script src="..." />) can be properly re-loaded if the
1533
0
    // cached copy of the document becomes stale.
1534
0
    nsresult rv;
1535
0
    nsCOMPtr<nsIURI> docURI =
1536
0
        mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
1537
0
1538
0
    while (1) {
1539
0
        // Begin (or resume) walking the current prototype.
1540
0
1541
0
        while (mContextStack.Depth() > 0) {
1542
0
            // Look at the top of the stack to determine what we're
1543
0
            // currently working on.
1544
0
            // This will always be a node already constructed and
1545
0
            // inserted to the actual document.
1546
0
            nsXULPrototypeElement* proto;
1547
0
            nsCOMPtr<nsIContent> element;
1548
0
            int32_t indx; // all children of proto before indx (not
1549
0
                          // inclusive) have already been constructed
1550
0
            rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
1551
0
            if (NS_FAILED(rv)) return rv;
1552
0
1553
0
            if (indx >= (int32_t)proto->mChildren.Length()) {
1554
0
                if (element) {
1555
0
                    // We've processed all of the prototype's children. If
1556
0
                    // we're in the master prototype, do post-order
1557
0
                    // document-level hookup.
1558
0
                    AddElementToDocumentPost(element->AsElement());
1559
0
1560
0
                    if (element->NodeInfo()->Equals(nsGkAtoms::style,
1561
0
                                                    kNameSpaceID_XHTML) ||
1562
0
                        element->NodeInfo()->Equals(nsGkAtoms::style,
1563
0
                                                    kNameSpaceID_SVG)) {
1564
0
                        // XXX sucks that we have to do this -
1565
0
                        // see bug 370111
1566
0
                        nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
1567
0
                            do_QueryInterface(element);
1568
0
                        NS_ASSERTION(ssle, "<html:style> doesn't implement "
1569
0
                                           "nsIStyleSheetLinkingElement?");
1570
0
                        Unused << ssle->UpdateStyleSheet(nullptr);
1571
0
                    }
1572
0
                }
1573
0
                // Now pop the context stack back up to the parent
1574
0
                // element and continue the prototype walk.
1575
0
                mContextStack.Pop();
1576
0
                continue;
1577
0
            }
1578
0
1579
0
            // Grab the next child, and advance the current context stack
1580
0
            // to the next sibling to our right.
1581
0
            nsXULPrototypeNode* childproto = proto->mChildren[indx];
1582
0
            mContextStack.SetTopIndex(++indx);
1583
0
1584
0
            NS_ASSERTION(element, "no element on context stack");
1585
0
1586
0
            switch (childproto->mType) {
1587
0
            case nsXULPrototypeNode::eType_Element: {
1588
0
                // An 'element', which may contain more content.
1589
0
                nsXULPrototypeElement* protoele =
1590
0
                    static_cast<nsXULPrototypeElement*>(childproto);
1591
0
1592
0
                RefPtr<Element> child;
1593
0
1594
0
1595
0
                rv = CreateElementFromPrototype(protoele,
1596
0
                                                getter_AddRefs(child),
1597
0
                                                false);
1598
0
                if (NS_FAILED(rv)) return rv;
1599
0
1600
0
                // ...and append it to the content model.
1601
0
                rv = element->AppendChildTo(child, false);
1602
0
                if (NS_FAILED(rv)) return rv;
1603
0
1604
0
                // do pre-order document-level hookup.
1605
0
                AddElementToDocumentPre(child);
1606
0
1607
0
                // If it has children, push the element onto the context
1608
0
                // stack and begin to process them.
1609
0
                if (protoele->mChildren.Length() > 0) {
1610
0
                    rv = mContextStack.Push(protoele, child);
1611
0
                    if (NS_FAILED(rv)) return rv;
1612
0
                }
1613
0
                else {
1614
0
                    // If there are no children, do post-order document hookup
1615
0
                    // immediately.
1616
0
                    AddElementToDocumentPost(child);
1617
0
                }
1618
0
            }
1619
0
            break;
1620
0
1621
0
            case nsXULPrototypeNode::eType_Script: {
1622
0
                // A script reference. Execute the script immediately;
1623
0
                // this may have side effects in the content model.
1624
0
                nsXULPrototypeScript* scriptproto =
1625
0
                    static_cast<nsXULPrototypeScript*>(childproto);
1626
0
1627
0
                if (scriptproto->mSrcURI) {
1628
0
                    // A transcluded script reference; this may
1629
0
                    // "block" our prototype walk if the script isn't
1630
0
                    // cached, or the cached copy of the script is
1631
0
                    // stale and must be reloaded.
1632
0
                    bool blocked;
1633
0
                    rv = LoadScript(scriptproto, &blocked);
1634
0
                    // If the script cannot be loaded, just keep going!
1635
0
1636
0
                    if (NS_SUCCEEDED(rv) && blocked)
1637
0
                        return NS_OK;
1638
0
                }
1639
0
                else if (scriptproto->HasScriptObject()) {
1640
0
                    // An inline script
1641
0
                    rv = ExecuteScript(scriptproto);
1642
0
                    if (NS_FAILED(rv)) return rv;
1643
0
                }
1644
0
            }
1645
0
            break;
1646
0
1647
0
            case nsXULPrototypeNode::eType_Text: {
1648
0
                // A simple text node.
1649
0
                RefPtr<nsTextNode> text =
1650
0
                    new nsTextNode(mNodeInfoManager);
1651
0
1652
0
                nsXULPrototypeText* textproto =
1653
0
                    static_cast<nsXULPrototypeText*>(childproto);
1654
0
                text->SetText(textproto->mValue, false);
1655
0
1656
0
                rv = element->AppendChildTo(text, false);
1657
0
                NS_ENSURE_SUCCESS(rv, rv);
1658
0
            }
1659
0
            break;
1660
0
1661
0
            case nsXULPrototypeNode::eType_PI: {
1662
0
                nsXULPrototypePI* piProto =
1663
0
                    static_cast<nsXULPrototypePI*>(childproto);
1664
0
1665
0
                // <?xml-stylesheet?> doesn't have an effect
1666
0
                // outside the prolog, like it used to. Issue a warning.
1667
0
1668
0
                if (piProto->mTarget.EqualsLiteral("xml-stylesheet")) {
1669
0
1670
0
                    const char16_t* params[] = { piProto->mTarget.get() };
1671
0
1672
0
                    nsContentUtils::ReportToConsole(
1673
0
                                        nsIScriptError::warningFlag,
1674
0
                                        NS_LITERAL_CSTRING("XUL Document"), nullptr,
1675
0
                                        nsContentUtils::eXUL_PROPERTIES,
1676
0
                                        "PINotInProlog",
1677
0
                                        params, ArrayLength(params),
1678
0
                                        docURI);
1679
0
                }
1680
0
1681
0
                nsIContent* parent = element.get();
1682
0
1683
0
                if (parent) {
1684
0
                    // an inline script could have removed the root element
1685
0
                    rv = CreateAndInsertPI(piProto, parent, nullptr);
1686
0
                    NS_ENSURE_SUCCESS(rv, rv);
1687
0
                }
1688
0
            }
1689
0
            break;
1690
0
1691
0
            default:
1692
0
                MOZ_ASSERT_UNREACHABLE("Unexpected nsXULPrototypeNode::Type");
1693
0
            }
1694
0
        }
1695
0
1696
0
        // Once we get here, the context stack will have been
1697
0
        // depleted. That means that the entire prototype has been
1698
0
        // walked and content has been constructed.
1699
0
        break;
1700
0
    }
1701
0
1702
0
    // If we get here, there is nothing left for us to walk. The content
1703
0
    // model is built and ready for layout.
1704
0
1705
0
    ApplyPersistentAttributes();
1706
0
1707
0
    mStillWalking = false;
1708
0
    if (mPendingSheets == 0) {
1709
0
        rv = DoneWalking();
1710
0
    }
1711
0
    return rv;
1712
0
}
1713
1714
nsresult
1715
XULDocument::DoneWalking()
1716
0
{
1717
0
    MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
1718
0
    MOZ_ASSERT(!mStillWalking, "walk not done");
1719
0
1720
0
    // XXXldb This is where we should really be setting the chromehidden
1721
0
    // attribute.
1722
0
1723
0
    if (!mDocumentLoaded) {
1724
0
        // Make sure we don't reenter here from StartLayout().  Note that
1725
0
        // setting mDocumentLoaded to true here means that if StartLayout()
1726
0
        // causes ResumeWalk() to be reentered, we'll take the other branch of
1727
0
        // the |if (!mDocumentLoaded)| check above and since
1728
0
        // mInitialLayoutComplete will be false will follow the else branch
1729
0
        // there too.  See the big comment there for how such reentry can
1730
0
        // happen.
1731
0
        mDocumentLoaded = true;
1732
0
1733
0
        NotifyPossibleTitleChange(false);
1734
0
1735
0
        // For performance reasons, we want to trigger the DocumentL10n's `TriggerInitialDocumentTranslation` within the same
1736
0
        // microtask that will be created for a `MozBeforeInitialXULLayout`
1737
0
        // event listener.
1738
0
        AddEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"), mDocumentL10n, true, false);
1739
0
1740
0
        nsContentUtils::DispatchTrustedEvent(
1741
0
            this,
1742
0
            static_cast<nsIDocument*>(this),
1743
0
            NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
1744
0
            CanBubble::eYes,
1745
0
            Cancelable::eNo);
1746
0
1747
0
        RemoveEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"), mDocumentL10n, true);
1748
0
1749
0
        // Before starting layout, check whether we're a toplevel chrome
1750
0
        // window.  If we are, setup some state so that we don't have to restyle
1751
0
        // the whole tree after StartLayout.
1752
0
        if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
1753
0
            // We're the chrome document!
1754
0
            win->BeforeStartLayout();
1755
0
        }
1756
0
1757
0
        StartLayout();
1758
0
1759
0
        if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
1760
0
            nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
1761
0
1762
0
        NS_ASSERTION(mDelayFrameLoaderInitialization,
1763
0
                     "mDelayFrameLoaderInitialization should be true!");
1764
0
        mDelayFrameLoaderInitialization = false;
1765
0
        NS_WARNING_ASSERTION(
1766
0
          mUpdateNestLevel == 0,
1767
0
          "Constructing XUL document in middle of an update?");
1768
0
        if (mUpdateNestLevel == 0) {
1769
0
            MaybeInitializeFinalizeFrameLoaders();
1770
0
        }
1771
0
1772
0
        NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
1773
0
1774
0
        // DispatchContentLoadedEvents undoes the onload-blocking we
1775
0
        // did in PrepareToWalk().
1776
0
        DispatchContentLoadedEvents();
1777
0
1778
0
        mInitialLayoutComplete = true;
1779
0
    }
1780
0
1781
0
    return NS_OK;
1782
0
}
1783
1784
NS_IMETHODIMP
1785
XULDocument::StyleSheetLoaded(StyleSheet* aSheet,
1786
                              bool aWasDeferred,
1787
                              nsresult aStatus)
1788
0
{
1789
0
    if (!aWasDeferred) {
1790
0
        // Don't care about when alternate sheets finish loading
1791
0
        MOZ_ASSERT(mPendingSheets > 0,
1792
0
                   "Unexpected StyleSheetLoaded notification");
1793
0
1794
0
        --mPendingSheets;
1795
0
1796
0
        if (!mStillWalking && mPendingSheets == 0) {
1797
0
            return DoneWalking();
1798
0
        }
1799
0
    }
1800
0
1801
0
    return NS_OK;
1802
0
}
1803
1804
void
1805
XULDocument::MaybeBroadcast()
1806
0
{
1807
0
    // Only broadcast when not in an update and when safe to run scripts.
1808
0
    if (mUpdateNestLevel == 0 &&
1809
0
        (mDelayedAttrChangeBroadcasts.Length() ||
1810
0
         mDelayedBroadcasters.Length())) {
1811
0
        if (!nsContentUtils::IsSafeToRunScript()) {
1812
0
            if (!mInDestructor) {
1813
0
              nsContentUtils::AddScriptRunner(
1814
0
                NewRunnableMethod("dom::XULDocument::MaybeBroadcast",
1815
0
                                  this,
1816
0
                                  &XULDocument::MaybeBroadcast));
1817
0
            }
1818
0
            return;
1819
0
        }
1820
0
        if (!mHandlingDelayedAttrChange) {
1821
0
            mHandlingDelayedAttrChange = true;
1822
0
            for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
1823
0
                nsAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
1824
0
                if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
1825
0
                    nsCOMPtr<Element> listener =
1826
0
                        do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
1827
0
                    const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
1828
0
                    if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
1829
0
                        listener->SetAttr(kNameSpaceID_None, attrName, value,
1830
0
                                          true);
1831
0
                    } else {
1832
0
                        listener->UnsetAttr(kNameSpaceID_None, attrName,
1833
0
                                            true);
1834
0
                    }
1835
0
                }
1836
0
                ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
1837
0
                                             mDelayedAttrChangeBroadcasts[i].mListener,
1838
0
                                             attrName);
1839
0
            }
1840
0
            mDelayedAttrChangeBroadcasts.Clear();
1841
0
            mHandlingDelayedAttrChange = false;
1842
0
        }
1843
0
1844
0
        uint32_t length = mDelayedBroadcasters.Length();
1845
0
        if (length) {
1846
0
            bool oldValue = mHandlingDelayedBroadcasters;
1847
0
            mHandlingDelayedBroadcasters = true;
1848
0
            nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
1849
0
            mDelayedBroadcasters.SwapElements(delayedBroadcasters);
1850
0
            for (uint32_t i = 0; i < length; ++i) {
1851
0
                SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
1852
0
                                             delayedBroadcasters[i].mListener,
1853
0
                                             delayedBroadcasters[i].mAttr);
1854
0
            }
1855
0
            mHandlingDelayedBroadcasters = oldValue;
1856
0
        }
1857
0
    }
1858
0
}
1859
1860
void
1861
XULDocument::EndUpdate()
1862
0
{
1863
0
    XMLDocument::EndUpdate();
1864
0
    MaybeBroadcast();
1865
0
}
1866
1867
nsresult
1868
XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
1869
0
{
1870
0
    // Load a transcluded script
1871
0
    nsresult rv;
1872
0
1873
0
    bool isChromeDoc = IsChromeURI(mDocumentURI);
1874
0
1875
0
    if (isChromeDoc && aScriptProto->HasScriptObject()) {
1876
0
        rv = ExecuteScript(aScriptProto);
1877
0
1878
0
        // Ignore return value from execution, and don't block
1879
0
        *aBlock = false;
1880
0
        return NS_OK;
1881
0
    }
1882
0
1883
0
    // Try the XUL script cache, in case two XUL documents source the same
1884
0
    // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
1885
0
    // XXXbe the cache relies on aScriptProto's GC root!
1886
0
    bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
1887
0
1888
0
    if (isChromeDoc && useXULCache) {
1889
0
        JSScript* newScriptObject =
1890
0
            nsXULPrototypeCache::GetInstance()->GetScript(
1891
0
                                   aScriptProto->mSrcURI);
1892
0
        if (newScriptObject) {
1893
0
            // The script language for a proto must remain constant - we
1894
0
            // can't just change it for this unexpected language.
1895
0
            aScriptProto->Set(newScriptObject);
1896
0
        }
1897
0
1898
0
        if (aScriptProto->HasScriptObject()) {
1899
0
            rv = ExecuteScript(aScriptProto);
1900
0
1901
0
            // Ignore return value from execution, and don't block
1902
0
            *aBlock = false;
1903
0
            return NS_OK;
1904
0
        }
1905
0
    }
1906
0
1907
0
    // Release script objects from FastLoad since we decided against using them
1908
0
    aScriptProto->UnlinkJSObjects();
1909
0
1910
0
    // Set the current script prototype so that OnStreamComplete can report
1911
0
    // the right file if there are errors in the script.
1912
0
    NS_ASSERTION(!mCurrentScriptProto,
1913
0
                 "still loading a script when starting another load?");
1914
0
    mCurrentScriptProto = aScriptProto;
1915
0
1916
0
    if (isChromeDoc && aScriptProto->mSrcLoading) {
1917
0
        // Another XULDocument load has started, which is still in progress.
1918
0
        // Remember to ResumeWalk this document when the load completes.
1919
0
        mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
1920
0
        aScriptProto->mSrcLoadWaiters = this;
1921
0
        NS_ADDREF_THIS();
1922
0
    }
1923
0
    else {
1924
0
        nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
1925
0
1926
0
        // Note: the loader will keep itself alive while it's loading.
1927
0
        nsCOMPtr<nsIStreamLoader> loader;
1928
0
        rv = NS_NewStreamLoader(getter_AddRefs(loader),
1929
0
                                aScriptProto->mSrcURI,
1930
0
                                this, // aObserver
1931
0
                                this, // aRequestingContext
1932
0
                                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
1933
0
                                nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
1934
0
                                group);
1935
0
1936
0
        if (NS_FAILED(rv)) {
1937
0
            mCurrentScriptProto = nullptr;
1938
0
            return rv;
1939
0
        }
1940
0
1941
0
        aScriptProto->mSrcLoading = true;
1942
0
    }
1943
0
1944
0
    // Block until OnStreamComplete resumes us.
1945
0
    *aBlock = true;
1946
0
    return NS_OK;
1947
0
}
1948
1949
NS_IMETHODIMP
1950
XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
1951
                              nsISupports* context,
1952
                              nsresult aStatus,
1953
                              uint32_t stringLen,
1954
                              const uint8_t* string)
1955
0
{
1956
0
    nsCOMPtr<nsIRequest> request;
1957
0
    aLoader->GetRequest(getter_AddRefs(request));
1958
0
    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
1959
0
1960
#ifdef DEBUG
1961
    // print a load error on bad status
1962
    if (NS_FAILED(aStatus)) {
1963
        if (channel) {
1964
            nsCOMPtr<nsIURI> uri;
1965
            channel->GetURI(getter_AddRefs(uri));
1966
            if (uri) {
1967
                printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
1968
            }
1969
        }
1970
    }
1971
#endif
1972
1973
0
    // This is the completion routine that will be called when a
1974
0
    // transcluded script completes. Compile and execute the script
1975
0
    // if the load was successful, then continue building content
1976
0
    // from the prototype.
1977
0
    nsresult rv = aStatus;
1978
0
1979
0
    NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
1980
0
                 "script source not loading on unichar stream complete?");
1981
0
    if (!mCurrentScriptProto) {
1982
0
        // XXX Wallpaper for bug 270042
1983
0
        return NS_OK;
1984
0
    }
1985
0
1986
0
    if (NS_SUCCEEDED(aStatus)) {
1987
0
        // If the including XUL document is a FastLoad document, and we're
1988
0
        // compiling an out-of-line script (one with src=...), then we must
1989
0
        // be writing a new FastLoad file.  If we were reading this script
1990
0
        // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
1991
0
        // nsXULContentSink.cpp) would have already deserialized a non-null
1992
0
        // script->mScriptObject, causing control flow at the top of LoadScript
1993
0
        // not to reach here.
1994
0
        nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
1995
0
1996
0
        // XXX should also check nsIHttpChannel::requestSucceeded
1997
0
1998
0
        MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
1999
0
                                            !mOffThreadCompileStringBuf),
2000
0
                   "XULDocument can't load multiple scripts at once");
2001
0
2002
0
        rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen,
2003
0
                                          EmptyString(), this,
2004
0
                                          mOffThreadCompileStringBuf,
2005
0
                                          mOffThreadCompileStringLength);
2006
0
        if (NS_SUCCEEDED(rv)) {
2007
0
            // Attempt to give ownership of the buffer to the JS engine.  If
2008
0
            // we hit offthread compilation, however, we will have to take it
2009
0
            // back below in order to keep the memory alive until compilation
2010
0
            // completes.
2011
0
            JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
2012
0
                                          mOffThreadCompileStringLength,
2013
0
                                          JS::SourceBufferHolder::GiveOwnership);
2014
0
            mOffThreadCompileStringBuf = nullptr;
2015
0
            mOffThreadCompileStringLength = 0;
2016
0
2017
0
            rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
2018
0
            if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
2019
0
                // We will be notified via OnOffThreadCompileComplete when the
2020
0
                // compile finishes. The JS engine has taken ownership of the
2021
0
                // source buffer.
2022
0
                MOZ_RELEASE_ASSERT(!srcBuf.ownsChars());
2023
0
                mOffThreadCompiling = true;
2024
0
                BlockOnload();
2025
0
                return NS_OK;
2026
0
            }
2027
0
        }
2028
0
    }
2029
0
2030
0
    return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
2031
0
}
2032
2033
NS_IMETHODIMP
2034
XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
2035
0
{
2036
0
    // When compiling off thread the script will not have been attached to the
2037
0
    // script proto yet.
2038
0
    if (aScript && !mCurrentScriptProto->HasScriptObject())
2039
0
        mCurrentScriptProto->Set(aScript);
2040
0
2041
0
    // Allow load events to be fired once off thread compilation finishes.
2042
0
    if (mOffThreadCompiling) {
2043
0
        mOffThreadCompiling = false;
2044
0
        UnblockOnload(false);
2045
0
    }
2046
0
2047
0
    // After compilation finishes the script's characters are no longer needed.
2048
0
    if (mOffThreadCompileStringBuf) {
2049
0
      js_free(mOffThreadCompileStringBuf);
2050
0
      mOffThreadCompileStringBuf = nullptr;
2051
0
      mOffThreadCompileStringLength = 0;
2052
0
    }
2053
0
2054
0
    // Clear mCurrentScriptProto now, but save it first for use below in
2055
0
    // the execute code, and in the while loop that resumes walks of other
2056
0
    // documents that raced to load this script.
2057
0
    nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
2058
0
    mCurrentScriptProto = nullptr;
2059
0
2060
0
    // Clear the prototype's loading flag before executing the script or
2061
0
    // resuming document walks, in case any of those control flows starts a
2062
0
    // new script load.
2063
0
    scriptProto->mSrcLoading = false;
2064
0
2065
0
    nsresult rv = aStatus;
2066
0
    if (NS_SUCCEEDED(rv)) {
2067
0
        rv = ExecuteScript(scriptProto);
2068
0
2069
0
        // If the XUL cache is enabled, save the script object there in
2070
0
        // case different XUL documents source the same script.
2071
0
        //
2072
0
        // But don't save the script in the cache unless the master XUL
2073
0
        // document URL is a chrome: URL.  It is valid for a URL such as
2074
0
        // about:config to translate into a master document URL, whose
2075
0
        // prototype document nodes -- including prototype scripts that
2076
0
        // hold GC roots protecting their mJSObject pointers -- are not
2077
0
        // cached in the XUL prototype cache.  See StartDocumentLoad,
2078
0
        // the fillXULCache logic.
2079
0
        //
2080
0
        // A document such as about:config is free to load a script via
2081
0
        // a URL such as chrome://global/content/config.js, and we must
2082
0
        // not cache that script object without a prototype cache entry
2083
0
        // containing a companion nsXULPrototypeScript node that owns a
2084
0
        // GC root protecting the script object.  Otherwise, the script
2085
0
        // cache entry will dangle once the uncached prototype document
2086
0
        // is released when its owning XULDocument is unloaded.
2087
0
        //
2088
0
        // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
2089
0
        // the true crime story.)
2090
0
        bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
2091
0
2092
0
        if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasScriptObject()) {
2093
0
            JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
2094
0
            nsXULPrototypeCache::GetInstance()->PutScript(
2095
0
                               scriptProto->mSrcURI, script);
2096
0
        }
2097
0
        // ignore any evaluation errors
2098
0
    }
2099
0
2100
0
    rv = ResumeWalk();
2101
0
2102
0
    // Load a pointer to the prototype-script's list of XULDocuments who
2103
0
    // raced to load the same script
2104
0
    XULDocument** docp = &scriptProto->mSrcLoadWaiters;
2105
0
2106
0
    // Resume walking other documents that waited for this one's load, first
2107
0
    // executing the script we just compiled, in each doc's script context
2108
0
    XULDocument* doc;
2109
0
    while ((doc = *docp) != nullptr) {
2110
0
        NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
2111
0
                     "waiting for wrong script to load?");
2112
0
        doc->mCurrentScriptProto = nullptr;
2113
0
2114
0
        // Unlink doc from scriptProto's list before executing and resuming
2115
0
        *docp = doc->mNextSrcLoadWaiter;
2116
0
        doc->mNextSrcLoadWaiter = nullptr;
2117
0
2118
0
        if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasScriptObject()) {
2119
0
            // If the previous doc load was aborted, we want to try loading
2120
0
            // again for the next doc. Otherwise, one abort would lead to all
2121
0
            // subsequent waiting docs to abort as well.
2122
0
            bool block = false;
2123
0
            doc->LoadScript(scriptProto, &block);
2124
0
            NS_RELEASE(doc);
2125
0
            return rv;
2126
0
        }
2127
0
2128
0
        // Execute only if we loaded and compiled successfully, then resume
2129
0
        if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
2130
0
            doc->ExecuteScript(scriptProto);
2131
0
        }
2132
0
        doc->ResumeWalk();
2133
0
        NS_RELEASE(doc);
2134
0
    }
2135
0
2136
0
    return rv;
2137
0
}
2138
2139
nsresult
2140
XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
2141
0
{
2142
0
    MOZ_ASSERT(aScript != nullptr, "null ptr");
2143
0
    NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
2144
0
    NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
2145
0
2146
0
    nsresult rv;
2147
0
    rv = mScriptGlobalObject->EnsureScriptEnvironment();
2148
0
    NS_ENSURE_SUCCESS(rv, rv);
2149
0
2150
0
    // Execute the precompiled script with the given version
2151
0
    nsAutoMicroTask mt;
2152
0
2153
0
    // We're about to run script via JS::CloneAndExecuteScript, so we need an
2154
0
    // AutoEntryScript. This is Gecko specific and not in any spec.
2155
0
    AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
2156
0
    JSContext* cx = aes.cx();
2157
0
2158
0
    JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
2159
0
    NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
2160
0
2161
0
    JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
2162
0
    NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
2163
0
2164
0
    JS::ExposeObjectToActiveJS(global);
2165
0
    JSAutoRealm ar(cx, global);
2166
0
2167
0
    // The script is in the compilation scope. Clone it into the target scope
2168
0
    // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
2169
0
    // there is no need to manually check the return value.
2170
0
    JS::RootedValue rval(cx);
2171
0
    JS::CloneAndExecuteScript(cx, scriptObject, &rval);
2172
0
2173
0
    return NS_OK;
2174
0
}
2175
2176
2177
nsresult
2178
XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
2179
                                        Element** aResult,
2180
                                        bool aIsRoot)
2181
0
{
2182
0
    // Create a content model element from a prototype element.
2183
0
    MOZ_ASSERT(aPrototype != nullptr, "null ptr");
2184
0
    if (! aPrototype)
2185
0
        return NS_ERROR_NULL_POINTER;
2186
0
2187
0
    *aResult = nullptr;
2188
0
    nsresult rv = NS_OK;
2189
0
2190
0
    if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
2191
0
        MOZ_LOG(gXULLog, LogLevel::Debug,
2192
0
               ("xul: creating <%s> from prototype",
2193
0
                NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
2194
0
    }
2195
0
2196
0
    RefPtr<Element> result;
2197
0
2198
0
    if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
2199
0
        // If it's a XUL element, it'll be lightweight until somebody
2200
0
        // monkeys with it.
2201
0
        rv = nsXULElement::CreateFromPrototype(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
2202
0
        if (NS_FAILED(rv)) return rv;
2203
0
    }
2204
0
    else {
2205
0
        // If it's not a XUL element, it's gonna be heavyweight no matter
2206
0
        // what. So we need to copy everything out of the prototype
2207
0
        // into the element.  Get a nodeinfo from our nodeinfo manager
2208
0
        // for this node.
2209
0
        RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
2210
0
        newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
2211
0
                                                    aPrototype->mNodeInfo->GetPrefixAtom(),
2212
0
                                                    aPrototype->mNodeInfo->NamespaceID(),
2213
0
                                                    ELEMENT_NODE);
2214
0
        if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
2215
0
        RefPtr<mozilla::dom::NodeInfo> xtfNi = newNodeInfo;
2216
0
        rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
2217
0
                           NOT_FROM_PARSER);
2218
0
        if (NS_FAILED(rv))
2219
0
            return rv;
2220
0
2221
0
        rv = AddAttributes(aPrototype, result);
2222
0
        if (NS_FAILED(rv)) return rv;
2223
0
    }
2224
0
2225
0
    result.forget(aResult);
2226
0
2227
0
    return NS_OK;
2228
0
}
2229
2230
nsresult
2231
XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
2232
                           Element* aElement)
2233
0
{
2234
0
    nsresult rv;
2235
0
2236
0
    for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
2237
0
        nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
2238
0
        nsAutoString  valueStr;
2239
0
        protoattr->mValue.ToString(valueStr);
2240
0
2241
0
        rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
2242
0
                               protoattr->mName.LocalName(),
2243
0
                               protoattr->mName.GetPrefix(),
2244
0
                               valueStr,
2245
0
                               false);
2246
0
        if (NS_FAILED(rv)) return rv;
2247
0
    }
2248
0
2249
0
    return NS_OK;
2250
0
}
2251
2252
2253
//----------------------------------------------------------------------
2254
2255
nsresult
2256
XULDocument::FindBroadcaster(Element* aElement,
2257
                             Element** aListener,
2258
                             nsString& aBroadcasterID,
2259
                             nsString& aAttribute,
2260
                             Element** aBroadcaster)
2261
0
{
2262
0
    mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
2263
0
    *aListener = nullptr;
2264
0
    *aBroadcaster = nullptr;
2265
0
2266
0
    if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
2267
0
        // It's an <observes> element, which means that the actual
2268
0
        // listener is the _parent_ node. This element should have an
2269
0
        // 'element' attribute that specifies the ID of the
2270
0
        // broadcaster element, and an 'attribute' element, which
2271
0
        // specifies the name of the attribute to observe.
2272
0
        nsIContent* parent = aElement->GetParent();
2273
0
        if (!parent) {
2274
0
             // <observes> is the root element
2275
0
            return NS_FINDBROADCASTER_NOT_FOUND;
2276
0
        }
2277
0
2278
0
        *aListener = Element::FromNode(parent);
2279
0
        NS_IF_ADDREF(*aListener);
2280
0
2281
0
        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
2282
0
        if (aBroadcasterID.IsEmpty()) {
2283
0
            return NS_FINDBROADCASTER_NOT_FOUND;
2284
0
        }
2285
0
        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
2286
0
    }
2287
0
    else {
2288
0
        // It's a generic element, which means that we'll use the
2289
0
        // value of the 'observes' attribute to determine the ID of
2290
0
        // the broadcaster element, and we'll watch _all_ of its
2291
0
        // values.
2292
0
        aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
2293
0
2294
0
        // Bail if there's no aBroadcasterID
2295
0
        if (aBroadcasterID.IsEmpty()) {
2296
0
            // Try the command attribute next.
2297
0
            aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
2298
0
            if (!aBroadcasterID.IsEmpty()) {
2299
0
                // We've got something in the command attribute.  We
2300
0
                // only treat this as a normal broadcaster if we are
2301
0
                // not a menuitem or a key.
2302
0
2303
0
                if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
2304
0
                    ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
2305
0
                return NS_FINDBROADCASTER_NOT_FOUND;
2306
0
              }
2307
0
            }
2308
0
            else {
2309
0
              return NS_FINDBROADCASTER_NOT_FOUND;
2310
0
            }
2311
0
        }
2312
0
2313
0
        *aListener = aElement;
2314
0
        NS_ADDREF(*aListener);
2315
0
2316
0
        aAttribute.Assign('*');
2317
0
    }
2318
0
2319
0
    // Make sure we got a valid listener.
2320
0
    NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
2321
0
2322
0
    // Try to find the broadcaster element in the document.
2323
0
    *aBroadcaster = GetElementById(aBroadcasterID);
2324
0
2325
0
    // The broadcaster element is missing.
2326
0
    if (! *aBroadcaster) {
2327
0
        return NS_FINDBROADCASTER_NOT_FOUND;
2328
0
    }
2329
0
2330
0
    NS_ADDREF(*aBroadcaster);
2331
0
2332
0
    return NS_FINDBROADCASTER_FOUND;
2333
0
}
2334
2335
nsresult
2336
XULDocument::CheckBroadcasterHookup(Element* aElement)
2337
0
{
2338
0
    // Resolve a broadcaster hookup. Look at the element that we're
2339
0
    // trying to resolve: it could be an '<observes>' element, or just
2340
0
    // a vanilla element with an 'observes' attribute on it.
2341
0
    nsresult rv;
2342
0
2343
0
    nsCOMPtr<Element> listener;
2344
0
    nsAutoString broadcasterID;
2345
0
    nsAutoString attribute;
2346
0
    nsCOMPtr<Element> broadcaster;
2347
0
2348
0
    rv = FindBroadcaster(aElement, getter_AddRefs(listener),
2349
0
                         broadcasterID, attribute, getter_AddRefs(broadcaster));
2350
0
    switch (rv) {
2351
0
        case NS_FINDBROADCASTER_NOT_FOUND:
2352
0
            return NS_OK;
2353
0
        case NS_FINDBROADCASTER_FOUND:
2354
0
            break;
2355
0
        default:
2356
0
            return rv;
2357
0
    }
2358
0
2359
0
    NS_ENSURE_ARG(broadcaster && listener);
2360
0
    ErrorResult domRv;
2361
0
    AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
2362
0
    if (domRv.Failed()) {
2363
0
        return domRv.StealNSResult();
2364
0
    }
2365
0
2366
0
    // Tell the world we succeeded
2367
0
    if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
2368
0
        nsCOMPtr<nsIContent> content =
2369
0
            do_QueryInterface(listener);
2370
0
2371
0
        NS_ASSERTION(content != nullptr, "not an nsIContent");
2372
0
        if (! content)
2373
0
            return rv;
2374
0
2375
0
        nsAutoCString attributeC,broadcasteridC;
2376
0
        LossyCopyUTF16toASCII(attribute, attributeC);
2377
0
        LossyCopyUTF16toASCII(broadcasterID, broadcasteridC);
2378
0
        MOZ_LOG(gXULLog, LogLevel::Debug,
2379
0
               ("xul: broadcaster hookup <%s attribute='%s'> to %s",
2380
0
                nsAtomCString(content->NodeInfo()->NameAtom()).get(),
2381
0
                attributeC.get(),
2382
0
                broadcasteridC.get()));
2383
0
    }
2384
0
2385
0
    return NS_OK;
2386
0
}
2387
2388
//----------------------------------------------------------------------
2389
//
2390
// CachedChromeStreamListener
2391
//
2392
2393
XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
2394
    : mDocument(aDocument),
2395
      mProtoLoaded(aProtoLoaded)
2396
0
{
2397
0
}
2398
2399
2400
XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
2401
0
{
2402
0
}
2403
2404
2405
NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
2406
                  nsIRequestObserver, nsIStreamListener)
2407
2408
NS_IMETHODIMP
2409
XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
2410
                                                        nsISupports* acontext)
2411
0
{
2412
0
    return NS_ERROR_PARSED_DATA_CACHED;
2413
0
}
2414
2415
2416
NS_IMETHODIMP
2417
XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
2418
                                                       nsISupports* aContext,
2419
                                                       nsresult aStatus)
2420
0
{
2421
0
    if (! mProtoLoaded)
2422
0
        return NS_OK;
2423
0
2424
0
    return mDocument->OnPrototypeLoadDone(true);
2425
0
}
2426
2427
2428
NS_IMETHODIMP
2429
XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
2430
                                                         nsISupports* aContext,
2431
                                                         nsIInputStream* aInStr,
2432
                                                         uint64_t aSourceOffset,
2433
                                                         uint32_t aCount)
2434
0
{
2435
0
    MOZ_ASSERT_UNREACHABLE("CachedChromeStream doesn't receive data");
2436
0
    return NS_ERROR_UNEXPECTED;
2437
0
}
2438
2439
bool
2440
XULDocument::IsDocumentRightToLeft()
2441
0
{
2442
0
    // setting the localedir attribute on the root element forces a
2443
0
    // specific direction for the document.
2444
0
    Element* element = GetRootElement();
2445
0
    if (element) {
2446
0
        static Element::AttrValuesArray strings[] =
2447
0
            {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
2448
0
        switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
2449
0
                                         strings, eCaseMatters)) {
2450
0
            case 0: return false;
2451
0
            case 1: return true;
2452
0
            default: break; // otherwise, not a valid value, so fall through
2453
0
        }
2454
0
    }
2455
0
2456
0
    // otherwise, get the locale from the chrome registry and
2457
0
    // look up the intl.uidirection.<locale> preference
2458
0
    nsCOMPtr<nsIXULChromeRegistry> reg =
2459
0
        mozilla::services::GetXULChromeRegistryService();
2460
0
    if (!reg)
2461
0
        return false;
2462
0
2463
0
    nsAutoCString package;
2464
0
    bool isChrome;
2465
0
    if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
2466
0
        isChrome) {
2467
0
        mDocumentURI->GetHostPort(package);
2468
0
    }
2469
0
    else {
2470
0
        // use the 'global' package for about and resource uris.
2471
0
        // otherwise, just default to left-to-right.
2472
0
        bool isAbout, isResource;
2473
0
        if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
2474
0
            isAbout) {
2475
0
            package.AssignLiteral("global");
2476
0
        }
2477
0
        else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
2478
0
            isResource) {
2479
0
            package.AssignLiteral("global");
2480
0
        }
2481
0
        else {
2482
0
            return false;
2483
0
        }
2484
0
    }
2485
0
2486
0
    bool isRTL = false;
2487
0
    reg->IsLocaleRTL(package, &isRTL);
2488
0
    return isRTL;
2489
0
}
2490
2491
void
2492
XULDocument::ResetDocumentDirection()
2493
0
{
2494
0
    DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
2495
0
}
2496
2497
void
2498
XULDocument::DirectionChanged(const char* aPrefName, XULDocument* aDoc)
2499
0
{
2500
0
  // Reset the direction and restyle the document if necessary.
2501
0
  if (aDoc) {
2502
0
      aDoc->ResetDocumentDirection();
2503
0
  }
2504
0
}
2505
2506
JSObject*
2507
XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
2508
0
{
2509
0
  return XULDocument_Binding::Wrap(aCx, this, aGivenProto);
2510
0
}
2511
2512
} // namespace dom
2513
} // namespace mozilla