Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / parser / html / nsHtml5Parser.cpp
blob3be3389d212d520a00de18eadef5ad3d2f42f725
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 /* 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/. */
7 #include "nsHtml5Parser.h"
9 #include "mozilla/AutoRestore.h"
10 #include "nsContentUtils.h" // for kLoadAsData
11 #include "nsHtml5Tokenizer.h"
12 #include "nsHtml5TreeBuilder.h"
13 #include "nsHtml5AtomTable.h"
14 #include "nsHtml5DependentUTF16Buffer.h"
15 #include "nsNetUtil.h"
17 NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
18 NS_INTERFACE_TABLE(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
19 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
20 NS_INTERFACE_MAP_END
22 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
23 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
25 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
27 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExecutor)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetStreamParser())
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
33 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
34 tmp->DropStreamParser();
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
37 nsHtml5Parser::nsHtml5Parser()
38 : mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr))
39 , mLastBuffer(mFirstBuffer)
40 , mExecutor(new nsHtml5TreeOpExecutor())
41 , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr))
42 , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false))
43 , mRootContextLineNumber(1)
45 mTokenizer->setInterner(&mAtomTable);
46 // There's a zeroing operator new for everything else
49 nsHtml5Parser::~nsHtml5Parser()
51 mTokenizer->end();
52 if (mDocWriteSpeculativeTokenizer) {
53 mDocWriteSpeculativeTokenizer->end();
57 NS_IMETHODIMP_(void)
58 nsHtml5Parser::SetContentSink(nsIContentSink* aSink)
60 NS_ASSERTION(aSink == static_cast<nsIContentSink*> (mExecutor),
61 "Attempt to set a foreign sink.");
64 NS_IMETHODIMP_(nsIContentSink*)
65 nsHtml5Parser::GetContentSink()
67 return static_cast<nsIContentSink*> (mExecutor);
70 NS_IMETHODIMP_(void)
71 nsHtml5Parser::GetCommand(nsCString& aCommand)
73 aCommand.AssignLiteral("view");
76 NS_IMETHODIMP_(void)
77 nsHtml5Parser::SetCommand(const char* aCommand)
79 NS_ASSERTION(!strcmp(aCommand, "view") ||
80 !strcmp(aCommand, "view-source") ||
81 !strcmp(aCommand, "external-resource") ||
82 !strcmp(aCommand, "import") ||
83 !strcmp(aCommand, kLoadAsData),
84 "Unsupported parser command");
87 NS_IMETHODIMP_(void)
88 nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
90 NS_ASSERTION(aParserCommand == eViewNormal,
91 "Parser command was not eViewNormal.");
94 NS_IMETHODIMP_(void)
95 nsHtml5Parser::SetDocumentCharset(const nsACString& aCharset,
96 int32_t aCharsetSource)
98 NS_PRECONDITION(!mExecutor->HasStarted(),
99 "Document charset set too late.");
100 NS_PRECONDITION(GetStreamParser(), "Setting charset on a script-only parser.");
101 nsAutoCString trimmed;
102 trimmed.Assign(aCharset);
103 trimmed.Trim(" \t\r\n\f");
104 GetStreamParser()->SetDocumentCharset(trimmed, aCharsetSource);
105 mExecutor->SetDocumentCharsetAndSource(trimmed,
106 aCharsetSource);
109 NS_IMETHODIMP
110 nsHtml5Parser::GetChannel(nsIChannel** aChannel)
112 if (GetStreamParser()) {
113 return GetStreamParser()->GetChannel(aChannel);
114 } else {
115 return NS_ERROR_NOT_AVAILABLE;
119 NS_IMETHODIMP
120 nsHtml5Parser::GetDTD(nsIDTD** aDTD)
122 *aDTD = nullptr;
123 return NS_OK;
126 nsIStreamListener*
127 nsHtml5Parser::GetStreamListener()
129 return mStreamListener;
132 NS_IMETHODIMP
133 nsHtml5Parser::ContinueInterruptedParsing()
135 NS_NOTREACHED("Don't call. For interface compat only.");
136 return NS_ERROR_NOT_IMPLEMENTED;
139 NS_IMETHODIMP_(void)
140 nsHtml5Parser::BlockParser()
142 mBlocked = true;
145 NS_IMETHODIMP_(void)
146 nsHtml5Parser::UnblockParser()
148 mBlocked = false;
149 mExecutor->ContinueInterruptedParsingAsync();
152 NS_IMETHODIMP_(void)
153 nsHtml5Parser::ContinueInterruptedParsingAsync()
155 mExecutor->ContinueInterruptedParsingAsync();
158 NS_IMETHODIMP_(bool)
159 nsHtml5Parser::IsParserEnabled()
161 return !mBlocked;
164 NS_IMETHODIMP_(bool)
165 nsHtml5Parser::IsComplete()
167 return mExecutor->IsComplete();
170 NS_IMETHODIMP
171 nsHtml5Parser::Parse(nsIURI* aURL,
172 nsIRequestObserver* aObserver,
173 void* aKey, // legacy; ignored
174 nsDTDMode aMode) // legacy; ignored
177 * Do NOT cause WillBuildModel to be called synchronously from here!
178 * The document won't be ready for it until OnStartRequest!
180 NS_PRECONDITION(!mExecutor->HasStarted(),
181 "Tried to start parse without initializing the parser.");
182 NS_PRECONDITION(GetStreamParser(),
183 "Can't call this Parse() variant on script-created parser");
184 GetStreamParser()->SetObserver(aObserver);
185 GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
186 mExecutor->SetStreamParser(GetStreamParser());
187 mExecutor->SetParser(this);
188 return NS_OK;
191 NS_IMETHODIMP
192 nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
193 void* aKey,
194 const nsACString& aContentType,
195 bool aLastCall,
196 nsDTDMode aMode) // ignored
198 nsresult rv;
199 if (NS_FAILED(rv = mExecutor->IsBroken())) {
200 return rv;
202 if (aSourceBuffer.Length() > INT32_MAX) {
203 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
206 // Maintain a reference to ourselves so we don't go away
207 // till we're completely done. The old parser grips itself in this method.
208 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
210 // Gripping the other objects just in case, since the other old grip
211 // required grips to these, too.
212 nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
213 nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
215 if (!mExecutor->HasStarted()) {
216 NS_ASSERTION(!GetStreamParser(),
217 "Had stream parser but document.write started life cycle.");
218 // This is the first document.write() on a document.open()ed document
219 mExecutor->SetParser(this);
220 mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
222 bool isSrcdoc = false;
223 nsCOMPtr<nsIChannel> channel;
224 rv = GetChannel(getter_AddRefs(channel));
225 if (NS_SUCCEEDED(rv)) {
226 isSrcdoc = NS_IsSrcdocChannel(channel);
228 mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
230 mTokenizer->start();
231 mExecutor->Start();
232 if (!aContentType.EqualsLiteral("text/html")) {
233 mTreeBuilder->StartPlainText();
234 mTokenizer->StartPlainText();
237 * If you move the following line, be very careful not to cause
238 * WillBuildModel to be called before the document has had its
239 * script global object set.
241 rv = mExecutor->WillBuildModel(eDTDMode_unknown);
242 NS_ENSURE_SUCCESS(rv, rv);
245 // Return early if the parser has processed EOF
246 if (mExecutor->IsComplete()) {
247 return NS_OK;
250 if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
251 // document.close()
252 NS_ASSERTION(!GetStreamParser(),
253 "Had stream parser but got document.close().");
254 if (mDocumentClosed) {
255 // already closed
256 return NS_OK;
258 mDocumentClosed = true;
259 if (!mBlocked && !mInDocumentWrite) {
260 return ParseUntilBlocked();
262 return NS_OK;
265 // If we got this far, we are dealing with a document.write or
266 // document.writeln call--not document.close().
268 NS_ASSERTION(IsInsertionPointDefined(),
269 "Doc.write reached parser with undefined insertion point.");
271 NS_ASSERTION(!(GetStreamParser() && !aKey),
272 "Got a null key in a non-script-created parser");
274 // XXX is this optimization bogus?
275 if (aSourceBuffer.IsEmpty()) {
276 return NS_OK;
279 // This guard is here to prevent document.close from tokenizing synchronously
280 // while a document.write (that wrote the script that called document.close!)
281 // is still on the call stack.
282 mozilla::AutoRestore<bool> guard(mInDocumentWrite);
283 mInDocumentWrite = true;
285 // The script is identified by aKey. If there's nothing in the buffer
286 // chain for that key, we'll insert at the head of the queue.
287 // When the script leaves something in the queue, a zero-length
288 // key-holder "buffer" is inserted in the queue. If the same script
289 // leaves something in the chain again, it will be inserted immediately
290 // before the old key holder belonging to the same script.
292 // We don't do the actual data insertion yet in the hope that the data gets
293 // tokenized and there no data or less data to copy to the heap after
294 // tokenization. Also, this way, we avoid inserting one empty data buffer
295 // per document.write, which matters for performance when the parser isn't
296 // blocked and a badly-authored script calls document.write() once per
297 // input character. (As seen in a benchmark!)
299 // The insertion into the input stream happens conceptually before anything
300 // gets tokenized. To make sure multi-level document.write works right,
301 // it's necessary to establish the location of our parser key up front
302 // in case this is the first write with this key.
304 // In a document.open() case, the first write level has a null key, so that
305 // case is handled separately, because normal buffers containing data
306 // have null keys.
308 // These don't need to be owning references, because they always point to
309 // the buffer queue and buffers can't be removed from the buffer queue
310 // before document.write() returns. The buffer queue clean-up happens the
311 // next time ParseUntilBlocked() is called.
312 // However, they are made owning just in case the reasoning above is flawed
313 // and a flaw would lead to worse problems with plain pointers. If this
314 // turns out to be a perf problem, it's worthwhile to consider making
315 // prevSearchbuf a plain pointer again.
316 nsRefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
317 nsRefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
319 if (aKey) {
320 if (mFirstBuffer == mLastBuffer) {
321 nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
322 keyHolder->next = mLastBuffer;
323 mFirstBuffer = keyHolder;
324 } else if (mFirstBuffer->key != aKey) {
325 prevSearchBuf = mFirstBuffer;
326 for (;;) {
327 if (prevSearchBuf->next == mLastBuffer) {
328 // key was not found
329 nsHtml5OwningUTF16Buffer* keyHolder =
330 new nsHtml5OwningUTF16Buffer(aKey);
331 keyHolder->next = mFirstBuffer;
332 mFirstBuffer = keyHolder;
333 prevSearchBuf = nullptr;
334 break;
336 if (prevSearchBuf->next->key == aKey) {
337 // found a key holder
338 break;
340 prevSearchBuf = prevSearchBuf->next;
342 } // else mFirstBuffer is the keyholder
344 // prevSearchBuf is the previous buffer before the keyholder or null if
345 // there isn't one.
346 } else {
347 // We have a first-level write in the document.open() case. We insert before
348 // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
349 // and redesignating the previous mLastBuffer as our firstLevelMarker. We
350 // need to put a marker there, because otherwise additional document.writes
351 // from nested event loops would insert in the wrong place. Sigh.
352 mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
353 firstLevelMarker = mLastBuffer;
354 mLastBuffer = mLastBuffer->next;
357 nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
359 while (!mBlocked && stackBuffer.hasMore()) {
360 stackBuffer.adjust(mLastWasCR);
361 mLastWasCR = false;
362 if (stackBuffer.hasMore()) {
363 int32_t lineNumberSave;
364 bool inRootContext = (!GetStreamParser() && !aKey);
365 if (inRootContext) {
366 mTokenizer->setLineNumber(mRootContextLineNumber);
367 } else {
368 // we aren't the root context, so save the line number on the
369 // *stack* so that we can restore it.
370 lineNumberSave = mTokenizer->getLineNumber();
373 mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
375 if (inRootContext) {
376 mRootContextLineNumber = mTokenizer->getLineNumber();
377 } else {
378 mTokenizer->setLineNumber(lineNumberSave);
381 if (mTreeBuilder->HasScript()) {
382 mTreeBuilder->Flush(); // Move ops to the executor
383 rv = mExecutor->FlushDocumentWrite(); // run the ops
384 NS_ENSURE_SUCCESS(rv, rv);
385 // Flushing tree ops can cause all sorts of things.
386 // Return early if the parser got terminated.
387 if (mExecutor->IsComplete()) {
388 return NS_OK;
391 // Ignore suspension requests
395 nsRefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
396 if (stackBuffer.hasMore()) {
397 // The buffer wasn't tokenized to completion. Create a copy of the tail
398 // on the heap.
399 heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
400 if (!heapBuffer) {
401 // Allocation failed. The parser is now broken.
402 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
406 if (heapBuffer) {
407 // We have something to insert before the keyholder holding in the non-null
408 // aKey case and we have something to swap into firstLevelMarker in the
409 // null aKey case.
410 if (aKey) {
411 NS_ASSERTION(mFirstBuffer != mLastBuffer,
412 "Where's the keyholder?");
413 // the key holder is still somewhere further down the list from
414 // prevSearchBuf (which may be null)
415 if (mFirstBuffer->key == aKey) {
416 NS_ASSERTION(!prevSearchBuf,
417 "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
418 heapBuffer->next = mFirstBuffer;
419 mFirstBuffer = heapBuffer;
420 } else {
421 if (!prevSearchBuf) {
422 prevSearchBuf = mFirstBuffer;
424 // We created a key holder earlier, so we will find it without walking
425 // past the end of the list.
426 while (prevSearchBuf->next->key != aKey) {
427 prevSearchBuf = prevSearchBuf->next;
429 heapBuffer->next = prevSearchBuf->next;
430 prevSearchBuf->next = heapBuffer;
432 } else {
433 NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
434 firstLevelMarker->Swap(heapBuffer);
438 if (!mBlocked) { // buffer was tokenized to completion
439 NS_ASSERTION(!stackBuffer.hasMore(),
440 "Buffer wasn't tokenized to completion?");
441 // Scripting semantics require a forced tree builder flush here
442 mTreeBuilder->Flush(); // Move ops to the executor
443 rv = mExecutor->FlushDocumentWrite(); // run the ops
444 NS_ENSURE_SUCCESS(rv, rv);
445 } else if (stackBuffer.hasMore()) {
446 // The buffer wasn't tokenized to completion. Tokenize the untokenized
447 // content in order to preload stuff. This content will be retokenized
448 // later for normal parsing.
449 if (!mDocWriteSpeculatorActive) {
450 mDocWriteSpeculatorActive = true;
451 if (!mDocWriteSpeculativeTreeBuilder) {
452 // Lazily initialize if uninitialized
453 mDocWriteSpeculativeTreeBuilder =
454 new nsHtml5TreeBuilder(nullptr, mExecutor->GetStage());
455 mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
456 mTreeBuilder->isScriptingEnabled());
457 mDocWriteSpeculativeTokenizer =
458 new nsHtml5Tokenizer(mDocWriteSpeculativeTreeBuilder, false);
459 mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
460 mDocWriteSpeculativeTokenizer->start();
462 mDocWriteSpeculativeTokenizer->resetToDataState();
463 mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
464 mDocWriteSpeculativeLastWasCR = false;
467 // Note that with multilevel document.write if we didn't just activate the
468 // speculator, it's possible that the speculator is now in the wrong state.
469 // That's OK for the sake of simplicity. The worst that can happen is
470 // that the speculative loads aren't exactly right. The content will be
471 // reparsed anyway for non-preload purposes.
473 // The buffer position for subsequent non-speculative parsing now lives
474 // in heapBuffer, so it's ok to let the buffer position of stackBuffer
475 // to be overwritten and not restored below.
476 while (stackBuffer.hasMore()) {
477 stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
478 if (stackBuffer.hasMore()) {
479 mDocWriteSpeculativeLastWasCR =
480 mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
484 mDocWriteSpeculativeTreeBuilder->Flush();
485 mDocWriteSpeculativeTreeBuilder->DropHandles();
486 mExecutor->FlushSpeculativeLoads();
489 return NS_OK;
492 NS_IMETHODIMP
493 nsHtml5Parser::Terminate()
495 // We should only call DidBuildModel once, so don't do anything if this is
496 // the second time that Terminate has been called.
497 if (mExecutor->IsComplete()) {
498 return NS_OK;
500 // XXX - [ until we figure out a way to break parser-sink circularity ]
501 // Hack - Hold a reference until we are completely done...
502 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
503 nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
504 nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
505 if (GetStreamParser()) {
506 GetStreamParser()->Terminate();
508 return mExecutor->DidBuildModel(true);
511 NS_IMETHODIMP
512 nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
513 nsTArray<nsString>& aTagStack)
515 return NS_ERROR_NOT_IMPLEMENTED;
518 NS_IMETHODIMP
519 nsHtml5Parser::BuildModel()
521 NS_NOTREACHED("Don't call this!");
522 return NS_ERROR_NOT_IMPLEMENTED;
525 NS_IMETHODIMP
526 nsHtml5Parser::CancelParsingEvents()
528 NS_NOTREACHED("Don't call this!");
529 return NS_ERROR_NOT_IMPLEMENTED;
532 void
533 nsHtml5Parser::Reset()
535 NS_NOTREACHED("Don't call this!");
538 bool
539 nsHtml5Parser::CanInterrupt()
541 // nsContentSink needs this to let nsContentSink::DidProcessATokenImpl
542 // interrupt.
543 return true;
546 bool
547 nsHtml5Parser::IsInsertionPointDefined()
549 return !mExecutor->IsFlushing() &&
550 (!GetStreamParser() || mParserInsertedScriptsBeingEvaluated);
553 void
554 nsHtml5Parser::BeginEvaluatingParserInsertedScript()
556 ++mParserInsertedScriptsBeingEvaluated;
559 void
560 nsHtml5Parser::EndEvaluatingParserInsertedScript()
562 --mParserInsertedScriptsBeingEvaluated;
565 void
566 nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
568 NS_PRECONDITION(!mStreamListener, "Must not call this twice.");
569 eParserMode mode = NORMAL;
570 if (!nsCRT::strcmp(aCommand, "view-source")) {
571 mode = VIEW_SOURCE_HTML;
572 } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
573 mode = VIEW_SOURCE_XML;
574 } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
575 mode = VIEW_SOURCE_PLAIN;
576 } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
577 mode = PLAIN_TEXT;
578 } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
579 mode = LOAD_AS_DATA;
581 #ifdef DEBUG
582 else {
583 NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
584 !nsCRT::strcmp(aCommand, "external-resource") ||
585 !nsCRT::strcmp(aCommand, "import"),
586 "Unsupported parser command!");
588 #endif
589 mStreamListener =
590 new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
593 bool
594 nsHtml5Parser::IsScriptCreated()
596 return !GetStreamParser();
599 /* End nsIParser */
601 // not from interface
602 nsresult
603 nsHtml5Parser::ParseUntilBlocked()
605 nsresult rv = mExecutor->IsBroken();
606 NS_ENSURE_SUCCESS(rv, rv);
607 if (mBlocked || mExecutor->IsComplete()) {
608 return NS_OK;
610 NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
611 NS_ASSERTION(!mInDocumentWrite,
612 "ParseUntilBlocked entered while in doc.write!");
614 mDocWriteSpeculatorActive = false;
616 for (;;) {
617 if (!mFirstBuffer->hasMore()) {
618 if (mFirstBuffer == mLastBuffer) {
619 if (mExecutor->IsComplete()) {
620 // something like cache manisfests stopped the parse in mid-flight
621 return NS_OK;
623 if (mDocumentClosed) {
624 NS_ASSERTION(!GetStreamParser(),
625 "This should only happen with script-created parser.");
626 mTokenizer->eof();
627 mTreeBuilder->StreamEnded();
628 mTreeBuilder->Flush();
629 mExecutor->FlushDocumentWrite();
630 // The below call does memory cleanup, so call it even if the
631 // parser has been marked as broken.
632 mTokenizer->end();
633 return NS_OK;
635 // never release the last buffer.
636 NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
637 "Sentinel buffer had its indeces changed.");
638 if (GetStreamParser()) {
639 if (mReturnToStreamParserPermitted &&
640 !mExecutor->IsScriptExecuting()) {
641 mTreeBuilder->Flush();
642 mReturnToStreamParserPermitted = false;
643 GetStreamParser()->ContinueAfterScripts(mTokenizer,
644 mTreeBuilder,
645 mLastWasCR);
647 } else {
648 // Script-created parser
649 mTreeBuilder->Flush();
650 // No need to flush the executor, because the executor is already
651 // in a flush
652 NS_ASSERTION(mExecutor->IsInFlushLoop(),
653 "How did we come here without being in the flush loop?");
655 return NS_OK; // no more data for now but expecting more
657 mFirstBuffer = mFirstBuffer->next;
658 continue;
661 if (mBlocked || mExecutor->IsComplete()) {
662 return NS_OK;
665 // now we have a non-empty buffer
666 mFirstBuffer->adjust(mLastWasCR);
667 mLastWasCR = false;
668 if (mFirstBuffer->hasMore()) {
669 bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
670 if (inRootContext) {
671 mTokenizer->setLineNumber(mRootContextLineNumber);
673 mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
674 if (inRootContext) {
675 mRootContextLineNumber = mTokenizer->getLineNumber();
677 if (mTreeBuilder->HasScript()) {
678 mTreeBuilder->Flush();
679 nsresult rv = mExecutor->FlushDocumentWrite();
680 NS_ENSURE_SUCCESS(rv, rv);
682 if (mBlocked) {
683 return NS_OK;
686 continue;
690 nsresult
691 nsHtml5Parser::Initialize(nsIDocument* aDoc,
692 nsIURI* aURI,
693 nsISupports* aContainer,
694 nsIChannel* aChannel)
696 return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
699 void
700 nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
702 bool isSrcdoc = false;
703 nsCOMPtr<nsIChannel> channel;
704 nsresult rv = GetChannel(getter_AddRefs(channel));
705 if (NS_SUCCEEDED(rv)) {
706 isSrcdoc = NS_IsSrcdocChannel(channel);
708 mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
710 mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
711 mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
712 mTokenizer->start();
715 void
716 nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
717 int32_t aLine)
719 mTokenizer->resetToDataState();
720 mTokenizer->setLineNumber(aLine);
721 mTreeBuilder->loadState(aState, &mAtomTable);
722 mLastWasCR = false;
723 mReturnToStreamParserPermitted = true;
726 void
727 nsHtml5Parser::ContinueAfterFailedCharsetSwitch()
729 NS_PRECONDITION(GetStreamParser(),
730 "Tried to continue after failed charset switch without a stream parser");
731 GetStreamParser()->ContinueAfterFailedCharsetSwitch();