Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / html / parser / HTMLDocumentParser.cpp
index e0dfa89..55a36a6 100644 (file)
 #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 {
@@ -74,6 +75,30 @@ static HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElem
     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)
@@ -267,7 +292,7 @@ bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& ses
 
     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)
@@ -282,7 +307,7 @@ bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& ses
     }
 
     // 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
@@ -320,6 +345,11 @@ void HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(PassOwnPtr<Pa
     pumpPendingSpeculations();
 }
 
+void HTMLDocumentParser::didReceiveEncodingDataFromBackgroundParser(const DocumentEncodingData& data)
+{
+    document()->setEncodingData(data);
+}
+
 void HTMLDocumentParser::validateSpeculations(PassOwnPtr<ParsedChunk> chunk)
 {
     ASSERT(chunk);
@@ -410,8 +440,7 @@ void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Parse
     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.
@@ -446,6 +475,10 @@ void HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Parse
         ASSERT(!m_tokenizer);
         ASSERT(!m_token);
     }
+
+    // Make sure any pending text nodes are emitted before returning.
+    if (!isStopped())
+        m_treeBuilder->flush();
 }
 
 void HTMLDocumentParser::pumpPendingSpeculations()
@@ -464,6 +497,9 @@ 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();
@@ -471,11 +507,8 @@ void HTMLDocumentParser::pumpPendingSpeculations()
     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()) {
@@ -484,7 +517,10 @@ void HTMLDocumentParser::pumpPendingSpeculations()
         }
     }
 
+    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()
@@ -509,6 +545,14 @@ Document* HTMLDocumentParser::contextForParsingSession()
     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());
@@ -526,6 +570,9 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
     // 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);
@@ -557,18 +604,26 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
     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());
 }
 
@@ -618,7 +673,7 @@ void HTMLDocumentParser::insert(const SegmentedString& source)
     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.
@@ -640,7 +695,7 @@ void HTMLDocumentParser::insert(const SegmentedString& source)
         // 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());
@@ -651,25 +706,27 @@ void HTMLDocumentParser::insert(const SegmentedString& source)
 
 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()
@@ -687,19 +744,9 @@ void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
     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.
@@ -965,4 +1012,45 @@ void HTMLDocumentParser::resumeScheduledTasks()
         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()));
+}
+
 }