parser/html/nsHtml5Parser.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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

mercurial