/src/mozilla-central/dom/xul/XULDocument.h
Line | Count | Source (jump to first uncovered line) |
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 "nsTArray.h" |
13 | | |
14 | | #include "mozilla/dom/XMLDocument.h" |
15 | | #include "mozilla/StyleSheet.h" |
16 | | #include "nsIContent.h" |
17 | | #include "nsCOMArray.h" |
18 | | #include "nsIURI.h" |
19 | | #include "nsIStreamListener.h" |
20 | | #include "nsIStreamLoader.h" |
21 | | #include "nsICSSLoaderObserver.h" |
22 | | #include "nsIXULStore.h" |
23 | | |
24 | | #include "mozilla/Attributes.h" |
25 | | #include "mozilla/dom/ScriptLoader.h" |
26 | | |
27 | | #include "js/TracingAPI.h" |
28 | | #include "js/TypeDecls.h" |
29 | | |
30 | | class nsPIWindowRoot; |
31 | | class nsXULPrototypeElement; |
32 | | #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript)) |
33 | | class nsIObjectInputStream; |
34 | | class nsIObjectOutputStream; |
35 | | #else |
36 | | #include "nsIObjectInputStream.h" |
37 | | #include "nsIObjectOutputStream.h" |
38 | | #include "nsXULElement.h" |
39 | | #endif |
40 | | #include "nsURIHashKey.h" |
41 | | #include "nsInterfaceHashtable.h" |
42 | | |
43 | | /** |
44 | | * The XUL document class |
45 | | */ |
46 | | |
47 | | // Factory function. |
48 | | nsresult NS_NewXULDocument(nsIDocument** result); |
49 | | |
50 | | namespace mozilla { |
51 | | namespace dom { |
52 | | |
53 | | class XULDocument final : public XMLDocument, |
54 | | public nsIStreamLoaderObserver, |
55 | | public nsICSSLoaderObserver, |
56 | | public nsIOffThreadScriptReceiver |
57 | | { |
58 | | public: |
59 | | XULDocument(); |
60 | | |
61 | | // nsISupports interface |
62 | | NS_DECL_ISUPPORTS_INHERITED |
63 | | NS_DECL_NSISTREAMLOADEROBSERVER |
64 | | |
65 | | // nsIDocument interface |
66 | | virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override; |
67 | | virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup, |
68 | | nsIPrincipal* aPrincipal) override; |
69 | | |
70 | | virtual nsresult StartDocumentLoad(const char* aCommand, |
71 | | nsIChannel *channel, |
72 | | nsILoadGroup* aLoadGroup, |
73 | | nsISupports* aContainer, |
74 | | nsIStreamListener **aDocListener, |
75 | | bool aReset = true, |
76 | | nsIContentSink* aSink = nullptr) override; |
77 | | |
78 | | virtual void SetContentType(const nsAString& aContentType) override; |
79 | | |
80 | | virtual void EndLoad() override; |
81 | | |
82 | | // nsIMutationObserver interface |
83 | | NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED |
84 | | NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED |
85 | | NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED |
86 | | NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED |
87 | | |
88 | | /** |
89 | | * Notify the XUL document that a subtree has been added |
90 | | */ |
91 | | nsresult AddSubtreeToDocument(nsIContent* aContent); |
92 | | /** |
93 | | * Notify the XUL document that a subtree has been removed |
94 | | */ |
95 | | nsresult RemoveSubtreeFromDocument(nsIContent* aContent); |
96 | | /** |
97 | | * This is invoked whenever the prototype for this document is loaded |
98 | | * and should be walked, regardless of whether the XUL cache is |
99 | | * disabled, whether the protototype was loaded, whether the |
100 | | * prototype was loaded from the cache or created by parsing the |
101 | | * actual XUL source, etc. |
102 | | * |
103 | | * @param aResumeWalk whether this should also call ResumeWalk(). |
104 | | * Sometimes the caller of OnPrototypeLoadDone resumes the walk itself |
105 | | */ |
106 | | nsresult OnPrototypeLoadDone(bool aResumeWalk); |
107 | | |
108 | | // nsINode interface overrides |
109 | | virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; |
110 | | |
111 | | // nsICSSLoaderObserver |
112 | | NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, |
113 | | bool aWasAlternate, |
114 | | nsresult aStatus) override; |
115 | | |
116 | | virtual void EndUpdate() override; |
117 | | |
118 | | virtual bool IsDocumentRightToLeft() override; |
119 | | |
120 | | /** |
121 | | * Reset the document direction so that it is recomputed. |
122 | | */ |
123 | | void ResetDocumentDirection(); |
124 | | |
125 | | NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override; |
126 | | |
127 | | NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument) |
128 | | |
129 | | void TraceProtos(JSTracer* aTrc); |
130 | | |
131 | | void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener, |
132 | | const nsAString& aAttr); |
133 | | |
134 | | protected: |
135 | | virtual ~XULDocument(); |
136 | | |
137 | | // Implementation methods |
138 | | friend nsresult |
139 | | (::NS_NewXULDocument(nsIDocument** aResult)); |
140 | | |
141 | | nsresult Init(void) override; |
142 | | nsresult StartLayout(void); |
143 | | |
144 | | nsresult PrepareToLoad(nsISupports* aContainer, |
145 | | const char* aCommand, |
146 | | nsIChannel* aChannel, |
147 | | nsILoadGroup* aLoadGroup, |
148 | | nsIParser** aResult); |
149 | | |
150 | | nsresult |
151 | | PrepareToLoadPrototype(nsIURI* aURI, |
152 | | const char* aCommand, |
153 | | nsIPrincipal* aDocumentPrincipal, |
154 | | nsIParser** aResult); |
155 | | |
156 | | nsresult ApplyPersistentAttributes(); |
157 | | nsresult ApplyPersistentAttributesInternal(); |
158 | | nsresult ApplyPersistentAttributesToElements(const nsAString &aID, |
159 | | nsCOMArray<Element>& aElements); |
160 | | |
161 | | nsresult |
162 | | AddElementToDocumentPre(Element* aElement); |
163 | | |
164 | | nsresult |
165 | | AddElementToDocumentPost(Element* aElement); |
166 | | |
167 | | void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, |
168 | | const nsAString& aAttr, ErrorResult& aRv); |
169 | | |
170 | | nsresult |
171 | | ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, |
172 | | Element* aListener, |
173 | | nsAtom* aAttr); |
174 | | |
175 | | static void DirectionChanged(const char* aPrefName, XULDocument* aData); |
176 | | |
177 | | // pseudo constants |
178 | | static int32_t gRefCnt; |
179 | | |
180 | | static LazyLogModule gXULLog; |
181 | | |
182 | | void |
183 | | Persist(mozilla::dom::Element* aElement, |
184 | | int32_t aNameSpaceID, |
185 | | nsAtom* aAttribute); |
186 | | |
187 | | virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; |
188 | | |
189 | | // IMPORTANT: The ownership implicit in the following member |
190 | | // variables has been explicitly checked and set using nsCOMPtr |
191 | | // for owning pointers and raw COM interface pointers for weak |
192 | | // (ie, non owning) references. If you add any members to this |
193 | | // class, please make the ownership explicit (pinkerton, scc). |
194 | | // NOTE, THIS IS STILL IN PROGRESS, TALK TO PINK OR SCC BEFORE |
195 | | // CHANGING |
196 | | |
197 | | XULDocument* mNextSrcLoadWaiter; // [OWNER] but not COMPtr |
198 | | |
199 | | nsCOMPtr<nsIXULStore> mLocalStore; |
200 | | bool mApplyingPersistedAttrs; |
201 | | bool mIsWritingFastLoad; |
202 | | bool mDocumentLoaded; |
203 | | /** |
204 | | * Since ResumeWalk is interruptible, it's possible that last |
205 | | * stylesheet finishes loading while the PD walk is still in |
206 | | * progress (waiting for an overlay to finish loading). |
207 | | * mStillWalking prevents DoneLoading (and StartLayout) from being |
208 | | * called in this situation. |
209 | | */ |
210 | | bool mStillWalking; |
211 | | |
212 | | uint32_t mPendingSheets; |
213 | | |
214 | | /** |
215 | | * Context stack, which maintains the state of the Builder and allows |
216 | | * it to be interrupted. |
217 | | */ |
218 | | class ContextStack { |
219 | | protected: |
220 | | struct Entry { |
221 | | nsXULPrototypeElement* mPrototype; |
222 | | nsIContent* mElement; |
223 | | int32_t mIndex; |
224 | | Entry* mNext; |
225 | | }; |
226 | | |
227 | | Entry* mTop; |
228 | | int32_t mDepth; |
229 | | |
230 | | public: |
231 | | ContextStack(); |
232 | | ~ContextStack(); |
233 | | |
234 | 0 | int32_t Depth() { return mDepth; } |
235 | | |
236 | | nsresult Push(nsXULPrototypeElement* aPrototype, nsIContent* aElement); |
237 | | nsresult Pop(); |
238 | | nsresult Peek(nsXULPrototypeElement** aPrototype, nsIContent** aElement, int32_t* aIndex); |
239 | | |
240 | | nsresult SetTopIndex(int32_t aIndex); |
241 | | }; |
242 | | |
243 | | friend class ContextStack; |
244 | | ContextStack mContextStack; |
245 | | |
246 | | /** |
247 | | * Load the transcluded script at the specified URI. If the |
248 | | * prototype construction must 'block' until the load has |
249 | | * completed, aBlock will be set to true. |
250 | | */ |
251 | | nsresult LoadScript(nsXULPrototypeScript *aScriptProto, bool* aBlock); |
252 | | |
253 | | /** |
254 | | * Execute the precompiled script object scoped by this XUL document's |
255 | | * containing window object. |
256 | | */ |
257 | | nsresult ExecuteScript(nsXULPrototypeScript *aScript); |
258 | | |
259 | | /** |
260 | | * Create a delegate content model element from a prototype. |
261 | | * Note that the resulting content node is not bound to any tree |
262 | | */ |
263 | | nsresult CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, |
264 | | Element** aResult, |
265 | | bool aIsRoot); |
266 | | |
267 | | /** |
268 | | * Add attributes from the prototype to the element. |
269 | | */ |
270 | | nsresult AddAttributes(nsXULPrototypeElement* aPrototype, Element* aElement); |
271 | | |
272 | | /** |
273 | | * The prototype-script of the current transcluded script that is being |
274 | | * loaded. For document.write('<script src="nestedwrite.js"><\/script>') |
275 | | * to work, these need to be in a stack element type, and we need to hold |
276 | | * the top of stack here. |
277 | | */ |
278 | | nsXULPrototypeScript* mCurrentScriptProto; |
279 | | |
280 | | /** |
281 | | * Whether the current transcluded script is being compiled off thread. |
282 | | * The load event is blocked while this is in progress. |
283 | | */ |
284 | | bool mOffThreadCompiling; |
285 | | |
286 | | /** |
287 | | * If the current transcluded script is being compiled off thread, the |
288 | | * source for that script. |
289 | | */ |
290 | | char16_t* mOffThreadCompileStringBuf; |
291 | | size_t mOffThreadCompileStringLength; |
292 | | |
293 | | |
294 | | protected: |
295 | | // The out params of FindBroadcaster only have values that make sense when |
296 | | // the method returns NS_FINDBROADCASTER_FOUND. In all other cases, the |
297 | | // values of the out params should not be relied on (though *aListener and |
298 | | // *aBroadcaster do need to be released if non-null, of course). |
299 | | nsresult |
300 | | FindBroadcaster(Element* aElement, |
301 | | Element** aListener, |
302 | | nsString& aBroadcasterID, |
303 | | nsString& aAttribute, |
304 | | Element** aBroadcaster); |
305 | | |
306 | | nsresult |
307 | | CheckBroadcasterHookup(Element* aElement); |
308 | | |
309 | | void |
310 | | SynchronizeBroadcastListener(Element *aBroadcaster, |
311 | | Element *aListener, |
312 | | const nsAString &aAttr); |
313 | | |
314 | | /** |
315 | | * The current prototype that we are walking to construct the |
316 | | * content model. |
317 | | */ |
318 | | RefPtr<nsXULPrototypeDocument> mCurrentPrototype; |
319 | | |
320 | | /** |
321 | | * Owning references to all of the prototype documents that were |
322 | | * used to construct this document. |
323 | | */ |
324 | | nsTArray< RefPtr<nsXULPrototypeDocument> > mPrototypes; |
325 | | |
326 | | /** |
327 | | * Prepare to walk the current prototype. |
328 | | */ |
329 | | nsresult PrepareToWalk(); |
330 | | |
331 | | /** |
332 | | * Creates a processing instruction based on aProtoPI and inserts |
333 | | * it to the DOM. |
334 | | */ |
335 | | nsresult |
336 | | CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, |
337 | | nsINode* aParent, nsINode* aBeforeThis); |
338 | | |
339 | | /** |
340 | | * Inserts the passed <?xml-stylesheet ?> PI at the specified |
341 | | * index. Loads and applies the associated stylesheet |
342 | | * asynchronously. |
343 | | * The prototype document walk can happen before the stylesheets |
344 | | * are loaded, but the final steps in the load process (see |
345 | | * DoneWalking()) are not run before all the stylesheets are done |
346 | | * loading. |
347 | | */ |
348 | | nsresult |
349 | | InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, |
350 | | nsINode* aParent, |
351 | | nsINode* aBeforeThis, |
352 | | nsIContent* aPINode); |
353 | | |
354 | | /** |
355 | | * Resume (or initiate) an interrupted (or newly prepared) |
356 | | * prototype walk. |
357 | | */ |
358 | | nsresult ResumeWalk(); |
359 | | |
360 | | /** |
361 | | * Called at the end of ResumeWalk() and from StyleSheetLoaded(). |
362 | | * Expects that both the prototype document walk is complete and |
363 | | * all referenced stylesheets finished loading. |
364 | | */ |
365 | | nsresult DoneWalking(); |
366 | | |
367 | | class CachedChromeStreamListener : public nsIStreamListener { |
368 | | protected: |
369 | | RefPtr<XULDocument> mDocument; |
370 | | bool mProtoLoaded; |
371 | | |
372 | | virtual ~CachedChromeStreamListener(); |
373 | | |
374 | | public: |
375 | | CachedChromeStreamListener(XULDocument* aDocument, |
376 | | bool aProtoLoaded); |
377 | | |
378 | | NS_DECL_ISUPPORTS |
379 | | NS_DECL_NSIREQUESTOBSERVER |
380 | | NS_DECL_NSISTREAMLISTENER |
381 | | }; |
382 | | |
383 | | friend class CachedChromeStreamListener; |
384 | | |
385 | | /** |
386 | | * A map from a broadcaster element to a list of listener elements. |
387 | | */ |
388 | | PLDHashTable* mBroadcasterMap; |
389 | | |
390 | | bool mInitialLayoutComplete; |
391 | | |
392 | | class nsDelayedBroadcastUpdate |
393 | | { |
394 | | public: |
395 | | nsDelayedBroadcastUpdate(Element* aBroadcaster, |
396 | | Element* aListener, |
397 | | const nsAString &aAttr) |
398 | | : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr), |
399 | 0 | mSetAttr(false), mNeedsAttrChange(false) {} |
400 | | |
401 | | nsDelayedBroadcastUpdate(Element* aBroadcaster, |
402 | | Element* aListener, |
403 | | nsAtom* aAttrName, |
404 | | const nsAString &aAttr, |
405 | | bool aSetAttr, |
406 | | bool aNeedsAttrChange) |
407 | | : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr), |
408 | | mAttrName(aAttrName), mSetAttr(aSetAttr), |
409 | 0 | mNeedsAttrChange(aNeedsAttrChange) {} |
410 | | |
411 | | nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther) |
412 | | : mBroadcaster(aOther.mBroadcaster), mListener(aOther.mListener), |
413 | | mAttr(aOther.mAttr), mAttrName(aOther.mAttrName), |
414 | 0 | mSetAttr(aOther.mSetAttr), mNeedsAttrChange(aOther.mNeedsAttrChange) {} |
415 | | |
416 | | nsCOMPtr<Element> mBroadcaster; |
417 | | nsCOMPtr<Element> mListener; |
418 | | // Note if mAttrName isn't used, this is the name of the attr, otherwise |
419 | | // this is the value of the attribute. |
420 | | nsString mAttr; |
421 | | RefPtr<nsAtom> mAttrName; |
422 | | bool mSetAttr; |
423 | | bool mNeedsAttrChange; |
424 | | |
425 | | class Comparator { |
426 | | public: |
427 | 0 | static bool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) { |
428 | 0 | return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName; |
429 | 0 | } |
430 | | }; |
431 | | }; |
432 | | |
433 | | nsTArray<nsDelayedBroadcastUpdate> mDelayedBroadcasters; |
434 | | nsTArray<nsDelayedBroadcastUpdate> mDelayedAttrChangeBroadcasts; |
435 | | bool mHandlingDelayedAttrChange; |
436 | | bool mHandlingDelayedBroadcasters; |
437 | | |
438 | | void MaybeBroadcast(); |
439 | | private: |
440 | | // helpers |
441 | | |
442 | | }; |
443 | | |
444 | | } // namespace dom |
445 | | } // namespace mozilla |
446 | | |
447 | | inline mozilla::dom::XULDocument* |
448 | | nsIDocument::AsXULDocument() |
449 | 0 | { |
450 | 0 | MOZ_ASSERT(IsXULDocument()); |
451 | 0 | return static_cast<mozilla::dom::XULDocument*>(this); |
452 | 0 | } |
453 | | |
454 | | #endif // mozilla_dom_XULDocument_h |