#include "core/html/parser/HTMLDocumentParser.h"
#include "HTMLNames.h"
+#include "core/css/MediaValuesCached.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/CompactHTMLToken.h"
-#include "core/html/parser/HTMLIdentifier.h"
#include "core/html/parser/HTMLParserScheduler.h"
#include "core/html/parser/HTMLParserThread.h"
-#include "core/html/parser/HTMLPreloadScanner.h"
#include "core/html/parser/HTMLScriptRunner.h"
-#include "core/html/parser/HTMLTokenizer.h"
#include "core/html/parser/HTMLTreeBuilder.h"
#include "core/inspector/InspectorInstrumentation.h"
-#include "core/frame/Frame.h"
+#include "core/inspector/InspectorTraceEvents.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 {
return HTMLTokenizer::DataState;
}
+class ParserDataReceiver : public blink::WebThreadedDataReceiver {
+public:
+ explicit ParserDataReceiver(WeakPtr<BackgroundHTMLParser> backgroundParser)
+ : m_backgroundParser(backgroundParser)
+ {
+ }
+
+ // WebThreadedDataReceiver
+ 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)
if (isWaitingForScripts()) {
if (mode == AllowYield)
- m_parserScheduler->checkForYieldBeforeScript(session);
+ session.didSeeScript = true;
// If we don't run the script, we cannot allow the next token to be taken.
if (session.needsYield)
}
// FIXME: It's wrong for the HTMLDocumentParser to reach back to the
- // Frame, but this approach is how the old parser handled
+ // LocalFrame, but this approach is how the old parser handled
// stopping when the page assigns window.location. What really
// should happen is that assigning window.location causes the
// parser to stop parsing cleanly. The problem is we're not
pumpPendingSpeculations();
}
+void HTMLDocumentParser::didReceiveEncodingDataFromBackgroundParser(const DocumentEncodingData& data)
+{
+ document()->setEncodingData(data);
+}
+
void HTMLDocumentParser::validateSpeculations(PassOwnPtr<ParsedChunk> chunk)
{
ASSERT(chunk);
for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != tokens->end(); ++it) {
ASSERT(!isWaitingForScripts());
- if (!isParsingFragment()
- && document()->frame() && document()->frame()->navigationScheduler().locationChangePending()) {
+ if (document()->frame() && document()->frame()->navigationScheduler().locationChangePending()) {
// To match main-thread parser behavior (which never checks locationChangePending on the EOF path)
// we peek to see if this chunk has an EOF and process it anyway.
ASSERT(!m_tokenizer);
ASSERT(!m_token);
}
+
+ // Make sure any pending text nodes are emitted before returning.
+ if (!isStopped())
+ m_treeBuilder->flush();
}
void HTMLDocumentParser::pumpPendingSpeculations()
ASSERT(!isStopped());
// FIXME: Pass in current input length.
+ TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "beginData", InspectorParseHtmlEvent::beginData(document(), lineNumber().zeroBasedInt()));
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
+ // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), lineNumber().zeroBasedInt());
double startTime = currentTime();
while (!m_speculations.isEmpty()) {
processParsedChunkFromBackgroundParser(m_speculations.takeFirst());
- // The order matters! If this isStopped(), isWaitingForScripts() can hit and ASSERT since
- // m_document can be null which is used to decide the readiness.
- if (isStopped())
- break;
- if (isWaitingForScripts())
+ // Always check isStopped first as m_document may be null.
+ if (isStopped() || isWaitingForScripts())
break;
if (currentTime() - startTime > parserTimeLimit && !m_speculations.isEmpty()) {
}
}
+ TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "endLine", lineNumber().zeroBasedInt());
+ // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
+ TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data());
}
void HTMLDocumentParser::forcePlaintextForTextDocument()
return document();
}
+static PassRefPtr<MediaValues> createMediaValues(Document* document)
+{
+ ASSERT(document);
+ RefPtr<MediaValues> mediaValues = MediaValuesCached::create(*document);
+ ASSERT(mediaValues->isSafeToSendToAnotherThread());
+ return mediaValues;
+}
+
void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
{
ASSERT(!isStopped());
// FIXME: m_input.current().length() is only accurate if we
// end up parsing the whole buffer in this pump. We should pass how
// much we parsed as part of didWriteHTML instead of willWriteHTML.
+ 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());
+ // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().currentLine().zeroBasedInt());
m_xssAuditor.init(document(), &m_xssAuditorDelegate);
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();
if (isWaitingForScripts()) {
ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
if (!m_preloadScanner) {
- m_preloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), document()->devicePixelRatio()));
+ 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());
}
+ TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ParseHTML", "endLine", m_input.current().currentLine().zeroBasedInt());
+ // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
InspectorInstrumentation::didWriteHTML(cookie, m_input.current().currentLine().zeroBasedInt());
}
if (isStopped())
return;
- TRACE_EVENT0("webkit", "HTMLDocumentParser::insert");
+ TRACE_EVENT1("webkit", "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.
// Check the document.write() output with a separate preload scanner as
// the main scanner can't deal with insertions.
if (!m_insertionPreloadScanner)
- m_insertionPreloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), document()->devicePixelRatio()));
+ m_insertionPreloadScanner = adoptPtr(new HTMLPreloadScanner(m_options, document()->url(), createMediaValues(document())));
m_insertionPreloadScanner->appendToEnd(source);
m_insertionPreloadScanner->scan(m_preloader.get(), document()->baseElementURL());
void HTMLDocumentParser::startBackgroundParser()
{
+ ASSERT(!isStopped());
ASSERT(shouldUseThreading());
ASSERT(!m_haveBackgroundParser);
m_haveBackgroundParser = true;
- HTMLIdentifier::init();
-
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(), document()->devicePixelRatio()));
+ 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::create, reference.release(), config.release()));
+ HTMLParserThread::shared()->postTask(bind(&BackgroundHTMLParser::start, reference.release(), config.release()));
}
void HTMLDocumentParser::stopBackgroundParser()
if (isStopped())
return;
- if (shouldUseThreading()) {
- if (!m_haveBackgroundParser)
- startBackgroundParser();
-
- ASSERT(inputSource->hasOneRef());
- TRACE_EVENT1("net", "HTMLDocumentParser::append", "size", inputSource->length());
- // NOTE: Important that the String temporary is destroyed before we post the task
- // otherwise the String could call deref() on a StringImpl now owned by the background parser.
- // We would like to ASSERT(closure.arg3()->hasOneRef()) but sadly the args are private.
- Closure closure = bind(&BackgroundHTMLParser::append, m_backgroundParser, String(inputSource));
- HTMLParserThread::shared()->postTask(closure);
- 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.
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 we've got no decoder, we never received any data.
+ 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()));
+}
+
}