parser/html/nsHtml5Parser.cpp

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

mercurial