HTMLDocumentParser.cpp [plain text]
#include "config.h"
#include "HTMLDocumentParser.h"
#include "AtomicHTMLToken.h"
#include "BackgroundHTMLParser.h"
#include "CompactHTMLToken.h"
#include "ContentSecurityPolicy.h"
#include "DocumentFragment.h"
#include "DocumentLoader.h"
#include "Element.h"
#include "Frame.h"
#include "HTMLIdentifier.h"
#include "HTMLNames.h"
#include "HTMLParserScheduler.h"
#include "HTMLParserThread.h"
#include "HTMLTokenizer.h"
#include "HTMLPreloadScanner.h"
#include "HTMLScriptRunner.h"
#include "HTMLTreeBuilder.h"
#include "HTMLDocument.h"
#include "InspectorInstrumentation.h"
#include "NestingLevelIncrementer.h"
#include "Settings.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;
}
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)
#if ENABLE(THREADED_HTML_PARSER)
, m_weakFactory(this)
#endif
, 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())
#if ENABLE(THREADED_HTML_PARSER)
, m_weakFactory(this)
#endif
, 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);
}
#if ENABLE(THREADED_HTML_PARSER)
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);
}
}
#endif
void HTMLDocumentParser::detach()
{
#if ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser)
stopBackgroundParser();
#endif
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 ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser)
stopBackgroundParser();
#endif
}
void HTMLDocumentParser::prepareToStopParsing()
{
ASSERT(!hasInsertionPoint() || m_haveBackgroundParser);
RefPtr<HTMLDocumentParser> protect(this);
#if ENABLE(THREADED_HTML_PARSER)
if (m_tokenizer) {
ASSERT(!m_haveBackgroundParser);
pumpTokenizerIfPossible(ForceSynchronous);
}
#else
pumpTokenizerIfPossible(ForceSynchronous);
#endif
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() || isWaitingForScripts())
return;
if (isScheduledForResume()) {
ASSERT(mode == AllowYield);
return;
}
pumpTokenizer(mode);
}
bool HTMLDocumentParser::isScheduledForResume() const
{
return m_parserScheduler && m_parserScheduler->isScheduledForResume();
}
void HTMLDocumentParser::resumeParsingAfterYield()
{
RefPtr<HTMLDocumentParser> protect(this);
#if ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser) {
pumpPendingSpeculations();
return;
}
#endif
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 (isWaitingForScripts() || isStopped())
return false;
}
if (!isParsingFragment()
&& document()->frame() && document()->frame()->navigationScheduler()->locationChangePending())
return false;
if (mode == AllowYield)
m_parserScheduler->checkForYieldBeforeToken(session);
return true;
}
#if ENABLE(THREADED_HTML_PARSER)
void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> chunk)
{
if (isWaitingForScripts() || !m_speculations.isEmpty()) {
m_preloader->takeAndPreload(chunk->preloads);
m_speculations.append(chunk);
return;
}
RefPtr<HTMLDocumentParser> protect(this);
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), lineNumber().zeroBasedInt());
ASSERT(m_speculations.isEmpty());
chunk->preloads.clear(); processParsedChunkFromBackgroundParser(chunk);
InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
}
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)
{
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);
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), lineNumber().zeroBasedInt());
double startTime = currentTime();
while (!m_speculations.isEmpty()) {
processParsedChunkFromBackgroundParser(m_speculations.takeFirst());
if (isWaitingForScripts() || isStopped())
break;
if (currentTime() - startTime > parserTimeLimit && !m_speculations.isEmpty()) {
m_parserScheduler->scheduleForResume();
break;
}
}
InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
}
#endif // ENABLE(THREADED_HTML_PARSER)
void HTMLDocumentParser::forcePlaintextForTextDocument()
{
#if ENABLE(THREADED_HTML_PARSER)
if (shouldUseThreading()) {
if (!m_haveBackgroundParser)
startBackgroundParser();
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::forcePlaintextForTextDocument, m_backgroundParser));
} else
#endif
m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
}
Document* HTMLDocumentParser::contextForParsingSession()
{
if (isParsingFragment())
return 0;
return document();
}
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 (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()));
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();
}
}
#if ENABLE(THREADED_HTML_PARSER)
void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToken& compactToken)
{
AtomicHTMLToken token(compactToken);
m_treeBuilder->constructTree(&token);
}
#endif
bool HTMLDocumentParser::hasInsertionPoint()
{
return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile());
}
void HTMLDocumentParser::insert(const SegmentedString& source)
{
if (isStopped())
return;
RefPtr<HTMLDocumentParser> protect(this);
#if ENABLE(THREADED_HTML_PARSER)
if (!m_tokenizer) {
ASSERT(!inPumpSession());
ASSERT(m_haveBackgroundParser || wasCreatedByScript());
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
#endif
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()));
m_insertionPreloadScanner->appendToEnd(source);
m_insertionPreloadScanner->scan(m_preloader.get(), document()->baseElementURL());
}
endIfDelayed();
}
#if ENABLE(THREADED_HTML_PARSER)
void HTMLDocumentParser::startBackgroundParser()
{
ASSERT(shouldUseThreading());
ASSERT(!m_haveBackgroundParser);
m_haveBackgroundParser = true;
HTMLIdentifier::init();
RefPtr<WeakReference<BackgroundHTMLParser> > reference = WeakReference<BackgroundHTMLParser>::createUnbound();
m_backgroundParser = WeakPtr<BackgroundHTMLParser>(reference);
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()));
ASSERT(config->xssAuditor->isSafeToSendToAnotherThread());
ASSERT(config->preloadScanner->isSafeToSendToAnotherThread());
HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::create, 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();
}
#endif
void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
{
if (isStopped())
return;
#if ENABLE(THREADED_HTML_PARSER)
if (shouldUseThreading()) {
if (!m_haveBackgroundParser)
startBackgroundParser();
ASSERT(inputSource->hasOneRef());
Closure closure = bind(&BackgroundHTMLParser::append, m_backgroundParser, String(inputSource));
HTMLParserThread::shared()->postTask(closure);
return;
}
#endif
RefPtr<HTMLDocumentParser> protect(this);
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;
}
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::end()
{
ASSERT(!isDetached());
ASSERT(!isScheduledForResume());
#if ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser)
stopBackgroundParser();
#endif
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 ENABLE(THREADED_HTML_PARSER)
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);
}
#endif
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 ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser)
return m_textPosition.m_line;
#endif
return m_input.current().currentLine();
}
TextPosition HTMLDocumentParser::textPosition() const
{
#if ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser)
return m_textPosition;
#endif
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 ENABLE(THREADED_HTML_PARSER)
if (m_haveBackgroundParser) {
validateSpeculations(m_lastChunkBeforeScript.release());
ASSERT(!m_lastChunkBeforeScript);
RefPtr<HTMLDocumentParser> protect(this);
pumpPendingSpeculations();
return;
}
#endif
m_insertionPreloadScanner.clear();
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript)
{
ASSERT(!cachedScript->isLoaded());
cachedScript->addClient(this);
}
void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript)
{
cachedScript->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(CachedResource* cachedResource)
{
RefPtr<HTMLDocumentParser> protect(this);
ASSERT(m_scriptRunner);
ASSERT(!isExecutingScript());
if (isStopping()) {
attemptToRunDeferredScriptsAndEnd();
return;
}
m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
if (!isWaitingForScripts())
resumeParsingAfterScriptExecution();
}
void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
{
ASSERT(m_scriptRunner);
if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
return;
RefPtr<HTMLDocumentParser> protect(this);
m_scriptRunner->executeScriptsWaitingForStylesheets();
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();
}
}