1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef mozilla_dom_XULDocument_h
7 #define mozilla_dom_XULDocument_h
8 
9 #include "nsAutoPtr.h"
10 #include "nsCOMPtr.h"
11 #include "nsXULPrototypeDocument.h"
12 #include "nsXULPrototypeCache.h"
13 #include "nsTArray.h"
14 
15 #include "mozilla/dom/XMLDocument.h"
16 #include "mozilla/StyleSheet.h"
17 #include "nsForwardReference.h"
18 #include "nsIContent.h"
19 #include "nsIDOMXULCommandDispatcher.h"
20 #include "nsIDOMXULDocument.h"
21 #include "nsCOMArray.h"
22 #include "nsIURI.h"
23 #include "nsIXULDocument.h"
24 #include "nsScriptLoader.h"
25 #include "nsIStreamListener.h"
26 #include "nsIStreamLoader.h"
27 #include "nsICSSLoaderObserver.h"
28 #include "nsIXULStore.h"
29 
30 #include "mozilla/Attributes.h"
31 
32 #include "js/TracingAPI.h"
33 #include "js/TypeDecls.h"
34 
35 class nsIRDFResource;
36 class nsIRDFService;
37 class nsPIWindowRoot;
38 #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript))
39 class nsIObjectInputStream;
40 class nsIObjectOutputStream;
41 #else
42 #include "nsIObjectInputStream.h"
43 #include "nsIObjectOutputStream.h"
44 #include "nsXULElement.h"
45 #endif
46 #include "nsURIHashKey.h"
47 #include "nsInterfaceHashtable.h"
48 
49 class nsRefMapEntry : public nsStringHashKey
50 {
51 public:
nsRefMapEntry(const nsAString & aKey)52   explicit nsRefMapEntry(const nsAString& aKey) :
53     nsStringHashKey(&aKey)
54   {
55   }
nsRefMapEntry(const nsAString * aKey)56   explicit nsRefMapEntry(const nsAString* aKey) :
57     nsStringHashKey(aKey)
58   {
59   }
nsRefMapEntry(const nsRefMapEntry & aOther)60   nsRefMapEntry(const nsRefMapEntry& aOther) :
61     nsStringHashKey(&aOther.GetKey())
62   {
63     NS_ERROR("Should never be called");
64   }
65 
66   mozilla::dom::Element* GetFirstElement();
67   void AppendAll(nsCOMArray<nsIContent>* aElements);
68   /**
69    * @return true if aElement was added, false if we failed due to OOM
70    */
71   bool AddElement(mozilla::dom::Element* aElement);
72   /**
73    * @return true if aElement was removed and it was the last content for
74    * this ref, so this entry should be removed from the map
75    */
76   bool RemoveElement(mozilla::dom::Element* aElement);
77 
78 private:
79   nsTArray<mozilla::dom::Element*> mRefContentList;
80 };
81 
82 /**
83  * The XUL document class
84  */
85 
86 namespace mozilla {
87 namespace dom {
88 
89 class XULDocument final : public XMLDocument,
90                           public nsIXULDocument,
91                           public nsIDOMXULDocument,
92                           public nsIStreamLoaderObserver,
93                           public nsICSSLoaderObserver,
94                           public nsIOffThreadScriptReceiver
95 {
96 public:
97     XULDocument();
98 
99     // nsISupports interface
100     NS_DECL_ISUPPORTS_INHERITED
101     NS_DECL_NSISTREAMLOADEROBSERVER
102 
103     // nsIDocument interface
104     virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override;
105     virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup,
106                             nsIPrincipal* aPrincipal) override;
107 
108     virtual nsresult StartDocumentLoad(const char* aCommand,
109                                        nsIChannel *channel,
110                                        nsILoadGroup* aLoadGroup,
111                                        nsISupports* aContainer,
112                                        nsIStreamListener **aDocListener,
113                                        bool aReset = true,
114                                        nsIContentSink* aSink = nullptr) override;
115 
116     virtual void SetContentType(const nsAString& aContentType) override;
117 
118     virtual void EndLoad() override;
119 
120     // nsIMutationObserver interface
121     NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
122     NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
123     NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
124     NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
125     NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
126 
127     // nsIXULDocument interface
128     virtual void GetElementsForID(const nsAString& aID,
129                                   nsCOMArray<nsIContent>& aElements) override;
130 
131     NS_IMETHOD AddSubtreeToDocument(nsIContent* aContent) override;
132     NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aContent) override;
133     NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent,
134                                      nsIXULTemplateBuilder* aBuilder) override;
135     NS_IMETHOD GetTemplateBuilderFor(nsIContent* aContent,
136                                      nsIXULTemplateBuilder** aResult) override;
137     NS_IMETHOD OnPrototypeLoadDone(bool aResumeWalk) override;
138     bool OnDocumentParserError() override;
139 
140     // nsINode interface overrides
141     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
142 
143     // nsIDOMNode interface
144     NS_FORWARD_NSIDOMNODE_TO_NSINODE
145 
146     // nsIDOMDocument interface
147     using nsDocument::CreateElement;
148     using nsDocument::CreateElementNS;
149     NS_FORWARD_NSIDOMDOCUMENT(XMLDocument::)
150     // And explicitly import the things from nsDocument that we just shadowed
151     using nsDocument::GetImplementation;
152     using nsDocument::GetTitle;
153     using nsDocument::SetTitle;
154     using nsDocument::GetLastStyleSheetSet;
155     using nsDocument::MozSetImageElement;
156     using nsDocument::GetMozFullScreenElement;
157     using nsIDocument::GetLocation;
158 
159     // nsDocument interface overrides
160     virtual Element* GetElementById(const nsAString & elementId) override;
161 
162     // nsIDOMXULDocument interface
163     NS_DECL_NSIDOMXULDOCUMENT
164 
165     // nsICSSLoaderObserver
166     NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet,
167                                 bool aWasAlternate,
168                                 nsresult aStatus) override;
169 
170     virtual void EndUpdate(nsUpdateType aUpdateType) override;
171 
172     virtual bool IsDocumentRightToLeft() override;
173 
174     virtual void ResetDocumentDirection() override;
175 
176     virtual int GetDocumentLWTheme() override;
177 
ResetDocumentLWTheme()178     virtual void ResetDocumentLWTheme() override { mDocLWTheme = Doc_Theme_Uninitialized; }
179 
180     NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override;
181 
182     static bool
183     MatchAttribute(nsIContent* aContent,
184                    int32_t aNameSpaceID,
185                    nsIAtom* aAttrName,
186                    void* aData);
187 
188     NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument)
189 
190     void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber);
191 
192     // WebIDL API
193     already_AddRefed<nsINode> GetPopupNode();
194     void SetPopupNode(nsINode* aNode);
195     already_AddRefed<nsINode> GetPopupRangeParent(ErrorResult& aRv);
196     int32_t GetPopupRangeOffset(ErrorResult& aRv);
197     already_AddRefed<nsINode> GetTooltipNode();
SetTooltipNode(nsINode * aNode)198     void SetTooltipNode(nsINode* aNode) { /* do nothing */ }
GetCommandDispatcher()199     nsIDOMXULCommandDispatcher* GetCommandDispatcher() const
200     {
201         return mCommandDispatcher;
202     }
203     int32_t GetWidth(ErrorResult& aRv);
204     int32_t GetHeight(ErrorResult& aRv);
205     already_AddRefed<nsINodeList>
206       GetElementsByAttribute(const nsAString& aAttribute,
207                              const nsAString& aValue);
208     already_AddRefed<nsINodeList>
209       GetElementsByAttributeNS(const nsAString& aNamespaceURI,
210                                const nsAString& aAttribute,
211                                const nsAString& aValue,
212                                ErrorResult& aRv);
213     void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
214                                  const nsAString& aAttr, ErrorResult& aRv);
215     void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
216                                     const nsAString& aAttr);
Persist(const nsAString & aId,const nsAString & aAttr,ErrorResult & aRv)217     void Persist(const nsAString& aId, const nsAString& aAttr, ErrorResult& aRv)
218     {
219         aRv = Persist(aId, aAttr);
220     }
221     using nsDocument::GetBoxObjectFor;
LoadOverlay(const nsAString & aURL,nsIObserver * aObserver,ErrorResult & aRv)222     void LoadOverlay(const nsAString& aURL, nsIObserver* aObserver,
223                      ErrorResult& aRv)
224     {
225         aRv = LoadOverlay(aURL, aObserver);
226     }
227 
228 protected:
229     virtual ~XULDocument();
230 
231     // Implementation methods
232     friend nsresult
233     (::NS_NewXULDocument(nsIXULDocument** aResult));
234 
235     nsresult Init(void) override;
236     nsresult StartLayout(void);
237 
238     nsresult
239     AddElementToRefMap(Element* aElement);
240     void
241     RemoveElementFromRefMap(Element* aElement);
242 
243     nsresult GetViewportSize(int32_t* aWidth, int32_t* aHeight);
244 
245     nsresult PrepareToLoad(nsISupports* aContainer,
246                            const char* aCommand,
247                            nsIChannel* aChannel,
248                            nsILoadGroup* aLoadGroup,
249                            nsIParser** aResult);
250 
251     nsresult
252     PrepareToLoadPrototype(nsIURI* aURI,
253                            const char* aCommand,
254                            nsIPrincipal* aDocumentPrincipal,
255                            nsIParser** aResult);
256 
257     nsresult
258     LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, bool* aShouldReturn,
259                         bool* aFailureFromContent);
260 
261     nsresult ApplyPersistentAttributes();
262     nsresult ApplyPersistentAttributesInternal();
263     nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
264                                                  nsCOMArray<nsIContent>& aElements);
265 
266     nsresult
267     AddElementToDocumentPre(Element* aElement);
268 
269     nsresult
270     AddElementToDocumentPost(Element* aElement);
271 
272     nsresult
273     ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
274                                  Element* aListener,
275                                  nsIAtom* aAttr);
276 
277     nsresult
278     BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
279                                         int32_t aNameSpaceID,
280                                         nsIAtom* aAttribute,
281                                         nsIAtom* aPrefix,
282                                         const nsAString& aValue);
283 
284     already_AddRefed<nsPIWindowRoot> GetWindowRoot();
285 
286     static void DirectionChanged(const char* aPrefName, void* aData);
287 
288     // pseudo constants
289     static int32_t gRefCnt;
290 
291     static nsIAtom** kIdentityAttrs[];
292 
293     static nsIRDFService* gRDFService;
294     static nsIRDFResource* kNC_persist;
295     static nsIRDFResource* kNC_attribute;
296     static nsIRDFResource* kNC_value;
297 
298     static LazyLogModule gXULLog;
299 
300     nsresult
301     Persist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute);
302     // Just like Persist but ignores the return value so we can use it
303     // as a runnable method.
DoPersist(nsIContent * aElement,int32_t aNameSpaceID,nsIAtom * aAttribute)304     void DoPersist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute)
305     {
306         Persist(aElement, aNameSpaceID, aAttribute);
307     }
308 
309     virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
310 
311     // IMPORTANT: The ownership implicit in the following member
312     // variables has been explicitly checked and set using nsCOMPtr
313     // for owning pointers and raw COM interface pointers for weak
314     // (ie, non owning) references. If you add any members to this
315     // class, please make the ownership explicit (pinkerton, scc).
316     // NOTE, THIS IS STILL IN PROGRESS, TALK TO PINK OR SCC BEFORE
317     // CHANGING
318 
319     XULDocument*             mNextSrcLoadWaiter;  // [OWNER] but not COMPtr
320 
321     // Tracks elements with a 'ref' attribute, or an 'id' attribute where
322     // the element's namespace has no registered ID attribute name.
323     nsTHashtable<nsRefMapEntry> mRefMap;
324     nsCOMPtr<nsIXULStore>       mLocalStore;
325     bool                        mApplyingPersistedAttrs;
326     bool                        mIsWritingFastLoad;
327     bool                        mDocumentLoaded;
328     /**
329      * Since ResumeWalk is interruptible, it's possible that last
330      * stylesheet finishes loading while the PD walk is still in
331      * progress (waiting for an overlay to finish loading).
332      * mStillWalking prevents DoneLoading (and StartLayout) from being
333      * called in this situation.
334      */
335     bool                       mStillWalking;
336 
337     /**
338      * These two values control where persistent attributes get applied.
339      */
340     bool                           mRestrictPersistence;
341     nsTHashtable<nsStringHashKey>  mPersistenceIds;
342 
343     /**
344      * An array of style sheets, that will be added (preserving order) to the
345      * document after all of them are loaded (in DoneWalking).
346      */
347     nsTArray<RefPtr<StyleSheet>> mOverlaySheets;
348 
349     nsCOMPtr<nsIDOMXULCommandDispatcher>     mCommandDispatcher; // [OWNER] of the focus tracker
350 
351     // Maintains the template builders that have been attached to
352     // content elements
353     typedef nsInterfaceHashtable<nsISupportsHashKey, nsIXULTemplateBuilder>
354         BuilderTable;
355     BuilderTable* mTemplateBuilderTable;
356 
357     uint32_t mPendingSheets;
358 
359     /**
360      * document lightweight theme for use with :-moz-lwtheme, :-moz-lwtheme-brighttext
361      * and :-moz-lwtheme-darktext
362      */
363     DocumentTheme                         mDocLWTheme;
364 
365     /**
366      * Context stack, which maintains the state of the Builder and allows
367      * it to be interrupted.
368      */
369     class ContextStack {
370     protected:
371         struct Entry {
372             nsXULPrototypeElement* mPrototype;
373             nsIContent*            mElement;
374             int32_t                mIndex;
375             Entry*                 mNext;
376         };
377 
378         Entry* mTop;
379         int32_t mDepth;
380 
381     public:
382         ContextStack();
383         ~ContextStack();
384 
Depth()385         int32_t Depth() { return mDepth; }
386 
387         nsresult Push(nsXULPrototypeElement* aPrototype, nsIContent* aElement);
388         nsresult Pop();
389         nsresult Peek(nsXULPrototypeElement** aPrototype, nsIContent** aElement, int32_t* aIndex);
390 
391         nsresult SetTopIndex(int32_t aIndex);
392     };
393 
394     friend class ContextStack;
395     ContextStack mContextStack;
396 
397     enum State { eState_Master, eState_Overlay };
398     State mState;
399 
400     /**
401      * An array of overlay nsIURIs that have yet to be resolved. The
402      * order of the array is significant: overlays at the _end_ of the
403      * array are resolved before overlays earlier in the array (i.e.,
404      * it is a stack).
405      *
406      * In the current implementation the order the overlays are loaded
407      * in is as follows: first overlays from xul-overlay PIs, in the
408      * same order as in the document, then the overlays from the chrome
409      * registry.
410      */
411     nsTArray<nsCOMPtr<nsIURI> > mUnloadedOverlays;
412 
413     /**
414      * Load the transcluded script at the specified URI. If the
415      * prototype construction must 'block' until the load has
416      * completed, aBlock will be set to true.
417      */
418     nsresult LoadScript(nsXULPrototypeScript *aScriptProto, bool* aBlock);
419 
420     /**
421      * Execute the precompiled script object scoped by this XUL document's
422      * containing window object.
423      */
424     nsresult ExecuteScript(nsXULPrototypeScript *aScript);
425 
426     /**
427      * Create a delegate content model element from a prototype.
428      * Note that the resulting content node is not bound to any tree
429      */
430     nsresult CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
431                                         Element** aResult,
432                                         bool aIsRoot);
433 
434     /**
435      * Create a hook-up element to which content nodes can be attached for
436      * later resolution.
437      */
438     nsresult CreateOverlayElement(nsXULPrototypeElement* aPrototype,
439                                   Element** aResult);
440 
441     /**
442      * Add attributes from the prototype to the element.
443      */
444     nsresult AddAttributes(nsXULPrototypeElement* aPrototype, nsIContent* aElement);
445 
446     /**
447      * The prototype-script of the current transcluded script that is being
448      * loaded.  For document.write('<script src="nestedwrite.js"><\/script>')
449      * to work, these need to be in a stack element type, and we need to hold
450      * the top of stack here.
451      */
452     nsXULPrototypeScript* mCurrentScriptProto;
453 
454     /**
455      * Whether the current transcluded script is being compiled off thread.
456      * The load event is blocked while this is in progress.
457      */
458     bool mOffThreadCompiling;
459 
460     /**
461      * If the current transcluded script is being compiled off thread, the
462      * source for that script.
463      */
464     char16_t* mOffThreadCompileStringBuf;
465     size_t mOffThreadCompileStringLength;
466 
467     /**
468      * Check if a XUL template builder has already been hooked up.
469      */
470     static nsresult
471     CheckTemplateBuilderHookup(nsIContent* aElement, bool* aNeedsHookup);
472 
473     /**
474      * Create a XUL template builder on the specified node.
475      */
476     static nsresult
477     CreateTemplateBuilder(nsIContent* aElement);
478 
479     /**
480      * Add the current prototype's style sheets (currently it's just
481      * style overlays from the chrome registry) to the document.
482      */
483     nsresult AddPrototypeSheets();
484 
485 
486 protected:
487     /* Declarations related to forward references.
488      *
489      * Forward references are declarations which are added to the temporary
490      * list (mForwardReferences) during the document (or overlay) load and
491      * are resolved later, when the document loading is almost complete.
492      */
493 
494     /**
495      * The list of different types of forward references to resolve. After
496      * a reference is resolved, it is removed from this array (and
497      * automatically deleted)
498      */
499     nsTArray<nsAutoPtr<nsForwardReference> > mForwardReferences;
500 
501     /** Indicates what kind of forward references are still to be processed. */
502     nsForwardReference::Phase mResolutionPhase;
503 
504     /**
505      * Adds aRef to the mForwardReferences array. Takes the ownership of aRef.
506      */
507     nsresult AddForwardReference(nsForwardReference* aRef);
508 
509     /**
510      * Resolve all of the document's forward references.
511      */
512     nsresult ResolveForwardReferences();
513 
514     /**
515      * Used to resolve broadcaster references
516      */
517     class BroadcasterHookup : public nsForwardReference
518     {
519     protected:
520         XULDocument* mDocument;              // [WEAK]
521         RefPtr<Element> mObservesElement; // [OWNER]
522         bool mResolved;
523 
524     public:
BroadcasterHookup(XULDocument * aDocument,Element * aObservesElement)525         BroadcasterHookup(XULDocument* aDocument,
526                           Element* aObservesElement)
527             : mDocument(aDocument),
528               mObservesElement(aObservesElement),
529               mResolved(false)
530         {
531         }
532 
533         virtual ~BroadcasterHookup();
534 
GetPhase()535         virtual Phase GetPhase() override { return eHookup; }
536         virtual Result Resolve() override;
537     };
538 
539     friend class BroadcasterHookup;
540 
541 
542     /**
543      * Used to hook up overlays
544      */
545     class OverlayForwardReference : public nsForwardReference
546     {
547     protected:
548         XULDocument* mDocument;      // [WEAK]
549         nsCOMPtr<nsIContent> mOverlay; // [OWNER]
550         bool mResolved;
551 
552         nsresult Merge(nsIContent* aTargetNode, nsIContent* aOverlayNode, bool aNotify);
553 
554     public:
OverlayForwardReference(XULDocument * aDocument,nsIContent * aOverlay)555         OverlayForwardReference(XULDocument* aDocument, nsIContent* aOverlay)
556             : mDocument(aDocument), mOverlay(aOverlay), mResolved(false) {}
557 
558         virtual ~OverlayForwardReference();
559 
GetPhase()560         virtual Phase GetPhase() override { return eConstruction; }
561         virtual Result Resolve() override;
562     };
563 
564     friend class OverlayForwardReference;
565 
566     class TemplateBuilderHookup : public nsForwardReference
567     {
568     protected:
569         nsCOMPtr<nsIContent> mElement; // [OWNER]
570 
571     public:
TemplateBuilderHookup(nsIContent * aElement)572         explicit TemplateBuilderHookup(nsIContent* aElement)
573             : mElement(aElement) {}
574 
GetPhase()575         virtual Phase GetPhase() override { return eHookup; }
576         virtual Result Resolve() override;
577     };
578 
579     friend class TemplateBuilderHookup;
580 
581     // The out params of FindBroadcaster only have values that make sense when
582     // the method returns NS_FINDBROADCASTER_FOUND.  In all other cases, the
583     // values of the out params should not be relied on (though *aListener and
584     // *aBroadcaster do need to be released if non-null, of course).
585     nsresult
586     FindBroadcaster(Element* aElement,
587                     Element** aListener,
588                     nsString& aBroadcasterID,
589                     nsString& aAttribute,
590                     Element** aBroadcaster);
591 
592     nsresult
593     CheckBroadcasterHookup(Element* aElement,
594                            bool* aNeedsHookup,
595                            bool* aDidResolve);
596 
597     void
598     SynchronizeBroadcastListener(Element *aBroadcaster,
599                                  Element *aListener,
600                                  const nsAString &aAttr);
601 
602     static
603     nsresult
604     InsertElement(nsINode* aParent, nsIContent* aChild, bool aNotify);
605 
606     static
607     nsresult
608     RemoveElement(nsINode* aParent, nsINode* aChild);
609 
610     /**
611      * The current prototype that we are walking to construct the
612      * content model.
613      */
614     RefPtr<nsXULPrototypeDocument> mCurrentPrototype;
615 
616     /**
617      * The master document (outermost, .xul) prototype, from which
618      * all subdocuments get their security principals.
619      */
620     RefPtr<nsXULPrototypeDocument> mMasterPrototype;
621 
622     /**
623      * Owning references to all of the prototype documents that were
624      * used to construct this document.
625      */
626     nsTArray< RefPtr<nsXULPrototypeDocument> > mPrototypes;
627 
628     /**
629      * Prepare to walk the current prototype.
630      */
631     nsresult PrepareToWalk();
632 
633     /**
634      * Creates a processing instruction based on aProtoPI and inserts
635      * it to the DOM (as the aIndex-th child of aParent).
636      */
637     nsresult
638     CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
639                       nsINode* aParent, uint32_t aIndex);
640 
641     /**
642      * Inserts the passed <?xml-stylesheet ?> PI at the specified
643      * index. Loads and applies the associated stylesheet
644      * asynchronously.
645      * The prototype document walk can happen before the stylesheets
646      * are loaded, but the final steps in the load process (see
647      * DoneWalking()) are not run before all the stylesheets are done
648      * loading.
649      */
650     nsresult
651     InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
652                           nsINode* aParent,
653                           uint32_t aIndex,
654                           nsIContent* aPINode);
655 
656     /**
657      * Inserts the passed <?xul-overlay ?> PI at the specified index.
658      * Schedules the referenced overlay URI for further processing.
659      */
660     nsresult
661     InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
662                        nsINode* aParent,
663                        uint32_t aIndex,
664                        nsIContent* aPINode);
665 
666     /**
667      * Add overlays from the chrome registry to the set of unprocessed
668      * overlays still to do.
669      */
670     nsresult AddChromeOverlays();
671 
672     /**
673      * Resume (or initiate) an interrupted (or newly prepared)
674      * prototype walk.
675      */
676     nsresult ResumeWalk();
677 
678     /**
679      * Called at the end of ResumeWalk() and from StyleSheetLoaded().
680      * Expects that both the prototype document walk is complete and
681      * all referenced stylesheets finished loading.
682      */
683     nsresult DoneWalking();
684 
685     /**
686      * Report that an overlay failed to load
687      * @param aURI the URI of the overlay that failed to load
688      */
689     void ReportMissingOverlay(nsIURI* aURI);
690 
691     class CachedChromeStreamListener : public nsIStreamListener {
692     protected:
693         RefPtr<XULDocument> mDocument;
694         bool mProtoLoaded;
695 
696         virtual ~CachedChromeStreamListener();
697 
698     public:
699         CachedChromeStreamListener(XULDocument* aDocument,
700                                    bool aProtoLoaded);
701 
702         NS_DECL_ISUPPORTS
703         NS_DECL_NSIREQUESTOBSERVER
704         NS_DECL_NSISTREAMLISTENER
705     };
706 
707     friend class CachedChromeStreamListener;
708 
709 
710     class ParserObserver : public nsIRequestObserver {
711     protected:
712         RefPtr<XULDocument> mDocument;
713         RefPtr<nsXULPrototypeDocument> mPrototype;
714         virtual ~ParserObserver();
715 
716     public:
717         ParserObserver(XULDocument* aDocument,
718                        nsXULPrototypeDocument* aPrototype);
719 
720         NS_DECL_ISUPPORTS
721         NS_DECL_NSIREQUESTOBSERVER
722     };
723 
724     friend class ParserObserver;
725 
726     /**
727      * A map from a broadcaster element to a list of listener elements.
728      */
729     PLDHashTable* mBroadcasterMap;
730 
731     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mOverlayLoadObservers;
732     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mPendingOverlayLoadNotifications;
733 
734     bool mInitialLayoutComplete;
735 
736     class nsDelayedBroadcastUpdate
737     {
738     public:
nsDelayedBroadcastUpdate(Element * aBroadcaster,Element * aListener,const nsAString & aAttr)739       nsDelayedBroadcastUpdate(Element* aBroadcaster,
740                                Element* aListener,
741                                const nsAString &aAttr)
742       : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
743         mSetAttr(false), mNeedsAttrChange(false) {}
744 
nsDelayedBroadcastUpdate(Element * aBroadcaster,Element * aListener,nsIAtom * aAttrName,const nsAString & aAttr,bool aSetAttr,bool aNeedsAttrChange)745       nsDelayedBroadcastUpdate(Element* aBroadcaster,
746                                Element* aListener,
747                                nsIAtom* aAttrName,
748                                const nsAString &aAttr,
749                                bool aSetAttr,
750                                bool aNeedsAttrChange)
751       : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr),
752         mAttrName(aAttrName), mSetAttr(aSetAttr),
753         mNeedsAttrChange(aNeedsAttrChange) {}
754 
nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate & aOther)755       nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther)
756       : mBroadcaster(aOther.mBroadcaster), mListener(aOther.mListener),
757         mAttr(aOther.mAttr), mAttrName(aOther.mAttrName),
758         mSetAttr(aOther.mSetAttr), mNeedsAttrChange(aOther.mNeedsAttrChange) {}
759 
760       nsCOMPtr<Element>       mBroadcaster;
761       nsCOMPtr<Element>       mListener;
762       // Note if mAttrName isn't used, this is the name of the attr, otherwise
763       // this is the value of the attribute.
764       nsString                mAttr;
765       nsCOMPtr<nsIAtom>       mAttrName;
766       bool                    mSetAttr;
767       bool                    mNeedsAttrChange;
768 
769       class Comparator {
770         public:
Equals(const nsDelayedBroadcastUpdate & a,const nsDelayedBroadcastUpdate & b)771           static bool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) {
772             return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName;
773           }
774       };
775     };
776 
777     nsTArray<nsDelayedBroadcastUpdate> mDelayedBroadcasters;
778     nsTArray<nsDelayedBroadcastUpdate> mDelayedAttrChangeBroadcasts;
779     bool                               mHandlingDelayedAttrChange;
780     bool                               mHandlingDelayedBroadcasters;
781 
782     void MaybeBroadcast();
783 private:
784     // helpers
785 
786 };
787 
788 } // namespace dom
789 } // namespace mozilla
790 
791 #endif // mozilla_dom_XULDocument_h
792