Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / parser / html / nsHtml5Parser.cpp
blobf8184a45a8ab2fc61f16f2853880bba56200dfb2
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 "mozilla/UniquePtr.h"
11 #include "nsCRT.h"
12 #include "nsContentUtils.h" // for kLoadAsData
13 #include "nsHtml5AtomTable.h"
14 #include "nsHtml5DependentUTF16Buffer.h"
15 #include "nsHtml5Tokenizer.h"
16 #include "nsHtml5TreeBuilder.h"
17 #include "nsNetUtil.h"
19 NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
20 NS_INTERFACE_TABLE(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
21 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
22 NS_INTERFACE_MAP_END
24 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
25 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
27 NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExecutor)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetStreamParser())
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
35 NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
37 tmp->DropStreamParser();
38 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
40 nsHtml5Parser::nsHtml5Parser()
41 : mLastWasCR(false),
42 mDocWriteSpeculativeLastWasCR(false),
43 mBlocked(0),
44 mDocWriteSpeculatorActive(false),
45 mScriptNestingLevel(0),
46 mDocumentClosed(false),
47 mInDocumentWrite(false),
48 mInsertionPointPermanentlyUndefined(false),
49 mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr)),
50 mLastBuffer(mFirstBuffer),
51 mExecutor(new nsHtml5TreeOpExecutor()),
52 mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr, false)),
53 mTokenizer(new nsHtml5Tokenizer(mTreeBuilder.get(), false)),
54 mRootContextLineNumber(1),
55 mReturnToStreamParserPermitted(false) {
56 mTokenizer->setInterner(&mAtomTable);
59 nsHtml5Parser::~nsHtml5Parser() {
60 mTokenizer->end();
61 if (mDocWriteSpeculativeTokenizer) {
62 mDocWriteSpeculativeTokenizer->end();
66 NS_IMETHODIMP_(void)
67 nsHtml5Parser::SetContentSink(nsIContentSink* aSink) {
68 NS_ASSERTION(aSink == static_cast<nsIContentSink*>(mExecutor),
69 "Attempt to set a foreign sink.");
72 NS_IMETHODIMP_(nsIContentSink*)
73 nsHtml5Parser::GetContentSink() {
74 return static_cast<nsIContentSink*>(mExecutor);
77 NS_IMETHODIMP_(void)
78 nsHtml5Parser::GetCommand(nsCString& aCommand) {
79 aCommand.AssignLiteral("view");
82 NS_IMETHODIMP_(void)
83 nsHtml5Parser::SetCommand(const char* aCommand) {
84 NS_ASSERTION(!strcmp(aCommand, "view") || !strcmp(aCommand, "view-source") ||
85 !strcmp(aCommand, "external-resource") ||
86 !strcmp(aCommand, "import") ||
87 !strcmp(aCommand, kLoadAsData),
88 "Unsupported parser command");
91 NS_IMETHODIMP_(void)
92 nsHtml5Parser::SetCommand(eParserCommands aParserCommand) {
93 NS_ASSERTION(aParserCommand == eViewNormal,
94 "Parser command was not eViewNormal.");
97 void nsHtml5Parser::SetDocumentCharset(NotNull<const Encoding*> aEncoding,
98 int32_t aCharsetSource,
99 bool aForceAutoDetection) {
100 MOZ_ASSERT(!mExecutor->HasStarted(), "Document charset set too late.");
101 MOZ_ASSERT(GetStreamParser(), "Setting charset on a script-only parser.");
102 GetStreamParser()->SetDocumentCharset(
103 aEncoding, (nsCharsetSource)aCharsetSource, aForceAutoDetection);
104 mExecutor->SetDocumentCharsetAndSource(aEncoding,
105 (nsCharsetSource)aCharsetSource);
108 nsresult nsHtml5Parser::GetChannel(nsIChannel** aChannel) {
109 if (GetStreamParser()) {
110 return GetStreamParser()->GetChannel(aChannel);
111 } else {
112 return NS_ERROR_NOT_AVAILABLE;
116 nsIStreamListener* nsHtml5Parser::GetStreamListener() {
117 return mStreamListener;
120 NS_IMETHODIMP
121 nsHtml5Parser::ContinueInterruptedParsing() {
122 MOZ_ASSERT_UNREACHABLE("Don't call. For interface compat only.");
123 return NS_ERROR_NOT_IMPLEMENTED;
126 NS_IMETHODIMP_(void)
127 nsHtml5Parser::BlockParser() { mBlocked++; }
129 NS_IMETHODIMP_(void)
130 nsHtml5Parser::UnblockParser() {
131 MOZ_DIAGNOSTIC_ASSERT(mBlocked > 0);
132 if (MOZ_LIKELY(mBlocked > 0)) {
133 mBlocked--;
135 if (MOZ_LIKELY(mBlocked == 0) && mExecutor) {
136 mExecutor->ContinueInterruptedParsingAsync();
140 NS_IMETHODIMP_(void)
141 nsHtml5Parser::ContinueInterruptedParsingAsync() {
142 if (mExecutor) {
143 mExecutor->ContinueInterruptedParsingAsync();
147 NS_IMETHODIMP_(bool)
148 nsHtml5Parser::IsParserEnabled() { return !mBlocked; }
150 NS_IMETHODIMP_(bool)
151 nsHtml5Parser::IsParserClosed() { return mDocumentClosed; }
153 NS_IMETHODIMP_(bool)
154 nsHtml5Parser::IsComplete() { return mExecutor->IsComplete(); }
156 NS_IMETHODIMP
157 nsHtml5Parser::Parse(nsIURI* aURL) {
159 * Do NOT cause WillBuildModel to be called synchronously from here!
160 * The document won't be ready for it until OnStartRequest!
162 MOZ_ASSERT(!mExecutor->HasStarted(),
163 "Tried to start parse without initializing the parser.");
164 MOZ_ASSERT(GetStreamParser(),
165 "Can't call this Parse() variant on script-created parser");
167 GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
168 mExecutor->SetStreamParser(GetStreamParser());
169 mExecutor->SetParser(this);
170 return NS_OK;
173 nsresult nsHtml5Parser::Parse(const nsAString& aSourceBuffer, void* aKey,
174 bool aLastCall) {
175 nsresult rv;
176 if (NS_FAILED(rv = mExecutor->IsBroken())) {
177 return rv;
179 if (aSourceBuffer.Length() > INT32_MAX) {
180 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
183 // Maintain a reference to ourselves so we don't go away
184 // till we're completely done. The old parser grips itself in this method.
185 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
187 // Gripping the other objects just in case, since the other old grip
188 // required grips to these, too.
189 RefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
190 mozilla::Unused << streamKungFuDeathGrip; // Not used within function
191 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
193 MOZ_RELEASE_ASSERT(executor->HasStarted());
195 // Return early if the parser has processed EOF
196 if (executor->IsComplete()) {
197 return NS_OK;
200 if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
201 // document.close()
202 NS_ASSERTION(!GetStreamParser(),
203 "Had stream parser but got document.close().");
204 if (mDocumentClosed) {
205 // already closed
206 return NS_OK;
208 mDocumentClosed = true;
209 if (!mBlocked && !mInDocumentWrite) {
210 return ParseUntilBlocked();
212 return NS_OK;
215 // If we got this far, we are dealing with a document.write or
216 // document.writeln call--not document.close().
218 MOZ_RELEASE_ASSERT(
219 IsInsertionPointDefined(),
220 "Doc.write reached parser with undefined insertion point.");
222 MOZ_RELEASE_ASSERT(!(GetStreamParser() && !aKey),
223 "Got a null key in a non-script-created parser");
225 // XXX is this optimization bogus?
226 if (aSourceBuffer.IsEmpty()) {
227 return NS_OK;
230 // This guard is here to prevent document.close from tokenizing synchronously
231 // while a document.write (that wrote the script that called document.close!)
232 // is still on the call stack.
233 mozilla::AutoRestore<bool> guard(mInDocumentWrite);
234 mInDocumentWrite = true;
236 // The script is identified by aKey. If there's nothing in the buffer
237 // chain for that key, we'll insert at the head of the queue.
238 // When the script leaves something in the queue, a zero-length
239 // key-holder "buffer" is inserted in the queue. If the same script
240 // leaves something in the chain again, it will be inserted immediately
241 // before the old key holder belonging to the same script.
243 // We don't do the actual data insertion yet in the hope that the data gets
244 // tokenized and there no data or less data to copy to the heap after
245 // tokenization. Also, this way, we avoid inserting one empty data buffer
246 // per document.write, which matters for performance when the parser isn't
247 // blocked and a badly-authored script calls document.write() once per
248 // input character. (As seen in a benchmark!)
250 // The insertion into the input stream happens conceptually before anything
251 // gets tokenized. To make sure multi-level document.write works right,
252 // it's necessary to establish the location of our parser key up front
253 // in case this is the first write with this key.
255 // In a document.open() case, the first write level has a null key, so that
256 // case is handled separately, because normal buffers containing data
257 // have null keys.
259 // These don't need to be owning references, because they always point to
260 // the buffer queue and buffers can't be removed from the buffer queue
261 // before document.write() returns. The buffer queue clean-up happens the
262 // next time ParseUntilBlocked() is called.
263 // However, they are made owning just in case the reasoning above is flawed
264 // and a flaw would lead to worse problems with plain pointers. If this
265 // turns out to be a perf problem, it's worthwhile to consider making
266 // prevSearchbuf a plain pointer again.
267 RefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
268 RefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
270 if (aKey) {
271 if (mFirstBuffer == mLastBuffer) {
272 nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
273 keyHolder->next = mLastBuffer;
274 mFirstBuffer = keyHolder;
275 } else if (mFirstBuffer->key != aKey) {
276 prevSearchBuf = mFirstBuffer;
277 for (;;) {
278 if (prevSearchBuf->next == mLastBuffer) {
279 // key was not found
280 nsHtml5OwningUTF16Buffer* keyHolder =
281 new nsHtml5OwningUTF16Buffer(aKey);
282 keyHolder->next = mFirstBuffer;
283 mFirstBuffer = keyHolder;
284 prevSearchBuf = nullptr;
285 break;
287 if (prevSearchBuf->next->key == aKey) {
288 // found a key holder
289 break;
291 prevSearchBuf = prevSearchBuf->next;
293 } // else mFirstBuffer is the keyholder
295 // prevSearchBuf is the previous buffer before the keyholder or null if
296 // there isn't one.
297 } else {
298 // We have a first-level write in the document.open() case. We insert before
299 // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
300 // and redesignating the previous mLastBuffer as our firstLevelMarker. We
301 // need to put a marker there, because otherwise additional document.writes
302 // from nested event loops would insert in the wrong place. Sigh.
303 mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
304 firstLevelMarker = mLastBuffer;
305 mLastBuffer = mLastBuffer->next;
308 nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
310 while (!mBlocked && stackBuffer.hasMore()) {
311 stackBuffer.adjust(mLastWasCR);
312 mLastWasCR = false;
313 if (stackBuffer.hasMore()) {
314 int32_t lineNumberSave;
315 bool inRootContext = (!GetStreamParser() && !aKey);
316 if (inRootContext) {
317 mTokenizer->setLineNumber(mRootContextLineNumber);
318 } else {
319 // we aren't the root context, so save the line number on the
320 // *stack* so that we can restore it.
321 lineNumberSave = mTokenizer->getLineNumber();
324 if (!mTokenizer->EnsureBufferSpace(stackBuffer.getLength())) {
325 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
327 mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
328 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
329 return executor->MarkAsBroken(rv);
332 if (inRootContext) {
333 mRootContextLineNumber = mTokenizer->getLineNumber();
334 } else {
335 mTokenizer->setLineNumber(lineNumberSave);
338 if (mTreeBuilder->HasScriptThatMayDocumentWriteOrBlock()) {
339 auto r = mTreeBuilder->Flush(); // Move ops to the executor
340 if (r.isErr()) {
341 return executor->MarkAsBroken(r.unwrapErr());
343 rv = executor->FlushDocumentWrite(); // run the ops
344 NS_ENSURE_SUCCESS(rv, rv);
345 // Flushing tree ops can cause all sorts of things.
346 // Return early if the parser got terminated.
347 if (executor->IsComplete()) {
348 return NS_OK;
351 // Ignore suspension requests
355 RefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
356 if (stackBuffer.hasMore()) {
357 // The buffer wasn't tokenized to completion. Create a copy of the tail
358 // on the heap.
359 heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
360 if (!heapBuffer) {
361 // Allocation failed. The parser is now broken.
362 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
366 if (heapBuffer) {
367 // We have something to insert before the keyholder holding in the non-null
368 // aKey case and we have something to swap into firstLevelMarker in the
369 // null aKey case.
370 if (aKey) {
371 NS_ASSERTION(mFirstBuffer != mLastBuffer, "Where's the keyholder?");
372 // the key holder is still somewhere further down the list from
373 // prevSearchBuf (which may be null)
374 if (mFirstBuffer->key == aKey) {
375 NS_ASSERTION(
376 !prevSearchBuf,
377 "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
378 heapBuffer->next = mFirstBuffer;
379 mFirstBuffer = heapBuffer;
380 } else {
381 if (!prevSearchBuf) {
382 prevSearchBuf = mFirstBuffer;
384 // We created a key holder earlier, so we will find it without walking
385 // past the end of the list.
386 while (prevSearchBuf->next->key != aKey) {
387 prevSearchBuf = prevSearchBuf->next;
389 heapBuffer->next = prevSearchBuf->next;
390 prevSearchBuf->next = heapBuffer;
392 } else {
393 NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
394 firstLevelMarker->Swap(heapBuffer);
398 if (!mBlocked) { // buffer was tokenized to completion
399 NS_ASSERTION(!stackBuffer.hasMore(),
400 "Buffer wasn't tokenized to completion?");
401 // Scripting semantics require a forced tree builder flush here
402 auto r = mTreeBuilder->Flush(); // Move ops to the executor
403 if (r.isErr()) {
404 return executor->MarkAsBroken(r.unwrapErr());
406 rv = executor->FlushDocumentWrite(); // run the ops
407 NS_ENSURE_SUCCESS(rv, rv);
408 } else if (stackBuffer.hasMore()) {
409 // The buffer wasn't tokenized to completion. Tokenize the untokenized
410 // content in order to preload stuff. This content will be retokenized
411 // later for normal parsing.
412 if (!mDocWriteSpeculatorActive) {
413 mDocWriteSpeculatorActive = true;
414 if (!mDocWriteSpeculativeTreeBuilder) {
415 // Lazily initialize if uninitialized
416 mDocWriteSpeculativeTreeBuilder =
417 mozilla::MakeUnique<nsHtml5TreeBuilder>(nullptr,
418 executor->GetStage(), true);
419 mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
420 mTreeBuilder->isScriptingEnabled());
421 mDocWriteSpeculativeTreeBuilder->setAllowDeclarativeShadowRoots(
422 mTreeBuilder->isAllowDeclarativeShadowRoots());
423 mDocWriteSpeculativeTokenizer = mozilla::MakeUnique<nsHtml5Tokenizer>(
424 mDocWriteSpeculativeTreeBuilder.get(), false);
425 mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
426 mDocWriteSpeculativeTokenizer->start();
428 mDocWriteSpeculativeTokenizer->resetToDataState();
429 mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder.get());
430 mDocWriteSpeculativeLastWasCR = false;
433 // Note that with multilevel document.write if we didn't just activate the
434 // speculator, it's possible that the speculator is now in the wrong state.
435 // That's OK for the sake of simplicity. The worst that can happen is
436 // that the speculative loads aren't exactly right. The content will be
437 // reparsed anyway for non-preload purposes.
439 // The buffer position for subsequent non-speculative parsing now lives
440 // in heapBuffer, so it's ok to let the buffer position of stackBuffer
441 // to be overwritten and not restored below.
442 while (stackBuffer.hasMore()) {
443 stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
444 if (stackBuffer.hasMore()) {
445 if (!mDocWriteSpeculativeTokenizer->EnsureBufferSpace(
446 stackBuffer.getLength())) {
447 return executor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
449 mDocWriteSpeculativeLastWasCR =
450 mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
451 nsresult rv;
452 if (NS_FAILED((rv = mDocWriteSpeculativeTreeBuilder->IsBroken()))) {
453 return executor->MarkAsBroken(rv);
458 auto r = mDocWriteSpeculativeTreeBuilder->Flush();
459 if (r.isErr()) {
460 return executor->MarkAsBroken(r.unwrapErr());
462 mDocWriteSpeculativeTreeBuilder->DropHandles();
463 executor->FlushSpeculativeLoads();
466 return NS_OK;
469 NS_IMETHODIMP
470 nsHtml5Parser::Terminate() {
471 // Prevent a second call to DidBuildModel via document.close()
472 mDocumentClosed = true;
473 // We should only call DidBuildModel once, so don't do anything if this is
474 // the second time that Terminate has been called.
475 if (mExecutor->IsComplete()) {
476 return NS_OK;
478 // XXX - [ until we figure out a way to break parser-sink circularity ]
479 // Hack - Hold a reference until we are completely done...
480 nsCOMPtr<nsIParser> kungFuDeathGrip(this);
481 RefPtr<nsHtml5StreamParser> streamParser(GetStreamParser());
482 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
483 if (streamParser) {
484 streamParser->Terminate();
486 return executor->DidBuildModel(true);
489 bool nsHtml5Parser::IsInsertionPointDefined() {
490 return !mExecutor->IsFlushing() && !mInsertionPointPermanentlyUndefined &&
491 (!GetStreamParser() || mScriptNestingLevel != 0);
494 void nsHtml5Parser::IncrementScriptNestingLevel() { ++mScriptNestingLevel; }
496 void nsHtml5Parser::DecrementScriptNestingLevel() { --mScriptNestingLevel; }
498 bool nsHtml5Parser::HasNonzeroScriptNestingLevel() const {
499 return mScriptNestingLevel != 0;
502 void nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand) {
503 MOZ_ASSERT(!mStreamListener, "Must not call this twice.");
504 eParserMode mode = NORMAL;
505 if (!nsCRT::strcmp(aCommand, "view-source")) {
506 mode = VIEW_SOURCE_HTML;
507 } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
508 mode = VIEW_SOURCE_XML;
509 } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
510 mode = VIEW_SOURCE_PLAIN;
511 } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
512 mode = PLAIN_TEXT;
513 } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
514 mode = LOAD_AS_DATA;
516 #ifdef DEBUG
517 else {
518 NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
519 !nsCRT::strcmp(aCommand, "external-resource") ||
520 !nsCRT::strcmp(aCommand, "import"),
521 "Unsupported parser command!");
523 #endif
524 mStreamListener =
525 new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
528 bool nsHtml5Parser::IsScriptCreated() { return !GetStreamParser(); }
530 /* End nsIParser */
532 // not from interface
533 nsresult nsHtml5Parser::ParseUntilBlocked() {
534 nsresult rv = mExecutor->IsBroken();
535 NS_ENSURE_SUCCESS(rv, rv);
536 if (mBlocked || mInsertionPointPermanentlyUndefined ||
537 mExecutor->IsComplete()) {
538 return NS_OK;
540 NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
541 NS_ASSERTION(!mInDocumentWrite,
542 "ParseUntilBlocked entered while in doc.write!");
544 mDocWriteSpeculatorActive = false;
546 for (;;) {
547 if (!mFirstBuffer->hasMore()) {
548 if (mFirstBuffer == mLastBuffer) {
549 if (mExecutor->IsComplete()) {
550 // something like cache manisfests stopped the parse in mid-flight
551 return NS_OK;
553 if (mDocumentClosed) {
554 PermanentlyUndefineInsertionPoint();
555 nsresult rv;
556 MOZ_RELEASE_ASSERT(
557 !GetStreamParser(),
558 "This should only happen with script-created parser.");
559 if (NS_SUCCEEDED((rv = mExecutor->IsBroken()))) {
560 mTokenizer->eof();
561 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
562 mExecutor->MarkAsBroken(rv);
563 } else {
564 mTreeBuilder->StreamEnded();
567 auto r = mTreeBuilder->Flush();
568 if (r.isErr()) {
569 return mExecutor->MarkAsBroken(r.unwrapErr());
571 mExecutor->FlushDocumentWrite();
572 // The below call does memory cleanup, so call it even if the
573 // parser has been marked as broken.
574 mTokenizer->end();
575 return rv;
577 // never release the last buffer.
578 NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
579 "Sentinel buffer had its indeces changed.");
580 if (GetStreamParser()) {
581 if (mReturnToStreamParserPermitted &&
582 !mExecutor->IsScriptExecuting()) {
583 auto r = mTreeBuilder->Flush();
584 if (r.isErr()) {
585 return mExecutor->MarkAsBroken(r.unwrapErr());
587 mReturnToStreamParserPermitted = false;
588 GetStreamParser()->ContinueAfterScriptsOrEncodingCommitment(
589 mTokenizer.get(), mTreeBuilder.get(), mLastWasCR);
591 } else {
592 // Script-created parser
593 auto r = mTreeBuilder->Flush();
594 if (r.isErr()) {
595 return mExecutor->MarkAsBroken(r.unwrapErr());
597 // No need to flush the executor, because the executor is already
598 // in a flush
599 NS_ASSERTION(mExecutor->IsInFlushLoop(),
600 "How did we come here without being in the flush loop?");
602 return NS_OK; // no more data for now but expecting more
604 mFirstBuffer = mFirstBuffer->next;
605 continue;
608 if (mBlocked || mExecutor->IsComplete()) {
609 return NS_OK;
612 // now we have a non-empty buffer
613 mFirstBuffer->adjust(mLastWasCR);
614 mLastWasCR = false;
615 if (mFirstBuffer->hasMore()) {
616 bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
617 if (inRootContext) {
618 mTokenizer->setLineNumber(mRootContextLineNumber);
620 if (!mTokenizer->EnsureBufferSpace(mFirstBuffer->getLength())) {
621 return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
623 mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
624 nsresult rv;
625 if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
626 return mExecutor->MarkAsBroken(rv);
628 if (inRootContext) {
629 mRootContextLineNumber = mTokenizer->getLineNumber();
631 if (mTreeBuilder->HasScriptThatMayDocumentWriteOrBlock()) {
632 auto r = mTreeBuilder->Flush();
633 if (r.isErr()) {
634 return mExecutor->MarkAsBroken(r.unwrapErr());
636 rv = mExecutor->FlushDocumentWrite();
637 NS_ENSURE_SUCCESS(rv, rv);
639 if (mBlocked) {
640 return NS_OK;
646 nsresult nsHtml5Parser::StartExecutor() {
647 MOZ_ASSERT(!GetStreamParser(),
648 "Had stream parser but document.write started life cycle.");
649 // This is part of the setup document.open() does.
650 RefPtr<nsHtml5TreeOpExecutor> executor(mExecutor);
651 executor->SetParser(this);
652 mTreeBuilder->setScriptingEnabled(executor->IsScriptEnabled());
653 mTreeBuilder->setAllowDeclarativeShadowRoots(
654 executor->GetDocument()->AllowsDeclarativeShadowRoots());
656 mTreeBuilder->setIsSrcdocDocument(false);
658 mTokenizer->start();
659 executor->Start();
662 * We know we're in document.open(), so our document must already
663 * have a script global andthe WillBuildModel call is safe.
665 return executor->WillBuildModel();
668 nsresult nsHtml5Parser::Initialize(mozilla::dom::Document* aDoc, nsIURI* aURI,
669 nsISupports* aContainer,
670 nsIChannel* aChannel) {
671 mTreeBuilder->setAllowDeclarativeShadowRoots(
672 aDoc->AllowsDeclarativeShadowRoots());
673 return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
676 void nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
677 bool isSrcdoc = false;
678 nsCOMPtr<nsIChannel> channel;
679 nsresult rv = GetChannel(getter_AddRefs(channel));
680 if (NS_SUCCEEDED(rv)) {
681 isSrcdoc = NS_IsSrcdocChannel(channel);
683 mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
685 mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
686 mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
687 mTreeBuilder->setAllowDeclarativeShadowRoots(
688 mExecutor->GetDocument()->AllowsDeclarativeShadowRoots());
689 mTokenizer->start();
692 void nsHtml5Parser::InitializeDocWriteParserState(
693 nsAHtml5TreeBuilderState* aState, int32_t aLine) {
694 mTokenizer->resetToDataState();
695 mTokenizer->setLineNumber(aLine);
696 mTreeBuilder->loadState(aState);
697 mLastWasCR = false;
698 mReturnToStreamParserPermitted = true;
701 void nsHtml5Parser::ContinueAfterFailedCharsetSwitch() {
702 MOZ_ASSERT(
703 GetStreamParser(),
704 "Tried to continue after failed charset switch without a stream parser");
705 GetStreamParser()->ContinueAfterFailedCharsetSwitch();