parser/html/nsHtml5Parser.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/parser/html/nsHtml5Parser.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,731 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set sw=2 ts=2 et tw=79: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsHtml5Parser.h"
    1.11 +
    1.12 +#include "mozilla/AutoRestore.h"
    1.13 +#include "nsContentUtils.h" // for kLoadAsData
    1.14 +#include "nsHtml5Tokenizer.h"
    1.15 +#include "nsHtml5TreeBuilder.h"
    1.16 +#include "nsHtml5AtomTable.h"
    1.17 +#include "nsHtml5DependentUTF16Buffer.h"
    1.18 +#include "nsNetUtil.h"
    1.19 +
    1.20 +NS_INTERFACE_TABLE_HEAD(nsHtml5Parser)
    1.21 +  NS_INTERFACE_TABLE(nsHtml5Parser, nsIParser, nsISupportsWeakReference)
    1.22 +  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsHtml5Parser)
    1.23 +NS_INTERFACE_MAP_END
    1.24 +
    1.25 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHtml5Parser)
    1.26 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHtml5Parser)
    1.27 +
    1.28 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsHtml5Parser)
    1.29 +
    1.30 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsHtml5Parser)
    1.31 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExecutor)
    1.32 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetStreamParser())
    1.33 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1.34 +
    1.35 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsHtml5Parser)
    1.36 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mExecutor)
    1.37 +  tmp->DropStreamParser();
    1.38 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.39 +
    1.40 +nsHtml5Parser::nsHtml5Parser()
    1.41 +  : mFirstBuffer(new nsHtml5OwningUTF16Buffer((void*)nullptr))
    1.42 +  , mLastBuffer(mFirstBuffer)
    1.43 +  , mExecutor(new nsHtml5TreeOpExecutor())
    1.44 +  , mTreeBuilder(new nsHtml5TreeBuilder(mExecutor, nullptr))
    1.45 +  , mTokenizer(new nsHtml5Tokenizer(mTreeBuilder, false))
    1.46 +  , mRootContextLineNumber(1)
    1.47 +{
    1.48 +  mTokenizer->setInterner(&mAtomTable);
    1.49 +  // There's a zeroing operator new for everything else
    1.50 +}
    1.51 +
    1.52 +nsHtml5Parser::~nsHtml5Parser()
    1.53 +{
    1.54 +  mTokenizer->end();
    1.55 +  if (mDocWriteSpeculativeTokenizer) {
    1.56 +    mDocWriteSpeculativeTokenizer->end();
    1.57 +  }
    1.58 +}
    1.59 +
    1.60 +NS_IMETHODIMP_(void)
    1.61 +nsHtml5Parser::SetContentSink(nsIContentSink* aSink)
    1.62 +{
    1.63 +  NS_ASSERTION(aSink == static_cast<nsIContentSink*> (mExecutor), 
    1.64 +               "Attempt to set a foreign sink.");
    1.65 +}
    1.66 +
    1.67 +NS_IMETHODIMP_(nsIContentSink*)
    1.68 +nsHtml5Parser::GetContentSink()
    1.69 +{
    1.70 +  return static_cast<nsIContentSink*> (mExecutor);
    1.71 +}
    1.72 +
    1.73 +NS_IMETHODIMP_(void)
    1.74 +nsHtml5Parser::GetCommand(nsCString& aCommand)
    1.75 +{
    1.76 +  aCommand.Assign("view");
    1.77 +}
    1.78 +
    1.79 +NS_IMETHODIMP_(void)
    1.80 +nsHtml5Parser::SetCommand(const char* aCommand)
    1.81 +{
    1.82 +  NS_ASSERTION(!strcmp(aCommand, "view") ||
    1.83 +               !strcmp(aCommand, "view-source") ||
    1.84 +               !strcmp(aCommand, "external-resource") ||
    1.85 +               !strcmp(aCommand, kLoadAsData),
    1.86 +               "Unsupported parser command");
    1.87 +}
    1.88 +
    1.89 +NS_IMETHODIMP_(void)
    1.90 +nsHtml5Parser::SetCommand(eParserCommands aParserCommand)
    1.91 +{
    1.92 +  NS_ASSERTION(aParserCommand == eViewNormal, 
    1.93 +               "Parser command was not eViewNormal.");
    1.94 +}
    1.95 +
    1.96 +NS_IMETHODIMP_(void)
    1.97 +nsHtml5Parser::SetDocumentCharset(const nsACString& aCharset,
    1.98 +                                  int32_t aCharsetSource)
    1.99 +{
   1.100 +  NS_PRECONDITION(!mExecutor->HasStarted(),
   1.101 +                  "Document charset set too late.");
   1.102 +  NS_PRECONDITION(GetStreamParser(), "Setting charset on a script-only parser.");
   1.103 +  nsAutoCString trimmed;
   1.104 +  trimmed.Assign(aCharset);
   1.105 +  trimmed.Trim(" \t\r\n\f");
   1.106 +  GetStreamParser()->SetDocumentCharset(trimmed, aCharsetSource);
   1.107 +  mExecutor->SetDocumentCharsetAndSource(trimmed,
   1.108 +                                         aCharsetSource);
   1.109 +}
   1.110 +
   1.111 +NS_IMETHODIMP
   1.112 +nsHtml5Parser::GetChannel(nsIChannel** aChannel)
   1.113 +{
   1.114 +  if (GetStreamParser()) {
   1.115 +    return GetStreamParser()->GetChannel(aChannel);
   1.116 +  } else {
   1.117 +    return NS_ERROR_NOT_AVAILABLE;
   1.118 +  }
   1.119 +}
   1.120 +
   1.121 +NS_IMETHODIMP
   1.122 +nsHtml5Parser::GetDTD(nsIDTD** aDTD)
   1.123 +{
   1.124 +  *aDTD = nullptr;
   1.125 +  return NS_OK;
   1.126 +}
   1.127 +
   1.128 +nsIStreamListener*
   1.129 +nsHtml5Parser::GetStreamListener()
   1.130 +{
   1.131 +  return mStreamListener;
   1.132 +}
   1.133 +
   1.134 +NS_IMETHODIMP
   1.135 +nsHtml5Parser::ContinueInterruptedParsing()
   1.136 +{
   1.137 +  NS_NOTREACHED("Don't call. For interface compat only.");
   1.138 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.139 +}
   1.140 +
   1.141 +NS_IMETHODIMP_(void)
   1.142 +nsHtml5Parser::BlockParser()
   1.143 +{
   1.144 +  mBlocked = true;
   1.145 +}
   1.146 +
   1.147 +NS_IMETHODIMP_(void)
   1.148 +nsHtml5Parser::UnblockParser()
   1.149 +{
   1.150 +  mBlocked = false;
   1.151 +  mExecutor->ContinueInterruptedParsingAsync();
   1.152 +}
   1.153 +
   1.154 +NS_IMETHODIMP_(void)
   1.155 +nsHtml5Parser::ContinueInterruptedParsingAsync()
   1.156 +{
   1.157 +  mExecutor->ContinueInterruptedParsingAsync();
   1.158 +}
   1.159 +
   1.160 +NS_IMETHODIMP_(bool)
   1.161 +nsHtml5Parser::IsParserEnabled()
   1.162 +{
   1.163 +  return !mBlocked;
   1.164 +}
   1.165 +
   1.166 +NS_IMETHODIMP_(bool)
   1.167 +nsHtml5Parser::IsComplete()
   1.168 +{
   1.169 +  return mExecutor->IsComplete();
   1.170 +}
   1.171 +
   1.172 +NS_IMETHODIMP
   1.173 +nsHtml5Parser::Parse(nsIURI* aURL,
   1.174 +                     nsIRequestObserver* aObserver,
   1.175 +                     void* aKey, // legacy; ignored
   1.176 +                     nsDTDMode aMode) // legacy; ignored
   1.177 +{
   1.178 +  /*
   1.179 +   * Do NOT cause WillBuildModel to be called synchronously from here!
   1.180 +   * The document won't be ready for it until OnStartRequest!
   1.181 +   */
   1.182 +  NS_PRECONDITION(!mExecutor->HasStarted(), 
   1.183 +                  "Tried to start parse without initializing the parser.");
   1.184 +  NS_PRECONDITION(GetStreamParser(),
   1.185 +                  "Can't call this Parse() variant on script-created parser");
   1.186 +  GetStreamParser()->SetObserver(aObserver);
   1.187 +  GetStreamParser()->SetViewSourceTitle(aURL); // In case we're viewing source
   1.188 +  mExecutor->SetStreamParser(GetStreamParser());
   1.189 +  mExecutor->SetParser(this);
   1.190 +  return NS_OK;
   1.191 +}
   1.192 +
   1.193 +NS_IMETHODIMP
   1.194 +nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
   1.195 +                     void* aKey,
   1.196 +                     const nsACString& aContentType,
   1.197 +                     bool aLastCall,
   1.198 +                     nsDTDMode aMode) // ignored
   1.199 +{
   1.200 +  nsresult rv;
   1.201 +  if (NS_FAILED(rv = mExecutor->IsBroken())) {
   1.202 +    return rv;
   1.203 +  }
   1.204 +  if (aSourceBuffer.Length() > INT32_MAX) {
   1.205 +    return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
   1.206 +  }
   1.207 +
   1.208 +  // Maintain a reference to ourselves so we don't go away
   1.209 +  // till we're completely done. The old parser grips itself in this method.
   1.210 +  nsCOMPtr<nsIParser> kungFuDeathGrip(this);
   1.211 +  
   1.212 +  // Gripping the other objects just in case, since the other old grip
   1.213 +  // required grips to these, too.
   1.214 +  nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
   1.215 +  nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
   1.216 +
   1.217 +  if (!mExecutor->HasStarted()) {
   1.218 +    NS_ASSERTION(!GetStreamParser(),
   1.219 +                 "Had stream parser but document.write started life cycle.");
   1.220 +    // This is the first document.write() on a document.open()ed document
   1.221 +    mExecutor->SetParser(this);
   1.222 +    mTreeBuilder->setScriptingEnabled(mExecutor->IsScriptEnabled());
   1.223 +
   1.224 +    bool isSrcdoc = false;
   1.225 +    nsCOMPtr<nsIChannel> channel;
   1.226 +    rv = GetChannel(getter_AddRefs(channel));
   1.227 +    if (NS_SUCCEEDED(rv)) {
   1.228 +      isSrcdoc = NS_IsSrcdocChannel(channel);
   1.229 +    }
   1.230 +    mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
   1.231 +
   1.232 +    mTokenizer->start();
   1.233 +    mExecutor->Start();
   1.234 +    if (!aContentType.EqualsLiteral("text/html")) {
   1.235 +      mTreeBuilder->StartPlainText();
   1.236 +      mTokenizer->StartPlainText();
   1.237 +    }
   1.238 +    /*
   1.239 +     * If you move the following line, be very careful not to cause 
   1.240 +     * WillBuildModel to be called before the document has had its 
   1.241 +     * script global object set.
   1.242 +     */
   1.243 +    rv = mExecutor->WillBuildModel(eDTDMode_unknown);
   1.244 +    NS_ENSURE_SUCCESS(rv, rv);
   1.245 +  }
   1.246 +
   1.247 +  // Return early if the parser has processed EOF
   1.248 +  if (mExecutor->IsComplete()) {
   1.249 +    return NS_OK;
   1.250 +  }
   1.251 +
   1.252 +  if (aLastCall && aSourceBuffer.IsEmpty() && !aKey) {
   1.253 +    // document.close()
   1.254 +    NS_ASSERTION(!GetStreamParser(),
   1.255 +                 "Had stream parser but got document.close().");
   1.256 +    if (mDocumentClosed) {
   1.257 +      // already closed
   1.258 +      return NS_OK;
   1.259 +    }
   1.260 +    mDocumentClosed = true;
   1.261 +    if (!mBlocked && !mInDocumentWrite) {
   1.262 +      return ParseUntilBlocked();
   1.263 +    }
   1.264 +    return NS_OK;
   1.265 +  }
   1.266 +
   1.267 +  // If we got this far, we are dealing with a document.write or
   1.268 +  // document.writeln call--not document.close().
   1.269 +
   1.270 +  NS_ASSERTION(IsInsertionPointDefined(),
   1.271 +               "Doc.write reached parser with undefined insertion point.");
   1.272 +
   1.273 +  NS_ASSERTION(!(GetStreamParser() && !aKey),
   1.274 +               "Got a null key in a non-script-created parser");
   1.275 +
   1.276 +  // XXX is this optimization bogus?
   1.277 +  if (aSourceBuffer.IsEmpty()) {
   1.278 +    return NS_OK;
   1.279 +  }
   1.280 +
   1.281 +  // This guard is here to prevent document.close from tokenizing synchronously
   1.282 +  // while a document.write (that wrote the script that called document.close!)
   1.283 +  // is still on the call stack.
   1.284 +  mozilla::AutoRestore<bool> guard(mInDocumentWrite);
   1.285 +  mInDocumentWrite = true;
   1.286 +
   1.287 +  // The script is identified by aKey. If there's nothing in the buffer
   1.288 +  // chain for that key, we'll insert at the head of the queue.
   1.289 +  // When the script leaves something in the queue, a zero-length
   1.290 +  // key-holder "buffer" is inserted in the queue. If the same script
   1.291 +  // leaves something in the chain again, it will be inserted immediately
   1.292 +  // before the old key holder belonging to the same script.
   1.293 +  //
   1.294 +  // We don't do the actual data insertion yet in the hope that the data gets
   1.295 +  // tokenized and there no data or less data to copy to the heap after
   1.296 +  // tokenization. Also, this way, we avoid inserting one empty data buffer
   1.297 +  // per document.write, which matters for performance when the parser isn't
   1.298 +  // blocked and a badly-authored script calls document.write() once per
   1.299 +  // input character. (As seen in a benchmark!)
   1.300 +  //
   1.301 +  // The insertion into the input stream happens conceptually before anything
   1.302 +  // gets tokenized. To make sure multi-level document.write works right,
   1.303 +  // it's necessary to establish the location of our parser key up front
   1.304 +  // in case this is the first write with this key.
   1.305 +  //
   1.306 +  // In a document.open() case, the first write level has a null key, so that
   1.307 +  // case is handled separately, because normal buffers containing data
   1.308 +  // have null keys.
   1.309 +
   1.310 +  // These don't need to be owning references, because they always point to
   1.311 +  // the buffer queue and buffers can't be removed from the buffer queue
   1.312 +  // before document.write() returns. The buffer queue clean-up happens the
   1.313 +  // next time ParseUntilBlocked() is called.
   1.314 +  // However, they are made owning just in case the reasoning above is flawed
   1.315 +  // and a flaw would lead to worse problems with plain pointers. If this
   1.316 +  // turns out to be a perf problem, it's worthwhile to consider making
   1.317 +  // prevSearchbuf a plain pointer again.
   1.318 +  nsRefPtr<nsHtml5OwningUTF16Buffer> prevSearchBuf;
   1.319 +  nsRefPtr<nsHtml5OwningUTF16Buffer> firstLevelMarker;
   1.320 +
   1.321 +  if (aKey) {
   1.322 +    if (mFirstBuffer == mLastBuffer) {
   1.323 +      nsHtml5OwningUTF16Buffer* keyHolder = new nsHtml5OwningUTF16Buffer(aKey);
   1.324 +      keyHolder->next = mLastBuffer;
   1.325 +      mFirstBuffer = keyHolder;
   1.326 +    } else if (mFirstBuffer->key != aKey) {
   1.327 +      prevSearchBuf = mFirstBuffer;
   1.328 +      for (;;) {
   1.329 +        if (prevSearchBuf->next == mLastBuffer) {
   1.330 +          // key was not found
   1.331 +          nsHtml5OwningUTF16Buffer* keyHolder =
   1.332 +            new nsHtml5OwningUTF16Buffer(aKey);
   1.333 +          keyHolder->next = mFirstBuffer;
   1.334 +          mFirstBuffer = keyHolder;
   1.335 +          prevSearchBuf = nullptr;
   1.336 +          break;
   1.337 +        }
   1.338 +        if (prevSearchBuf->next->key == aKey) {
   1.339 +          // found a key holder
   1.340 +          break;
   1.341 +        }
   1.342 +        prevSearchBuf = prevSearchBuf->next;
   1.343 +      }
   1.344 +    } // else mFirstBuffer is the keyholder
   1.345 +
   1.346 +    // prevSearchBuf is the previous buffer before the keyholder or null if
   1.347 +    // there isn't one.
   1.348 +  } else {
   1.349 +    // We have a first-level write in the document.open() case. We insert before
   1.350 +    // mLastBuffer, effectively, by making mLastBuffer be a new sentinel object
   1.351 +    // and redesignating the previous mLastBuffer as our firstLevelMarker.  We
   1.352 +    // need to put a marker there, because otherwise additional document.writes
   1.353 +    // from nested event loops would insert in the wrong place. Sigh.
   1.354 +    mLastBuffer->next = new nsHtml5OwningUTF16Buffer((void*)nullptr);
   1.355 +    firstLevelMarker = mLastBuffer;
   1.356 +    mLastBuffer = mLastBuffer->next;
   1.357 +  }
   1.358 +
   1.359 +  nsHtml5DependentUTF16Buffer stackBuffer(aSourceBuffer);
   1.360 +
   1.361 +  while (!mBlocked && stackBuffer.hasMore()) {
   1.362 +    stackBuffer.adjust(mLastWasCR);
   1.363 +    mLastWasCR = false;
   1.364 +    if (stackBuffer.hasMore()) {
   1.365 +      int32_t lineNumberSave;
   1.366 +      bool inRootContext = (!GetStreamParser() && !aKey);
   1.367 +      if (inRootContext) {
   1.368 +        mTokenizer->setLineNumber(mRootContextLineNumber);
   1.369 +      } else {
   1.370 +        // we aren't the root context, so save the line number on the
   1.371 +        // *stack* so that we can restore it.
   1.372 +        lineNumberSave = mTokenizer->getLineNumber();
   1.373 +      }
   1.374 +
   1.375 +      mLastWasCR = mTokenizer->tokenizeBuffer(&stackBuffer);
   1.376 +
   1.377 +      if (inRootContext) {
   1.378 +        mRootContextLineNumber = mTokenizer->getLineNumber();
   1.379 +      } else {
   1.380 +        mTokenizer->setLineNumber(lineNumberSave);
   1.381 +      }
   1.382 +
   1.383 +      if (mTreeBuilder->HasScript()) {
   1.384 +        mTreeBuilder->Flush(); // Move ops to the executor
   1.385 +        rv = mExecutor->FlushDocumentWrite(); // run the ops
   1.386 +        NS_ENSURE_SUCCESS(rv, rv);
   1.387 +        // Flushing tree ops can cause all sorts of things.
   1.388 +        // Return early if the parser got terminated.
   1.389 +        if (mExecutor->IsComplete()) {
   1.390 +          return NS_OK;
   1.391 +        }
   1.392 +      }
   1.393 +      // Ignore suspension requests
   1.394 +    }
   1.395 +  }
   1.396 +
   1.397 +  nsRefPtr<nsHtml5OwningUTF16Buffer> heapBuffer;
   1.398 +  if (stackBuffer.hasMore()) {
   1.399 +    // The buffer wasn't tokenized to completion. Create a copy of the tail
   1.400 +    // on the heap.
   1.401 +    heapBuffer = stackBuffer.FalliblyCopyAsOwningBuffer();
   1.402 +    if (!heapBuffer) {
   1.403 +      // Allocation failed. The parser is now broken.
   1.404 +      return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
   1.405 +    }
   1.406 +  }
   1.407 +
   1.408 +  if (heapBuffer) {
   1.409 +    // We have something to insert before the keyholder holding in the non-null
   1.410 +    // aKey case and we have something to swap into firstLevelMarker in the
   1.411 +    // null aKey case.
   1.412 +    if (aKey) {
   1.413 +      NS_ASSERTION(mFirstBuffer != mLastBuffer,
   1.414 +        "Where's the keyholder?");
   1.415 +      // the key holder is still somewhere further down the list from
   1.416 +      // prevSearchBuf (which may be null)
   1.417 +      if (mFirstBuffer->key == aKey) {
   1.418 +        NS_ASSERTION(!prevSearchBuf,
   1.419 +          "Non-null prevSearchBuf when mFirstBuffer is the key holder?");
   1.420 +        heapBuffer->next = mFirstBuffer;
   1.421 +        mFirstBuffer = heapBuffer;
   1.422 +      } else {
   1.423 +        if (!prevSearchBuf) {
   1.424 +          prevSearchBuf = mFirstBuffer;
   1.425 +        }
   1.426 +        // We created a key holder earlier, so we will find it without walking
   1.427 +        // past the end of the list.
   1.428 +        while (prevSearchBuf->next->key != aKey) {
   1.429 +          prevSearchBuf = prevSearchBuf->next;
   1.430 +        }
   1.431 +        heapBuffer->next = prevSearchBuf->next;
   1.432 +        prevSearchBuf->next = heapBuffer;
   1.433 +      }
   1.434 +    } else {
   1.435 +      NS_ASSERTION(firstLevelMarker, "How come we don't have a marker.");
   1.436 +      firstLevelMarker->Swap(heapBuffer);
   1.437 +    }
   1.438 +  }
   1.439 +
   1.440 +  if (!mBlocked) { // buffer was tokenized to completion
   1.441 +    NS_ASSERTION(!stackBuffer.hasMore(),
   1.442 +      "Buffer wasn't tokenized to completion?");
   1.443 +    // Scripting semantics require a forced tree builder flush here
   1.444 +    mTreeBuilder->Flush(); // Move ops to the executor
   1.445 +    rv = mExecutor->FlushDocumentWrite(); // run the ops
   1.446 +    NS_ENSURE_SUCCESS(rv, rv);
   1.447 +  } else if (stackBuffer.hasMore()) {
   1.448 +    // The buffer wasn't tokenized to completion. Tokenize the untokenized
   1.449 +    // content in order to preload stuff. This content will be retokenized
   1.450 +    // later for normal parsing.
   1.451 +    if (!mDocWriteSpeculatorActive) {
   1.452 +      mDocWriteSpeculatorActive = true;
   1.453 +      if (!mDocWriteSpeculativeTreeBuilder) {
   1.454 +        // Lazily initialize if uninitialized
   1.455 +        mDocWriteSpeculativeTreeBuilder =
   1.456 +            new nsHtml5TreeBuilder(nullptr, mExecutor->GetStage());
   1.457 +        mDocWriteSpeculativeTreeBuilder->setScriptingEnabled(
   1.458 +            mTreeBuilder->isScriptingEnabled());
   1.459 +        mDocWriteSpeculativeTokenizer =
   1.460 +            new nsHtml5Tokenizer(mDocWriteSpeculativeTreeBuilder, false);
   1.461 +        mDocWriteSpeculativeTokenizer->setInterner(&mAtomTable);
   1.462 +        mDocWriteSpeculativeTokenizer->start();
   1.463 +      }
   1.464 +      mDocWriteSpeculativeTokenizer->resetToDataState();
   1.465 +      mDocWriteSpeculativeTreeBuilder->loadState(mTreeBuilder, &mAtomTable);
   1.466 +      mDocWriteSpeculativeLastWasCR = false;
   1.467 +    }
   1.468 +
   1.469 +    // Note that with multilevel document.write if we didn't just activate the
   1.470 +    // speculator, it's possible that the speculator is now in the wrong state.
   1.471 +    // That's OK for the sake of simplicity. The worst that can happen is
   1.472 +    // that the speculative loads aren't exactly right. The content will be
   1.473 +    // reparsed anyway for non-preload purposes.
   1.474 +
   1.475 +    // The buffer position for subsequent non-speculative parsing now lives
   1.476 +    // in heapBuffer, so it's ok to let the buffer position of stackBuffer
   1.477 +    // to be overwritten and not restored below.
   1.478 +    while (stackBuffer.hasMore()) {
   1.479 +      stackBuffer.adjust(mDocWriteSpeculativeLastWasCR);
   1.480 +      if (stackBuffer.hasMore()) {
   1.481 +        mDocWriteSpeculativeLastWasCR =
   1.482 +            mDocWriteSpeculativeTokenizer->tokenizeBuffer(&stackBuffer);
   1.483 +      }
   1.484 +    }
   1.485 +
   1.486 +    mDocWriteSpeculativeTreeBuilder->Flush();
   1.487 +    mDocWriteSpeculativeTreeBuilder->DropHandles();
   1.488 +    mExecutor->FlushSpeculativeLoads();
   1.489 +  }
   1.490 +
   1.491 +  return NS_OK;
   1.492 +}
   1.493 +
   1.494 +NS_IMETHODIMP
   1.495 +nsHtml5Parser::Terminate()
   1.496 +{
   1.497 +  // We should only call DidBuildModel once, so don't do anything if this is
   1.498 +  // the second time that Terminate has been called.
   1.499 +  if (mExecutor->IsComplete()) {
   1.500 +    return NS_OK;
   1.501 +  }
   1.502 +  // XXX - [ until we figure out a way to break parser-sink circularity ]
   1.503 +  // Hack - Hold a reference until we are completely done...
   1.504 +  nsCOMPtr<nsIParser> kungFuDeathGrip(this);
   1.505 +  nsRefPtr<nsHtml5StreamParser> streamKungFuDeathGrip(GetStreamParser());
   1.506 +  nsRefPtr<nsHtml5TreeOpExecutor> treeOpKungFuDeathGrip(mExecutor);
   1.507 +  if (GetStreamParser()) {
   1.508 +    GetStreamParser()->Terminate();
   1.509 +  }
   1.510 +  return mExecutor->DidBuildModel(true);
   1.511 +}
   1.512 +
   1.513 +NS_IMETHODIMP
   1.514 +nsHtml5Parser::ParseFragment(const nsAString& aSourceBuffer,
   1.515 +                             nsTArray<nsString>& aTagStack)
   1.516 +{
   1.517 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.518 +}
   1.519 +
   1.520 +NS_IMETHODIMP
   1.521 +nsHtml5Parser::BuildModel()
   1.522 +{
   1.523 +  NS_NOTREACHED("Don't call this!");
   1.524 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.525 +}
   1.526 +
   1.527 +NS_IMETHODIMP
   1.528 +nsHtml5Parser::CancelParsingEvents()
   1.529 +{
   1.530 +  NS_NOTREACHED("Don't call this!");
   1.531 +  return NS_ERROR_NOT_IMPLEMENTED;
   1.532 +}
   1.533 +
   1.534 +void
   1.535 +nsHtml5Parser::Reset()
   1.536 +{
   1.537 +  NS_NOTREACHED("Don't call this!");
   1.538 +}
   1.539 +
   1.540 +bool
   1.541 +nsHtml5Parser::CanInterrupt()
   1.542 +{
   1.543 +  // nsContentSink needs this to let nsContentSink::DidProcessATokenImpl
   1.544 +  // interrupt.
   1.545 +  return true;
   1.546 +}
   1.547 +
   1.548 +bool
   1.549 +nsHtml5Parser::IsInsertionPointDefined()
   1.550 +{
   1.551 +  return !mExecutor->IsFlushing() &&
   1.552 +    (!GetStreamParser() || mParserInsertedScriptsBeingEvaluated);
   1.553 +}
   1.554 +
   1.555 +void
   1.556 +nsHtml5Parser::BeginEvaluatingParserInsertedScript()
   1.557 +{
   1.558 +  ++mParserInsertedScriptsBeingEvaluated;
   1.559 +}
   1.560 +
   1.561 +void
   1.562 +nsHtml5Parser::EndEvaluatingParserInsertedScript()
   1.563 +{
   1.564 +  --mParserInsertedScriptsBeingEvaluated;
   1.565 +}
   1.566 +
   1.567 +void
   1.568 +nsHtml5Parser::MarkAsNotScriptCreated(const char* aCommand)
   1.569 +{
   1.570 +  NS_PRECONDITION(!mStreamListener, "Must not call this twice.");
   1.571 +  eParserMode mode = NORMAL;
   1.572 +  if (!nsCRT::strcmp(aCommand, "view-source")) {
   1.573 +    mode = VIEW_SOURCE_HTML;
   1.574 +  } else if (!nsCRT::strcmp(aCommand, "view-source-xml")) {
   1.575 +    mode = VIEW_SOURCE_XML;
   1.576 +  } else if (!nsCRT::strcmp(aCommand, "view-source-plain")) {
   1.577 +    mode = VIEW_SOURCE_PLAIN;
   1.578 +  } else if (!nsCRT::strcmp(aCommand, "plain-text")) {
   1.579 +    mode = PLAIN_TEXT;
   1.580 +  } else if (!nsCRT::strcmp(aCommand, kLoadAsData)) {
   1.581 +    mode = LOAD_AS_DATA;
   1.582 +  }
   1.583 +#ifdef DEBUG
   1.584 +  else {
   1.585 +    NS_ASSERTION(!nsCRT::strcmp(aCommand, "view") ||
   1.586 +                 !nsCRT::strcmp(aCommand, "external-resource"),
   1.587 +                 "Unsupported parser command!");
   1.588 +  }
   1.589 +#endif
   1.590 +  mStreamListener =
   1.591 +    new nsHtml5StreamListener(new nsHtml5StreamParser(mExecutor, this, mode));
   1.592 +}
   1.593 +
   1.594 +bool
   1.595 +nsHtml5Parser::IsScriptCreated()
   1.596 +{
   1.597 +  return !GetStreamParser();
   1.598 +}
   1.599 +
   1.600 +/* End nsIParser  */
   1.601 +
   1.602 +// not from interface
   1.603 +nsresult
   1.604 +nsHtml5Parser::ParseUntilBlocked()
   1.605 +{
   1.606 +  nsresult rv = mExecutor->IsBroken();
   1.607 +  NS_ENSURE_SUCCESS(rv, rv);
   1.608 +  if (mBlocked || mExecutor->IsComplete()) {
   1.609 +    return NS_OK;
   1.610 +  }
   1.611 +  NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
   1.612 +  NS_ASSERTION(!mInDocumentWrite,
   1.613 +    "ParseUntilBlocked entered while in doc.write!");
   1.614 +
   1.615 +  mDocWriteSpeculatorActive = false;
   1.616 +
   1.617 +  for (;;) {
   1.618 +    if (!mFirstBuffer->hasMore()) {
   1.619 +      if (mFirstBuffer == mLastBuffer) {
   1.620 +        if (mExecutor->IsComplete()) {
   1.621 +          // something like cache manisfests stopped the parse in mid-flight
   1.622 +          return NS_OK;
   1.623 +        }
   1.624 +        if (mDocumentClosed) {
   1.625 +          NS_ASSERTION(!GetStreamParser(),
   1.626 +                       "This should only happen with script-created parser.");
   1.627 +          mTokenizer->eof();
   1.628 +          mTreeBuilder->StreamEnded();
   1.629 +          mTreeBuilder->Flush();
   1.630 +          mExecutor->FlushDocumentWrite();
   1.631 +          // The below call does memory cleanup, so call it even if the
   1.632 +          // parser has been marked as broken.
   1.633 +          mTokenizer->end();
   1.634 +          return NS_OK;
   1.635 +        }
   1.636 +        // never release the last buffer.
   1.637 +        NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
   1.638 +                     "Sentinel buffer had its indeces changed.");
   1.639 +        if (GetStreamParser()) {
   1.640 +          if (mReturnToStreamParserPermitted &&
   1.641 +              !mExecutor->IsScriptExecuting()) {
   1.642 +            mTreeBuilder->Flush();
   1.643 +            mReturnToStreamParserPermitted = false;
   1.644 +            GetStreamParser()->ContinueAfterScripts(mTokenizer,
   1.645 +                                                mTreeBuilder,
   1.646 +                                                mLastWasCR);
   1.647 +          }
   1.648 +        } else {
   1.649 +          // Script-created parser
   1.650 +          mTreeBuilder->Flush();
   1.651 +          // No need to flush the executor, because the executor is already
   1.652 +          // in a flush
   1.653 +          NS_ASSERTION(mExecutor->IsInFlushLoop(),
   1.654 +              "How did we come here without being in the flush loop?");
   1.655 +        }
   1.656 +        return NS_OK; // no more data for now but expecting more
   1.657 +      }
   1.658 +      mFirstBuffer = mFirstBuffer->next;
   1.659 +      continue;
   1.660 +    }
   1.661 +
   1.662 +    if (mBlocked || mExecutor->IsComplete()) {
   1.663 +      return NS_OK;
   1.664 +    }
   1.665 +
   1.666 +    // now we have a non-empty buffer
   1.667 +    mFirstBuffer->adjust(mLastWasCR);
   1.668 +    mLastWasCR = false;
   1.669 +    if (mFirstBuffer->hasMore()) {
   1.670 +      bool inRootContext = (!GetStreamParser() && !mFirstBuffer->key);
   1.671 +      if (inRootContext) {
   1.672 +        mTokenizer->setLineNumber(mRootContextLineNumber);
   1.673 +      }
   1.674 +      mLastWasCR = mTokenizer->tokenizeBuffer(mFirstBuffer);
   1.675 +      if (inRootContext) {
   1.676 +        mRootContextLineNumber = mTokenizer->getLineNumber();
   1.677 +      }
   1.678 +      if (mTreeBuilder->HasScript()) {
   1.679 +        mTreeBuilder->Flush();
   1.680 +        nsresult rv = mExecutor->FlushDocumentWrite();
   1.681 +        NS_ENSURE_SUCCESS(rv, rv);
   1.682 +      }
   1.683 +      if (mBlocked) {
   1.684 +        return NS_OK;
   1.685 +      }
   1.686 +    }
   1.687 +    continue;
   1.688 +  }
   1.689 +}
   1.690 +
   1.691 +nsresult
   1.692 +nsHtml5Parser::Initialize(nsIDocument* aDoc,
   1.693 +                          nsIURI* aURI,
   1.694 +                          nsISupports* aContainer,
   1.695 +                          nsIChannel* aChannel)
   1.696 +{
   1.697 +  return mExecutor->Init(aDoc, aURI, aContainer, aChannel);
   1.698 +}
   1.699 +
   1.700 +void
   1.701 +nsHtml5Parser::StartTokenizer(bool aScriptingEnabled) {
   1.702 +
   1.703 +  bool isSrcdoc = false;
   1.704 +  nsCOMPtr<nsIChannel> channel;
   1.705 +  nsresult rv = GetChannel(getter_AddRefs(channel));
   1.706 +  if (NS_SUCCEEDED(rv)) {
   1.707 +    isSrcdoc = NS_IsSrcdocChannel(channel);
   1.708 +  }
   1.709 +  mTreeBuilder->setIsSrcdocDocument(isSrcdoc);
   1.710 +
   1.711 +  mTreeBuilder->SetPreventScriptExecution(!aScriptingEnabled);
   1.712 +  mTreeBuilder->setScriptingEnabled(aScriptingEnabled);
   1.713 +  mTokenizer->start();
   1.714 +}
   1.715 +
   1.716 +void
   1.717 +nsHtml5Parser::InitializeDocWriteParserState(nsAHtml5TreeBuilderState* aState,
   1.718 +                                             int32_t aLine)
   1.719 +{
   1.720 +  mTokenizer->resetToDataState();
   1.721 +  mTokenizer->setLineNumber(aLine);
   1.722 +  mTreeBuilder->loadState(aState, &mAtomTable);
   1.723 +  mLastWasCR = false;
   1.724 +  mReturnToStreamParserPermitted = true;
   1.725 +}
   1.726 +
   1.727 +void
   1.728 +nsHtml5Parser::ContinueAfterFailedCharsetSwitch()
   1.729 +{
   1.730 +  NS_PRECONDITION(GetStreamParser(),
   1.731 +    "Tried to continue after failed charset switch without a stream parser");
   1.732 +  GetStreamParser()->ContinueAfterFailedCharsetSwitch();
   1.733 +}
   1.734 +

mercurial