Title: [144714] trunk
Revision
144714
Author
[email protected]
Date
2013-03-04 20:57:15 -0800 (Mon, 04 Mar 2013)

Log Message

Background HTML parser can rewind the tokenizer after end-of-file
https://bugs.webkit.org/show_bug.cgi?id=111365

Reviewed by Eric Seidel.

Source/WebCore:

Prior to this patch, it was possible to call didFailSpeculation after
processing the end-of-file token because checkForSpeculationFailure
didn't zero out m_tokenizer in some control paths.

This patch renames checkForSpeculationFailure to validateSpeculations
and ensures that it always takes ownership of the main thread's
HTMLTokenizer.

This patch also adds a number of ASSERTs to make sure the parser state
machine stays in the correct configuration (e.g., that we don't have a
main thread tokenizer while we're supposed to be tokenizing on the
background thread).

Test: fast/parser/document-write-fighting-eof.html

* html/parser/BackgroundHTMLInputStream.cpp:
(WebCore::BackgroundHTMLInputStream::rewindTo):
* html/parser/BackgroundHTMLParser.cpp:
(WebCore::BackgroundHTMLParser::append):
* html/parser/HTMLDocumentParser.cpp:
(WebCore::HTMLDocumentParser::validateSpeculations):
(WebCore::HTMLDocumentParser::processParsedChunkFromBackgroundParser):
(WebCore::HTMLDocumentParser::pumpPendingSpeculations):
(WebCore::HTMLDocumentParser::resumeParsingAfterScriptExecution):
* html/parser/HTMLDocumentParser.h:
(HTMLDocumentParser):

LayoutTests:

* fast/parser/document-write-fighting-eof.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (144713 => 144714)


--- trunk/LayoutTests/ChangeLog	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/LayoutTests/ChangeLog	2013-03-05 04:57:15 UTC (rev 144714)
@@ -1,3 +1,12 @@
+2013-03-04  Adam Barth  <[email protected]>
+
+        Background HTML parser can rewind the tokenizer after end-of-file
+        https://bugs.webkit.org/show_bug.cgi?id=111365
+
+        Reviewed by Eric Seidel.
+
+        * fast/parser/document-write-fighting-eof.html: Added.
+
 2013-03-04  Tim 'mithro' Ansell  <[email protected]>
 
         Make sure that clearOwnerNode also clears StyleResolver references (via didMutate).

Added: trunk/LayoutTests/fast/parser/document-write-fighting-eof-expected.txt (0 => 144714)


--- trunk/LayoutTests/fast/parser/document-write-fighting-eof-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/parser/document-write-fighting-eof-expected.txt	2013-03-05 04:57:15 UTC (rev 144714)
@@ -0,0 +1 @@
+PASS

Added: trunk/LayoutTests/fast/parser/document-write-fighting-eof.html (0 => 144714)


--- trunk/LayoutTests/fast/parser/document-write-fighting-eof.html	                        (rev 0)
+++ trunk/LayoutTests/fast/parser/document-write-fighting-eof.html	2013-03-05 04:57:15 UTC (rev 144714)
@@ -0,0 +1,6 @@
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+</script>
+PA<script _onbeforeload_="document.write('<span')" src=""
+>SS

Modified: trunk/Source/WebCore/ChangeLog (144713 => 144714)


--- trunk/Source/WebCore/ChangeLog	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/Source/WebCore/ChangeLog	2013-03-05 04:57:15 UTC (rev 144714)
@@ -1,3 +1,37 @@
+2013-03-04  Adam Barth  <[email protected]>
+
+        Background HTML parser can rewind the tokenizer after end-of-file
+        https://bugs.webkit.org/show_bug.cgi?id=111365
+
+        Reviewed by Eric Seidel.
+
+        Prior to this patch, it was possible to call didFailSpeculation after
+        processing the end-of-file token because checkForSpeculationFailure
+        didn't zero out m_tokenizer in some control paths.
+
+        This patch renames checkForSpeculationFailure to validateSpeculations
+        and ensures that it always takes ownership of the main thread's
+        HTMLTokenizer.
+
+        This patch also adds a number of ASSERTs to make sure the parser state
+        machine stays in the correct configuration (e.g., that we don't have a
+        main thread tokenizer while we're supposed to be tokenizing on the
+        background thread).
+
+        Test: fast/parser/document-write-fighting-eof.html
+
+        * html/parser/BackgroundHTMLInputStream.cpp:
+        (WebCore::BackgroundHTMLInputStream::rewindTo):
+        * html/parser/BackgroundHTMLParser.cpp:
+        (WebCore::BackgroundHTMLParser::append):
+        * html/parser/HTMLDocumentParser.cpp:
+        (WebCore::HTMLDocumentParser::validateSpeculations):
+        (WebCore::HTMLDocumentParser::processParsedChunkFromBackgroundParser):
+        (WebCore::HTMLDocumentParser::pumpPendingSpeculations):
+        (WebCore::HTMLDocumentParser::resumeParsingAfterScriptExecution):
+        * html/parser/HTMLDocumentParser.h:
+        (HTMLDocumentParser):
+
 2013-03-04  Tim 'mithro' Ansell  <[email protected]>
 
         Make sure that clearOwnerNode also clears StyleResolver references (via didMutate).

Modified: trunk/Source/WebCore/html/parser/BackgroundHTMLInputStream.cpp (144713 => 144714)


--- trunk/Source/WebCore/html/parser/BackgroundHTMLInputStream.cpp	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/Source/WebCore/html/parser/BackgroundHTMLInputStream.cpp	2013-03-05 04:57:15 UTC (rev 144714)
@@ -71,6 +71,8 @@
     if (isClosed && !m_current.isClosed())
         m_current.close();
 
+    ASSERT(m_current.isClosed() == isClosed);
+
     m_segments.clear();
     m_checkpoints.clear();
 }

Modified: trunk/Source/WebCore/html/parser/BackgroundHTMLParser.cpp (144713 => 144714)


--- trunk/Source/WebCore/html/parser/BackgroundHTMLParser.cpp	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/Source/WebCore/html/parser/BackgroundHTMLParser.cpp	2013-03-05 04:57:15 UTC (rev 144714)
@@ -146,6 +146,7 @@
 
 void BackgroundHTMLParser::append(const String& input)
 {
+    ASSERT(!m_input.current().isClosed());
     m_input.append(input);
     pumpTokenizer();
 }

Modified: trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp (144713 => 144714)


--- trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp	2013-03-05 04:57:15 UTC (rev 144714)
@@ -328,24 +328,38 @@
     InspectorInstrumentation::didWriteHTML(cookie, lineNumber().zeroBasedInt());
 }
 
-void HTMLDocumentParser::checkForSpeculationFailure()
+void HTMLDocumentParser::validateSpeculations()
 {
-    if (!m_tokenizer)
+    OwnPtr<HTMLTokenizer> tokenizer = m_tokenizer.release();
+    OwnPtr<HTMLToken> token = m_token.release();
+
+    if (!tokenizer) {
+        // There must not have been any changes to the HTMLTokenizer state on
+        // the main thread, which means the speculation buffer is correct.
         return;
-    if (!m_currentChunk)
+    }
+
+    if (!m_currentChunk) {
+        // If there is no m_currentChunk, we must have already called didFailSpeculation
+        // for this chunk.
+        // FIXME: In this case, we're losing whatever state has been changed since
+        // we called didFailSpeculation. See https://bugs.webkit.org/show_bug.cgi?id=110546
         return;
+    }
+
     // Currently we're only smart enough to reuse the speculation buffer if the tokenizer
     // both starts and ends in the DataState. That state is simplest because the HTMLToken
     // is always in the Uninitialized state. We should consider whether we can reuse the
     // speculation buffer in other states, but we'd likely need to do something more
     // sophisticated with the HTMLToken.
-    if (m_currentChunk->tokenizerState == HTMLTokenizer::DataState && m_tokenizer->state() == HTMLTokenizer::DataState && m_input.current().isEmpty()) {
-        ASSERT(m_token->isUninitialized());
-        m_tokenizer.clear();
-        m_token.clear();
+    if (m_currentChunk->tokenizerState == HTMLTokenizer::DataState
+        && tokenizer->state() == HTMLTokenizer::DataState
+        && m_input.current().isEmpty()) {
+        ASSERT(token->isUninitialized());
         return;
     }
-    didFailSpeculation(m_token.release(), m_tokenizer.release());
+
+    didFailSpeculation(token.release(), tokenizer.release());
 }
 
 void HTMLDocumentParser::didFailSpeculation(PassOwnPtr<HTMLToken> token, PassOwnPtr<HTMLTokenizer> tokenizer)
@@ -372,6 +386,8 @@
     // ASSERT that this object is both attached to the Document and protected.
     ASSERT(refCount() >= 2);
     ASSERT(shouldUseThreading());
+    ASSERT(!m_tokenizer);
+    ASSERT(!m_token);
 
     ActiveParserSession session(contextForParsingSession());
 
@@ -385,6 +401,7 @@
 
         if (XSSInfo* xssInfo = it->xssInfo())
             m_xssAuditorDelegate.didBlockScript(*xssInfo);
+
         constructTreeFromCompactHTMLToken(*it);
 
         if (isStopped())
@@ -395,25 +412,30 @@
 
             // 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.
-            if (tokens->last().type() == HTMLToken::EndOfFile)
+            if (tokens->last().type() == HTMLToken::EndOfFile) {
+                ASSERT(m_speculations.isEmpty());
                 prepareToStopParsing();
+            }
             break;
         }
 
         if (isWaitingForScripts()) {
             ASSERT(it + 1 == tokens->end()); // The </script> is assumed to be the last token of this bunch.
             runScriptsForPausedTreeBuilder();
+            validateSpeculations();
             break;
         }
 
         if (it->type() == HTMLToken::EndOfFile) {
             ASSERT(it + 1 == tokens->end()); // The EOF is assumed to be the last token of this bunch.
+            ASSERT(m_speculations.isEmpty());
             prepareToStopParsing();
             break;
         }
+
+        ASSERT(!m_tokenizer);
+        ASSERT(!m_token);
     }
-
-    checkForSpeculationFailure();
 }
 
 void HTMLDocumentParser::pumpPendingSpeculations()
@@ -423,6 +445,10 @@
 
     // ASSERT that this object is both attached to the Document and protected.
     ASSERT(refCount() >= 2);
+    // 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);
 
     // FIXME: Pass in current input length.
     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), 0, lineNumber().zeroBasedInt());
@@ -846,8 +872,7 @@
 
 #if ENABLE(THREADED_HTML_PARSER)
     if (m_haveBackgroundParser) {
-        checkForSpeculationFailure();
-
+        validateSpeculations();
         // processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document,
         // but we need to ensure it isn't deleted yet.
         RefPtr<HTMLDocumentParser> protect(this);

Modified: trunk/Source/WebCore/html/parser/HTMLDocumentParser.h (144713 => 144714)


--- trunk/Source/WebCore/html/parser/HTMLDocumentParser.h	2013-03-05 04:37:32 UTC (rev 144713)
+++ trunk/Source/WebCore/html/parser/HTMLDocumentParser.h	2013-03-05 04:57:15 UTC (rev 144714)
@@ -140,7 +140,7 @@
 #if ENABLE(THREADED_HTML_PARSER)
     void startBackgroundParser();
     void stopBackgroundParser();
-    void checkForSpeculationFailure();
+    void validateSpeculations();
     void didFailSpeculation(PassOwnPtr<HTMLToken>, PassOwnPtr<HTMLTokenizer>);
     void processParsedChunkFromBackgroundParser(PassOwnPtr<ParsedChunk>);
     void pumpPendingSpeculations();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to