提交 61a2a00c 编写于 作者: A Adam Barth

Live the dream

This CL plumbs the data pipe from the network stack all the way to the parser
thread. We now drain the data pipe on the parser thread, which means network
bytes don't need to transit the main thread to reach the parser.

R=eseidel@chromium.org

Review URL: https://codereview.chromium.org/664573004
上级 b07dff70
......@@ -3,4 +3,6 @@ include_rules = [
"+core",
"+platform",
"+public/platform",
"+mojo/public",
"+mojo/services/public",
]
......@@ -1000,7 +1000,6 @@ sky_core_files = [
"inspector/ScriptCallStack.cpp",
"inspector/ScriptCallStack.h",
"loader/DocumentLoadTiming.cpp",
"loader/DocumentWriter.cpp",
"loader/EmptyClients.cpp",
"loader/EmptyClients.h",
"loader/FrameFetchContext.cpp",
......
......@@ -33,7 +33,6 @@ namespace blink {
DecodedDataDocumentParser::DecodedDataDocumentParser(Document& document)
: DocumentParser(&document)
, m_decoder(TextResourceDecoder::create())
{
}
......@@ -41,33 +40,4 @@ DecodedDataDocumentParser::~DecodedDataDocumentParser()
{
}
void DecodedDataDocumentParser::appendBytes(const char* data, size_t length)
{
if (!length)
return;
// This should be checking isStopped(), but XMLDocumentParser prematurely
// stops parsing when handling an XSLT processing instruction and still
// needs to receive decoded bytes.
if (isDetached())
return;
String decodedData = m_decoder->decode(data, length);
if (!decodedData.isEmpty())
append(decodedData.releaseImpl());
}
void DecodedDataDocumentParser::flush()
{
// This should be checking isStopped(), but XMLDocumentParser prematurely
// stops parsing when handling an XSLT processing instruction and still
// needs to receive decoded bytes.
if (isDetached())
return;
String decodedData = m_decoder->flush();
if (!decodedData.isEmpty())
append(decodedData.releaseImpl());
}
};
......@@ -33,19 +33,9 @@ namespace blink {
class TextResourceDecoder;
class DecodedDataDocumentParser : public DocumentParser {
public:
virtual void appendBytes(const char* bytes, size_t length) override;
virtual void flush() override;
protected:
explicit DecodedDataDocumentParser(Document&);
virtual ~DecodedDataDocumentParser();
private:
// append is used by DocumentWriter::replaceDocumentWhileExecutingJavaScriptURL.
virtual void append(PassRefPtr<StringImpl>) = 0;
OwnPtr<TextResourceDecoder> m_decoder;
};
}
......
......@@ -1481,7 +1481,7 @@ void Document::cancelParsing()
checkCompleted();
}
PassRefPtrWillBeRawPtr<DocumentParser> Document::startParsing()
void Document::startParsing()
{
ASSERT(!m_parser);
ASSERT(!m_isParsing);
......@@ -1491,7 +1491,6 @@ PassRefPtrWillBeRawPtr<DocumentParser> Document::startParsing()
m_parser = createParser();
setParsing(true);
setReadyState(Loading);
return m_parser;
}
Element* Document::viewportDefiningElement(RenderStyle* rootStyle) const
......
......@@ -328,7 +328,8 @@ public:
DocumentLoadTiming* timing() const;
PassRefPtrWillBeRawPtr<DocumentParser> startParsing();
void startParsing();
void cancelParsing();
// close() is the DOM API document.close()
void close(ExceptionState& = ASSERT_NO_EXCEPTION);
......@@ -349,8 +350,6 @@ public:
};
PageDismissalType pageDismissalEventBeingDispatched() const;
void cancelParsing();
const KURL& url() const { return m_url; }
void setURL(const KURL&);
......
......@@ -24,6 +24,7 @@
#ifndef DocumentParser_h
#define DocumentParser_h
#include "mojo/public/cpp/system/core.h"
#include "platform/heap/Handle.h"
#include "wtf/Forward.h"
#include "wtf/RefCounted.h"
......@@ -42,22 +43,7 @@ public:
virtual HTMLDocumentParser* asHTMLDocumentParser() { return 0; }
// http://www.whatwg.org/specs/web-apps/current-work/#insertion-point
virtual bool hasInsertionPoint() { return true; }
// insert is used by document.write.
virtual void insert(const SegmentedString&) = 0;
// The below functions are used by DocumentWriter (the loader).
virtual void appendBytes(const char* bytes, size_t length) = 0;
// FIXME: append() should be private, but DocumentWriter::replaceDocumentWhileExecutingJavaScriptURL uses it for now.
// FIXME: This really should take a PassOwnPtr to signify that it expects to take
// ownership of the buffer. The parser expects the PassRefPtr to hold the only ref of the StringImpl.
virtual void append(PassRefPtr<StringImpl>) = 0;
virtual void finish() = 0;
virtual void parse(mojo::ScopedDataPipeConsumerHandle) = 0;
virtual bool processingData() const { return false; }
// document() will return 0 after detach() is called.
......@@ -89,8 +75,6 @@ public:
protected:
explicit DocumentParser(Document*);
virtual void flush() = 0;
private:
enum ParserState {
ParsingState,
......
......@@ -32,13 +32,12 @@
#include "core/html/imports/HTMLImportLoader.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentParser.h"
#include "core/dom/StyleEngine.h"
#include "core/dom/custom/CustomElementSyncMicrotaskQueue.h"
#include "core/html/HTMLDocument.h"
#include "core/html/imports/HTMLImportChild.h"
#include "core/html/imports/HTMLImportsController.h"
#include "core/loader/DocumentWriter.h"
namespace blink {
......@@ -71,7 +70,6 @@ void HTMLImportLoader::clear()
m_document.clear();
}
m_fetcher.clear();
m_drainer.clear();
}
#endif
......@@ -86,20 +84,7 @@ void HTMLImportLoader::OnReceivedResponse(mojo::URLResponsePtr response)
setState(StateError);
return;
}
mojo::ScopedDataPipeConsumerHandle body = response->body.Pass();
setState(startWritingAndParsing(response.Pass()));
m_drainer = adoptPtr(new DataPipeDrainer(this, body.Pass()));
}
void HTMLImportLoader::OnDataAvailable(const void* data, size_t length)
{
RefPtrWillBeRawPtr<DocumentWriter> protectingWriter(m_writer.get());
m_writer->addData(static_cast<const char*>(data), length);
}
void HTMLImportLoader::OnDataComplete()
{
setState(finishWriting());
}
HTMLImportLoader::State HTMLImportLoader::startWritingAndParsing(mojo::URLResponsePtr response)
......@@ -109,8 +94,8 @@ HTMLImportLoader::State HTMLImportLoader::startWritingAndParsing(mojo::URLRespon
DocumentInit init = DocumentInit(url, 0, m_controller->master()->contextDocument(), m_controller)
.withRegistrationContext(m_controller->master()->registrationContext());
m_document = HTMLDocument::create(init);
m_writer = DocumentWriter::create(m_document.get());
m_document->startParsing();
m_document->parser()->parse(response->body.Pass());
return StateLoading;
}
......@@ -136,10 +121,8 @@ void HTMLImportLoader::setState(State state)
m_state = state;
if (m_state == StateParsed || m_state == StateError || m_state == StateWritten) {
if (RefPtrWillBeRawPtr<DocumentWriter> writer = m_writer.release())
writer->end();
}
if (m_state == StateParsed || m_state == StateError || m_state == StateWritten)
m_document->cancelParsing();
// Since DocumentWriter::end() can let setState() reenter, we shouldn't refer to m_state here.
if (state == StateLoaded || state == StateError)
......@@ -209,7 +192,6 @@ void HTMLImportLoader::trace(Visitor* visitor)
visitor->trace(m_imports);
#endif
visitor->trace(m_document);
visitor->trace(m_writer);
visitor->trace(m_microtaskQueue);
}
......
......@@ -42,20 +42,11 @@ namespace blink {
class CustomElementSyncMicrotaskQueue;
class Document;
class DocumentWriter;
class HTMLImportChild;
class HTMLImportsController;
//
// Owning imported Document lifetime. It also implements ResourceClient through ResourceOwner
// to feed fetched bytes to the DocumentWriter of the imported document.
// HTMLImportLoader is owned by HTMLImportsController.
//
//
class HTMLImportLoader final : public NoBaseWillBeGarbageCollectedFinalized<HTMLImportLoader>,
public MojoFetcher::Client,
public DataPipeDrainer::Client {
public MojoFetcher::Client {
public:
enum State {
StateLoading,
......@@ -108,10 +99,6 @@ private:
// MojoFetcher::Client
void OnReceivedResponse(mojo::URLResponsePtr) override;
// DataPipeDrainer::Client
void OnDataAvailable(const void* data, size_t num_bytes) override;
void OnDataComplete() override;
State startWritingAndParsing(mojo::URLResponsePtr);
State finishWriting();
State finishParsing();
......@@ -127,11 +114,9 @@ private:
WillBeHeapVector<RawPtrWillBeMember<HTMLImportChild> > m_imports;
State m_state;
RefPtrWillBeMember<Document> m_document;
RefPtrWillBeMember<DocumentWriter> m_writer;
RefPtrWillBeMember<CustomElementSyncMicrotaskQueue> m_microtaskQueue;
OwnPtr<MojoFetcher> m_fetcher;
OwnPtr<DataPipeDrainer> m_drainer;
};
} // namespace blink
......
......@@ -62,6 +62,7 @@ BackgroundHTMLParser::BackgroundHTMLParser(PassOwnPtr<Configuration> config)
, m_parser(config->parser)
, m_pendingTokens(adoptPtr(new CompactHTMLTokenStream))
, m_decoder(TextResourceDecoder::create())
, m_source(config->source.Pass())
, m_weakFactory(this)
{
}
......@@ -70,42 +71,37 @@ BackgroundHTMLParser::~BackgroundHTMLParser()
{
}
void BackgroundHTMLParser::appendRawBytesFromMainThread(PassOwnPtr<Vector<char> > buffer)
void BackgroundHTMLParser::start()
{
updateDocument(m_decoder->decode(buffer->data(), buffer->size()));
m_drainer = adoptPtr(new DataPipeDrainer(this, m_source.Pass()));
}
void BackgroundHTMLParser::appendDecodedBytes(const String& input)
void BackgroundHTMLParser::stop()
{
ASSERT(!m_input.isClosed());
m_input.append(SegmentedString(input));
pumpTokenizer();
delete this;
}
void BackgroundHTMLParser::flush()
void BackgroundHTMLParser::OnDataAvailable(const void* data, size_t numberOfBytes)
{
updateDocument(m_decoder->flush());
ASSERT(!m_input.isClosed());
String input = m_decoder->decode(static_cast<const char*>(data), numberOfBytes);
m_input.append(SegmentedString(input));
pumpTokenizer();
}
void BackgroundHTMLParser::updateDocument(const String& decodedData)
void BackgroundHTMLParser::OnDataComplete()
{
if (decodedData.isEmpty())
return;
appendDecodedBytes(decodedData);
ASSERT(!m_input.isClosed());
finish();
}
void BackgroundHTMLParser::finish()
{
m_input.append(SegmentedString(m_decoder->flush()));
markEndOfFile();
pumpTokenizer();
}
void BackgroundHTMLParser::stop()
{
delete this;
}
void BackgroundHTMLParser::markEndOfFile()
{
ASSERT(!m_input.isClosed());
......
......@@ -31,6 +31,8 @@
#include "core/html/parser/HTMLParserOptions.h"
#include "core/html/parser/HTMLTokenizer.h"
#include "core/html/parser/TextResourceDecoder.h"
#include "mojo/public/cpp/system/core.h"
#include "platform/fetcher/DataPipeDrainer.h"
#include "platform/text/SegmentedString.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/WeakPtr.h"
......@@ -40,30 +42,31 @@ namespace blink {
class HTMLDocumentParser;
class SharedBuffer;
class BackgroundHTMLParser {
class BackgroundHTMLParser : public DataPipeDrainer::Client {
WTF_MAKE_FAST_ALLOCATED;
public:
struct Configuration {
HTMLParserOptions options;
mojo::ScopedDataPipeConsumerHandle source;
WeakPtr<HTMLDocumentParser> parser;
};
static base::WeakPtr<BackgroundHTMLParser> create(PassOwnPtr<Configuration>);
void appendRawBytesFromMainThread(PassOwnPtr<Vector<char> >);
void flush();
void finish();
void start();
void stop();
private:
explicit BackgroundHTMLParser(PassOwnPtr<Configuration>);
~BackgroundHTMLParser();
void appendDecodedBytes(const String&);
void OnDataAvailable(const void* data, size_t numberOfBytes) override;
void OnDataComplete() override;
void finish();
void markEndOfFile();
void pumpTokenizer();
void sendTokensToMainThread();
void updateDocument(const String& decodedData);
bool updateTokenizerState(const CompactHTMLToken& token);
SegmentedString m_input;
......@@ -74,6 +77,9 @@ private:
OwnPtr<CompactHTMLTokenStream> m_pendingTokens;
OwnPtr<TextResourceDecoder> m_decoder;
mojo::ScopedDataPipeConsumerHandle m_source;
OwnPtr<DataPipeDrainer> m_drainer;
base::WeakPtrFactory<BackgroundHTMLParser> m_weakFactory;
};
......
......@@ -48,8 +48,6 @@ namespace blink {
HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors)
: DecodedDataDocumentParser(document)
, m_options(&document)
, m_token(m_options.useThreading ? nullptr : adoptPtr(new HTMLToken))
, m_tokenizer(m_options.useThreading ? nullptr : HTMLTokenizer::create(m_options))
, m_treeBuilder(HTMLTreeBuilder::create(this, &document, reportErrors, m_options))
, m_parserScheduler(HTMLParserScheduler::create(this))
, m_weakFactory(this)
......@@ -58,7 +56,7 @@ HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors
, m_haveBackgroundParser(false)
, m_pumpSessionNestingLevel(0)
{
ASSERT(shouldUseThreading() || (m_token && m_tokenizer));
ASSERT(shouldUseThreading());
}
HTMLDocumentParser::~HTMLDocumentParser()
......@@ -84,6 +82,23 @@ void HTMLDocumentParser::trace(Visitor* visitor)
DecodedDataDocumentParser::trace(visitor);
}
void HTMLDocumentParser::parse(mojo::ScopedDataPipeConsumerHandle source)
{
ASSERT(!isStopped());
ASSERT(shouldUseThreading());
ASSERT(!m_haveBackgroundParser);
m_haveBackgroundParser = true;
OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new BackgroundHTMLParser::Configuration);
config->options = m_options;
config->source = source.Pass();
config->parser = m_weakFactory.createWeakPtr();
m_backgroundParser = BackgroundHTMLParser::create(config.release());
HTMLParserThread::taskRunner()->PostTask(FROM_HERE,
base::Bind(&BackgroundHTMLParser::start, m_backgroundParser));
}
void HTMLDocumentParser::detach()
{
if (m_haveBackgroundParser)
......@@ -115,13 +130,6 @@ void HTMLDocumentParser::prepareToStopParsing()
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
// NOTE: This pump should only ever emit buffered character tokens,
// so ForceSynchronous vs. AllowYield should be meaningless.
if (m_tokenizer) {
ASSERT(!m_haveBackgroundParser);
pumpTokenizerIfPossible(ForceSynchronous);
}
if (isStopped())
return;
......@@ -151,22 +159,6 @@ bool HTMLDocumentParser::processingData() const
return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser;
}
void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
{
if (isStopped())
return;
if (isWaitingForScripts())
return;
// Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
if (isScheduledForResume()) {
ASSERT(mode == AllowYield);
return;
}
pumpTokenizer(mode);
}
bool HTMLDocumentParser::isScheduledForResume() const
{
return m_parserScheduler && m_parserScheduler->isScheduledForResume();
......@@ -179,15 +171,8 @@ void HTMLDocumentParser::resumeParsingAfterYield()
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
if (m_haveBackgroundParser) {
pumpPendingSpeculations();
return;
}
// We should never be here unless we can pump immediately. Call pumpTokenizer()
// directly so that ASSERTS will fire if we're wrong.
pumpTokenizer(AllowYield);
endIfDelayed();
ASSERT(m_haveBackgroundParser);
pumpPendingSpeculations();
}
void HTMLDocumentParser::runScriptsForPausedTreeBuilder()
......@@ -199,35 +184,6 @@ void HTMLDocumentParser::runScriptsForPausedTreeBuilder()
m_scriptRunner.runScript(toHTMLScriptElement(scriptToProcess.get()), scriptStartPosition);
}
bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session)
{
if (isStopped())
return false;
ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
if (isWaitingForScripts()) {
if (mode == AllowYield)
session.didSeeScript = true;
// If we don't run the script, we cannot allow the next token to be taken.
if (session.needsYield)
return false;
// If we're paused waiting for a script, we try to execute scripts before continuing.
runScriptsForPausedTreeBuilder();
if (isStopped())
return false;
if (isWaitingForScripts())
return false;
}
if (mode == AllowYield)
m_parserScheduler->checkForYieldBeforeToken(session);
return true;
}
void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk> chunk)
{
TRACE_EVENT0("blink", "HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser");
......@@ -276,8 +232,6 @@ void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Parse
ASSERT(refCount() >= 2);
#endif
ASSERT(shouldUseThreading());
ASSERT(!m_tokenizer);
ASSERT(!m_token);
ASSERT(!m_lastChunkBeforeScript);
ActiveParserSession session(contextForParsingSession());
......@@ -308,9 +262,6 @@ void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Parse
prepareToStopParsing();
break;
}
ASSERT(!m_tokenizer);
ASSERT(!m_token);
}
// Make sure any pending text nodes are emitted before returning.
......@@ -327,10 +278,6 @@ void HTMLDocumentParser::pumpPendingSpeculations()
// ASSERT that this object is both attached to the Document and protected.
ASSERT(refCount() >= 2);
#endif
// If this assert fails, you need to call validateSpeculations to make sure
// m_tokenizer and m_token don't have state that invalidates m_speculations.
ASSERT(!m_tokenizer);
ASSERT(!m_token);
ASSERT(!m_lastChunkBeforeScript);
ASSERT(!isWaitingForScripts());
ASSERT(!isStopped());
......@@ -367,52 +314,6 @@ Document* HTMLDocumentParser::contextForParsingSession()
return document();
}
void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
{
ASSERT(!isStopped());
ASSERT(!isScheduledForResume());
#if !ENABLE(OILPAN)
// ASSERT that this object is both attached to the Document and protected.
ASSERT(refCount() >= 2);
#endif
ASSERT(m_tokenizer);
ASSERT(m_token);
ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous);
PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession());
TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "beginData", InspectorParseHtmlEvent::beginData(document(), m_input.current().currentLine().zeroBasedInt()));
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
while (canTakeNextToken(mode, session) && !session.needsYield) {
if (!m_tokenizer->nextToken(m_input.current(), token()))
break;
constructTreeFromHTMLToken(token());
ASSERT(token().isUninitialized());
}
#if !ENABLE(OILPAN)
// Ensure we haven't been totally deref'ed after pumping. Any caller of this
// function should be holding a RefPtr to this to ensure we weren't deleted.
ASSERT(refCount() >= 1);
#endif
if (isStopped())
return;
// There should only be PendingText left since the tree-builder always flushes
// the task queue before returning. In case that ever changes, crash.
if (mode == ForceSynchronous)
m_treeBuilder->flush();
RELEASE_ASSERT(!isStopped());
if (session.needsYield)
m_parserScheduler->scheduleForResume();
TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "endLine", m_input.current().currentLine().zeroBasedInt());
}
void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken)
{
AtomicHTMLToken token(rawToken);
......@@ -446,47 +347,11 @@ void HTMLDocumentParser::constructTreeFromCompactHTMLToken(const CompactHTMLToke
bool HTMLDocumentParser::hasInsertionPoint()
{
return m_input.hasInsertionPoint();
}
void HTMLDocumentParser::insert(const SegmentedString& source)
{
if (isStopped())
return;
TRACE_EVENT1("blink", "HTMLDocumentParser::insert", "source_length", source.length());
// pumpTokenizer can cause this parser to be detached from the Document,
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
if (!m_tokenizer) {
ASSERT(!inPumpSession());
ASSERT(m_haveBackgroundParser);
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
SegmentedString excludedLineNumberSource(source);
excludedLineNumberSource.setExcludeLineNumbers();
m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
pumpTokenizerIfPossible(ForceSynchronous);
endIfDelayed();
return false;
}
void HTMLDocumentParser::startBackgroundParser()
{
ASSERT(!isStopped());
ASSERT(shouldUseThreading());
ASSERT(!m_haveBackgroundParser);
m_haveBackgroundParser = true;
OwnPtr<BackgroundHTMLParser::Configuration> config = adoptPtr(new BackgroundHTMLParser::Configuration);
config->options = m_options;
config->parser = m_weakFactory.createWeakPtr();
m_backgroundParser = BackgroundHTMLParser::create(config.release());
}
void HTMLDocumentParser::stopBackgroundParser()
......@@ -500,35 +365,6 @@ void HTMLDocumentParser::stopBackgroundParser()
m_weakFactory.revokeAll();
}
void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
{
if (isStopped())
return;
// We should never reach this point if we're using a parser thread,
// as appendBytes() will directly ship the data to the thread.
ASSERT(!shouldUseThreading());
// pumpTokenizer can cause this parser to be detached from the Document,
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->length());
String source(inputSource);
m_input.appendToEnd(source);
if (inPumpSession()) {
// We've gotten data off the network in a nested write.
// We don't want to consume any more of the input stream now. Do
// not worry. We'll consume this data in a less-nested write().
return;
}
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
}
void HTMLDocumentParser::end()
{
ASSERT(!isDetached());
......@@ -543,9 +379,6 @@ void HTMLDocumentParser::end()
void HTMLDocumentParser::attemptToEnd()
{
// finish() indicates we will not receive any more data. If we are waiting on
// an external script to load, we can't finish parsing quite yet.
if (shouldDelayEnd()) {
m_endWasDelayed = true;
return;
......@@ -566,47 +399,6 @@ void HTMLDocumentParser::endIfDelayed()
prepareToStopParsing();
}
void HTMLDocumentParser::finish()
{
// FIXME: We should ASSERT(!m_parserStopped) here, since it does not
// makes sense to call any methods on DocumentParser once it's been stopped.
// However, FrameLoader::stop calls DocumentParser::finish unconditionally.
// flush may ending up executing arbitrary script, and possibly detach the parser.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
flush();
if (isDetached())
return;
// Empty documents never got an append() call, and thus have never started
// a background parser. In those cases, we ignore shouldUseThreading()
// and fall through to the non-threading case.
if (m_haveBackgroundParser) {
if (!m_input.haveSeenEndOfFile())
m_input.closeWithoutMarkingEndOfFile();
HTMLParserThread::taskRunner()->PostTask(FROM_HERE,
base::Bind(&BackgroundHTMLParser::finish, m_backgroundParser));
return;
}
if (!m_tokenizer) {
ASSERT(!m_token);
// We're finishing before receiving any data. Rather than booting up
// the background parser just to spin it down, we finish parsing
// synchronously.
m_token = adoptPtr(new HTMLToken);
m_tokenizer = HTMLTokenizer::create(m_options);
}
// We're not going to get any more data off the network, so we tell the
// input stream we've reached the end of file. finish() can be called more
// than once, if the first time does not call end().
if (!m_input.haveSeenEndOfFile())
m_input.markEndOfFile();
attemptToEnd();
}
bool HTMLDocumentParser::isExecutingScript() const
{
return m_scriptRunner.isExecutingScript();
......@@ -614,22 +406,14 @@ bool HTMLDocumentParser::isExecutingScript() const
OrdinalNumber HTMLDocumentParser::lineNumber() const
{
if (m_haveBackgroundParser)
return m_textPosition.m_line;
return m_input.current().currentLine();
ASSERT(m_haveBackgroundParser);
return m_textPosition.m_line;
}
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);
ASSERT(m_haveBackgroundParser);
return m_textPosition;
}
bool HTMLDocumentParser::isWaitingForScripts() const
......@@ -641,19 +425,14 @@ void HTMLDocumentParser::resumeParsingAfterScriptExecution()
{
ASSERT(!isExecutingScript());
ASSERT(!isWaitingForScripts());
ASSERT(m_haveBackgroundParser);
if (m_haveBackgroundParser) {
validateSpeculations(m_lastChunkBeforeScript.release());
ASSERT(!m_lastChunkBeforeScript);
// processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
pumpPendingSpeculations();
return;
}
pumpTokenizerIfPossible(AllowYield);
endIfDelayed();
validateSpeculations(m_lastChunkBeforeScript.release());
ASSERT(!m_lastChunkBeforeScript);
// processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
// but we need to ensure it isn't deleted yet.
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this);
pumpPendingSpeculations();
}
void HTMLDocumentParser::executeScriptsWaitingForResources()
......@@ -666,39 +445,4 @@ void HTMLDocumentParser::executeScriptsWaitingForResources()
resumeParsingAfterScriptExecution();
}
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::taskRunner()->PostTask(FROM_HERE,
base::Bind(&BackgroundHTMLParser::appendRawBytesFromMainThread, m_backgroundParser, buffer.release()));
return;
}
DecodedDataDocumentParser::appendBytes(data, length);
}
void HTMLDocumentParser::flush()
{
// If we've got no decoder, we never received any data.
if (isDetached())
return;
if (m_haveBackgroundParser) {
HTMLParserThread::taskRunner()->PostTask(FROM_HERE,
base::Bind(&BackgroundHTMLParser::flush, m_backgroundParser));
} else {
DecodedDataDocumentParser::flush();
}
}
}
......@@ -66,13 +66,14 @@ public:
return adoptRefWillBeNoop(new HTMLDocumentParser(document, reportErrors));
}
virtual ~HTMLDocumentParser();
void parse(mojo::ScopedDataPipeConsumerHandle) override;
virtual void trace(Visitor*) override;
// Exposed for HTMLParserScheduler
void resumeParsingAfterYield();
HTMLTokenizer* tokenizer() const { return m_tokenizer.get(); }
TextPosition textPosition() const;
OrdinalNumber lineNumber() const;
......@@ -81,34 +82,27 @@ public:
};
void didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk>);
virtual void appendBytes(const char* bytes, size_t length) override;
virtual void flush() override final;
bool isWaitingForScripts() const;
bool isExecutingScript() const;
void executeScriptsWaitingForResources();
UseCounter* useCounter() { return UseCounter::getFrom(contextForParsingSession()); }
protected:
virtual void insert(const SegmentedString&) override final;
virtual void append(PassRefPtr<StringImpl>) override;
virtual void finish() override final;
private:
HTMLDocumentParser(HTMLDocument&, bool reportErrors);
HTMLTreeBuilder* treeBuilder() const { return m_treeBuilder.get(); }
private:
virtual HTMLDocumentParser* asHTMLDocumentParser() override final { return this; }
// DocumentParser
virtual void detach() override final;
virtual bool hasInsertionPoint() override final;
virtual bool processingData() const override final;
virtual void prepareToStopParsing() override final;
virtual void stopParsing() override final;
bool hasInsertionPoint();
void startBackgroundParser();
void stopBackgroundParser();
void validateSpeculations(PassOwnPtr<ParsedChunk> lastChunk);
......@@ -117,13 +111,6 @@ private:
Document* contextForParsingSession();
enum SynchronousMode {
AllowYield,
ForceSynchronous,
};
bool canTakeNextToken(SynchronousMode, PumpSession&);
void pumpTokenizer(SynchronousMode);
void pumpTokenizerIfPossible(SynchronousMode);
void constructTreeFromHTMLToken(HTMLToken&);
void constructTreeFromCompactHTMLToken(const CompactHTMLToken&);
......@@ -141,21 +128,14 @@ private:
bool inPumpSession() const { return m_pumpSessionNestingLevel > 0; }
bool shouldDelayEnd() const { return inPumpSession() || isWaitingForScripts() || isScheduledForResume() || isExecutingScript(); }
HTMLToken& token() { return *m_token; }
HTMLParserOptions m_options;
HTMLInputStream m_input;
OwnPtr<HTMLToken> m_token;
OwnPtr<HTMLTokenizer> m_tokenizer;
OwnPtrWillBeMember<HTMLTreeBuilder> m_treeBuilder;
OwnPtr<HTMLParserScheduler> m_parserScheduler;
TextPosition m_textPosition;
HTMLScriptRunner m_scriptRunner;
// FIXME: m_lastChunkBeforeScript, m_tokenizer, m_token, and m_input should be combined into a single state object
// so they can be set and cleared together and passed between threads together.
OwnPtr<ParsedChunk> m_lastChunkBeforeScript;
Deque<OwnPtr<ParsedChunk> > m_speculations;
WeakPtrFactory<HTMLDocumentParser> m_weakFactory;
......
......@@ -35,16 +35,7 @@ namespace blink {
HTMLParserOptions::HTMLParserOptions(Document* document)
{
// We force the main-thread parser for two cases:
// - about:blank and javascript (which uses about:blank) for compatibility
// with historical synchronous loading/parsing behavior.
// - instances where the Document has no Frame (this happens sometimes for
// HTML imports, and possibly other cases).
// FIXME: We want to use the threaded parser for XHRs (where there is no
// frame) so the second case should go away eventually.
// FIXME: Gecko does not load javascript: urls synchronously, why do we?
// See tests/loader/iframe-sync-loads.html
useThreading = document && document->frame() && !document->url().isAboutBlankURL();
useThreading = document && !document->url().isAboutBlankURL();
}
}
......@@ -212,13 +212,6 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken* token)
m_scriptToProcess = m_tree.currentElement();
m_tree.openElements()->pop();
setInsertionMode(m_originalInsertionMode);
if (m_parser->tokenizer()) {
// We must set the tokenizer's state to
// DataState explicitly if the tokenizer didn't have a chance to.
ASSERT(m_parser->tokenizer()->state() == HTMLTokenizer::DataState || m_options.useThreading);
m_parser->tokenizer()->setState(HTMLTokenizer::DataState);
}
return;
}
m_tree.openElements()->pop();
......@@ -241,8 +234,6 @@ void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken* token)
{
ASSERT(token->type() == HTMLToken::StartTag);
m_tree.insertHTMLElement(token);
if (m_parser->tokenizer())
m_parser->tokenizer()->setState(HTMLTokenizer::RAWTEXTState);
m_originalInsertionMode = m_insertionMode;
setInsertionMode(TextMode);
}
......@@ -251,8 +242,6 @@ void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken* token)
{
ASSERT(token->type() == HTMLToken::StartTag);
m_tree.insertScriptElement(token);
if (m_parser->tokenizer())
m_parser->tokenizer()->setState(HTMLTokenizer::ScriptDataState);
m_originalInsertionMode = m_insertionMode;
TextPosition position = m_parser->textPosition();
m_scriptToProcessStartPosition = position;
......
/*
* Copyright (C) 2010. Adam Barth. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "core/loader/DocumentWriter.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentParser.h"
#include "core/frame/LocalFrame.h"
namespace blink {
PassRefPtr<DocumentWriter> DocumentWriter::create(Document* document)
{
return adoptRef(new DocumentWriter(document));
}
DocumentWriter::DocumentWriter(Document* document)
: m_document(document)
{
m_document->startParsing();
}
DocumentWriter::~DocumentWriter()
{
}
void DocumentWriter::addData(const char* bytes, size_t length)
{
m_document->parser()->appendBytes(bytes, length);
}
void DocumentWriter::end()
{
m_document->parser()->finish();
}
} // namespace blink
/*
* Copyright (C) 2010. Adam Barth. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Adam Barth. ("Adam Barth") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DocumentWriter_h
#define DocumentWriter_h
#include "wtf/RefCounted.h"
#include "wtf/PassRefPtr.h"
namespace blink {
class Document;
class DocumentWriter : public RefCounted<DocumentWriter> {
WTF_MAKE_NONCOPYABLE(DocumentWriter);
public:
static PassRefPtr<DocumentWriter> create(Document*);
~DocumentWriter();
void end();
void addData(const char* bytes, size_t length);
private:
explicit DocumentWriter(Document*);
Document* m_document;
};
} // namespace blink
#endif // DocumentWriter_h
......@@ -5,7 +5,6 @@
#include "config.h"
#include "core/loader/MojoLoader.h"
#include "base/bind.h"
#include "core/dom/Document.h"
#include "core/dom/DocumentInit.h"
#include "core/frame/LocalDOMWindow.h"
......@@ -43,18 +42,7 @@ void MojoLoader::load(const KURL& url, ScopedDataPipeConsumerHandle responseStre
// response headers and set them on Document::contentLanguage.
document->startParsing();
m_drainJob = adoptPtr(new DataPipeDrainer(this, responseStream.Pass()));
}
void MojoLoader::OnDataAvailable(const void* data, size_t numberOfBytes)
{
m_frame.document()->parser()->appendBytes(static_cast<const char*>(data), numberOfBytes);
}
void MojoLoader::OnDataComplete()
{
m_frame.document()->parser()->finish();
document->parser()->parse(responseStream.Pass());
}
}
......@@ -5,17 +5,14 @@
#ifndef MojoLoader_h
#define MojoLoader_h
#include "base/memory/weak_ptr.h"
#include "mojo/common/handle_watcher.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "platform/fetcher/DataPipeDrainer.h"
#include "platform/weborigin/KURL.h"
namespace blink {
class LocalFrame;
class MojoLoader : public DataPipeDrainer::Client {
class MojoLoader {
public:
explicit MojoLoader(LocalFrame&);
......@@ -23,12 +20,6 @@ public:
private:
LocalFrame& m_frame;
// From DataPipeDrainer::Client
void OnDataAvailable(const void* data, size_t numberOfBytes) override;
void OnDataComplete() override;
OwnPtr<DataPipeDrainer> m_drainJob;
};
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册