1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is HTML Parser Gecko integration code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Henri Sivonen <hsivonen@iki.fi>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #ifndef nsHtml5TreeOpExecutor_h__
39 : #define nsHtml5TreeOpExecutor_h__
40 :
41 : #include "prtypes.h"
42 : #include "nsIAtom.h"
43 : #include "nsINameSpaceManager.h"
44 : #include "nsIContent.h"
45 : #include "nsIDocument.h"
46 : #include "nsTraceRefcnt.h"
47 : #include "nsHtml5TreeOperation.h"
48 : #include "nsHtml5SpeculativeLoad.h"
49 : #include "nsHtml5PendingNotification.h"
50 : #include "nsTArray.h"
51 : #include "nsContentSink.h"
52 : #include "nsNodeInfoManager.h"
53 : #include "nsHtml5DocumentMode.h"
54 : #include "nsIScriptElement.h"
55 : #include "nsIParser.h"
56 : #include "nsAHtml5TreeOpSink.h"
57 : #include "nsHtml5TreeOpStage.h"
58 : #include "nsIURI.h"
59 : #include "nsTHashtable.h"
60 : #include "nsHashKeys.h"
61 :
62 : class nsHtml5Parser;
63 : class nsHtml5TreeBuilder;
64 : class nsHtml5Tokenizer;
65 : class nsHtml5StreamParser;
66 :
67 : typedef nsIContent* nsIContentPtr;
68 :
69 : enum eHtml5FlushState {
70 : eNotFlushing = 0, // not flushing
71 : eInFlush = 1, // the Flush() method is on the call stack
72 : eInDocUpdate = 2, // inside an update batch on the document
73 : eNotifying = 3 // flushing pending append notifications
74 : };
75 :
76 : class nsHtml5TreeOpExecutor : public nsContentSink,
77 : public nsIContentSink,
78 : public nsAHtml5TreeOpSink
79 : {
80 : friend class nsHtml5FlushLoopGuard;
81 :
82 : public:
83 48 : NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
84 : NS_DECL_ISUPPORTS_INHERITED
85 1466 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5TreeOpExecutor, nsContentSink)
86 :
87 : private:
88 : #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
89 : static PRUint32 sAppendBatchMaxSize;
90 : static PRUint32 sAppendBatchSlotsExamined;
91 : static PRUint32 sAppendBatchExaminations;
92 : static PRUint32 sLongestTimeOffTheEventLoop;
93 : static PRUint32 sTimesFlushLoopInterrupted;
94 : #endif
95 :
96 : /**
97 : * Whether EOF needs to be suppressed
98 : */
99 : bool mSuppressEOF;
100 :
101 : bool mReadingFromStage;
102 : nsTArray<nsHtml5TreeOperation> mOpQueue;
103 : nsTArray<nsIContentPtr> mElementsSeenInThisAppendBatch;
104 : nsTArray<nsHtml5PendingNotification> mPendingNotifications;
105 : nsHtml5StreamParser* mStreamParser;
106 : nsTArray<nsCOMPtr<nsIContent> > mOwnedElements;
107 :
108 : /**
109 : * URLs already preloaded/preloading.
110 : */
111 : nsTHashtable<nsCStringHashKey> mPreloadedURLs;
112 :
113 : nsCOMPtr<nsIURI> mSpeculationBaseURI;
114 :
115 : nsCOMPtr<nsIURI> mViewSourceBaseURI;
116 :
117 : /**
118 : * Whether the parser has started
119 : */
120 : bool mStarted;
121 :
122 : nsHtml5TreeOpStage mStage;
123 :
124 : eHtml5FlushState mFlushState;
125 :
126 : bool mRunFlushLoopOnStack;
127 :
128 : bool mCallContinueInterruptedParsingIfEnabled;
129 :
130 : /**
131 : * True if this parser should refuse to process any more input.
132 : * Currently, the only way a parser can break is if it drops some input
133 : * due to a memory allocation failure. In such a case, the whole parser
134 : * needs to be marked as broken, because some input has been lost and
135 : * parsing more input could lead to a DOM where pieces of HTML source
136 : * that weren't supposed to become scripts become scripts.
137 : */
138 : bool mBroken;
139 :
140 : public:
141 :
142 : nsHtml5TreeOpExecutor(bool aRunsToCompletion = false);
143 : virtual ~nsHtml5TreeOpExecutor();
144 :
145 : // nsIContentSink
146 :
147 : /**
148 : * Unimplemented. For interface compat only.
149 : */
150 : NS_IMETHOD WillParse();
151 :
152 : /**
153 : *
154 : */
155 0 : NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) {
156 0 : NS_ASSERTION(!mDocShell || GetDocument()->GetScriptGlobalObject(),
157 : "Script global object not ready");
158 0 : mDocument->AddObserver(this);
159 0 : WillBuildModelImpl();
160 0 : GetDocument()->BeginLoad();
161 0 : return NS_OK;
162 : }
163 :
164 : /**
165 : * Emits EOF.
166 : */
167 : NS_IMETHOD DidBuildModel(bool aTerminated);
168 :
169 : /**
170 : * Forwards to nsContentSink
171 : */
172 : NS_IMETHOD WillInterrupt();
173 :
174 : /**
175 : * Unimplemented. For interface compat only.
176 : */
177 : NS_IMETHOD WillResume();
178 :
179 : /**
180 : * Sets the parser.
181 : */
182 : NS_IMETHOD SetParser(nsParserBase* aParser);
183 :
184 : /**
185 : * No-op for backwards compat.
186 : */
187 : virtual void FlushPendingNotifications(mozFlushType aType);
188 :
189 : /**
190 : * Don't call. For interface compat only.
191 : */
192 0 : NS_IMETHOD SetDocumentCharset(nsACString& aCharset) {
193 0 : NS_NOTREACHED("No one should call this.");
194 0 : return NS_ERROR_NOT_IMPLEMENTED;
195 : }
196 :
197 : /**
198 : * Returns the document.
199 : */
200 : virtual nsISupports *GetTarget();
201 :
202 : // nsContentSink methods
203 : virtual void UpdateChildCounts();
204 : virtual nsresult FlushTags();
205 : virtual void ContinueInterruptedParsingAsync();
206 :
207 : /**
208 : * Sets up style sheet load / parse
209 : */
210 : void UpdateStyleSheet(nsIContent* aElement);
211 :
212 : // Getters and setters for fields from nsContentSink
213 1067 : nsIDocument* GetDocument() {
214 1067 : return mDocument;
215 : }
216 1066 : nsNodeInfoManager* GetNodeInfoManager() {
217 1066 : return mNodeInfoManager;
218 : }
219 : nsIDocShell* GetDocShell() {
220 : return mDocShell;
221 : }
222 :
223 0 : bool IsScriptExecuting() {
224 0 : return IsScriptExecutingImpl();
225 : }
226 :
227 234 : void SetNodeInfoManager(nsNodeInfoManager* aManager) {
228 234 : mNodeInfoManager = aManager;
229 234 : }
230 :
231 : // Not from interface
232 :
233 : void SetDocumentCharsetAndSource(nsACString& aCharset, PRInt32 aCharsetSource);
234 :
235 0 : void SetStreamParser(nsHtml5StreamParser* aStreamParser) {
236 0 : mStreamParser = aStreamParser;
237 0 : }
238 :
239 : void InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState, PRInt32 aLine);
240 :
241 : bool IsScriptEnabled();
242 :
243 : /**
244 : * Enables the fragment mode.
245 : *
246 : * @param aPreventScriptExecution if true, scripts are prevented from
247 : * executing; don't set to false when parsing a fragment directly into
248 : * a document--only when parsing to an actual DOM fragment
249 : */
250 0 : void EnableFragmentMode(bool aPreventScriptExecution) {
251 0 : mPreventScriptExecution = aPreventScriptExecution;
252 0 : }
253 :
254 234 : void PreventScriptExecution() {
255 234 : mPreventScriptExecution = true;
256 234 : }
257 :
258 780 : bool BelongsToStringParser() {
259 780 : return mRunsToCompletion;
260 : }
261 :
262 : /**
263 : * Marks this parser as broken and tells the stream parser (if any) to
264 : * terminate.
265 : */
266 : void MarkAsBroken();
267 :
268 : /**
269 : * Checks if this parser is broken.
270 : */
271 0 : inline bool IsBroken() {
272 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
273 0 : return mBroken;
274 : }
275 :
276 246 : inline void BeginDocUpdate() {
277 246 : NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update.");
278 246 : NS_PRECONDITION(mParser, "Started update without parser.");
279 246 : mFlushState = eInDocUpdate;
280 246 : mDocument->BeginUpdate(UPDATE_CONTENT_MODEL);
281 246 : }
282 :
283 480 : inline void EndDocUpdate() {
284 480 : NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync");
285 480 : if (mFlushState == eInDocUpdate) {
286 246 : FlushPendingAppendNotifications();
287 246 : mFlushState = eInFlush;
288 246 : mDocument->EndUpdate(UPDATE_CONTENT_MODEL);
289 : }
290 480 : }
291 :
292 832 : void PostPendingAppendNotification(nsIContent* aParent, nsIContent* aChild) {
293 832 : bool newParent = true;
294 832 : const nsIContentPtr* first = mElementsSeenInThisAppendBatch.Elements();
295 832 : const nsIContentPtr* last = first + mElementsSeenInThisAppendBatch.Length() - 1;
296 1101 : for (const nsIContentPtr* iter = last; iter >= first; --iter) {
297 : #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
298 : sAppendBatchSlotsExamined++;
299 : #endif
300 855 : if (*iter == aParent) {
301 586 : newParent = false;
302 586 : break;
303 : }
304 : }
305 832 : if (aChild->IsElement()) {
306 546 : mElementsSeenInThisAppendBatch.AppendElement(aChild);
307 : }
308 832 : mElementsSeenInThisAppendBatch.AppendElement(aParent);
309 832 : if (newParent) {
310 246 : mPendingNotifications.AppendElement(aParent);
311 : }
312 : #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
313 : sAppendBatchExaminations++;
314 : #endif
315 832 : }
316 :
317 480 : void FlushPendingAppendNotifications() {
318 480 : NS_PRECONDITION(mFlushState == eInDocUpdate, "Notifications flushed outside update");
319 480 : mFlushState = eNotifying;
320 480 : const nsHtml5PendingNotification* start = mPendingNotifications.Elements();
321 480 : const nsHtml5PendingNotification* end = start + mPendingNotifications.Length();
322 726 : for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) {
323 246 : iter->Fire();
324 : }
325 480 : mPendingNotifications.Clear();
326 : #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH
327 : if (mElementsSeenInThisAppendBatch.Length() > sAppendBatchMaxSize) {
328 : sAppendBatchMaxSize = mElementsSeenInThisAppendBatch.Length();
329 : }
330 : #endif
331 480 : mElementsSeenInThisAppendBatch.Clear();
332 480 : NS_ASSERTION(mFlushState == eNotifying, "mFlushState out of sync");
333 480 : mFlushState = eInDocUpdate;
334 480 : }
335 :
336 1 : inline bool HaveNotified(nsIContent* aNode) {
337 1 : NS_PRECONDITION(aNode, "HaveNotified called with null argument.");
338 1 : const nsHtml5PendingNotification* start = mPendingNotifications.Elements();
339 1 : const nsHtml5PendingNotification* end = start + mPendingNotifications.Length();
340 1 : for (;;) {
341 2 : nsIContent* parent = aNode->GetParent();
342 2 : if (!parent) {
343 0 : return true;
344 : }
345 3 : for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) {
346 2 : if (iter->Contains(parent)) {
347 1 : return iter->HaveNotifiedIndex(parent->IndexOf(aNode));
348 : }
349 : }
350 1 : aNode = parent;
351 : }
352 : }
353 :
354 : void StartLayout();
355 :
356 : void SetDocumentMode(nsHtml5DocumentMode m);
357 :
358 : nsresult Init(nsIDocument* aDoc, nsIURI* aURI,
359 : nsISupports* aContainer, nsIChannel* aChannel);
360 :
361 : void FlushSpeculativeLoads();
362 :
363 : void RunFlushLoop();
364 :
365 : void FlushDocumentWrite();
366 :
367 : void MaybeSuspend();
368 :
369 : void Start();
370 :
371 : void NeedsCharsetSwitchTo(const char* aEncoding, PRInt32 aSource);
372 :
373 0 : bool IsComplete() {
374 0 : return !mParser;
375 : }
376 :
377 234 : bool HasStarted() {
378 234 : return mStarted;
379 : }
380 :
381 0 : bool IsFlushing() {
382 0 : return mFlushState >= eInFlush;
383 : }
384 :
385 : #ifdef DEBUG
386 0 : bool IsInFlushLoop() {
387 0 : return mRunFlushLoopOnStack;
388 : }
389 : #endif
390 :
391 : void RunScript(nsIContent* aScriptElement);
392 :
393 : void Reset();
394 :
395 780 : inline void HoldElement(nsIContent* aContent) {
396 780 : mOwnedElements.AppendElement(aContent);
397 780 : }
398 :
399 : void DropHeldElements();
400 :
401 : /**
402 : * Flush the operations from the tree operations from the argument
403 : * queue unconditionally. (This is for the main thread case.)
404 : */
405 : virtual void MoveOpsFrom(nsTArray<nsHtml5TreeOperation>& aOpQueue);
406 :
407 0 : nsHtml5TreeOpStage* GetStage() {
408 0 : return &mStage;
409 : }
410 :
411 0 : void StartReadingFromStage() {
412 0 : mReadingFromStage = true;
413 0 : }
414 :
415 : void StreamEnded();
416 :
417 : #ifdef DEBUG
418 0 : void AssertStageEmpty() {
419 0 : mStage.AssertEmpty();
420 0 : }
421 : #endif
422 :
423 : nsIURI* GetViewSourceBaseURI();
424 :
425 : void PreloadScript(const nsAString& aURL,
426 : const nsAString& aCharset,
427 : const nsAString& aType,
428 : const nsAString& aCrossOrigin);
429 :
430 : void PreloadStyle(const nsAString& aURL, const nsAString& aCharset);
431 :
432 : void PreloadImage(const nsAString& aURL, const nsAString& aCrossOrigin);
433 :
434 : void SetSpeculationBase(const nsAString& aURL);
435 :
436 : private:
437 : nsHtml5Parser* GetParser();
438 :
439 : /**
440 : * Get a nsIURI for an nsString if the URL hasn't been preloaded yet.
441 : */
442 : already_AddRefed<nsIURI> ConvertIfNotPreloadedYet(const nsAString& aURL);
443 :
444 : };
445 :
446 : #endif // nsHtml5TreeOpExecutor_h__
|