PageRenderTime 64ms CodeModel.GetById 12ms app.highlight 46ms RepoModel.GetById 1ms app.codeStats 0ms

/parser/html/nsHtml5Parser.cpp

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