Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / parser / html / nsHtml5Parser.cpp
blob69d0d83486734b0e67616ccf36b1160a8de0d7f4
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
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/
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.
16 * The Original Code is mozilla.org code.
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.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Henri Sivonen <hsivonen@iki.fi>
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.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsCompatibility.h"
42 #include "nsScriptLoader.h"
43 #include "nsNetUtil.h"
44 #include "nsIStyleSheetLinkingElement.h"
45 #include "nsICharsetAlias.h"
46 #include "nsIWebShellServices.h"
47 #include "nsIDocShell.h"
48 #include "nsEncoderDecoderUtils.h"
49 #include "nsContentUtils.h"
50 #include "nsICharsetDetector.h"
51 #include "nsIScriptElement.h"
52 #include "nsIMarkupDocumentViewer.h"
53 #include "nsIDocShellTreeItem.h"
54 #include "nsIContentViewer.h"
55 #include "nsIScriptGlobalObjectOwner.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsHtml5DocumentMode.h"
58 #include "nsHtml5Tokenizer.h"
59 #include "nsHtml5UTF16Buffer.h"
60 #include "nsHtml5TreeBuilder.h"
61 #include "nsHtml5Parser.h"
62 #include "nsHtml5AtomTable.h"
63 #include "nsIDOMDocumentFragment.h"
65 NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
66 NS_INTERFACE_TABLE2(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
67 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
68 NS_INTERFACE_MAP_END
70 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
71 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
73 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
75 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mExecutor,
77 nsIContentSink)
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mStreamParser,
79 nsIStreamListener)
80 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
82 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
83 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mExecutor)
84 tmp->DropStreamParser();
85 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
87 nsHtml5Parser::nsHtml5Parser()
88 : mFirstBuffer(new nsHtml5UTF16Buffer(0))
89 , mLastBuffer(mFirstBuffer)
90 , mExecutor(new nsHtml5TreeOpExecutor())
91 , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nsnull))
92 , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder))
93 , mRootContextLineNumber(1)
95 mAtomTable.Init(); // we aren't checking for OOM anyway...
96 mTokenizer->setInterner(&mAtomTable);
97 // There's a zeroing operator new for everything else
100 nsHtml5Parser::~nsHtml5Parser()
102 mTokenizer->end();
103 if (mDocWriteSpeculativeTokenizer) {
104 mDocWriteSpeculativeTokenizer->end();
108 NS_IMETHODIMP_(void)
109 nsHtml5Parser::SetContentSink(nsIContentSink* aSink)
111 NS_ASSERTION(aSink == static_cast<nsIContentSink*> (mExecutor),
112 "Attempt to set a foreign sink.");
115 NS_IMETHODIMP_(nsIContentSink*)
116 nsHtml5Parser::GetContentSink()
118 return static_cast<nsIContentSink*> (mExecutor);
121 NS_IMETHODIMP_(void)
122 nsHtml5Parser::GetCommand(nsCString& aCommand)
124 aCommand.Assign("view");
127 NS_IMETHODIMP_(void)
128 nsHtml5Parser::SetCommand(const char* aCommand)
130 NS_ASSERTION(!strcmp(aCommand, "view"), "Parser command was not view");
133 NS_IMETHODIMP_(void)
134 nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
136 NS_ASSERTION(aParserCommand == eViewNormal,
137 "Parser command was not eViewNormal.");
140 NS_IMETHODIMP_(void)
141 nsHtml5Parser::SetDocumentCharset(const nsACString& aCharset,
142 PRInt32 aCharsetSource)
144 NS_PRECONDITION(!mExecutor->HasStarted(),
145 "Document charset set too late.");
146 NS_PRECONDITION(mStreamParser, "Setting charset on a script-only parser.");
147 nsCAutoString trimmed;
148 trimmed.Assign(aCharset);
149 trimmed.Trim(" \t\r\n\f");
150 mStreamParser->SetDocumentCharset(trimmed, aCharsetSource);
151 mExecutor->SetDocumentCharsetAndSource(trimmed,
152 aCharsetSource);
155 NS_IMETHODIMP_(void)
156 nsHtml5Parser::SetParserFilter(nsIParserFilter* aFilter)
158 NS_ERROR("Attempt to set a parser filter on HTML5 parser.");
161 NS_IMETHODIMP
162 nsHtml5Parser::GetChannel(nsIChannel** aChannel)
164 if (mStreamParser) {
165 return mStreamParser->GetChannel(aChannel);
166 } else {
167 return NS_ERROR_NOT_AVAILABLE;
171 NS_IMETHODIMP
172 nsHtml5Parser::GetDTD(nsIDTD** aDTD)
174 *aDTD = nsnull;
175 return NS_OK;
178 NS_IMETHODIMP
179 nsHtml5Parser::GetStreamListener(nsIStreamListener** aListener)
181 NS_IF_ADDREF(*aListener = mStreamParser);
182 return NS_OK;
185 NS_IMETHODIMP
186 nsHtml5Parser::ContinueInterruptedParsing()
188 NS_NOTREACHED("Don't call. For interface compat only.");
189 return NS_ERROR_NOT_IMPLEMENTED;
192 NS_IMETHODIMP_(void)
193 nsHtml5Parser::BlockParser()
195 mBlocked = PR_TRUE;
198 NS_IMETHODIMP_(void)
199 nsHtml5Parser::UnblockParser()
201 mBlocked = PR_FALSE;
204 NS_IMETHODIMP_(PRBool)
205 nsHtml5Parser::IsParserEnabled()
207 return !mBlocked;
210 NS_IMETHODIMP_(PRBool)
211 nsHtml5Parser::IsComplete()
213 return mExecutor->IsComplete();
216 NS_IMETHODIMP
217 nsHtml5Parser::Parse(nsIURI* aURL, // legacy parameter; ignored
218 nsIRequestObserver* aObserver,
219 void* aKey,
220 nsDTDMode aMode) // legacy; ignored
223 * Do NOT cause WillBuildModel to be called synchronously from here!
224 * The document won't be ready for it until OnStartRequest!
226 NS_PRECONDITION(!mExecutor->HasStarted(),
227 "Tried to start parse without initializing the parser.");
228 NS_PRECONDITION(mStreamParser,
229 "Can't call this Parse() variant on script-created parser");
230 mStreamParser->SetObserver(aObserver);
231 mExecutor->SetStreamParser(mStreamParser);
232 mExecutor->SetParser(this);
233 mRootContextKey = aKey;
234 return NS_OK;
237 NS_IMETHODIMP
238 nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
239 void* aKey,
240 const nsACString& aContentType, // ignored
241 PRBool aLastCall,
242 nsDTDMode aMode) // ignored
244 NS_PRECONDITION(!mExecutor->IsFragmentMode(),
245 "Document.write called in fragment mode!");
247 // Maintain a reference to ourselves so we don't go away
248 // till we're completely done. The old parser grips itself in this method.
249 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
251 // Gripping the other objects just in case, since the other old grip
252 // required grips to these, too.
253 nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
254 nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
256 if (!mExecutor->HasStarted()) {
257 NS_ASSERTION(!mStreamParser,
258 "Had stream parser but document.write started life cycle.");
259 // This is the first document.write() on a document.open()ed document
260 mExecutor->SetParser(this);
261 mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
262 mTokenizer->start();
263 mExecutor->Start();
265 * If you move the following line, be very careful not to cause
266 * WillBuildModel to be called before the document has had its
267 * script global object set.
269 mExecutor->WillBuildModel(eDTDMode_unknown);
272 // Return early if the parser has processed EOF
273 if (mExecutor->IsComplete()) {
274 return NS_OK;
277 if (aLastCall && aSourceBuffer.IsEmpty() && aKey == GetRootContextKey()) {
278 // document.close()
279 NS_ASSERTION(!mStreamParser,
280 "Had stream parser but got document.close().");
281 mDocumentClosed = PR_TRUE;
282 if (!mBlocked) {
283 ParseUntilBlocked();
285 return NS_OK;
288 NS_ASSERTION(IsInsertionPointDefined(),
289 "Doc.write reached parser with undefined insertion point.");
291 NS_ASSERTION(!(mStreamParser && !aKey),
292 "Got a null key in a non-script-created parser");
294 if (aSourceBuffer.IsEmpty()) {
295 return NS_OK;
298 nsRefPtr<nsHtml5UTF16Buffer> buffer =
299 new nsHtml5UTF16Buffer(aSourceBuffer.Length());
300 memcpy(buffer->getBuffer(),
301 aSourceBuffer.BeginReading(),
302 aSourceBuffer.Length() * sizeof(PRUnichar));
303 buffer->setEnd(aSourceBuffer.Length());
305 // The buffer is inserted to the stream here in case it won't be parsed
306 // to completion.
307 // The script is identified by aKey. If there's nothing in the buffer
308 // chain for that key, we'll insert at the head of the queue.
309 // When the script leaves something in the queue, a zero-length
310 // key-holder "buffer" is inserted in the queue. If the same script
311 // leaves something in the chain again, it will be inserted immediately
312 // before the old key holder belonging to the same script.
313 nsHtml5UTF16Buffer* prevSearchBuf = nsnull;
314 nsHtml5UTF16Buffer* searchBuf = mFirstBuffer;
316 // after document.open, the first level of document.write has null key
317 if (aKey) {
318 while (searchBuf != mLastBuffer) {
319 if (searchBuf->key == aKey) {
320 // found a key holder
321 // now insert the new buffer between the previous buffer
322 // and the key holder.
323 buffer->next = searchBuf;
324 if (prevSearchBuf) {
325 prevSearchBuf->next = buffer;
326 } else {
327 mFirstBuffer = buffer;
329 break;
331 prevSearchBuf = searchBuf;
332 searchBuf = searchBuf->next;
334 if (searchBuf == mLastBuffer) {
335 // key was not found
336 nsHtml5UTF16Buffer* keyHolder = new nsHtml5UTF16Buffer(aKey);
337 keyHolder->next = mFirstBuffer;
338 buffer->next = keyHolder;
339 mFirstBuffer = buffer;
341 } else {
342 // we have a first level document.write after document.open()
343 // insert immediately before mLastBuffer
344 while (searchBuf != mLastBuffer) {
345 prevSearchBuf = searchBuf;
346 searchBuf = searchBuf->next;
348 buffer->next = mLastBuffer;
349 if (prevSearchBuf) {
350 prevSearchBuf->next = buffer;
351 } else {
352 mFirstBuffer = buffer;
356 while (!mBlocked && buffer->hasMore()) {
357 buffer->adjust(mLastWasCR);
358 mLastWasCR = PR_FALSE;
359 if (buffer->hasMore()) {
360 PRInt32 lineNumberSave;
361 PRBool inRootContext = (!mStreamParser && (aKey == mRootContextKey));
362 if (inRootContext) {
363 mTokenizer->setLineNumber(mRootContextLineNumber);
364 } else {
365 // we aren't the root context, so save the line number on the
366 // *stack* so that we can restore it.
367 lineNumberSave = mTokenizer->getLineNumber();
370 mLastWasCR = mTokenizer->tokenizeBuffer(buffer);
372 if (inRootContext) {
373 mRootContextLineNumber = mTokenizer->getLineNumber();
374 } else {
375 mTokenizer->setLineNumber(lineNumberSave);
378 if (mTreeBuilder->HasScript()) {
379 mTreeBuilder->Flush(); // Move ops to the executor
380 mExecutor->FlushDocumentWrite(); // run the ops
382 // Ignore suspension requests
386 if (!mBlocked) { // buffer was tokenized to completion
387 NS_ASSERTION(!buffer->hasMore(), "Buffer wasn't tokenized to completion?");
388 // Scripting semantics require a forced tree builder flush here
389 mTreeBuilder->Flush(); // Move ops to the executor
390 mExecutor->FlushDocumentWrite(); // run the ops
391 } else if (buffer->hasMore()) {
392 // The buffer wasn't tokenized to completion. Tokenize the untokenized
393 // content in order to preload stuff. This content will be retokenized
394 // later for normal parsing.
395 if (!mDocWriteSpeculatorActive) {
396 mDocWriteSpeculatorActive = PR_TRUE;
397 if (!mDocWriteSpeculativeTreeBuilder) {
398 // Lazily initialize if uninitialized
399 mDocWriteSpeculativeTreeBuilder =
400 new nsHtml5TreeBuilder(nsnull, mExecutor->GetStage());
401 mDocWriteSpeculativeTokenizer =
402 new nsHtml5Tokenizer(mDocWriteSpeculativeTreeBuilder);
403 mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
404 mDocWriteSpeculativeTokenizer->start();
406 mDocWriteSpeculativeTokenizer->resetToDataState();
407 mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
408 mDocWriteSpeculativeLastWasCR = PR_FALSE;
411 // Note that with multilevel document.write if we didn't just activate the
412 // speculator, it's possible that the speculator is now in the wrong state.
413 // That's OK for the sake of simplicity. The worst that can happen is
414 // that the speculative loads aren't exactly right. The content will be
415 // reparsed anyway for non-preload purposes.
417 PRInt32 originalStart = buffer->getStart();
418 while (buffer->hasMore()) {
419 buffer->adjust(mDocWriteSpeculativeLastWasCR);
420 if (buffer->hasMore()) {
421 mDocWriteSpeculativeLastWasCR =
422 mDocWriteSpeculativeTokenizer->tokenizeBuffer(buffer);
425 buffer->setStart(originalStart);
427 mDocWriteSpeculativeTreeBuilder->Flush();
428 mDocWriteSpeculativeTreeBuilder->DropHandles();
429 mExecutor->FlushSpeculativeLoads();
432 return NS_OK;
436 * This magic value is passed to the previous method on document.close()
438 NS_IMETHODIMP_(void *)
439 nsHtml5Parser::GetRootContextKey()
441 return mRootContextKey;
444 NS_IMETHODIMP
445 nsHtml5Parser::Terminate()
447 // We should only call DidBuildModel once, so don't do anything if this is
448 // the second time that Terminate has been called.
449 if (mExecutor->IsComplete()) {
450 return NS_OK;
452 // XXX - [ until we figure out a way to break parser-sink circularity ]
453 // Hack - Hold a reference until we are completely done...
454 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
455 nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(mStreamParser);
456 nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
457 if (mStreamParser) {
458 mStreamParser->Terminate();
460 return mExecutor->DidBuildModel(PR_TRUE);
463 NS_IMETHODIMP
464 nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
465 void* aKey,
466 nsTArray<nsString>& aTagStack,
467 PRBool aXMLMode,
468 const nsACString& aContentType,
469 nsDTDMode aMode)
471 return NS_ERROR_NOT_IMPLEMENTED;
474 NS_IMETHODIMP
475 nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
476 nsIContent* aTargetNode,
477 nsIAtom* aContextLocalName,
478 PRInt32 aContextNamespace,
479 PRBool aQuirks)
481 return NS_ERROR_NOT_IMPLEMENTED;
484 NS_IMETHODIMP
485 nsHtml5Parser::ParseHtml5Fragment(const nsAString& aSourceBuffer,
486 nsIContent* aTargetNode,
487 nsIAtom* aContextLocalName,
488 PRInt32 aContextNamespace,
489 PRBool aQuirks,
490 PRBool aPreventScriptExecution)
492 nsIDocument* doc = aTargetNode->GetOwnerDoc();
493 NS_ENSURE_TRUE(doc, NS_ERROR_NOT_AVAILABLE);
495 nsIURI* uri = doc->GetDocumentURI();
496 NS_ENSURE_TRUE(uri, NS_ERROR_NOT_AVAILABLE);
498 Initialize(doc, uri, nsnull, nsnull);
500 mExecutor->SetParser(this);
501 mExecutor->SetNodeInfoManager(doc->NodeInfoManager());
503 nsIContent* target = aTargetNode;
504 mTreeBuilder->setFragmentContext(aContextLocalName,
505 aContextNamespace,
506 &target,
507 aQuirks);
509 #ifdef DEBUG
510 if (!aPreventScriptExecution) {
511 nsCOMPtr<nsIDOMDocumentFragment> domFrag = do_QueryInterface(aTargetNode);
512 NS_ASSERTION(domFrag,
513 "If script execution isn't prevented, must parse to DOM fragment.");
515 #endif
517 mExecutor->EnableFragmentMode(aPreventScriptExecution);
519 NS_PRECONDITION(!mExecutor->HasStarted(),
520 "Tried to start parse without initializing the parser.");
521 mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
522 mTokenizer->start();
523 mExecutor->Start(); // Don't call WillBuildModel in fragment case
524 if (!aSourceBuffer.IsEmpty()) {
525 PRBool lastWasCR = PR_FALSE;
526 nsHtml5UTF16Buffer buffer(aSourceBuffer.Length());
527 memcpy(buffer.getBuffer(),
528 aSourceBuffer.BeginReading(),
529 aSourceBuffer.Length() * sizeof(PRUnichar));
530 buffer.setEnd(aSourceBuffer.Length());
531 while (buffer.hasMore()) {
532 buffer.adjust(lastWasCR);
533 lastWasCR = PR_FALSE;
534 if (buffer.hasMore()) {
535 lastWasCR = mTokenizer->tokenizeBuffer(&buffer);
539 mTokenizer->eof();
540 mTreeBuilder->StreamEnded();
541 mTreeBuilder->Flush();
542 mExecutor->FlushDocumentWrite();
543 mTokenizer->end();
544 mExecutor->DropParserAndPerfHint();
545 mExecutor->DropHeldElements();
546 mTreeBuilder->DropHandles();
547 mAtomTable.Clear();
548 return NS_OK;
551 NS_IMETHODIMP
552 nsHtml5Parser::BuildModel()
554 NS_NOTREACHED("Don't call this!");
555 return NS_ERROR_NOT_IMPLEMENTED;
558 NS_IMETHODIMP
559 nsHtml5Parser::CancelParsingEvents()
561 NS_NOTREACHED("Don't call this!");
562 return NS_ERROR_NOT_IMPLEMENTED;
565 void
566 nsHtml5Parser::Reset()
568 NS_PRECONDITION(mExecutor->IsFragmentMode(),
569 "Reset called on a non-fragment parser.");
570 mExecutor->Reset();
571 mLastWasCR = PR_FALSE;
572 UnblockParser();
573 mDocumentClosed = PR_FALSE;
574 mStreamParser = nsnull;
575 mRootContextLineNumber = 1;
576 mParserInsertedScriptsBeingEvaluated = 0;
577 mRootContextKey = nsnull;
578 mAtomTable.Clear(); // should be already cleared in the fragment case anyway
579 // Portable parser objects
580 mFirstBuffer->next = nsnull;
581 mFirstBuffer->setStart(0);
582 mFirstBuffer->setEnd(0);
583 mLastBuffer = mFirstBuffer;
586 PRBool
587 nsHtml5Parser::CanInterrupt()
589 // nsContentSink needs this to let nsContentSink::DidProcessATokenImpl
590 // interrupt.
591 return PR_TRUE;
594 PRBool
595 nsHtml5Parser::IsInsertionPointDefined()
597 return !mExecutor->IsFlushing() &&
598 (!mStreamParser || mParserInsertedScriptsBeingEvaluated);
601 void
602 nsHtml5Parser::BeginEvaluatingParserInsertedScript()
604 ++mParserInsertedScriptsBeingEvaluated;
607 void
608 nsHtml5Parser::EndEvaluatingParserInsertedScript()
610 --mParserInsertedScriptsBeingEvaluated;
613 void
614 nsHtml5Parser::MarkAsNotScriptCreated()
616 NS_PRECONDITION(!mStreamParser, "Must not call this twice.");
617 mStreamParser = new nsHtml5StreamParser(mExecutor, this);
620 PRBool
621 nsHtml5Parser::IsScriptCreated()
623 return !mStreamParser;
626 /* End nsIParser */
628 // not from interface
629 void
630 nsHtml5Parser::ParseUntilBlocked()
632 NS_PRECONDITION(!mExecutor->IsFragmentMode(),
633 "ParseUntilBlocked called in fragment mode.");
635 if (mBlocked) {
636 return;
639 if (mExecutor->IsComplete()) {
640 return;
642 NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
644 mDocWriteSpeculatorActive = PR_FALSE;
646 for (;;) {
647 if (!mFirstBuffer->hasMore()) {
648 if (mFirstBuffer == mLastBuffer) {
649 if (mExecutor->IsComplete()) {
650 // something like cache manisfests stopped the parse in mid-flight
651 return;
653 if (mDocumentClosed) {
654 NS_ASSERTION(!mStreamParser,
655 "This should only happen with script-created parser.");
656 mTokenizer->eof();
657 mTreeBuilder->StreamEnded();
658 mTreeBuilder->Flush();
659 mExecutor->FlushDocumentWrite();
660 mTokenizer->end();
661 return;
663 // never release the last buffer.
664 NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
665 "Sentinel buffer had its indeces changed.");
666 if (mStreamParser) {
667 if (mReturnToStreamParserPermitted &&
668 !mExecutor->IsScriptExecuting()) {
669 mTreeBuilder->Flush();
670 mReturnToStreamParserPermitted = PR_FALSE;
671 mStreamParser->ContinueAfterScripts(mTokenizer,
672 mTreeBuilder,
673 mLastWasCR);
675 } else {
676 // Script-created parser
677 mTreeBuilder->Flush();
678 // No need to flush the executor, because the executor is already
679 // in a flush
680 NS_ASSERTION(mExecutor->IsInFlushLoop(),
681 "How did we come here without being in the flush loop?");
683 return; // no more data for now but expecting more
685 mFirstBuffer = mFirstBuffer->next;
686 continue;
689 if (mBlocked || mExecutor->IsComplete()) {
690 return;
693 // now we have a non-empty buffer
694 mFirstBuffer->adjust(mLastWasCR);
695 mLastWasCR = PR_FALSE;
696 if (mFirstBuffer->hasMore()) {
697 PRBool inRootContext = (!mStreamParser &&
698 (mFirstBuffer->key == mRootContextKey));
699 if (inRootContext) {
700 mTokenizer->setLineNumber(mRootContextLineNumber);
702 mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
703 if (inRootContext) {
704 mRootContextLineNumber = mTokenizer->getLineNumber();
706 if (mTreeBuilder->HasScript()) {
707 mTreeBuilder->Flush();
708 mExecutor->FlushDocumentWrite();
710 if (mBlocked) {
711 return;
714 continue;
718 nsresult
719 nsHtml5Parser::Initialize(nsIDocument* aDoc,
720 nsIURI* aURI,
721 nsISupports* aContainer,
722 nsIChannel* aChannel)
724 return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
727 void
728 nsHtml5Parser::StartTokenizer(PRBool aScriptingEnabled) {
729 mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
730 mTokenizer->start();
733 void
734 nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
735 PRInt32 aLine)
737 mTokenizer->resetToDataState();
738 mTokenizer->setLineNumber(aLine);
739 mTreeBuilder->loadState(aState, &mAtomTable);
740 mLastWasCR = PR_FALSE;
741 mReturnToStreamParserPermitted = PR_TRUE;
744 void
745 nsHtml5Parser::ContinueAfterFailedCharsetSwitch()
747 NS_PRECONDITION(mStreamParser,
748 "Tried to continue after failed charset switch without a stream parser");
749 mStreamParser->ContinueAfterFailedCharsetSwitch();