1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=2 et tw=79: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : * Henri Sivonen <hsivonen@iki.fi>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsCompatibility.h"
42 : #include "nsScriptLoader.h"
43 : #include "nsNetUtil.h"
44 : #include "nsIStyleSheetLinkingElement.h"
45 : #include "nsIWebShellServices.h"
46 : #include "nsIDocShell.h"
47 : #include "nsEncoderDecoderUtils.h"
48 : #include "nsContentUtils.h"
49 : #include "nsICharsetDetector.h"
50 : #include "nsIScriptElement.h"
51 : #include "nsIMarkupDocumentViewer.h"
52 : #include "nsIDocShellTreeItem.h"
53 : #include "nsIContentViewer.h"
54 : #include "nsIScriptGlobalObjectOwner.h"
55 : #include "nsIScriptSecurityManager.h"
56 : #include "nsHtml5DocumentMode.h"
57 : #include "nsHtml5Tokenizer.h"
58 : #include "nsHtml5UTF16Buffer.h"
59 : #include "nsHtml5TreeBuilder.h"
60 : #include "nsHtml5Parser.h"
61 : #include "nsHtml5AtomTable.h"
62 : #include "nsIDOMDocumentFragment.h"
63 : #include "nsHtml5DependentUTF16Buffer.h"
64 :
65 0 : NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
66 0 : NS_INTERFACE_TABLE2(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
67 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
68 0 : NS_INTERFACE_MAP_END
69 :
70 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
71 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
72 :
73 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
74 :
75 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
76 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mExecutor,
77 : nsIContentSink)
78 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStreamParser,
79 : nsIStreamListener)
80 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
81 :
82 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
83 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExecutor)
84 0 : tmp->DropStreamParser();
85 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
86 :
87 0 : nsHtml5Parser::nsHtml5Parser()
88 0 : : mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nsnull))
89 : , mLastBuffer(mFirstBuffer)
90 : , mExecutor(new nsHtml5TreeOpExecutor())
91 0 : , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nsnull))
92 0 : , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false))
93 0 : , mRootContextLineNumber(1)
94 : {
95 0 : mAtomTable.Init(); // we aren't checking for OOM anyway...
96 0 : mTokenizer->setInterner(&mAtomTable);
97 : // There's a zeroing operator new for everything else
98 0 : }
99 :
100 0 : nsHtml5Parser::~nsHtml5Parser()
101 : {
102 0 : mTokenizer->end();
103 0 : if (mDocWriteSpeculativeTokenizer) {
104 0 : mDocWriteSpeculativeTokenizer->end();
105 : }
106 0 : }
107 :
108 : NS_IMETHODIMP_(void)
109 0 : nsHtml5Parser::SetContentSink(nsIContentSink* aSink)
110 : {
111 0 : NS_ASSERTION(aSink == static_cast<nsIContentSink*> (mExecutor),
112 : "Attempt to set a foreign sink.");
113 0 : }
114 :
115 : NS_IMETHODIMP_(nsIContentSink*)
116 0 : nsHtml5Parser::GetContentSink()
117 : {
118 0 : return static_cast<nsIContentSink*> (mExecutor);
119 : }
120 :
121 : NS_IMETHODIMP_(void)
122 0 : nsHtml5Parser::GetCommand(nsCString& aCommand)
123 : {
124 0 : aCommand.Assign("view");
125 0 : }
126 :
127 : NS_IMETHODIMP_(void)
128 0 : nsHtml5Parser::SetCommand(const char* aCommand)
129 : {
130 0 : NS_ASSERTION(!strcmp(aCommand, "view") ||
131 : !strcmp(aCommand, "view-source") ||
132 : !strcmp(aCommand, "external-resource") ||
133 : !strcmp(aCommand, kLoadAsData),
134 : "Unsupported parser command");
135 0 : }
136 :
137 : NS_IMETHODIMP_(void)
138 0 : nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
139 : {
140 0 : NS_ASSERTION(aParserCommand == eViewNormal,
141 : "Parser command was not eViewNormal.");
142 0 : }
143 :
144 : NS_IMETHODIMP_(void)
145 0 : nsHtml5Parser::SetDocumentCharset(const nsACString& aCharset,
146 : PRInt32 aCharsetSource)
147 : {
148 0 : NS_PRECONDITION(!mExecutor->HasStarted(),
149 : "Document charset set too late.");
150 0 : NS_PRECONDITION(mStreamParser, "Setting charset on a script-only parser.");
151 0 : nsCAutoString trimmed;
152 0 : trimmed.Assign(aCharset);
153 0 : trimmed.Trim(" \t\r\n\f");
154 0 : mStreamParser->SetDocumentCharset(trimmed, aCharsetSource);
155 : mExecutor->SetDocumentCharsetAndSource(trimmed,
156 0 : aCharsetSource);
157 0 : }
158 :
159 : NS_IMETHODIMP
160 0 : nsHtml5Parser::GetChannel(nsIChannel** aChannel)
161 : {
162 0 : if (mStreamParser) {
163 0 : return mStreamParser->GetChannel(aChannel);
164 : } else {
165 0 : return NS_ERROR_NOT_AVAILABLE;
166 : }
167 : }
168 :
169 : NS_IMETHODIMP
170 0 : nsHtml5Parser::GetDTD(nsIDTD** aDTD)
171 : {
172 0 : *aDTD = nsnull;
173 0 : return NS_OK;
174 : }
175 :
176 : nsIStreamListener*
177 0 : nsHtml5Parser::GetStreamListener()
178 : {
179 0 : return mStreamParser;
180 : }
181 :
182 : NS_IMETHODIMP
183 0 : nsHtml5Parser::ContinueInterruptedParsing()
184 : {
185 0 : NS_NOTREACHED("Don't call. For interface compat only.");
186 0 : return NS_ERROR_NOT_IMPLEMENTED;
187 : }
188 :
189 : NS_IMETHODIMP_(void)
190 0 : nsHtml5Parser::BlockParser()
191 : {
192 0 : mBlocked = true;
193 0 : }
194 :
195 : NS_IMETHODIMP_(void)
196 0 : nsHtml5Parser::UnblockParser()
197 : {
198 0 : mBlocked = false;
199 0 : mExecutor->ContinueInterruptedParsingAsync();
200 0 : }
201 :
202 : NS_IMETHODIMP_(void)
203 0 : nsHtml5Parser::ContinueInterruptedParsingAsync()
204 : {
205 0 : mExecutor->ContinueInterruptedParsingAsync();
206 0 : }
207 :
208 : NS_IMETHODIMP_(bool)
209 0 : nsHtml5Parser::IsParserEnabled()
210 : {
211 0 : return !mBlocked;
212 : }
213 :
214 : NS_IMETHODIMP_(bool)
215 0 : nsHtml5Parser::IsComplete()
216 : {
217 0 : return mExecutor->IsComplete();
218 : }
219 :
220 : NS_IMETHODIMP
221 0 : nsHtml5Parser::Parse(nsIURI* aURL,
222 : nsIRequestObserver* aObserver,
223 : void* aKey, // legacy; ignored
224 : nsDTDMode aMode) // legacy; ignored
225 : {
226 : /*
227 : * Do NOT cause WillBuildModel to be called synchronously from here!
228 : * The document won't be ready for it until OnStartRequest!
229 : */
230 0 : NS_PRECONDITION(!mExecutor->HasStarted(),
231 : "Tried to start parse without initializing the parser.");
232 0 : NS_PRECONDITION(mStreamParser,
233 : "Can't call this Parse() variant on script-created parser");
234 0 : mStreamParser->SetObserver(aObserver);
235 0 : mStreamParser->SetViewSourceTitle(aURL); // In case we're viewing source
236 0 : mExecutor->SetStreamParser(mStreamParser);
237 0 : mExecutor->SetParser(this);
238 0 : return NS_OK;
239 : }
240 :
241 : NS_IMETHODIMP
242 0 : nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
243 : void* aKey,
244 : const nsACString& aContentType,
245 : bool aLastCall,
246 : nsDTDMode aMode) // ignored
247 : {
248 0 : if (mExecutor->IsBroken()) {
249 0 : return NS_ERROR_OUT_OF_MEMORY;
250 : }
251 0 : if (aSourceBuffer.Length() > PR_INT32_MAX) {
252 0 : mExecutor->MarkAsBroken();
253 0 : return NS_ERROR_OUT_OF_MEMORY;
254 : }
255 :
256 : // Maintain a reference to ourselves so we don't go away
257 : // till we're completely done. The old parser grips itself in this method.
258 0 : nsCOMPtr<nsIParser> kungFuDeathGrip(this);
259 :
260 : // Gripping the other objects just in case, since the other old grip
261 : // required grips to these, too.
262 0 : nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
263 0 : nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
264 :
265 0 : if (!mExecutor->HasStarted()) {
266 0 : NS_ASSERTION(!mStreamParser,
267 : "Had stream parser but document.write started life cycle.");
268 : // This is the first document.write() on a document.open()ed document
269 0 : mExecutor->SetParser(this);
270 0 : mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
271 0 : mTokenizer->start();
272 0 : mExecutor->Start();
273 0 : if (!aContentType.EqualsLiteral("text/html")) {
274 0 : mTreeBuilder->StartPlainText();
275 0 : mTokenizer->StartPlainText();
276 : }
277 : /*
278 : * If you move the following line, be very careful not to cause
279 : * WillBuildModel to be called before the document has had its
280 : * script global object set.
281 : */
282 0 : mExecutor->WillBuildModel(eDTDMode_unknown);
283 : }
284 :
285 : // Return early if the parser has processed EOF
286 0 : if (mExecutor->IsComplete()) {
287 0 : return NS_OK;
288 : }
289 :
290 0 : if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
291 : // document.close()
292 0 : NS_ASSERTION(!mStreamParser,
293 : "Had stream parser but got document.close().");
294 0 : if (mDocumentClosed) {
295 : // already closed
296 0 : return NS_OK;
297 : }
298 0 : mDocumentClosed = true;
299 0 : if (!mBlocked && !mInDocumentWrite) {
300 0 : ParseUntilBlocked();
301 : }
302 0 : return NS_OK;
303 : }
304 :
305 : // If we got this far, we are dealing with a document.write or
306 : // document.writeln call--not document.close().
307 :
308 0 : NS_ASSERTION(IsInsertionPointDefined(),
309 : "Doc.write reached parser with undefined insertion point.");
310 :
311 0 : NS_ASSERTION(!(mStreamParser && !aKey),
312 : "Got a null key in a non-script-created parser");
313 :
314 : // XXX is this optimization bogus?
315 0 : if (aSourceBuffer.IsEmpty()) {
316 0 : return NS_OK;
317 : }
318 :
319 : // This guard is here to prevent document.close from tokenizing synchronously
320 : // while a document.write (that wrote the script that called document.close!)
321 : // is still on the call stack.
322 0 : mozilla::AutoRestore<bool> guard(mInDocumentWrite);
323 0 : mInDocumentWrite = true;
324 :
325 : // The script is identified by aKey. If there's nothing in the buffer
326 : // chain for that key, we'll insert at the head of the queue.
327 : // When the script leaves something in the queue, a zero-length
328 : // key-holder "buffer" is inserted in the queue. If the same script
329 : // leaves something in the chain again, it will be inserted immediately
330 : // before the old key holder belonging to the same script.
331 : //
332 : // We don't do the actual data insertion yet in the hope that the data gets
333 : // tokenized and there no data or less data to copy to the heap after
334 : // tokenization. Also, this way, we avoid inserting one empty data buffer
335 : // per document.write, which matters for performance when the parser isn't
336 : // blocked and a badly-authored script calls document.write() once per
337 : // input character. (As seen in a benchmark!)
338 : //
339 : // The insertion into the input stream happens conceptually before anything
340 : // gets tokenized. To make sure multi-level document.write works right,
341 : // it's necessary to establish the location of our parser key up front
342 : // in case this is the first write with this key.
343 : //
344 : // In a document.open() case, the first write level has a null key, so that
345 : // case is handled separately, because normal buffers containing data
346 : // have null keys.
347 :
348 : // These don't need to be owning references, because they always point to
349 : // the buffer queue and buffers can't be removed from the buffer queue
350 : // before document.write() returns. The buffer queue clean-up happens the
351 : // next time ParseUntilBlocked() is called.
352 : // However, they are made owning just in case the reasoning above is flawed
353 : // and a flaw would lead to worse problems with plain pointers. If this
354 : // turns out to be a perf problem, it's worthwhile to consider making
355 : // prevSearchbuf a plain pointer again.
356 0 : nsRefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
357 0 : nsRefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
358 :
359 0 : if (aKey) {
360 0 : if (mFirstBuffer == mLastBuffer) {
361 0 : nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
362 0 : keyHolder->next = mLastBuffer;
363 0 : mFirstBuffer = keyHolder;
364 0 : } else if (mFirstBuffer->key != aKey) {
365 0 : prevSearchBuf = mFirstBuffer;
366 0 : for (;;) {
367 0 : if (prevSearchBuf->next == mLastBuffer) {
368 : // key was not found
369 : nsHtml5OwningUTF16Buffer* keyHolder =
370 0 : new nsHtml5OwningUTF16Buffer(aKey);
371 0 : keyHolder->next = mFirstBuffer;
372 0 : mFirstBuffer = keyHolder;
373 0 : prevSearchBuf = nsnull;
374 0 : break;
375 : }
376 0 : if (prevSearchBuf->next->key == aKey) {
377 : // found a key holder
378 0 : break;
379 : }
380 0 : prevSearchBuf = prevSearchBuf->next;
381 : }
382 : } // else mFirstBuffer is the keyholder
383 :
384 : // prevSearchBuf is the previous buffer before the keyholder or null if
385 : // there isn't one.
386 : } else {
387 : // We have a first-level write in the document.open() case. We insert
388 : // before mLastBuffer. We need to put a marker there, because otherwise
389 : // additional document.writes from nested event loops would insert in the
390 : // wrong place. Sigh.
391 0 : firstLevelMarker = new nsHtml5OwningUTF16Buffer((void*)nsnull);
392 0 : if (mFirstBuffer == mLastBuffer) {
393 0 : firstLevelMarker->next = mLastBuffer;
394 0 : mFirstBuffer = firstLevelMarker;
395 : } else {
396 0 : prevSearchBuf = mFirstBuffer;
397 0 : while (prevSearchBuf->next != mLastBuffer) {
398 0 : prevSearchBuf = prevSearchBuf->next;
399 : }
400 0 : firstLevelMarker->next = mLastBuffer;
401 0 : prevSearchBuf->next = firstLevelMarker;
402 : }
403 : }
404 :
405 0 : nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
406 :
407 0 : while (!mBlocked && stackBuffer.hasMore()) {
408 0 : stackBuffer.adjust(mLastWasCR);
409 0 : mLastWasCR = false;
410 0 : if (stackBuffer.hasMore()) {
411 : PRInt32 lineNumberSave;
412 0 : bool inRootContext = (!mStreamParser && !aKey);
413 0 : if (inRootContext) {
414 0 : mTokenizer->setLineNumber(mRootContextLineNumber);
415 : } else {
416 : // we aren't the root context, so save the line number on the
417 : // *stack* so that we can restore it.
418 0 : lineNumberSave = mTokenizer->getLineNumber();
419 : }
420 :
421 0 : mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
422 :
423 0 : if (inRootContext) {
424 0 : mRootContextLineNumber = mTokenizer->getLineNumber();
425 : } else {
426 0 : mTokenizer->setLineNumber(lineNumberSave);
427 : }
428 :
429 0 : if (mTreeBuilder->HasScript()) {
430 0 : mTreeBuilder->Flush(); // Move ops to the executor
431 0 : mExecutor->FlushDocumentWrite(); // run the ops
432 : // Flushing tree ops can cause all sorts of things.
433 : // Return early if the parser got terminated.
434 0 : if (mExecutor->IsComplete()) {
435 0 : return NS_OK;
436 : }
437 : }
438 : // Ignore suspension requests
439 : }
440 : }
441 :
442 0 : nsRefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
443 0 : if (stackBuffer.hasMore()) {
444 : // The buffer wasn't tokenized to completion. Create a copy of the tail
445 : // on the heap.
446 0 : heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
447 0 : if (!heapBuffer) {
448 : // Allocation failed. The parser is now broken.
449 0 : mExecutor->MarkAsBroken();
450 0 : return NS_ERROR_OUT_OF_MEMORY;
451 : }
452 : }
453 :
454 0 : if (heapBuffer) {
455 : // We have something to insert before the keyholder holding in the non-null
456 : // aKey case and we have something to swap into firstLevelMarker in the
457 : // null aKey case.
458 0 : if (aKey) {
459 0 : NS_ASSERTION(mFirstBuffer != mLastBuffer,
460 : "Where's the keyholder?");
461 : // the key holder is still somewhere further down the list from
462 : // prevSearchBuf (which may be null)
463 0 : if (mFirstBuffer->key == aKey) {
464 0 : NS_ASSERTION(!prevSearchBuf,
465 : "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
466 0 : heapBuffer->next = mFirstBuffer;
467 0 : mFirstBuffer = heapBuffer;
468 : } else {
469 0 : if (!prevSearchBuf) {
470 0 : prevSearchBuf = mFirstBuffer;
471 : }
472 : // We created a key holder earlier, so we will find it without walking
473 : // past the end of the list.
474 0 : while (prevSearchBuf->next->key != aKey) {
475 0 : prevSearchBuf = prevSearchBuf->next;
476 : }
477 0 : heapBuffer->next = prevSearchBuf->next;
478 0 : prevSearchBuf->next = heapBuffer;
479 : }
480 : } else {
481 0 : NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
482 0 : firstLevelMarker->Swap(heapBuffer);
483 : }
484 : }
485 :
486 0 : if (!mBlocked) { // buffer was tokenized to completion
487 0 : NS_ASSERTION(!stackBuffer.hasMore(),
488 : "Buffer wasn't tokenized to completion?");
489 : // Scripting semantics require a forced tree builder flush here
490 0 : mTreeBuilder->Flush(); // Move ops to the executor
491 0 : mExecutor->FlushDocumentWrite(); // run the ops
492 0 : } else if (stackBuffer.hasMore()) {
493 : // The buffer wasn't tokenized to completion. Tokenize the untokenized
494 : // content in order to preload stuff. This content will be retokenized
495 : // later for normal parsing.
496 0 : if (!mDocWriteSpeculatorActive) {
497 0 : mDocWriteSpeculatorActive = true;
498 0 : if (!mDocWriteSpeculativeTreeBuilder) {
499 : // Lazily initialize if uninitialized
500 : mDocWriteSpeculativeTreeBuilder =
501 0 : new nsHtml5TreeBuilder(nsnull, mExecutor->GetStage());
502 : mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
503 0 : mTreeBuilder->isScriptingEnabled());
504 : mDocWriteSpeculativeTokenizer =
505 0 : new nsHtml5Tokenizer(mDocWriteSpeculativeTreeBuilder, false);
506 0 : mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
507 0 : mDocWriteSpeculativeTokenizer->start();
508 : }
509 0 : mDocWriteSpeculativeTokenizer->resetToDataState();
510 0 : mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
511 0 : mDocWriteSpeculativeLastWasCR = false;
512 : }
513 :
514 : // Note that with multilevel document.write if we didn't just activate the
515 : // speculator, it's possible that the speculator is now in the wrong state.
516 : // That's OK for the sake of simplicity. The worst that can happen is
517 : // that the speculative loads aren't exactly right. The content will be
518 : // reparsed anyway for non-preload purposes.
519 :
520 : // The buffer position for subsequent non-speculative parsing now lives
521 : // in heapBuffer, so it's ok to let the buffer position of stackBuffer
522 : // to be overwritten and not restored below.
523 0 : while (stackBuffer.hasMore()) {
524 0 : stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
525 0 : if (stackBuffer.hasMore()) {
526 : mDocWriteSpeculativeLastWasCR =
527 0 : mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
528 : }
529 : }
530 :
531 0 : mDocWriteSpeculativeTreeBuilder->Flush();
532 0 : mDocWriteSpeculativeTreeBuilder->DropHandles();
533 0 : mExecutor->FlushSpeculativeLoads();
534 : }
535 :
536 0 : return NS_OK;
537 : }
538 :
539 : NS_IMETHODIMP
540 0 : nsHtml5Parser::Terminate()
541 : {
542 : // We should only call DidBuildModel once, so don't do anything if this is
543 : // the second time that Terminate has been called.
544 0 : if (mExecutor->IsComplete()) {
545 0 : return NS_OK;
546 : }
547 : // XXX - [ until we figure out a way to break parser-sink circularity ]
548 : // Hack - Hold a reference until we are completely done...
549 0 : nsCOMPtr<nsIParser> kungFuDeathGrip(this);
550 0 : nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
551 0 : nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
552 0 : if (mStreamParser) {
553 0 : mStreamParser->Terminate();
554 : }
555 0 : return mExecutor->DidBuildModel(true);
556 : }
557 :
558 : NS_IMETHODIMP
559 0 : nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
560 : nsTArray<nsString>& aTagStack)
561 : {
562 0 : return NS_ERROR_NOT_IMPLEMENTED;
563 : }
564 :
565 : NS_IMETHODIMP
566 0 : nsHtml5Parser::BuildModel()
567 : {
568 0 : NS_NOTREACHED("Don't call this!");
569 0 : return NS_ERROR_NOT_IMPLEMENTED;
570 : }
571 :
572 : NS_IMETHODIMP
573 0 : nsHtml5Parser::CancelParsingEvents()
574 : {
575 0 : NS_NOTREACHED("Don't call this!");
576 0 : return NS_ERROR_NOT_IMPLEMENTED;
577 : }
578 :
579 : void
580 0 : nsHtml5Parser::Reset()
581 : {
582 0 : NS_NOTREACHED("Don't call this!");
583 0 : }
584 :
585 : bool
586 0 : nsHtml5Parser::CanInterrupt()
587 : {
588 : // nsContentSink needs this to let nsContentSink::DidProcessATokenImpl
589 : // interrupt.
590 0 : return true;
591 : }
592 :
593 : bool
594 0 : nsHtml5Parser::IsInsertionPointDefined()
595 : {
596 0 : return !mExecutor->IsFlushing() &&
597 0 : (!mStreamParser || mParserInsertedScriptsBeingEvaluated);
598 : }
599 :
600 : void
601 0 : nsHtml5Parser::BeginEvaluatingParserInsertedScript()
602 : {
603 0 : ++mParserInsertedScriptsBeingEvaluated;
604 0 : }
605 :
606 : void
607 0 : nsHtml5Parser::EndEvaluatingParserInsertedScript()
608 : {
609 0 : --mParserInsertedScriptsBeingEvaluated;
610 0 : }
611 :
612 : void
613 0 : nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
614 : {
615 0 : NS_PRECONDITION(!mStreamParser, "Must not call this twice.");
616 0 : eParserMode mode = NORMAL;
617 0 : if (!nsCRT::strcmp(aCommand, "view-source")) {
618 0 : mode = VIEW_SOURCE_HTML;
619 0 : } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
620 0 : mode = VIEW_SOURCE_XML;
621 0 : } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
622 0 : mode = VIEW_SOURCE_PLAIN;
623 0 : } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
624 0 : mode = PLAIN_TEXT;
625 0 : } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
626 0 : mode = LOAD_AS_DATA;
627 : }
628 : #ifdef DEBUG
629 : else {
630 0 : NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
631 : !nsCRT::strcmp(aCommand, "external-resource"),
632 : "Unsupported parser command!");
633 : }
634 : #endif
635 0 : mStreamParser = new nsHtml5StreamParser(mExecutor, this, mode);
636 0 : }
637 :
638 : bool
639 0 : nsHtml5Parser::IsScriptCreated()
640 : {
641 0 : return !mStreamParser;
642 : }
643 :
644 : /* End nsIParser */
645 :
646 : // not from interface
647 : void
648 0 : nsHtml5Parser::ParseUntilBlocked()
649 : {
650 0 : if (mBlocked || mExecutor->IsComplete() || mExecutor->IsBroken()) {
651 0 : return;
652 : }
653 0 : NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
654 0 : NS_ASSERTION(!mInDocumentWrite,
655 : "ParseUntilBlocked entered while in doc.write!");
656 :
657 0 : mDocWriteSpeculatorActive = false;
658 :
659 0 : for (;;) {
660 0 : if (!mFirstBuffer->hasMore()) {
661 0 : if (mFirstBuffer == mLastBuffer) {
662 0 : if (mExecutor->IsComplete()) {
663 : // something like cache manisfests stopped the parse in mid-flight
664 0 : return;
665 : }
666 0 : if (mDocumentClosed) {
667 0 : NS_ASSERTION(!mStreamParser,
668 : "This should only happen with script-created parser.");
669 0 : mTokenizer->eof();
670 0 : mTreeBuilder->StreamEnded();
671 0 : mTreeBuilder->Flush();
672 0 : mExecutor->FlushDocumentWrite();
673 0 : mTokenizer->end();
674 0 : return;
675 : }
676 : // never release the last buffer.
677 0 : NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
678 : "Sentinel buffer had its indeces changed.");
679 0 : if (mStreamParser) {
680 0 : if (mReturnToStreamParserPermitted &&
681 0 : !mExecutor->IsScriptExecuting()) {
682 0 : mTreeBuilder->Flush();
683 0 : mReturnToStreamParserPermitted = false;
684 : mStreamParser->ContinueAfterScripts(mTokenizer,
685 : mTreeBuilder,
686 0 : mLastWasCR);
687 : }
688 : } else {
689 : // Script-created parser
690 0 : mTreeBuilder->Flush();
691 : // No need to flush the executor, because the executor is already
692 : // in a flush
693 0 : NS_ASSERTION(mExecutor->IsInFlushLoop(),
694 : "How did we come here without being in the flush loop?");
695 : }
696 0 : return; // no more data for now but expecting more
697 : }
698 0 : mFirstBuffer = mFirstBuffer->next;
699 0 : continue;
700 : }
701 :
702 0 : if (mBlocked || mExecutor->IsComplete()) {
703 0 : return;
704 : }
705 :
706 : // now we have a non-empty buffer
707 0 : mFirstBuffer->adjust(mLastWasCR);
708 0 : mLastWasCR = false;
709 0 : if (mFirstBuffer->hasMore()) {
710 0 : bool inRootContext = (!mStreamParser && !mFirstBuffer->key);
711 0 : if (inRootContext) {
712 0 : mTokenizer->setLineNumber(mRootContextLineNumber);
713 : }
714 0 : mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
715 0 : if (inRootContext) {
716 0 : mRootContextLineNumber = mTokenizer->getLineNumber();
717 : }
718 0 : if (mTreeBuilder->HasScript()) {
719 0 : mTreeBuilder->Flush();
720 0 : mExecutor->FlushDocumentWrite();
721 : }
722 0 : if (mBlocked) {
723 0 : return;
724 : }
725 : }
726 0 : continue;
727 : }
728 : }
729 :
730 : nsresult
731 0 : nsHtml5Parser::Initialize(nsIDocument* aDoc,
732 : nsIURI* aURI,
733 : nsISupports* aContainer,
734 : nsIChannel* aChannel)
735 : {
736 0 : return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
737 : }
738 :
739 : void
740 0 : nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
741 0 : if (!aScriptingEnabled) {
742 0 : mExecutor->PreventScriptExecution();
743 : }
744 0 : mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
745 0 : mTokenizer->start();
746 0 : }
747 :
748 : void
749 0 : nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
750 : PRInt32 aLine)
751 : {
752 0 : mTokenizer->resetToDataState();
753 0 : mTokenizer->setLineNumber(aLine);
754 0 : mTreeBuilder->loadState(aState, &mAtomTable);
755 0 : mLastWasCR = false;
756 0 : mReturnToStreamParserPermitted = true;
757 0 : }
758 :
759 : void
760 0 : nsHtml5Parser::ContinueAfterFailedCharsetSwitch()
761 : {
762 0 : NS_PRECONDITION(mStreamParser,
763 : "Tried to continue after failed charset switch without a stream parser");
764 0 : mStreamParser->ContinueAfterFailedCharsetSwitch();
765 4392 : }
|