Bug 1690340 - Part 1: Hide all the panel tools in the developer tools menu. r=jdescottes
[gecko.git] / parser / html / nsHtml5Parser.cpp
blob086945a622dce0dd7fa20c10936dc9a29dd6a8d5
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=80: */
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 "nsCRT.h"
11 #include "nsContentUtils.h" // for kLoadAsData
12 #include "nsHtml5AtomTable.h"
13 #include "nsHtml5DependentUTF16Buffer.h"
14 #include "nsHtml5Tokenizer.h"
15 #include "nsHtml5TreeBuilder.h"
16 #include "nsNetUtil.h"
18 NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
19 NS_INTERFACE_TABLE(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
20 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
21 NS_INTERFACE_MAP_END
23 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
24 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
26 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExecutor)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetStreamParser())
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
33 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
34 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
36 tmp->DropStreamParser();
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
39 nsHtml5Parser::nsHtml5Parser()
40 : mLastWasCR(false),
41 mDocWriteSpeculativeLastWasCR(false),
42 mBlocked(0),
43 mDocWriteSpeculatorActive(false),
44 mScriptNestingLevel(0),
45 mDocumentClosed(false),
46 mInDocumentWrite(false),
47 mInsertionPointPermanentlyUndefined(false),
48 mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr)),
49 mLastBuffer(mFirstBuffer),
50 mExecutor(new nsHtml5TreeOpExecutor()),
51 mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr)),
52 mTokenizer(new nsHtml5Tokenizer(mTreeBuilder.get(), false)),
53 mRootContextLineNumber(1),
54 mReturnToStreamParserPermitted(false) {
55 mTokenizer->setInterner(&mAtomTable);
58 nsHtml5Parser::~nsHtml5Parser() {
59 mTokenizer->end();
60 if (mDocWriteSpeculativeTokenizer) {
61 mDocWriteSpeculativeTokenizer->end();
65 NS_IMETHODIMP_(void)
66 nsHtml5Parser::SetContentSink(nsIContentSink* aSink) {
67 NS_ASSERTION(aSink == static_cast<nsIContentSink*>(mExecutor),
68 "Attempt to set a foreign sink.");
71 NS_IMETHODIMP_(nsIContentSink*)
72 nsHtml5Parser::GetContentSink() {
73 return static_cast<nsIContentSink*>(mExecutor);
76 NS_IMETHODIMP_(void)
77 nsHtml5Parser::GetCommand(nsCString& aCommand) {
78 aCommand.AssignLiteral("view");
81 NS_IMETHODIMP_(void)
82 nsHtml5Parser::SetCommand(const char* aCommand) {
83 NS_ASSERTION(!strcmp(aCommand, "view") || !strcmp(aCommand, "view-source") ||
84 !strcmp(aCommand, "external-resource") ||
85 !strcmp(aCommand, "import") ||
86 !strcmp(aCommand, kLoadAsData),
87 "Unsupported parser command");
90 NS_IMETHODIMP_(void)
91 nsHtml5Parser::SetCommand(eParserCommands aParserCommand) {
92 NS_ASSERTION(aParserCommand == eViewNormal,
93 "Parser command was not eViewNormal.");
96 void nsHtml5Parser::SetDocumentCharset(NotNull<const Encoding*> aEncoding,
97 int32_t aCharsetSource) {
98 MOZ_ASSERT(!mExecutor->HasStarted(), "Document charset set too late.");
99 MOZ_ASSERT(GetStreamParser(), "Setting charset on a script-only parser.");
100 GetStreamParser()->SetDocumentCharset(aEncoding, aCharsetSource);
101 mExecutor->SetDocumentCharsetAndSource(aEncoding, aCharsetSource);
104 NS_IMETHODIMP
105 nsHtml5Parser::GetChannel(nsIChannel** aChannel) {
106 if (GetStreamParser()) {
107 return GetStreamParser()->GetChannel(aChannel);
108 } else {
109 return NS_ERROR_NOT_AVAILABLE;
113 NS_IMETHODIMP
114 nsHtml5Parser::GetDTD(nsIDTD** aDTD) {
115 *aDTD = nullptr;
116 return NS_OK;
119 nsIStreamListener* nsHtml5Parser::GetStreamListener() {
120 return mStreamListener;
123 NS_IMETHODIMP
124 nsHtml5Parser::ContinueInterruptedParsing() {
125 MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
126 return NS_ERROR_NOT_IMPLEMENTED;
129 NS_IMETHODIMP_(void)
130 nsHtml5Parser::BlockParser() { mBlocked++; }
132 NS_IMETHODIMP_(void)
133 nsHtml5Parser::UnblockParser() {
134 MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
135 if (MOZ_LIKELY(mBlocked > 0)) {
136 mBlocked--;
138 if (MOZ_LIKELY(mBlocked == 0) && mExecutor) {
139 mExecutor->ContinueInterruptedParsingAsync();
143 NS_IMETHODIMP_(void)
144 nsHtml5Parser::ContinueInterruptedParsingAsync() {
145 if (mExecutor) {
146 mExecutor->ContinueInterruptedParsingAsync();
150 NS_IMETHODIMP_(bool)
151 nsHtml5Parser::IsParserEnabled() { return !mBlocked; }
153 NS_IMETHODIMP_(bool)
154 nsHtml5Parser::IsComplete() { return mExecutor->IsComplete(); }
156 NS_IMETHODIMP
157 nsHtml5Parser::Parse(nsIURI* aURL, nsIRequestObserver* aObserver,
158 void* aKey, // legacy; ignored
159 nsDTDMode aMode) // legacy; ignored
162 * Do NOT cause WillBuildModel to be called synchronously from here!
163 * The document won't be ready for it until OnStartRequest!
165 MOZ_ASSERT(!mExecutor->HasStarted(),
166 "Tried to start parse without initializing the parser.");
167 MOZ_ASSERT(GetStreamParser(),
168 "Can't call this Parse() variant on script-created parser");
170 GetStreamParser()->SetObserver(aObserver);
171 GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
172 mExecutor->SetStreamParser(GetStreamParser());
173 mExecutor->SetParser(this);
174 return NS_OK;
177 nsresult nsHtml5Parser::Parse(const nsAString& aSourceBuffer, void* aKey,
178 bool aLastCall) {
179 nsresult rv;
180 if (NS_FAILED(rv = mExecutor->IsBroken())) {
181 return rv;
183 if (aSourceBuffer.Length() > INT32_MAX) {
184 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
187 // Maintain a reference to ourselves so we don't go away
188 // till we're completely done. The old parser grips itself in this method.
189 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
191 // Gripping the other objects just in case, since the other old grip
192 // required grips to these, too.
193 RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
194 mozilla::Unused << streamKungFuDeathGrip; // Not used within function
195 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
197 MOZ_RELEASE_ASSERT(executor->HasStarted());
199 // Return early if the parser has processed EOF
200 if (executor->IsComplete()) {
201 return NS_OK;
204 if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
205 // document.close()
206 NS_ASSERTION(!GetStreamParser(),
207 "Had stream parser but got document.close().");
208 if (mDocumentClosed) {
209 // already closed
210 return NS_OK;
212 mDocumentClosed = true;
213 if (!mBlocked && !mInDocumentWrite) {
214 return ParseUntilBlocked();
216 return NS_OK;
219 // If we got this far, we are dealing with a document.write or
220 // document.writeln call--not document.close().
222 MOZ_RELEASE_ASSERT(
223 IsInsertionPointDefined(),
224 "Doc.write reached parser with undefined insertion point.");
226 MOZ_RELEASE_ASSERT(!(GetStreamParser() && !aKey),
227 "Got a null key in a non-script-created parser");
229 // XXX is this optimization bogus?
230 if (aSourceBuffer.IsEmpty()) {
231 return NS_OK;
234 // This guard is here to prevent document.close from tokenizing synchronously
235 // while a document.write (that wrote the script that called document.close!)
236 // is still on the call stack.
237 mozilla::AutoRestore<bool> guard(mInDocumentWrite);
238 mInDocumentWrite = true;
240 // The script is identified by aKey. If there's nothing in the buffer
241 // chain for that key, we'll insert at the head of the queue.
242 // When the script leaves something in the queue, a zero-length
243 // key-holder "buffer" is inserted in the queue. If the same script
244 // leaves something in the chain again, it will be inserted immediately
245 // before the old key holder belonging to the same script.
247 // We don't do the actual data insertion yet in the hope that the data gets
248 // tokenized and there no data or less data to copy to the heap after
249 // tokenization. Also, this way, we avoid inserting one empty data buffer
250 // per document.write, which matters for performance when the parser isn't
251 // blocked and a badly-authored script calls document.write() once per
252 // input character. (As seen in a benchmark!)
254 // The insertion into the input stream happens conceptually before anything
255 // gets tokenized. To make sure multi-level document.write works right,
256 // it's necessary to establish the location of our parser key up front
257 // in case this is the first write with this key.
259 // In a document.open() case, the first write level has a null key, so that
260 // case is handled separately, because normal buffers containing data
261 // have null keys.
263 // These don't need to be owning references, because they always point to
264 // the buffer queue and buffers can't be removed from the buffer queue
265 // before document.write() returns. The buffer queue clean-up happens the
266 // next time ParseUntilBlocked() is called.
267 // However, they are made owning just in case the reasoning above is flawed
268 // and a flaw would lead to worse problems with plain pointers. If this
269 // turns out to be a perf problem, it's worthwhile to consider making
270 // prevSearchbuf a plain pointer again.
271 RefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
272 RefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
274 if (aKey) {
275 if (mFirstBuffer == mLastBuffer) {
276 nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
277 keyHolder->next = mLastBuffer;
278 mFirstBuffer = keyHolder;
279 } else if (mFirstBuffer->key != aKey) {
280 prevSearchBuf = mFirstBuffer;
281 for (;;) {
282 if (prevSearchBuf->next == mLastBuffer) {
283 // key was not found
284 nsHtml5OwningUTF16Buffer* keyHolder =
285 new nsHtml5OwningUTF16Buffer(aKey);
286 keyHolder->next = mFirstBuffer;
287 mFirstBuffer = keyHolder;
288 prevSearchBuf = nullptr;
289 break;
291 if (prevSearchBuf->next->key == aKey) {
292 // found a key holder
293 break;
295 prevSearchBuf = prevSearchBuf->next;
297 } // else mFirstBuffer is the keyholder
299 // prevSearchBuf is the previous buffer before the keyholder or null if
300 // there isn't one.
301 } else {
302 // We have a first-level write in the document.open() case. We insert before
303 // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
304 // and redesignating the previous mLastBuffer as our firstLevelMarker. We
305 // need to put a marker there, because otherwise additional document.writes
306 // from nested event loops would insert in the wrong place. Sigh.
307 mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
308 firstLevelMarker = mLastBuffer;
309 mLastBuffer = mLastBuffer->next;
312 nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
314 while (!mBlocked && stackBuffer.hasMore()) {
315 stackBuffer.adjust(mLastWasCR);
316 mLastWasCR = false;
317 if (stackBuffer.hasMore()) {
318 int32_t lineNumberSave;
319 bool inRootContext = (!GetStreamParser() && !aKey);
320 if (inRootContext) {
321 mTokenizer->setLineNumber(mRootContextLineNumber);
322 } else {
323 // we aren't the root context, so save the line number on the
324 // *stack* so that we can restore it.
325 lineNumberSave = mTokenizer->getLineNumber();
328 if (!mTokenizer->EnsureBufferSpace(stackBuffer.getLength())) {
329 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
331 mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
332 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
333 return executor->MarkAsBroken(rv);
336 if (inRootContext) {
337 mRootContextLineNumber = mTokenizer->getLineNumber();
338 } else {
339 mTokenizer->setLineNumber(lineNumberSave);
342 if (mTreeBuilder->HasScript()) {
343 mTreeBuilder->Flush(); // Move ops to the executor
344 rv = executor->FlushDocumentWrite(); // run the ops
345 NS_ENSURE_SUCCESS(rv, rv);
346 // Flushing tree ops can cause all sorts of things.
347 // Return early if the parser got terminated.
348 if (executor->IsComplete()) {
349 return NS_OK;
352 // Ignore suspension requests
356 RefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
357 if (stackBuffer.hasMore()) {
358 // The buffer wasn't tokenized to completion. Create a copy of the tail
359 // on the heap.
360 heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
361 if (!heapBuffer) {
362 // Allocation failed. The parser is now broken.
363 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
367 if (heapBuffer) {
368 // We have something to insert before the keyholder holding in the non-null
369 // aKey case and we have something to swap into firstLevelMarker in the
370 // null aKey case.
371 if (aKey) {
372 NS_ASSERTION(mFirstBuffer != mLastBuffer, "Where's the keyholder?");
373 // the key holder is still somewhere further down the list from
374 // prevSearchBuf (which may be null)
375 if (mFirstBuffer->key == aKey) {
376 NS_ASSERTION(
377 !prevSearchBuf,
378 "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
379 heapBuffer->next = mFirstBuffer;
380 mFirstBuffer = heapBuffer;
381 } else {
382 if (!prevSearchBuf) {
383 prevSearchBuf = mFirstBuffer;
385 // We created a key holder earlier, so we will find it without walking
386 // past the end of the list.
387 while (prevSearchBuf->next->key != aKey) {
388 prevSearchBuf = prevSearchBuf->next;
390 heapBuffer->next = prevSearchBuf->next;
391 prevSearchBuf->next = heapBuffer;
393 } else {
394 NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
395 firstLevelMarker->Swap(heapBuffer);
399 if (!mBlocked) { // buffer was tokenized to completion
400 NS_ASSERTION(!stackBuffer.hasMore(),
401 "Buffer wasn't tokenized to completion?");
402 // Scripting semantics require a forced tree builder flush here
403 mTreeBuilder->Flush(); // Move ops to the executor
404 rv = executor->FlushDocumentWrite(); // run the ops
405 NS_ENSURE_SUCCESS(rv, rv);
406 } else if (stackBuffer.hasMore()) {
407 // The buffer wasn't tokenized to completion. Tokenize the untokenized
408 // content in order to preload stuff. This content will be retokenized
409 // later for normal parsing.
410 if (!mDocWriteSpeculatorActive) {
411 mDocWriteSpeculatorActive = true;
412 if (!mDocWriteSpeculativeTreeBuilder) {
413 // Lazily initialize if uninitialized
414 mDocWriteSpeculativeTreeBuilder =
415 MakeUnique<nsHtml5TreeBuilder>(nullptr, executor->GetStage());
416 mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
417 mTreeBuilder->isScriptingEnabled());
418 mDocWriteSpeculativeTokenizer = MakeUnique<nsHtml5Tokenizer>(
419 mDocWriteSpeculativeTreeBuilder.get(), false);
420 mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
421 mDocWriteSpeculativeTokenizer->start();
423 mDocWriteSpeculativeTokenizer->resetToDataState();
424 mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder.get());
425 mDocWriteSpeculativeLastWasCR = false;
428 // Note that with multilevel document.write if we didn't just activate the
429 // speculator, it's possible that the speculator is now in the wrong state.
430 // That's OK for the sake of simplicity. The worst that can happen is
431 // that the speculative loads aren't exactly right. The content will be
432 // reparsed anyway for non-preload purposes.
434 // The buffer position for subsequent non-speculative parsing now lives
435 // in heapBuffer, so it's ok to let the buffer position of stackBuffer
436 // to be overwritten and not restored below.
437 while (stackBuffer.hasMore()) {
438 stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
439 if (stackBuffer.hasMore()) {
440 if (!mDocWriteSpeculativeTokenizer->EnsureBufferSpace(
441 stackBuffer.getLength())) {
442 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
444 mDocWriteSpeculativeLastWasCR =
445 mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
446 nsresult rv;
447 if (NS_FAILED((rv = mDocWriteSpeculativeTreeBuilder->IsBroken()))) {
448 return executor->MarkAsBroken(rv);
453 mDocWriteSpeculativeTreeBuilder->Flush();
454 mDocWriteSpeculativeTreeBuilder->DropHandles();
455 executor->FlushSpeculativeLoads();
458 return NS_OK;
461 NS_IMETHODIMP
462 nsHtml5Parser::Terminate() {
463 // We should only call DidBuildModel once, so don't do anything if this is
464 // the second time that Terminate has been called.
465 if (mExecutor->IsComplete()) {
466 return NS_OK;
468 // XXX - [ until we figure out a way to break parser-sink circularity ]
469 // Hack - Hold a reference until we are completely done...
470 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
471 RefPtr<nsHtml5StreamParser> streamParser(GetStreamParser());
472 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
473 if (streamParser) {
474 streamParser->Terminate();
476 return executor->DidBuildModel(true);
479 NS_IMETHODIMP
480 nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
481 nsTArray<nsString>& aTagStack) {
482 return NS_ERROR_NOT_IMPLEMENTED;
485 NS_IMETHODIMP
486 nsHtml5Parser::BuildModel() {
487 MOZ_ASSERT_UNREACHABLE("Don't call this!");
488 return NS_ERROR_NOT_IMPLEMENTED;
491 NS_IMETHODIMP
492 nsHtml5Parser::CancelParsingEvents() {
493 MOZ_ASSERT_UNREACHABLE("Don't call this!");
494 return NS_ERROR_NOT_IMPLEMENTED;
497 void nsHtml5Parser::Reset() { MOZ_ASSERT_UNREACHABLE("Don't call this!"); }
499 bool nsHtml5Parser::IsInsertionPointDefined() {
500 return !mExecutor->IsFlushing() && !mInsertionPointPermanentlyUndefined &&
501 (!GetStreamParser() || mScriptNestingLevel != 0);
504 void nsHtml5Parser::IncrementScriptNestingLevel() { ++mScriptNestingLevel; }
506 void nsHtml5Parser::DecrementScriptNestingLevel() { --mScriptNestingLevel; }
508 bool nsHtml5Parser::HasNonzeroScriptNestingLevel() const {
509 return mScriptNestingLevel != 0;
512 void nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand) {
513 MOZ_ASSERT(!mStreamListener, "Must not call this twice.");
514 eParserMode mode = NORMAL;
515 if (!nsCRT::strcmp(aCommand, "view-source")) {
516 mode = VIEW_SOURCE_HTML;
517 } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
518 mode = VIEW_SOURCE_XML;
519 } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
520 mode = VIEW_SOURCE_PLAIN;
521 } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
522 mode = PLAIN_TEXT;
523 } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
524 mode = LOAD_AS_DATA;
526 #ifdef DEBUG
527 else {
528 NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
529 !nsCRT::strcmp(aCommand, "external-resource") ||
530 !nsCRT::strcmp(aCommand, "import"),
531 "Unsupported parser command!");
533 #endif
534 mStreamListener =
535 new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
538 bool nsHtml5Parser::IsScriptCreated() { return !GetStreamParser(); }
540 /* End nsIParser */
542 // not from interface
543 nsresult nsHtml5Parser::ParseUntilBlocked() {
544 nsresult rv = mExecutor->IsBroken();
545 NS_ENSURE_SUCCESS(rv, rv);
546 if (mBlocked || mInsertionPointPermanentlyUndefined ||
547 mExecutor->IsComplete()) {
548 return NS_OK;
550 NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
551 NS_ASSERTION(!mInDocumentWrite,
552 "ParseUntilBlocked entered while in doc.write!");
554 mDocWriteSpeculatorActive = false;
556 for (;;) {
557 if (!mFirstBuffer->hasMore()) {
558 if (mFirstBuffer == mLastBuffer) {
559 if (mExecutor->IsComplete()) {
560 // something like cache manisfests stopped the parse in mid-flight
561 return NS_OK;
563 if (mDocumentClosed) {
564 PermanentlyUndefineInsertionPoint();
565 nsresult rv;
566 MOZ_RELEASE_ASSERT(
567 !GetStreamParser(),
568 "This should only happen with script-created parser.");
569 if (NS_SUCCEEDED((rv = mExecutor->IsBroken()))) {
570 mTokenizer->eof();
571 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
572 mExecutor->MarkAsBroken(rv);
573 } else {
574 mTreeBuilder->StreamEnded();
577 mTreeBuilder->Flush();
578 mExecutor->FlushDocumentWrite();
579 // The below call does memory cleanup, so call it even if the
580 // parser has been marked as broken.
581 mTokenizer->end();
582 return rv;
584 // never release the last buffer.
585 NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
586 "Sentinel buffer had its indeces changed.");
587 if (GetStreamParser()) {
588 if (mReturnToStreamParserPermitted &&
589 !mExecutor->IsScriptExecuting()) {
590 mTreeBuilder->Flush();
591 mReturnToStreamParserPermitted = false;
592 GetStreamParser()->ContinueAfterScripts(
593 mTokenizer.get(), mTreeBuilder.get(), mLastWasCR);
595 } else {
596 // Script-created parser
597 mTreeBuilder->Flush();
598 // No need to flush the executor, because the executor is already
599 // in a flush
600 NS_ASSERTION(mExecutor->IsInFlushLoop(),
601 "How did we come here without being in the flush loop?");
603 return NS_OK; // no more data for now but expecting more
605 mFirstBuffer = mFirstBuffer->next;
606 continue;
609 if (mBlocked || mExecutor->IsComplete()) {
610 return NS_OK;
613 // now we have a non-empty buffer
614 mFirstBuffer->adjust(mLastWasCR);
615 mLastWasCR = false;
616 if (mFirstBuffer->hasMore()) {
617 bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
618 if (inRootContext) {
619 mTokenizer->setLineNumber(mRootContextLineNumber);
621 if (!mTokenizer->EnsureBufferSpace(mFirstBuffer->getLength())) {
622 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
624 mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
625 nsresult rv;
626 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
627 return mExecutor->MarkAsBroken(rv);
629 if (inRootContext) {
630 mRootContextLineNumber = mTokenizer->getLineNumber();
632 if (mTreeBuilder->HasScript()) {
633 mTreeBuilder->Flush();
634 rv = mExecutor->FlushDocumentWrite();
635 NS_ENSURE_SUCCESS(rv, rv);
637 if (mBlocked) {
638 return NS_OK;
644 nsresult nsHtml5Parser::StartExecutor() {
645 MOZ_ASSERT(!GetStreamParser(),
646 "Had stream parser but document.write started life cycle.");
647 // This is part of the setup document.open() does.
648 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
649 executor->SetParser(this);
650 mTreeBuilder->setScriptingEnabled(executor->IsScriptEnabled());
652 mTreeBuilder->setIsSrcdocDocument(false);
654 mTokenizer->start();
655 executor->Start();
658 * We know we're in document.open(), so our document must already
659 * have a script global andthe WillBuildModel call is safe.
661 return executor->WillBuildModel(eDTDMode_unknown);
664 nsresult nsHtml5Parser::Initialize(mozilla::dom::Document* aDoc, nsIURI* aURI,
665 nsISupports* aContainer,
666 nsIChannel* aChannel) {
667 return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
670 void nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
671 bool isSrcdoc = false;
672 nsCOMPtr<nsIChannel> channel;
673 nsresult rv = GetChannel(getter_AddRefs(channel));
674 if (NS_SUCCEEDED(rv)) {
675 isSrcdoc = NS_IsSrcdocChannel(channel);
677 mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
679 mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
680 mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
681 mTokenizer->start();
684 void nsHtml5Parser::InitializeDocWriteParserState(
685 nsAHtml5TreeBuilderState* aState, int32_t aLine) {
686 mTokenizer->resetToDataState();
687 mTokenizer->setLineNumber(aLine);
688 mTreeBuilder->loadState(aState);
689 mLastWasCR = false;
690 mReturnToStreamParserPermitted = true;
693 void nsHtml5Parser::ContinueAfterFailedCharsetSwitch() {
694 MOZ_ASSERT(
695 GetStreamParser(),
696 "Tried to continue after failed charset switch without a stream parser");
697 GetStreamParser()->ContinueAfterFailedCharsetSwitch();