BackgroundHTMLParser.cpp [plain text]
#include "config.h"
#if ENABLE(THREADED_HTML_PARSER)
#include "BackgroundHTMLParser.h"
#include "HTMLDocumentParser.h"
#include "HTMLParserIdioms.h"
#include "HTMLParserThread.h"
#include "HTMLTokenizer.h"
#include "XSSAuditor.h"
#include <wtf/MainThread.h>
#include <wtf/text/TextPosition.h>
namespace WebCore {
static const size_t outstandingCheckpointLimit = 10;
static const size_t pendingTokenLimit = 1000;
using namespace HTMLNames;
#ifndef NDEBUG
static void checkThatTokensAreSafeToSendToAnotherThread(const CompactHTMLTokenStream* tokens)
{
for (size_t i = 0; i < tokens->size(); ++i)
ASSERT(tokens->at(i).isSafeToSendToAnotherThread());
}
static void checkThatPreloadsAreSafeToSendToAnotherThread(const PreloadRequestStream& preloads)
{
for (size_t i = 0; i < preloads.size(); ++i)
ASSERT(preloads[i]->isSafeToSendToAnotherThread());
}
#endif
BackgroundHTMLParser::BackgroundHTMLParser(PassRefPtr<WeakReference<BackgroundHTMLParser> > reference, PassOwnPtr<Configuration> config)
: m_weakFactory(reference, this)
, m_token(adoptPtr(new HTMLToken))
, m_tokenizer(HTMLTokenizer::create(config->options))
, m_treeBuilderSimulator(config->options)
, m_options(config->options)
, m_parser(config->parser)
, m_pendingTokens(adoptPtr(new CompactHTMLTokenStream))
, m_xssAuditor(config->xssAuditor.release())
, m_preloadScanner(config->preloadScanner.release())
{
}
void BackgroundHTMLParser::append(const String& input)
{
ASSERT(!m_input.current().isClosed());
m_input.append(input);
pumpTokenizer();
}
void BackgroundHTMLParser::resumeFrom(PassOwnPtr<Checkpoint> checkpoint)
{
m_parser = checkpoint->parser;
m_token = checkpoint->token.release();
m_tokenizer = checkpoint->tokenizer.release();
m_treeBuilderSimulator.setState(checkpoint->treeBuilderState);
m_input.rewindTo(checkpoint->inputCheckpoint, checkpoint->unparsedInput);
m_preloadScanner->rewindTo(checkpoint->preloadScannerCheckpoint);
pumpTokenizer();
}
void BackgroundHTMLParser::startedChunkWithCheckpoint(HTMLInputCheckpoint inputCheckpoint)
{
m_input.invalidateCheckpointsBefore(inputCheckpoint);
pumpTokenizer();
}
void BackgroundHTMLParser::finish()
{
markEndOfFile();
pumpTokenizer();
}
void BackgroundHTMLParser::stop()
{
delete this;
}
void BackgroundHTMLParser::forcePlaintextForTextDocument()
{
m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
}
void BackgroundHTMLParser::markEndOfFile()
{
ASSERT(!m_input.current().isClosed());
m_input.append(String(&kEndOfFileMarker, 1));
m_input.close();
}
void BackgroundHTMLParser::pumpTokenizer()
{
if (m_input.outstandingCheckpointCount() > outstandingCheckpointLimit)
return;
while (true) {
m_sourceTracker.start(m_input.current(), m_tokenizer.get(), *m_token);
if (!m_tokenizer->nextToken(m_input.current(), *m_token.get())) {
sendTokensToMainThread();
break;
}
m_sourceTracker.end(m_input.current(), m_tokenizer.get(), *m_token);
{
TextPosition position = TextPosition(m_input.current().currentLine(), m_input.current().currentColumn());
if (OwnPtr<XSSInfo> xssInfo = m_xssAuditor->filterToken(FilterTokenRequest(*m_token, m_sourceTracker, m_tokenizer->shouldAllowCDATA()))) {
xssInfo->m_textPosition = position;
m_pendingXSSInfos.append(xssInfo.release());
}
CompactHTMLToken token(m_token.get(), TextPosition(m_input.current().currentLine(), m_input.current().currentColumn()));
m_preloadScanner->scan(token, m_pendingPreloads);
m_pendingTokens->append(token);
}
m_token->clear();
if (!m_treeBuilderSimulator.simulate(m_pendingTokens->last(), m_tokenizer.get()) || m_pendingTokens->size() >= pendingTokenLimit) {
sendTokensToMainThread();
if (m_input.outstandingCheckpointCount() > outstandingCheckpointLimit)
break;
}
}
}
void BackgroundHTMLParser::sendTokensToMainThread()
{
if (m_pendingTokens->isEmpty())
return;
#ifndef NDEBUG
checkThatTokensAreSafeToSendToAnotherThread(m_pendingTokens.get());
checkThatPreloadsAreSafeToSendToAnotherThread(m_pendingPreloads);
#endif
OwnPtr<HTMLDocumentParser::ParsedChunk> chunk = adoptPtr(new HTMLDocumentParser::ParsedChunk);
chunk->tokens = m_pendingTokens.release();
chunk->preloads.swap(m_pendingPreloads);
chunk->xssInfos.swap(m_pendingXSSInfos);
chunk->tokenizerState = m_tokenizer->state();
chunk->treeBuilderState = m_treeBuilderSimulator.state();
chunk->inputCheckpoint = m_input.createCheckpoint();
chunk->preloadScannerCheckpoint = m_preloadScanner->createCheckpoint();
callOnMainThread(bind(&HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser, m_parser, chunk.release()));
m_pendingTokens = adoptPtr(new CompactHTMLTokenStream);
}
}
#endif // ENABLE(THREADED_HTML_PARSER)