Bug 1799258 - Do color-management on Windows+DComp via IDCompositionFilterEffects...
[gecko.git] / parser / html / nsHtml5Parser.cpp
blob5425b7d8a76347221878eb36a228afd44ead137c
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, false)),
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 bool aForceAutoDetection) {
99 MOZ_ASSERT(!mExecutor->HasStarted(), "Document charset set too late.");
100 MOZ_ASSERT(GetStreamParser(), "Setting charset on a script-only parser.");
101 GetStreamParser()->SetDocumentCharset(
102 aEncoding, (nsCharsetSource)aCharsetSource, aForceAutoDetection);
103 mExecutor->SetDocumentCharsetAndSource(aEncoding,
104 (nsCharsetSource)aCharsetSource);
107 nsresult nsHtml5Parser::GetChannel(nsIChannel** aChannel) {
108 if (GetStreamParser()) {
109 return GetStreamParser()->GetChannel(aChannel);
110 } else {
111 return NS_ERROR_NOT_AVAILABLE;
115 nsIStreamListener* nsHtml5Parser::GetStreamListener() {
116 return mStreamListener;
119 NS_IMETHODIMP
120 nsHtml5Parser::ContinueInterruptedParsing() {
121 MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
122 return NS_ERROR_NOT_IMPLEMENTED;
125 NS_IMETHODIMP_(void)
126 nsHtml5Parser::BlockParser() { mBlocked++; }
128 NS_IMETHODIMP_(void)
129 nsHtml5Parser::UnblockParser() {
130 MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
131 if (MOZ_LIKELY(mBlocked > 0)) {
132 mBlocked--;
134 if (MOZ_LIKELY(mBlocked == 0) && mExecutor) {
135 mExecutor->ContinueInterruptedParsingAsync();
139 NS_IMETHODIMP_(void)
140 nsHtml5Parser::ContinueInterruptedParsingAsync() {
141 if (mExecutor) {
142 mExecutor->ContinueInterruptedParsingAsync();
146 NS_IMETHODIMP_(bool)
147 nsHtml5Parser::IsParserEnabled() { return !mBlocked; }
149 NS_IMETHODIMP_(bool)
150 nsHtml5Parser::IsComplete() { return mExecutor->IsComplete(); }
152 NS_IMETHODIMP
153 nsHtml5Parser::Parse(nsIURI* aURL) {
155 * Do NOT cause WillBuildModel to be called synchronously from here!
156 * The document won't be ready for it until OnStartRequest!
158 MOZ_ASSERT(!mExecutor->HasStarted(),
159 "Tried to start parse without initializing the parser.");
160 MOZ_ASSERT(GetStreamParser(),
161 "Can't call this Parse() variant on script-created parser");
163 GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
164 mExecutor->SetStreamParser(GetStreamParser());
165 mExecutor->SetParser(this);
166 return NS_OK;
169 nsresult nsHtml5Parser::Parse(const nsAString& aSourceBuffer, void* aKey,
170 bool aLastCall) {
171 nsresult rv;
172 if (NS_FAILED(rv = mExecutor->IsBroken())) {
173 return rv;
175 if (aSourceBuffer.Length() > INT32_MAX) {
176 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
179 // Maintain a reference to ourselves so we don't go away
180 // till we're completely done. The old parser grips itself in this method.
181 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
183 // Gripping the other objects just in case, since the other old grip
184 // required grips to these, too.
185 RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
186 mozilla::Unused << streamKungFuDeathGrip; // Not used within function
187 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
189 MOZ_RELEASE_ASSERT(executor->HasStarted());
191 // Return early if the parser has processed EOF
192 if (executor->IsComplete()) {
193 return NS_OK;
196 if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
197 // document.close()
198 NS_ASSERTION(!GetStreamParser(),
199 "Had stream parser but got document.close().");
200 if (mDocumentClosed) {
201 // already closed
202 return NS_OK;
204 mDocumentClosed = true;
205 if (!mBlocked && !mInDocumentWrite) {
206 return ParseUntilBlocked();
208 return NS_OK;
211 // If we got this far, we are dealing with a document.write or
212 // document.writeln call--not document.close().
214 MOZ_RELEASE_ASSERT(
215 IsInsertionPointDefined(),
216 "Doc.write reached parser with undefined insertion point.");
218 MOZ_RELEASE_ASSERT(!(GetStreamParser() && !aKey),
219 "Got a null key in a non-script-created parser");
221 // XXX is this optimization bogus?
222 if (aSourceBuffer.IsEmpty()) {
223 return NS_OK;
226 // This guard is here to prevent document.close from tokenizing synchronously
227 // while a document.write (that wrote the script that called document.close!)
228 // is still on the call stack.
229 mozilla::AutoRestore<bool> guard(mInDocumentWrite);
230 mInDocumentWrite = true;
232 // The script is identified by aKey. If there's nothing in the buffer
233 // chain for that key, we'll insert at the head of the queue.
234 // When the script leaves something in the queue, a zero-length
235 // key-holder "buffer" is inserted in the queue. If the same script
236 // leaves something in the chain again, it will be inserted immediately
237 // before the old key holder belonging to the same script.
239 // We don't do the actual data insertion yet in the hope that the data gets
240 // tokenized and there no data or less data to copy to the heap after
241 // tokenization. Also, this way, we avoid inserting one empty data buffer
242 // per document.write, which matters for performance when the parser isn't
243 // blocked and a badly-authored script calls document.write() once per
244 // input character. (As seen in a benchmark!)
246 // The insertion into the input stream happens conceptually before anything
247 // gets tokenized. To make sure multi-level document.write works right,
248 // it's necessary to establish the location of our parser key up front
249 // in case this is the first write with this key.
251 // In a document.open() case, the first write level has a null key, so that
252 // case is handled separately, because normal buffers containing data
253 // have null keys.
255 // These don't need to be owning references, because they always point to
256 // the buffer queue and buffers can't be removed from the buffer queue
257 // before document.write() returns. The buffer queue clean-up happens the
258 // next time ParseUntilBlocked() is called.
259 // However, they are made owning just in case the reasoning above is flawed
260 // and a flaw would lead to worse problems with plain pointers. If this
261 // turns out to be a perf problem, it's worthwhile to consider making
262 // prevSearchbuf a plain pointer again.
263 RefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
264 RefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
266 if (aKey) {
267 if (mFirstBuffer == mLastBuffer) {
268 nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
269 keyHolder->next = mLastBuffer;
270 mFirstBuffer = keyHolder;
271 } else if (mFirstBuffer->key != aKey) {
272 prevSearchBuf = mFirstBuffer;
273 for (;;) {
274 if (prevSearchBuf->next == mLastBuffer) {
275 // key was not found
276 nsHtml5OwningUTF16Buffer* keyHolder =
277 new nsHtml5OwningUTF16Buffer(aKey);
278 keyHolder->next = mFirstBuffer;
279 mFirstBuffer = keyHolder;
280 prevSearchBuf = nullptr;
281 break;
283 if (prevSearchBuf->next->key == aKey) {
284 // found a key holder
285 break;
287 prevSearchBuf = prevSearchBuf->next;
289 } // else mFirstBuffer is the keyholder
291 // prevSearchBuf is the previous buffer before the keyholder or null if
292 // there isn't one.
293 } else {
294 // We have a first-level write in the document.open() case. We insert before
295 // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
296 // and redesignating the previous mLastBuffer as our firstLevelMarker. We
297 // need to put a marker there, because otherwise additional document.writes
298 // from nested event loops would insert in the wrong place. Sigh.
299 mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
300 firstLevelMarker = mLastBuffer;
301 mLastBuffer = mLastBuffer->next;
304 nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
306 while (!mBlocked && stackBuffer.hasMore()) {
307 stackBuffer.adjust(mLastWasCR);
308 mLastWasCR = false;
309 if (stackBuffer.hasMore()) {
310 int32_t lineNumberSave;
311 bool inRootContext = (!GetStreamParser() && !aKey);
312 if (inRootContext) {
313 mTokenizer->setLineNumber(mRootContextLineNumber);
314 } else {
315 // we aren't the root context, so save the line number on the
316 // *stack* so that we can restore it.
317 lineNumberSave = mTokenizer->getLineNumber();
320 if (!mTokenizer->EnsureBufferSpace(stackBuffer.getLength())) {
321 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
323 mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
324 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
325 return executor->MarkAsBroken(rv);
328 if (inRootContext) {
329 mRootContextLineNumber = mTokenizer->getLineNumber();
330 } else {
331 mTokenizer->setLineNumber(lineNumberSave);
334 if (mTreeBuilder->HasScript()) {
335 auto r = mTreeBuilder->Flush(); // Move ops to the executor
336 if (r.isErr()) {
337 return executor->MarkAsBroken(r.unwrapErr());
339 rv = executor->FlushDocumentWrite(); // run the ops
340 NS_ENSURE_SUCCESS(rv, rv);
341 // Flushing tree ops can cause all sorts of things.
342 // Return early if the parser got terminated.
343 if (executor->IsComplete()) {
344 return NS_OK;
347 // Ignore suspension requests
351 RefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
352 if (stackBuffer.hasMore()) {
353 // The buffer wasn't tokenized to completion. Create a copy of the tail
354 // on the heap.
355 heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
356 if (!heapBuffer) {
357 // Allocation failed. The parser is now broken.
358 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
362 if (heapBuffer) {
363 // We have something to insert before the keyholder holding in the non-null
364 // aKey case and we have something to swap into firstLevelMarker in the
365 // null aKey case.
366 if (aKey) {
367 NS_ASSERTION(mFirstBuffer != mLastBuffer, "Where's the keyholder?");
368 // the key holder is still somewhere further down the list from
369 // prevSearchBuf (which may be null)
370 if (mFirstBuffer->key == aKey) {
371 NS_ASSERTION(
372 !prevSearchBuf,
373 "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
374 heapBuffer->next = mFirstBuffer;
375 mFirstBuffer = heapBuffer;
376 } else {
377 if (!prevSearchBuf) {
378 prevSearchBuf = mFirstBuffer;
380 // We created a key holder earlier, so we will find it without walking
381 // past the end of the list.
382 while (prevSearchBuf->next->key != aKey) {
383 prevSearchBuf = prevSearchBuf->next;
385 heapBuffer->next = prevSearchBuf->next;
386 prevSearchBuf->next = heapBuffer;
388 } else {
389 NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
390 firstLevelMarker->Swap(heapBuffer);
394 if (!mBlocked) { // buffer was tokenized to completion
395 NS_ASSERTION(!stackBuffer.hasMore(),
396 "Buffer wasn't tokenized to completion?");
397 // Scripting semantics require a forced tree builder flush here
398 auto r = mTreeBuilder->Flush(); // Move ops to the executor
399 if (r.isErr()) {
400 return executor->MarkAsBroken(r.unwrapErr());
402 rv = executor->FlushDocumentWrite(); // run the ops
403 NS_ENSURE_SUCCESS(rv, rv);
404 } else if (stackBuffer.hasMore()) {
405 // The buffer wasn't tokenized to completion. Tokenize the untokenized
406 // content in order to preload stuff. This content will be retokenized
407 // later for normal parsing.
408 if (!mDocWriteSpeculatorActive) {
409 mDocWriteSpeculatorActive = true;
410 if (!mDocWriteSpeculativeTreeBuilder) {
411 // Lazily initialize if uninitialized
412 mDocWriteSpeculativeTreeBuilder =
413 MakeUnique<nsHtml5TreeBuilder>(nullptr, executor->GetStage(), true);
414 mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
415 mTreeBuilder->isScriptingEnabled());
416 mDocWriteSpeculativeTokenizer = MakeUnique<nsHtml5Tokenizer>(
417 mDocWriteSpeculativeTreeBuilder.get(), false);
418 mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
419 mDocWriteSpeculativeTokenizer->start();
421 mDocWriteSpeculativeTokenizer->resetToDataState();
422 mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder.get());
423 mDocWriteSpeculativeLastWasCR = false;
426 // Note that with multilevel document.write if we didn't just activate the
427 // speculator, it's possible that the speculator is now in the wrong state.
428 // That's OK for the sake of simplicity. The worst that can happen is
429 // that the speculative loads aren't exactly right. The content will be
430 // reparsed anyway for non-preload purposes.
432 // The buffer position for subsequent non-speculative parsing now lives
433 // in heapBuffer, so it's ok to let the buffer position of stackBuffer
434 // to be overwritten and not restored below.
435 while (stackBuffer.hasMore()) {
436 stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
437 if (stackBuffer.hasMore()) {
438 if (!mDocWriteSpeculativeTokenizer->EnsureBufferSpace(
439 stackBuffer.getLength())) {
440 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
442 mDocWriteSpeculativeLastWasCR =
443 mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
444 nsresult rv;
445 if (NS_FAILED((rv = mDocWriteSpeculativeTreeBuilder->IsBroken()))) {
446 return executor->MarkAsBroken(rv);
451 auto r = mDocWriteSpeculativeTreeBuilder->Flush();
452 if (r.isErr()) {
453 return executor->MarkAsBroken(r.unwrapErr());
455 mDocWriteSpeculativeTreeBuilder->DropHandles();
456 executor->FlushSpeculativeLoads();
459 return NS_OK;
462 NS_IMETHODIMP
463 nsHtml5Parser::Terminate() {
464 // Prevent a second call to DidBuildModel via document.close()
465 mDocumentClosed = true;
466 // We should only call DidBuildModel once, so don't do anything if this is
467 // the second time that Terminate has been called.
468 if (mExecutor->IsComplete()) {
469 return NS_OK;
471 // XXX - [ until we figure out a way to break parser-sink circularity ]
472 // Hack - Hold a reference until we are completely done...
473 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
474 RefPtr<nsHtml5StreamParser> streamParser(GetStreamParser());
475 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
476 if (streamParser) {
477 streamParser->Terminate();
479 return executor->DidBuildModel(true);
482 bool nsHtml5Parser::IsInsertionPointDefined() {
483 return !mExecutor->IsFlushing() && !mInsertionPointPermanentlyUndefined &&
484 (!GetStreamParser() || mScriptNestingLevel != 0);
487 void nsHtml5Parser::IncrementScriptNestingLevel() { ++mScriptNestingLevel; }
489 void nsHtml5Parser::DecrementScriptNestingLevel() { --mScriptNestingLevel; }
491 bool nsHtml5Parser::HasNonzeroScriptNestingLevel() const {
492 return mScriptNestingLevel != 0;
495 void nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand) {
496 MOZ_ASSERT(!mStreamListener, "Must not call this twice.");
497 eParserMode mode = NORMAL;
498 if (!nsCRT::strcmp(aCommand, "view-source")) {
499 mode = VIEW_SOURCE_HTML;
500 } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
501 mode = VIEW_SOURCE_XML;
502 } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
503 mode = VIEW_SOURCE_PLAIN;
504 } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
505 mode = PLAIN_TEXT;
506 } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
507 mode = LOAD_AS_DATA;
509 #ifdef DEBUG
510 else {
511 NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
512 !nsCRT::strcmp(aCommand, "external-resource") ||
513 !nsCRT::strcmp(aCommand, "import"),
514 "Unsupported parser command!");
516 #endif
517 mStreamListener =
518 new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
521 bool nsHtml5Parser::IsScriptCreated() { return !GetStreamParser(); }
523 /* End nsIParser */
525 // not from interface
526 nsresult nsHtml5Parser::ParseUntilBlocked() {
527 nsresult rv = mExecutor->IsBroken();
528 NS_ENSURE_SUCCESS(rv, rv);
529 if (mBlocked || mInsertionPointPermanentlyUndefined ||
530 mExecutor->IsComplete()) {
531 return NS_OK;
533 NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
534 NS_ASSERTION(!mInDocumentWrite,
535 "ParseUntilBlocked entered while in doc.write!");
537 mDocWriteSpeculatorActive = false;
539 for (;;) {
540 if (!mFirstBuffer->hasMore()) {
541 if (mFirstBuffer == mLastBuffer) {
542 if (mExecutor->IsComplete()) {
543 // something like cache manisfests stopped the parse in mid-flight
544 return NS_OK;
546 if (mDocumentClosed) {
547 PermanentlyUndefineInsertionPoint();
548 nsresult rv;
549 MOZ_RELEASE_ASSERT(
550 !GetStreamParser(),
551 "This should only happen with script-created parser.");
552 if (NS_SUCCEEDED((rv = mExecutor->IsBroken()))) {
553 mTokenizer->eof();
554 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
555 mExecutor->MarkAsBroken(rv);
556 } else {
557 mTreeBuilder->StreamEnded();
560 auto r = mTreeBuilder->Flush();
561 if (r.isErr()) {
562 return mExecutor->MarkAsBroken(r.unwrapErr());
564 mExecutor->FlushDocumentWrite();
565 // The below call does memory cleanup, so call it even if the
566 // parser has been marked as broken.
567 mTokenizer->end();
568 return rv;
570 // never release the last buffer.
571 NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
572 "Sentinel buffer had its indeces changed.");
573 if (GetStreamParser()) {
574 if (mReturnToStreamParserPermitted &&
575 !mExecutor->IsScriptExecuting()) {
576 auto r = mTreeBuilder->Flush();
577 if (r.isErr()) {
578 return mExecutor->MarkAsBroken(r.unwrapErr());
580 mReturnToStreamParserPermitted = false;
581 GetStreamParser()->ContinueAfterScriptsOrEncodingCommitment(
582 mTokenizer.get(), mTreeBuilder.get(), mLastWasCR);
584 } else {
585 // Script-created parser
586 auto r = mTreeBuilder->Flush();
587 if (r.isErr()) {
588 return mExecutor->MarkAsBroken(r.unwrapErr());
590 // No need to flush the executor, because the executor is already
591 // in a flush
592 NS_ASSERTION(mExecutor->IsInFlushLoop(),
593 "How did we come here without being in the flush loop?");
595 return NS_OK; // no more data for now but expecting more
597 mFirstBuffer = mFirstBuffer->next;
598 continue;
601 if (mBlocked || mExecutor->IsComplete()) {
602 return NS_OK;
605 // now we have a non-empty buffer
606 mFirstBuffer->adjust(mLastWasCR);
607 mLastWasCR = false;
608 if (mFirstBuffer->hasMore()) {
609 bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
610 if (inRootContext) {
611 mTokenizer->setLineNumber(mRootContextLineNumber);
613 if (!mTokenizer->EnsureBufferSpace(mFirstBuffer->getLength())) {
614 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
616 mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
617 nsresult rv;
618 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
619 return mExecutor->MarkAsBroken(rv);
621 if (inRootContext) {
622 mRootContextLineNumber = mTokenizer->getLineNumber();
624 if (mTreeBuilder->HasScript()) {
625 auto r = mTreeBuilder->Flush();
626 if (r.isErr()) {
627 return mExecutor->MarkAsBroken(r.unwrapErr());
629 rv = mExecutor->FlushDocumentWrite();
630 NS_ENSURE_SUCCESS(rv, rv);
632 if (mBlocked) {
633 return NS_OK;
639 nsresult nsHtml5Parser::StartExecutor() {
640 MOZ_ASSERT(!GetStreamParser(),
641 "Had stream parser but document.write started life cycle.");
642 // This is part of the setup document.open() does.
643 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
644 executor->SetParser(this);
645 mTreeBuilder->setScriptingEnabled(executor->IsScriptEnabled());
647 mTreeBuilder->setIsSrcdocDocument(false);
649 mTokenizer->start();
650 executor->Start();
653 * We know we're in document.open(), so our document must already
654 * have a script global andthe WillBuildModel call is safe.
656 return executor->WillBuildModel();
659 nsresult nsHtml5Parser::Initialize(mozilla::dom::Document* aDoc, nsIURI* aURI,
660 nsISupports* aContainer,
661 nsIChannel* aChannel) {
662 return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
665 void nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
666 bool isSrcdoc = false;
667 nsCOMPtr<nsIChannel> channel;
668 nsresult rv = GetChannel(getter_AddRefs(channel));
669 if (NS_SUCCEEDED(rv)) {
670 isSrcdoc = NS_IsSrcdocChannel(channel);
672 mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
674 mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
675 mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
676 mTokenizer->start();
679 void nsHtml5Parser::InitializeDocWriteParserState(
680 nsAHtml5TreeBuilderState* aState, int32_t aLine) {
681 mTokenizer->resetToDataState();
682 mTokenizer->setLineNumber(aLine);
683 mTreeBuilder->loadState(aState);
684 mLastWasCR = false;
685 mReturnToStreamParserPermitted = true;
688 void nsHtml5Parser::ContinueAfterFailedCharsetSwitch() {
689 MOZ_ASSERT(
690 GetStreamParser(),
691 "Tried to continue after failed charset switch without a stream parser");
692 GetStreamParser()->ContinueAfterFailedCharsetSwitch();