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 +