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