This source file includes following definitions.
- tokenizerStateForContextElement
- acceptData
- backgroundThread
- m_pumpSessionNestingLevel
- m_pumpSessionNestingLevel
- pinToMainThread
- detach
- stopParsing
- prepareToStopParsing
- isParsingFragment
- processingData
- pumpTokenizerIfPossible
- isScheduledForResume
- resumeParsingAfterYield
- runScriptsForPausedTreeBuilder
- canTakeNextToken
- didReceiveParsedChunkFromBackgroundParser
- didReceiveEncodingDataFromBackgroundParser
- validateSpeculations
- discardSpeculationsAndResumeFrom
- processParsedChunkFromBackgroundParser
- pumpPendingSpeculations
- forcePlaintextForTextDocument
- contextForParsingSession
- createMediaValues
- pumpTokenizer
- constructTreeFromHTMLToken
- constructTreeFromCompactHTMLToken
- hasInsertionPoint
- insert
- startBackgroundParser
- stopBackgroundParser
- append
- end
- attemptToRunDeferredScriptsAndEnd
- attemptToEnd
- endIfDelayed
- finish
- isExecutingScript
- lineNumber
- textPosition
- isWaitingForScripts
- resumeParsingAfterScriptExecution
- watchForLoad
- stopWatchingForLoad
- appendCurrentInputStreamToPreloadScannerAndScan
- notifyFinished
- executeScriptsWaitingForResources
- parseDocumentFragment
- suspendScheduledTasks
- resumeScheduledTasks
- appendBytes
- flush
- setDecoder
#include "config.h"
#include "core/html/parser/HTMLDocumentParser.h"
#include "HTMLNames.h"
#include "core/css/MediaValues.h"
#include "core/dom/DocumentFragment.h"
#include "core/dom/Element.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLDocument.h"
#include "core/html/parser/AtomicHTMLToken.h"
#include "core/html/parser/BackgroundHTMLParser.h"
#include "core/html/parser/HTMLParserScheduler.h"
#include "core/html/parser/HTMLParserThread.h"
#include "core/html/parser/HTMLScriptRunner.h"
#include "core/html/parser/HTMLTreeBuilder.h"
#include "core/inspector/InspectorInstrumentation.h"
#include "core/loader/DocumentLoader.h"
#include "platform/SharedBuffer.h"
#include "platform/TraceEvent.h"
#include "public/platform/WebThreadedDataReceiver.h"
#include "wtf/Functional.h"
namespace WebCore {
using namespace HTMLNames;
static HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors, const HTMLParserOptions& options)
{
if (!contextElement)
return HTMLTokenizer::DataState;
const QualifiedName& contextTag = contextElement->tagQName();
if (contextTag.matches(titleTag) || contextTag.matches(textareaTag))
return HTMLTokenizer::RCDATAState;
if (contextTag.matches(styleTag)
|| contextTag.matches(xmpTag)
|| contextTag.matches(iframeTag)
|| (contextTag.matches(noembedTag) && options.pluginsEnabled)
|| (contextTag.matches(noscriptTag) && options.scriptEnabled)
|| contextTag.matches(noframesTag))
return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState;
if (contextTag.matches(scriptTag))
return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState;
if (contextTag.matches(plaintextTag))
return HTMLTokenizer::PLAINTEXTState;
return HTMLTokenizer::DataState;
}
class ParserDataReceiver : public blink::WebThreadedDataReceiver {
public:
explicit ParserDataReceiver(WeakPtr<BackgroundHTMLParser> backgroundParser)
: m_backgroundParser(backgroundParser)
{
}
virtual void acceptData(const char* data, int dataLength) OVERRIDE FINAL
{
ASSERT(backgroundThread()->isCurrentThread());
if (m_backgroundParser.get())
m_backgroundParser.get()->appendRawBytesFromParserThread(data, dataLength);
}
virtual blink::WebThread* backgroundThread() OVERRIDE FINAL
{
return &HTMLParserThread::shared()->platformThread();
}
private:
WeakPtr<BackgroundHTMLParser> m_backgroundParser;
};
HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors)
: ScriptableDocumentParser(document)
, m_options(document)
, m_token(m_options.useThreading ? nullptr : adoptPtr(new HTMLToken))
, m_tokenizer(m_options.useThreading ? nullptr : HTMLTokenizer::create(m_options))
, m_scriptRunner(HTMLScriptRunner::create(document, this))
, m_treeBuilder(HTMLTreeBuilder::create(this, document, parserContentPolicy(), reportErrors, m_options))
, m_parserScheduler(HTMLParserScheduler::create(this))
, m_xssAuditorDelegate(document)
, m_weakFactory(this)
, m_preloader(adoptPtr(new HTMLResourcePreloader(document)))
, m_isPinnedToMainThread(false)
, m_endWasDelayed(false)
, m_haveBackgroundParser(false)
, m_pumpSessionNestingLevel(0)
{
ASSERT(shouldUseThreading() || (m_token && m_tokenizer));
}
HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy)
: ScriptableDocumentParser(&fragment->document(), parserContentPolicy)
, m_options(&fragment->document())
, m_token(adoptPtr(new HTMLToken))
, m_tokenizer(HTMLTokenizer::create(m_options))
, m_treeBuilder(HTMLTreeBuilder::create(this, fragment, contextElement, this->parserContentPolicy(), m_options))
, m_xssAuditorDelegate(&fragment->document())
, m_weakFactory(this)
, m_isPinnedToMainThread(true)
, m_endWasDelayed(false)
, m_haveBackgroundParser(false)
, m_pumpSessionNestingLevel(0)
{
ASSERT(!shouldUseThreading());
bool reportErrors = false;
m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors, m_options));
m_xssAuditor.initForFragment();
}
HTMLDocumentParser::~HTMLDocumentParser()
{
ASSERT(!m_parserScheduler);
ASSERT(!m_pumpSessionNestingLevel);
ASSERT(!m_preloadScanner);
ASSERT(!m_insertionPreloadScanner);
ASSERT(!m_haveBackgroundParser);
}
void HTMLDocumentParser::pinToMainThread()
{
ASSERT(!m_haveBackgroundParser);
ASSERT(!m_isPinnedToMainThread);
m_isPinnedToMainThread = true;
if (!m_tokenizer) {
ASSERT(!m_token);
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
}
void HTMLDocumentParser::detach()
{
if (m_haveBackgroundParser)
stopBackgroundParser();
DocumentParser::detach();
if (m_scriptRunner)
m_scriptRunner->detach();
m_treeBuilder->detach();
m_preloadScanner.clear();
m_insertionPreloadScanner.clear();
m_parserScheduler.clear();
}
void HTMLDocumentParser::stopParsing()
{
DocumentParser::stopParsing();
m_parserScheduler.clear();
if (m_haveBackgroundParser)
stopBackgroundParser();
}
void HTMLDocumentParser::prepareToStopParsing()
{
ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
RefPtr<HTMLDocumentParser> protect(this);
if (m_tokenizer) {
ASSERT(!m_haveBackgroundParser);
pumpTokenizerIfPossible(ForceSynchronous);
}
if (isStopped())
return;
DocumentParser::prepareToStopParsing();
if (m_scriptRunner)
document()->setReadyState(Document::Interactive);
if (isDetached())
return;
attemptToRunDeferredScriptsAndEnd();
}
bool HTMLDocumentParser::isParsingFragment() const
{
return m_treeBuilder->isParsingFragment();
}
bool HTMLDocumentParser::processingData() const
{
return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser;
}
void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
{
if (isStopped())
return;
if (isWaitingForScripts())
return;
if (isScheduledForResume()) {
ASSERT(mode == AllowYield);
return;
}
pumpTokenizer(mode);
}
bool HTMLDocumentParser::isScheduledForResume() const
{
return m_parserScheduler && m_parserScheduler->isScheduledForResume();
}
void HTMLDocumentParser::resumeParsingAfterYield()
{
ASSERT(!m_isPinnedToMainThread);
RefPtr<HTMLDocumentParser> protect(this);
if (m_haveBackgroundParser) {
pumpPendingSpeculations();
return;
}
pumpTokenizer(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::runScriptsForPausedTreeBuilder()
{
ASSERT(scriptingContentIsAllowed(parserContentPolicy()));
TextPosition scriptStartPosition = TextPosition::belowRangePosition();
RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition);
if (m_scriptRunner)
m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
}
bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session)
{
if (isStopped())
return false;
ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
if (isWaitingForScripts()) {
if (mode == AllowYield)
m_parserScheduler->checkForYieldBeforeScript(session);
if (session.needsYield)
return false;
runScriptsForPausedTreeBuilder();
if (isStopped())
return false;
if (isWaitingForScripts())
return false;
}
if (!isParsingFragment()
&& document()->frame() && document()->frame()->navigationScheduler().locationChangePending())
return false;
if (mode == AllowYield)
m_parserScheduler->checkForYieldBeforeToken(session);
return true;
}
void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> chunk)
{
TRACE_EVENT0("webkit", "HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser");
if (isWaitingForScripts() || !m_speculations.isEmpty() || document()->activeParserCount() > 0) {
m_preloader->takeAndPreload(chunk->preloads);
m_speculations.append(chunk);
return;
}
RefPtr<HTMLDocumentParser> protect(this);
ASSERT(m_speculations.isEmpty());
chunk->preloads.clear();
m_speculations.append(chunk);
pumpPendingSpeculations();
}
void HTMLDocumentParser::didReceiveEncodingDataFromBackgroundParser(const DocumentEncodingData& data)
{
document()->setEncodingData(data);
}
void HTMLDocumentParser::validateSpeculations(PassOwnPtr<ParsedChunk> chunk)
{
ASSERT(chunk);
if (isWaitingForScripts()) {
ASSERT(!m_lastChunkBeforeScript);
m_lastChunkBeforeScript = chunk;
return;
}
ASSERT(!m_lastChunkBeforeScript);
OwnPtr<HTMLTokenizer> tokenizer = m_tokenizer.release();
OwnPtr<HTMLToken> token = m_token.release();
if (!tokenizer) {
return;
}
if (chunk->tokenizerState == HTMLTokenizer::DataState
&& tokenizer->state() == HTMLTokenizer::DataState
&& m_input.current().isEmpty()
&& chunk->treeBuilderState == HTMLTreeBuilderSimulator::stateFor(m_treeBuilder.get())) {
ASSERT(token->isUninitialized());
return;
}
discardSpeculationsAndResumeFrom(chunk, token.release(), tokenizer.release());
}
void HTMLDocumentParser::discardSpeculationsAndResumeFrom(PassOwnPtr<ParsedChunk> lastChunkBeforeScript, PassOwnPtr<HTMLToken> token, PassOwnPtr<HTMLTokenizer> tokenizer)
{
m_weakFactory.revokeAll();
m_speculations.clear();
OwnPtr<BackgroundHTMLParser::Checkpoint> checkpoint = adoptPtr(new BackgroundHTMLParser::Checkpoint);
checkpoint->parser = m_weakFactory.createWeakPtr();
checkpoint->token = token;
checkpoint->tokenizer = tokenizer;
checkpoint->treeBuilderState = HTMLTreeBuilderSimulator::stateFor(m_treeBuilder.get());
checkpoint->inputCheckpoint = lastChunkBeforeScript->inputCheckpoint;
checkpoint->preloadScannerCheckpoint = lastChunkBeforeScript->preloadScannerCheckpoint;
checkpoint->unparsedInput = m_input.current().toString().isolatedCopy();
m_input.current().clear();
ASSERT(checkpoint->unparsedInput.isSafeToSendToAnotherThread());
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::resumeFrom, m_backgroundParser, checkpoint.release()));
}
void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> popChunk)
{
TRACE_EVENT0("webkit", "HTMLDocumentParser::processParsedChunkFromBackgroundParser");
ASSERT_WITH_SECURITY_IMPLICATION(!document()->activeParserCount());
ASSERT(!isParsingFragment());
ASSERT(!isWaitingForScripts());
ASSERT(!isStopped());
ASSERT(refCount() >= 2);
ASSERT(shouldUseThreading());
ASSERT(!m_tokenizer);
ASSERT(!m_token);
ASSERT(!m_lastChunkBeforeScript);
ActiveParserSession session(contextForParsingSession());
OwnPtr<ParsedChunk> chunk(popChunk);
OwnPtr<CompactHTMLTokenStream> tokens = chunk->tokens.release();
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::startedChunkWithCheckpoint, m_backgroundParser, chunk->inputCheckpoint));
for (XSSInfoStream::const_iterator it = chunk->xssInfos.begin(); it != chunk->xssInfos.end(); ++it) {
m_textPosition = (*it)->m_textPosition;
m_xssAuditorDelegate.didBlockScript(**it);
if (isStopped())
break;
}
for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != tokens->end(); ++it) {
ASSERT(!isWaitingForScripts());
if (!isParsingFragment()
&& document()->frame() && document()->frame()->navigationScheduler().locationChangePending()) {
if (tokens->last().type() == HTMLToken::EndOfFile) {
ASSERT(m_speculations.isEmpty());
prepareToStopParsing();
}
break;
}
m_textPosition = it->textPosition();
constructTreeFromCompactHTMLToken(*it);
if (isStopped())
break;
if (isWaitingForScripts()) {
ASSERT(it + 1 == tokens->end());
runScriptsForPausedTreeBuilder();
validateSpeculations(chunk.release());
break;
}
if (it->type() == HTMLToken::EndOfFile) {
ASSERT(it + 1 == tokens->end());
ASSERT(m_speculations.isEmpty());
prepareToStopParsing();
break;
}
ASSERT(!m_tokenizer);
ASSERT(!m_token);
}
}
void HTMLDocumentParser::pumpPendingSpeculations()
{
const double parserTimeLimit = 0.500;
ASSERT(refCount() >= 2);
ASSERT(!m_tokenizer);
ASSERT(!m_token);
ASSERT(!m_lastChunkBeforeScript);
ASSERT(!isWaitingForScripts());
ASSERT(!isStopped());
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), lineNumber().zeroBasedInt());
double startTime = currentTime();
while (!m_speculations.isEmpty()) {
processParsedChunkFromBackgroundParser(m_speculations.takeFirst());
if (isStopped())
break;
if (isWaitingForScripts())
break;
if (currentTime() - startTime > parserTimeLimit && !m_speculations.isEmpty()) {
m_parserScheduler->scheduleForResume();
break;
}
}
InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
}
void HTMLDocumentParser::forcePlaintextForTextDocument()
{
if (shouldUseThreading()) {
if (!m_haveBackgroundParser)
startBackgroundParser();
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::forcePlaintextForTextDocument, m_backgroundParser));
} else
m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
}
Document* HTMLDocumentParser::contextForParsingSession()
{
if (isParsingFragment())
return 0;
return document();
}
static PassRefPtr<MediaValues> createMediaValues(Document* document)
{
RefPtr<MediaValues> mediaValues = MediaValues::create(document, MediaValues::CachingMode);
ASSERT(mediaValues->isSafeToSendToAnotherThread());
return mediaValues;
}
void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
{
ASSERT(!isStopped());
ASSERT(!isScheduledForResume());
ASSERT(refCount() >= 2);
ASSERT(m_tokenizer);
ASSERT(m_token);
ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession());
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().currentLine().zeroBasedInt());
m_xssAuditor.init(document(), &m_xssAuditorDelegate);
while (canTakeNextToken(mode, session) && !session.needsYield) {
if (!isParsingFragment())
m_sourceTracker.start(m_input.current(), m_tokenizer.get(), token());
if (!m_tokenizer->nextToken(m_input.current(), token()))
break;
if (!isParsingFragment()) {
m_sourceTracker.end(m_input.current(), m_tokenizer.get(), token());
if (OwnPtr<XSSInfo> xssInfo = m_xssAuditor.filterToken(FilterTokenRequest(token(), m_sourceTracker, m_tokenizer->shouldAllowCDATA())))
m_xssAuditorDelegate.didBlockScript(*xssInfo);
}
constructTreeFromHTMLToken(token());
ASSERT(token().isUninitialized());
}
ASSERT(refCount() >= 1);
if (isStopped())
return;
if (mode == ForceSynchronous)
m_treeBuilder->flush();
RELEASE_ASSERT(!isStopped());
if (session.needsYield)
m_parserScheduler->scheduleForResume();
if (isWaitingForScripts()) {
ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
if (!m_preloadScanner) {
m_preloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), createMediaValues(document())));
m_preloadScanner->appendToEnd(m_input.current());
}
m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
}
InspectorInstrumentation::didWriteHTML(cookie, m_input.current().currentLine().zeroBasedInt());
}
void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken)
{
AtomicHTMLToken token(rawToken);
if (rawToken.type() != HTMLToken::Character)
rawToken.clear();
m_treeBuilder->constructTree(&token);
if (!rawToken.isUninitialized()) {
ASSERT(rawToken.type() == HTMLToken::Character);
rawToken.clear();
}
}
void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToken& compactToken)
{
AtomicHTMLToken token(compactToken);
m_treeBuilder->constructTree(&token);
}
bool HTMLDocumentParser::hasInsertionPoint()
{
return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile());
}
void HTMLDocumentParser::insert(const SegmentedString& source)
{
if (isStopped())
return;
TRACE_EVENT1("webkit", "HTMLDocumentParser::insert", "source_length", source.length());
RefPtr<HTMLDocumentParser> protect(this);
if (!m_tokenizer) {
ASSERT(!inPumpSession());
ASSERT(m_haveBackgroundParser || wasCreatedByScript());
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
SegmentedString excludedLineNumberSource(source);
excludedLineNumberSource.setExcludeLineNumbers();
m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
pumpTokenizerIfPossible(ForceSynchronous);
if (isWaitingForScripts()) {
if (!m_insertionPreloadScanner)
m_insertionPreloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), createMediaValues(document())));
m_insertionPreloadScanner->appendToEnd(source);
m_insertionPreloadScanner->scan(m_preloader.get(), document()->baseElementURL());
}
endIfDelayed();
}
void HTMLDocumentParser::startBackgroundParser()
{
ASSERT(!isStopped());
ASSERT(shouldUseThreading());
ASSERT(!m_haveBackgroundParser);
m_haveBackgroundParser = true;
RefPtr<WeakReference<BackgroundHTMLParser> > reference = WeakReference<BackgroundHTMLParser>::createUnbound();
m_backgroundParser = WeakPtr<BackgroundHTMLParser>(reference);
document()->loader()->attachThreadedDataReceiver(adoptPtr(new ParserDataReceiver(m_backgroundParser)));
OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new BackgroundHTMLParser::Configuration);
config->options = m_options;
config->parser = m_weakFactory.createWeakPtr();
config->xssAuditor = adoptPtr(new XSSAuditor);
config->xssAuditor->init(document(), &m_xssAuditorDelegate);
config->preloadScanner = adoptPtr(new TokenPreloadScanner(document()->url().copy(), createMediaValues(document())));
config->decoder = takeDecoder();
ASSERT(config->xssAuditor->isSafeToSendToAnotherThread());
ASSERT(config->preloadScanner->isSafeToSendToAnotherThread());
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::start, reference.release(), config.release()));
}
void HTMLDocumentParser::stopBackgroundParser()
{
ASSERT(shouldUseThreading());
ASSERT(m_haveBackgroundParser);
m_haveBackgroundParser = false;
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::stop, m_backgroundParser));
m_weakFactory.revokeAll();
}
void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
{
if (isStopped())
return;
ASSERT(!shouldUseThreading());
RefPtr<HTMLDocumentParser> protect(this);
TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->length());
String source(inputSource);
if (m_preloadScanner) {
if (m_input.current().isEmpty() && !isWaitingForScripts()) {
m_preloadScanner.clear();
} else {
m_preloadScanner->appendToEnd(source);
if (isWaitingForScripts())
m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
}
}
m_input.appendToEnd(source);
if (inPumpSession()) {
return;
}
if (m_isPinnedToMainThread)
pumpTokenizerIfPossible(ForceSynchronous);
else
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::end()
{
ASSERT(!isDetached());
ASSERT(!isScheduledForResume());
if (m_haveBackgroundParser)
stopBackgroundParser();
m_treeBuilder->finished();
}
void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
{
ASSERT(isStopping());
ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
return;
end();
}
void HTMLDocumentParser::attemptToEnd()
{
if (shouldDelayEnd()) {
m_endWasDelayed = true;
return;
}
prepareToStopParsing();
}
void HTMLDocumentParser::endIfDelayed()
{
if (isDetached())
return;
if (!m_endWasDelayed || shouldDelayEnd())
return;
m_endWasDelayed = false;
prepareToStopParsing();
}
void HTMLDocumentParser::finish()
{
if (m_haveBackgroundParser) {
if (!m_input.haveSeenEndOfFile())
m_input.closeWithoutMarkingEndOfFile();
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::finish, m_backgroundParser));
return;
}
if (!m_tokenizer) {
ASSERT(!m_token);
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
if (!m_input.haveSeenEndOfFile())
m_input.markEndOfFile();
attemptToEnd();
}
bool HTMLDocumentParser::isExecutingScript() const
{
if (!m_scriptRunner)
return false;
return m_scriptRunner->isExecutingScript();
}
OrdinalNumber HTMLDocumentParser::lineNumber() const
{
if (m_haveBackgroundParser)
return m_textPosition.m_line;
return m_input.current().currentLine();
}
TextPosition HTMLDocumentParser::textPosition() const
{
if (m_haveBackgroundParser)
return m_textPosition;
const SegmentedString& currentString = m_input.current();
OrdinalNumber line = currentString.currentLine();
OrdinalNumber column = currentString.currentColumn();
return TextPosition(line, column);
}
bool HTMLDocumentParser::isWaitingForScripts() const
{
bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript();
bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript();
ASSERT(!(treeBuilderHasBlockingScript && scriptRunnerHasBlockingScript));
return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript;
}
void HTMLDocumentParser::resumeParsingAfterScriptExecution()
{
ASSERT(!isExecutingScript());
ASSERT(!isWaitingForScripts());
if (m_haveBackgroundParser) {
validateSpeculations(m_lastChunkBeforeScript.release());
ASSERT(!m_lastChunkBeforeScript);
RefPtr<HTMLDocumentParser> protect(this);
pumpPendingSpeculations();
return;
}
m_insertionPreloadScanner.clear();
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::watchForLoad(Resource* resource)
{
ASSERT(!resource->isLoaded());
resource->addClient(this);
}
void HTMLDocumentParser::stopWatchingForLoad(Resource* resource)
{
resource->removeClient(this);
}
void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan()
{
ASSERT(m_preloadScanner);
m_preloadScanner->appendToEnd(m_input.current());
m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL());
}
void HTMLDocumentParser::notifyFinished(Resource* cachedResource)
{
RefPtr<HTMLDocumentParser> protect(this);
ASSERT(m_scriptRunner);
ASSERT(!isExecutingScript());
if (isStopping()) {
attemptToRunDeferredScriptsAndEnd();
return;
}
m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
if (!isWaitingForScripts())
resumeParsingAfterScriptExecution();
}
void HTMLDocumentParser::executeScriptsWaitingForResources()
{
ASSERT(m_scriptRunner);
if (!m_scriptRunner->hasScriptsWaitingForResources())
return;
RefPtr<HTMLDocumentParser> protect(this);
m_scriptRunner->executeScriptsWaitingForResources();
if (!isWaitingForScripts())
resumeParsingAfterScriptExecution();
}
void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy)
{
RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, parserContentPolicy);
parser->insert(source);
parser->finish();
ASSERT(!parser->processingData());
parser->detach();
}
void HTMLDocumentParser::suspendScheduledTasks()
{
if (m_parserScheduler)
m_parserScheduler->suspend();
}
void HTMLDocumentParser::resumeScheduledTasks()
{
if (m_parserScheduler)
m_parserScheduler->resume();
}
void HTMLDocumentParser::appendBytes(const char* data, size_t length)
{
if (!length || isStopped())
return;
if (shouldUseThreading()) {
if (!m_haveBackgroundParser)
startBackgroundParser();
OwnPtr<Vector<char> > buffer = adoptPtr(new Vector<char>(length));
memcpy(buffer->data(), data, length);
TRACE_EVENT1("net", "HTMLDocumentParser::appendBytes", "size", (unsigned)length);
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_backgroundParser, buffer.release()));
return;
}
DecodedDataDocumentParser::appendBytes(data, length);
}
void HTMLDocumentParser::flush()
{
if (isDetached() || needsDecoder())
return;
if (m_haveBackgroundParser)
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::flush, m_backgroundParser));
else
DecodedDataDocumentParser::flush();
}
void HTMLDocumentParser::setDecoder(PassOwnPtr<TextResourceDecoder> decoder)
{
ASSERT(decoder);
DecodedDataDocumentParser::setDecoder(decoder);
if (m_haveBackgroundParser)
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::setDecoder, m_backgroundParser, takeDecoder()));
}
}