2010-08-16 Eric Seidel <eric@webkit.org>
authoreric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Aug 2010 19:32:02 +0000 (19:32 +0000)
committereric@webkit.org <eric@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Aug 2010 19:32:02 +0000 (19:32 +0000)
        Reviewed by Adam Barth.

        Make DocumentParser safer to use
        https://bugs.webkit.org/show_bug.cgi?id=43055

        This also includes a test from
        https://bugs.webkit.org/show_bug.cgi?id=42099

        These tests were kindly provided by Nate Chapin and Adam Barth.

        * fast/frames/document-write-in-iframe-onload-expected.txt: Copied from LayoutTests/editing/selection/5136696-expected.txt.
        * fast/frames/document-write-in-iframe-onload.html: Added.
        * fast/frames/resources/set-src-to-javascript-url.html: Added.
        * fast/frames/set-parent-src-synchronously-expected.txt: Added.
        * fast/frames/set-parent-src-synchronously.html: Added.
        * fast/parser/document-close-iframe-load-expected.txt: Added.
        * fast/parser/document-close-iframe-load.html: Added.
        * fast/parser/document-close-nested-iframe-load-expected.txt: Added.
        * fast/parser/document-close-nested-iframe-load.html: Added.
        * fast/parser/iframe-sets-parent-to-javascript-url-expected.txt: Added.
        * fast/parser/iframe-sets-parent-to-javascript-url.html: Added.
        * fast/parser/resources/set-parent-to-javascript-url.html: Added.
2010-08-17  Eric Seidel  <eric@webkit.org>

        Reviewed by Adam Barth.

        Make DocumentParser safer to use
        https://bugs.webkit.org/show_bug.cgi?id=43055

        Make DocumentParser and its subclasses RefCounted, and protect
        HTMLDocumentParser during parsing. It's possible for a parser to
        get deleted if certain actions (e.g., a document.write()) occur
        synchronously.

        The original version of this patch was written by Nate Chapin.

        DocumentParser doesn't actually have to be fully RefCounted, since
        the only two things which should ever hold a reference to it are
        Document and DocumentParser itself.  However using RefCounted and
        RefPtr was easier/cleaner than inventing a custom ref() scheme.

        This deploys a new "detach()" method throughout the parsing
        framework.  detach() causes the parser to disconnect from the
        document so that no further modifications will be made to the
        document while any possible DocumentParser stacks are unwound.

        The irony of this patch is that the new detach() system is never
        used, since Document always outlives the DocumentParser in all of
        our layout tests.  There is an ASSERT in ~Document() to verify
        that the DocumentParser will not outlive the Document.

        However I expect that we will soon either find new test cases, or change
        the architecture in such a way that DocumentParser will outlive
        Document.  At which point, the detach() plumbing will be crucial.
        Right now detach() serves as a safe-guard against use-after-free bugs
        for any case where DocumentParser does outlive the Document.

        This also fixes test cases attached to:
        https://bugs.webkit.org/show_bug.cgi?id=42099

        Tests: fast/frames/document-write-in-iframe-onload.html
               fast/frames/set-parent-src-synchronously.html
               fast/parser/document-close-iframe-load.html
               fast/parser/document-close-nested-iframe-load.html
               fast/parser/iframe-sets-parent-to-javascript-url.html

        * dom/Document.cpp:
         - Added a new detachParser() call to be used anywhere we
           used to call m_parser.clear().
           There is an ASSERT in ~DocumentParser which ensures that
           we get this right.
        (WebCore::Document::removedLastRef):
        (WebCore::Document::~Document):
        (WebCore::Document::createParser):
        (WebCore::Document::detachParser):
        (WebCore::Document::cancelParsing):
        (WebCore::Document::implicitOpen):
         - removed redundant m_parser.clear()
        (WebCore::Document::implicitClose):
        * dom/Document.h:
        * dom/DocumentParser.cpp:
        (WebCore::DocumentParser::~DocumentParser):
         - ASSERT that callers always call detach() before destruction.
         - This ASSERT might prove too cumbersome, but for now it's useful.
        (WebCore::DocumentParser::detach):
        * dom/DocumentParser.h:
        * dom/RawDataDocumentParser.h:
        * dom/XMLDocumentParser.cpp:
        (WebCore::XMLDocumentParser::finish):
         - Add a FIXME explaining part of the reason why
           stopParsing() and detach() are separate concepts.
        * dom/XMLDocumentParser.h:
        (WebCore::XMLDocumentParser::create):
        * dom/XMLDocumentParserLibxml2.cpp:
        (WebCore::XMLDocumentParser::parseDocumentFragment):
        * dom/XMLDocumentParserQt.cpp:
        (WebCore::XMLDocumentParser::parseDocumentFragment):
        * html/HTMLConstructionSite.cpp:
        (WebCore::HTMLConstructionSite::detach):
        (WebCore::HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded):
        * html/HTMLConstructionSite.h:
        * html/HTMLDocument.cpp:
        (WebCore::HTMLDocument::createParser):
        * html/HTMLDocument.h:
        * html/HTMLDocumentParser.cpp:
         - We need to protect(this) before calling into any code
           which might cause the parser to be destroyed.
        (WebCore::HTMLDocumentParser::~HTMLDocumentParser):
        (WebCore::HTMLDocumentParser::detach):
        (WebCore::HTMLDocumentParser::resumeParsingAfterYield):
        (WebCore::HTMLDocumentParser::pumpTokenizer):
        (WebCore::HTMLDocumentParser::insert):
        (WebCore::HTMLDocumentParser::append):
        (WebCore::HTMLDocumentParser::end):
        (WebCore::HTMLDocumentParser::finish):
        (WebCore::HTMLDocumentParser::notifyFinished):
        (WebCore::HTMLDocumentParser::executeScriptsWaitingForStylesheets):
        (WebCore::HTMLDocumentParser::parseDocumentFragment):
        * html/HTMLDocumentParser.h:
        (WebCore::HTMLDocumentParser::create):
        * html/HTMLScriptRunner.cpp:
        (WebCore::HTMLScriptRunner::detach):
        (WebCore::HTMLScriptRunner::executeParsingBlockingScript):
        (WebCore::HTMLScriptRunner::executeScript):
        (WebCore::HTMLScriptRunner::executeScriptsWaitingForStylesheets):
        (WebCore::HTMLScriptRunner::runScript):
        * html/HTMLScriptRunner.h:
        * html/HTMLTreeBuilder.cpp:
        (WebCore::HTMLTreeBuilder::detach):
        (WebCore::HTMLTreeBuilder::passTokenToLegacyParser):
        (WebCore::HTMLTreeBuilder::finished):
        * html/HTMLTreeBuilder.h:
        * html/HTMLViewSourceDocument.cpp:
        (WebCore::HTMLViewSourceDocument::createParser):
        * html/HTMLViewSourceDocument.h:
        * html/HTMLViewSourceParser.cpp:
        (WebCore::HTMLViewSourceParser::HTMLViewSourceParser):
        * html/HTMLViewSourceParser.h:
        (WebCore::HTMLViewSourceParser::create):
        * loader/FTPDirectoryDocument.cpp:
        (WebCore::FTPDirectoryDocumentParser::create):
        (WebCore::FTPDirectoryDocument::createParser):
        * loader/FTPDirectoryDocument.h:
        * loader/ImageDocument.cpp:
        (WebCore::ImageDocumentParser::create):
        (WebCore::ImageDocumentParser::ImageDocumentParser):
        (WebCore::ImageDocument::createParser):
        * loader/ImageDocument.h:
        * loader/MediaDocument.cpp:
        (WebCore::MediaDocumentParser::create):
        (WebCore::MediaDocument::createParser):
        * loader/MediaDocument.h:
        * loader/PluginDocument.cpp:
        (WebCore::PluginDocumentParser::create):
        (WebCore::PluginDocument::createParser):
        * loader/PluginDocument.h:
        * loader/SinkDocument.cpp:
        (WebCore::SinkDocumentParser::create):
        (WebCore::SinkDocument::createParser):
        * loader/SinkDocument.h:
        * loader/TextDocument.cpp:
        (WebCore::TextDocumentParser::create):
        (WebCore::TextDocument::createParser):
        (WebCore::createTextDocumentParser):
        * loader/TextDocument.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@65692 268f45cc-cd09-0410-ab3c-d52691b4dbfc

51 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/frames/document-write-in-iframe-onload-2-expected.txt [new file with mode: 0644]
LayoutTests/fast/frames/document-write-in-iframe-onload-2.html [new file with mode: 0644]
LayoutTests/fast/frames/document-write-in-iframe-onload-expected.txt [new file with mode: 0644]
LayoutTests/fast/frames/document-write-in-iframe-onload.html [new file with mode: 0644]
LayoutTests/fast/frames/resources/set-src-to-javascript-url.html [new file with mode: 0644]
LayoutTests/fast/frames/set-parent-src-synchronously-expected.txt [new file with mode: 0644]
LayoutTests/fast/frames/set-parent-src-synchronously.html [new file with mode: 0644]
LayoutTests/fast/parser/document-close-iframe-load-expected.txt [new file with mode: 0644]
LayoutTests/fast/parser/document-close-iframe-load.html [new file with mode: 0644]
LayoutTests/fast/parser/document-close-nested-iframe-load-expected.txt [new file with mode: 0644]
LayoutTests/fast/parser/document-close-nested-iframe-load.html [new file with mode: 0644]
LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url-expected.txt [new file with mode: 0644]
LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url.html [new file with mode: 0644]
LayoutTests/fast/parser/resources/set-parent-to-javascript-url.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/dom/Document.cpp
WebCore/dom/Document.h
WebCore/dom/DocumentParser.cpp
WebCore/dom/DocumentParser.h
WebCore/dom/RawDataDocumentParser.h
WebCore/dom/XMLDocumentParser.cpp
WebCore/dom/XMLDocumentParser.h
WebCore/dom/XMLDocumentParserLibxml2.cpp
WebCore/dom/XMLDocumentParserQt.cpp
WebCore/html/HTMLConstructionSite.cpp
WebCore/html/HTMLConstructionSite.h
WebCore/html/HTMLDocument.cpp
WebCore/html/HTMLDocument.h
WebCore/html/HTMLDocumentParser.cpp
WebCore/html/HTMLDocumentParser.h
WebCore/html/HTMLScriptRunner.cpp
WebCore/html/HTMLScriptRunner.h
WebCore/html/HTMLTreeBuilder.cpp
WebCore/html/HTMLTreeBuilder.h
WebCore/html/HTMLViewSourceDocument.cpp
WebCore/html/HTMLViewSourceDocument.h
WebCore/html/HTMLViewSourceParser.cpp
WebCore/html/HTMLViewSourceParser.h
WebCore/loader/FTPDirectoryDocument.cpp
WebCore/loader/FTPDirectoryDocument.h
WebCore/loader/ImageDocument.cpp
WebCore/loader/ImageDocument.h
WebCore/loader/MediaDocument.cpp
WebCore/loader/MediaDocument.h
WebCore/loader/PluginDocument.cpp
WebCore/loader/PluginDocument.h
WebCore/loader/SinkDocument.cpp
WebCore/loader/SinkDocument.h
WebCore/loader/TextDocument.cpp
WebCore/loader/TextDocument.h

index ffedce5..8d5663e 100644 (file)
@@ -1,3 +1,28 @@
+2010-08-16  Eric Seidel  <eric@webkit.org>
+
+        Reviewed by Adam Barth.
+
+        Make DocumentParser safer to use
+        https://bugs.webkit.org/show_bug.cgi?id=43055
+
+        This also includes a test from
+        https://bugs.webkit.org/show_bug.cgi?id=42099
+
+        These tests were kindly provided by Nate Chapin and Adam Barth.
+
+        * fast/frames/document-write-in-iframe-onload-expected.txt: Copied from LayoutTests/editing/selection/5136696-expected.txt.
+        * fast/frames/document-write-in-iframe-onload.html: Added.
+        * fast/frames/resources/set-src-to-javascript-url.html: Added.
+        * fast/frames/set-parent-src-synchronously-expected.txt: Added.
+        * fast/frames/set-parent-src-synchronously.html: Added.
+        * fast/parser/document-close-iframe-load-expected.txt: Added.
+        * fast/parser/document-close-iframe-load.html: Added.
+        * fast/parser/document-close-nested-iframe-load-expected.txt: Added.
+        * fast/parser/document-close-nested-iframe-load.html: Added.
+        * fast/parser/iframe-sets-parent-to-javascript-url-expected.txt: Added.
+        * fast/parser/iframe-sets-parent-to-javascript-url.html: Added.
+        * fast/parser/resources/set-parent-to-javascript-url.html: Added.
+
 2010-08-18  Zhenyao Mo  <zmo@google.com>
 
         Reviewed by Kenneth Russell.
diff --git a/LayoutTests/fast/frames/document-write-in-iframe-onload-2-expected.txt b/LayoutTests/fast/frames/document-write-in-iframe-onload-2-expected.txt
new file mode 100644 (file)
index 0000000..6d672e8
--- /dev/null
@@ -0,0 +1,3 @@
+PASS (1 of 2)
+
+PASS (2 of 2)
diff --git a/LayoutTests/fast/frames/document-write-in-iframe-onload-2.html b/LayoutTests/fast/frames/document-write-in-iframe-onload-2.html
new file mode 100644 (file)
index 0000000..732cc08
--- /dev/null
@@ -0,0 +1,6 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+</script>
+FAIL
+<iframe onload="document.close(); document.write('<p>PASS (1 of 2)<p>PASS (2 of 2)');"></iframe>
diff --git a/LayoutTests/fast/frames/document-write-in-iframe-onload-expected.txt b/LayoutTests/fast/frames/document-write-in-iframe-onload-expected.txt
new file mode 100644 (file)
index 0000000..7ef22e9
--- /dev/null
@@ -0,0 +1 @@
+PASS
diff --git a/LayoutTests/fast/frames/document-write-in-iframe-onload.html b/LayoutTests/fast/frames/document-write-in-iframe-onload.html
new file mode 100644 (file)
index 0000000..2669a84
--- /dev/null
@@ -0,0 +1,13 @@
+<html>
+<head>
+<script>
+    if (window.layoutTestController)
+        layoutTestController.dumpAsText();
+</script>
+</head>
+<body>
+FAIL: Text from the top of the origin document should not be visible after the onload event.<br>
+<iframe onload="document.open();document.write('PASS');document.close();"></iframe>
+FAIL: Text from the bottom of the origin document should not be visible after the onload event.<br>
+</body>
+</html>
diff --git a/LayoutTests/fast/frames/resources/set-src-to-javascript-url.html b/LayoutTests/fast/frames/resources/set-src-to-javascript-url.html
new file mode 100644 (file)
index 0000000..9edc158
--- /dev/null
@@ -0,0 +1,15 @@
+<html>
+<body>
+FAIL
+<script>
+    function returnStringValue() {
+        return "test";
+    }
+
+    if (window.layoutTestController)
+        layoutTestController.dumpAsText();
+
+    parent.document.getElementById("parent").setAttribute("src", "javascript: returnStringValue()");
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/frames/set-parent-src-synchronously-expected.txt b/LayoutTests/fast/frames/set-parent-src-synchronously-expected.txt
new file mode 100644 (file)
index 0000000..6e03b27
--- /dev/null
@@ -0,0 +1 @@
+This test fails if we crash or any text below this is visible (the iframe should detach itself from the page). 
diff --git a/LayoutTests/fast/frames/set-parent-src-synchronously.html b/LayoutTests/fast/frames/set-parent-src-synchronously.html
new file mode 100644 (file)
index 0000000..1a9d0e5
--- /dev/null
@@ -0,0 +1,7 @@
+<html>
+<body>
+This test fails if we crash or any text below this is visible (the iframe should detach itself from the page).
+
+<iframe id="parent" src="resources/set-src-to-javascript-url.html"></iframe>
+</body>
+</html>
diff --git a/LayoutTests/fast/parser/document-close-iframe-load-expected.txt b/LayoutTests/fast/parser/document-close-iframe-load-expected.txt
new file mode 100644 (file)
index 0000000..2c7db1a
--- /dev/null
@@ -0,0 +1,2 @@
+ALERT: PASS (just to make expected.txt non-empty)
+
diff --git a/LayoutTests/fast/parser/document-close-iframe-load.html b/LayoutTests/fast/parser/document-close-iframe-load.html
new file mode 100644 (file)
index 0000000..3f5f949
--- /dev/null
@@ -0,0 +1,7 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+alert('PASS (just to make expected.txt non-empty)');
+</script>
+This test passes if it doesn't crash.
+<iframe onload="document.open();document.close();" >
diff --git a/LayoutTests/fast/parser/document-close-nested-iframe-load-expected.txt b/LayoutTests/fast/parser/document-close-nested-iframe-load-expected.txt
new file mode 100644 (file)
index 0000000..4df18d9
--- /dev/null
@@ -0,0 +1 @@
+/PASS/
diff --git a/LayoutTests/fast/parser/document-close-nested-iframe-load.html b/LayoutTests/fast/parser/document-close-nested-iframe-load.html
new file mode 100644 (file)
index 0000000..1ada9eb
--- /dev/null
@@ -0,0 +1,6 @@
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText();
+</script>
+FAIL
+<iframe onload="document.open(); document.close(); document.write('<iframe onload=document.open();document.close();document.write(/PASS/); >')"></iframe>
diff --git a/LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url-expected.txt b/LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url-expected.txt
new file mode 100644 (file)
index 0000000..72fce4b
--- /dev/null
@@ -0,0 +1,10 @@
+ALERT: 1
+ALERT: 2
+ALERT: 3
+ALERT: 4
+
+
+--------
+Frame: '<!--framePath //<!--frame0-->-->'
+--------
+PASS
diff --git a/LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url.html b/LayoutTests/fast/parser/iframe-sets-parent-to-javascript-url.html
new file mode 100644 (file)
index 0000000..41ce70b
--- /dev/null
@@ -0,0 +1,13 @@
+<script>
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.dumpChildFramesAsText();
+    layoutTestController.waitUntilDone();
+}
+
+function done() {
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
+</script>
+<iframe src="resources/set-parent-to-javascript-url.html"></iframe>
diff --git a/LayoutTests/fast/parser/resources/set-parent-to-javascript-url.html b/LayoutTests/fast/parser/resources/set-parent-to-javascript-url.html
new file mode 100644 (file)
index 0000000..a64b9a0
--- /dev/null
@@ -0,0 +1,6 @@
+<script>
+alert(1);
+parent.document.getElementsByTagName('iframe')[0].src = "javascript:alert(2),'PASS<script>alert(3)<\/script>'";
+alert(4);
+parent.setTimeout("done()", 0);
+</script>
index 29f5749..7932e7a 100644 (file)
@@ -1,3 +1,147 @@
+2010-08-17  Eric Seidel  <eric@webkit.org>
+
+        Reviewed by Adam Barth.
+
+        Make DocumentParser safer to use
+        https://bugs.webkit.org/show_bug.cgi?id=43055
+
+        Make DocumentParser and its subclasses RefCounted, and protect
+        HTMLDocumentParser during parsing. It's possible for a parser to
+        get deleted if certain actions (e.g., a document.write()) occur
+        synchronously.
+
+        The original version of this patch was written by Nate Chapin.
+
+        DocumentParser doesn't actually have to be fully RefCounted, since
+        the only two things which should ever hold a reference to it are
+        Document and DocumentParser itself.  However using RefCounted and
+        RefPtr was easier/cleaner than inventing a custom ref() scheme.
+
+        This deploys a new "detach()" method throughout the parsing
+        framework.  detach() causes the parser to disconnect from the
+        document so that no further modifications will be made to the
+        document while any possible DocumentParser stacks are unwound.
+
+        The irony of this patch is that the new detach() system is never
+        used, since Document always outlives the DocumentParser in all of
+        our layout tests.  There is an ASSERT in ~Document() to verify
+        that the DocumentParser will not outlive the Document.
+
+        However I expect that we will soon either find new test cases, or change
+        the architecture in such a way that DocumentParser will outlive
+        Document.  At which point, the detach() plumbing will be crucial.
+        Right now detach() serves as a safe-guard against use-after-free bugs
+        for any case where DocumentParser does outlive the Document.
+
+        This also fixes test cases attached to:
+        https://bugs.webkit.org/show_bug.cgi?id=42099
+
+        Tests: fast/frames/document-write-in-iframe-onload.html
+               fast/frames/set-parent-src-synchronously.html
+               fast/parser/document-close-iframe-load.html
+               fast/parser/document-close-nested-iframe-load.html
+               fast/parser/iframe-sets-parent-to-javascript-url.html
+
+        * dom/Document.cpp:
+         - Added a new detachParser() call to be used anywhere we
+           used to call m_parser.clear().
+           There is an ASSERT in ~DocumentParser which ensures that
+           we get this right.
+        (WebCore::Document::removedLastRef):
+        (WebCore::Document::~Document):
+        (WebCore::Document::createParser):
+        (WebCore::Document::detachParser):
+        (WebCore::Document::cancelParsing):
+        (WebCore::Document::implicitOpen):
+         - removed redundant m_parser.clear()
+        (WebCore::Document::implicitClose):
+        * dom/Document.h:
+        * dom/DocumentParser.cpp:
+        (WebCore::DocumentParser::~DocumentParser):
+         - ASSERT that callers always call detach() before destruction.
+         - This ASSERT might prove too cumbersome, but for now it's useful.
+        (WebCore::DocumentParser::detach):
+        * dom/DocumentParser.h:
+        * dom/RawDataDocumentParser.h:
+        * dom/XMLDocumentParser.cpp:
+        (WebCore::XMLDocumentParser::finish):
+         - Add a FIXME explaining part of the reason why
+           stopParsing() and detach() are separate concepts.
+        * dom/XMLDocumentParser.h:
+        (WebCore::XMLDocumentParser::create):
+        * dom/XMLDocumentParserLibxml2.cpp:
+        (WebCore::XMLDocumentParser::parseDocumentFragment):
+        * dom/XMLDocumentParserQt.cpp:
+        (WebCore::XMLDocumentParser::parseDocumentFragment):
+        * html/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::detach):
+        (WebCore::HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded):
+        * html/HTMLConstructionSite.h:
+        * html/HTMLDocument.cpp:
+        (WebCore::HTMLDocument::createParser):
+        * html/HTMLDocument.h:
+        * html/HTMLDocumentParser.cpp:
+         - We need to protect(this) before calling into any code
+           which might cause the parser to be destroyed.
+        (WebCore::HTMLDocumentParser::~HTMLDocumentParser):
+        (WebCore::HTMLDocumentParser::detach):
+        (WebCore::HTMLDocumentParser::resumeParsingAfterYield):
+        (WebCore::HTMLDocumentParser::pumpTokenizer):
+        (WebCore::HTMLDocumentParser::insert):
+        (WebCore::HTMLDocumentParser::append):
+        (WebCore::HTMLDocumentParser::end):
+        (WebCore::HTMLDocumentParser::finish):
+        (WebCore::HTMLDocumentParser::notifyFinished):
+        (WebCore::HTMLDocumentParser::executeScriptsWaitingForStylesheets):
+        (WebCore::HTMLDocumentParser::parseDocumentFragment):
+        * html/HTMLDocumentParser.h:
+        (WebCore::HTMLDocumentParser::create):
+        * html/HTMLScriptRunner.cpp:
+        (WebCore::HTMLScriptRunner::detach):
+        (WebCore::HTMLScriptRunner::executeParsingBlockingScript):
+        (WebCore::HTMLScriptRunner::executeScript):
+        (WebCore::HTMLScriptRunner::executeScriptsWaitingForStylesheets):
+        (WebCore::HTMLScriptRunner::runScript):
+        * html/HTMLScriptRunner.h:
+        * html/HTMLTreeBuilder.cpp:
+        (WebCore::HTMLTreeBuilder::detach):
+        (WebCore::HTMLTreeBuilder::passTokenToLegacyParser):
+        (WebCore::HTMLTreeBuilder::finished):
+        * html/HTMLTreeBuilder.h:
+        * html/HTMLViewSourceDocument.cpp:
+        (WebCore::HTMLViewSourceDocument::createParser):
+        * html/HTMLViewSourceDocument.h:
+        * html/HTMLViewSourceParser.cpp:
+        (WebCore::HTMLViewSourceParser::HTMLViewSourceParser):
+        * html/HTMLViewSourceParser.h:
+        (WebCore::HTMLViewSourceParser::create):
+        * loader/FTPDirectoryDocument.cpp:
+        (WebCore::FTPDirectoryDocumentParser::create):
+        (WebCore::FTPDirectoryDocument::createParser):
+        * loader/FTPDirectoryDocument.h:
+        * loader/ImageDocument.cpp:
+        (WebCore::ImageDocumentParser::create):
+        (WebCore::ImageDocumentParser::ImageDocumentParser):
+        (WebCore::ImageDocument::createParser):
+        * loader/ImageDocument.h:
+        * loader/MediaDocument.cpp:
+        (WebCore::MediaDocumentParser::create):
+        (WebCore::MediaDocument::createParser):
+        * loader/MediaDocument.h:
+        * loader/PluginDocument.cpp:
+        (WebCore::PluginDocumentParser::create):
+        (WebCore::PluginDocument::createParser):
+        * loader/PluginDocument.h:
+        * loader/SinkDocument.cpp:
+        (WebCore::SinkDocumentParser::create):
+        (WebCore::SinkDocument::createParser):
+        * loader/SinkDocument.h:
+        * loader/TextDocument.cpp:
+        (WebCore::TextDocumentParser::create):
+        (WebCore::TextDocument::createParser):
+        (WebCore::createTextDocumentParser):
+        * loader/TextDocument.h:
+
 2010-08-19  David Kilzer  <ddkilzer@apple.com>
 
         Fix compilation of SelectElement.cpp with ARROW_KEYS_POP_MENU == 0
index b6adc95..075781c 100644 (file)
@@ -498,7 +498,7 @@ void Document::removedLastRef()
         deleteAllValues(m_markers);
         m_markers.clear();
 
-        m_parser.clear();
+        detachParser();
 
         m_cssCanvasElements.clear();
 
@@ -531,7 +531,14 @@ Document::~Document()
     destroyAllWrapperCaches();
 #endif
 
-    m_parser.clear();
+    // Currently we believe that Document can never outlive the parser.
+    // Although the Document may be replaced synchronously, DocumentParsers
+    // generally keep at least one reference to an Element which would in turn
+    // has a reference to the Document.  If you hit this ASSERT, then that
+    // assumption is wrong.  DocumentParser::detach() should ensure that even
+    // if the DocumentParser outlives the Document it won't cause badness.
+    ASSERT(!m_parser || m_parser->refCount() == 1);
+    detachParser();
     m_document = 0;
     m_docLoader.clear();
 
@@ -1778,10 +1785,10 @@ void Document::setVisuallyOrdered()
         renderer()->style()->setVisuallyOrdered(true);
 }
 
-DocumentParser* Document::createParser()
+PassRefPtr<DocumentParser> Document::createParser()
 {
     // FIXME: this should probably pass the frame instead
-    return new XMLDocumentParser(this, view());
+    return XMLDocumentParser::create(this, view());
 }
 
 ScriptableDocumentParser* Document::scriptableDocumentParser() const
@@ -1815,6 +1822,14 @@ void Document::open(Document* ownerDocument)
         m_frame->loader()->didExplicitOpen();
 }
 
+void Document::detachParser()
+{
+    if (!m_parser)
+        return;
+    m_parser->detach();
+    m_parser.clear();
+}
+
 void Document::cancelParsing()
 {
     if (m_parser) {
@@ -1822,7 +1837,7 @@ void Document::cancelParsing()
         // the onload handler when closing as a side effect of a cancel-style
         // change, such as opening a new document or closing the window while
         // still parsing
-        m_parser.clear();
+        detachParser();
         close();
     }
 }
@@ -1831,8 +1846,6 @@ void Document::implicitOpen()
 {
     cancelParsing();
 
-    m_parser.clear();
-
     removeChildren();
 
     m_parser = createParser();
@@ -1930,7 +1943,7 @@ void Document::implicitClose()
 
     // We have to clear the parser, in case someone document.write()s from the
     // onLoad event handler, as in Radar 3206524.
-    m_parser.clear();
+    detachParser();
 
     // Parser should have picked up all preloads by now
     m_docLoader->clearPreloads();
index e2912cd..748b776 100644 (file)
@@ -563,7 +563,7 @@ public:
     CSSStyleSheet* elementSheet();
     CSSStyleSheet* mappedElementSheet();
     
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
     DocumentParser* parser() const { return m_parser.get(); }
     ScriptableDocumentParser* scriptableDocumentParser() const;
     
@@ -1026,6 +1026,7 @@ protected:
     void clearXMLVersion() { m_xmlVersion = String(); }
 
 private:
+    void detachParser();
 
     typedef void (*ArgumentsCallback)(const String& keyString, const String& valueString, Document*, void* data);
     void processArguments(const String& features, void* data, ArgumentsCallback);
@@ -1070,7 +1071,7 @@ private:
 
     Frame* m_frame;
     OwnPtr<DocLoader> m_docLoader;
-    OwnPtr<DocumentParser> m_parser;
+    RefPtr<DocumentParser> m_parser;
     bool m_wellFormed;
 
     // Document URLs.
index b80927c..cc4c61b 100644 (file)
@@ -37,5 +37,18 @@ DocumentParser::DocumentParser(Document* document)
     ASSERT(document);
 }
 
+DocumentParser::~DocumentParser()
+{
+    // Document is expected to call detach() before releasing its ref.
+    // This ASSERT is slightly awkward for parsers with a fragment case
+    // as there is no Document to release the ref.
+    ASSERT(!m_document);
+}
+
+void DocumentParser::detach()
+{
+    m_document = 0;
+}
+
 };
 
index e942d1f..e51cf94 100644 (file)
@@ -24,7 +24,7 @@
 #ifndef DocumentParser_h
 #define DocumentParser_h
 
-#include <wtf/Noncopyable.h>
+#include <wtf/RefCounted.h>
 
 namespace WebCore {
 
@@ -34,9 +34,9 @@ class LegacyHTMLTreeBuilder;
 class SegmentedString;
 class ScriptableDocumentParser;
 
-class DocumentParser : public Noncopyable {
+class DocumentParser : public RefCounted<DocumentParser> {
 public:
-    virtual ~DocumentParser() { }
+    virtual ~DocumentParser();
 
     virtual ScriptableDocumentParser* asScriptableDocumentParser() { return 0; }
 
@@ -56,7 +56,6 @@ public:
     virtual void finish() = 0;
     virtual bool finishWasCalled() = 0;
 
-    virtual void stopParsing() { m_parserStopped = true; }
     // FIXME: processingData() is only used by DocumentLoader::isLoadingInAPISense
     // and is very unclear as to what it actually means.  The LegacyHTMLDocumentParser
     // used to implements it.
@@ -68,15 +67,30 @@ public:
 
     Document* document() const { return m_document; }
 
+    // Document is expected to detach the parser before releasing its ref.
+    // After detach, m_document is cleared.  The parser will unwind its
+    // callstacks, but not produce any more nodes.
+    // It is impossible for the parser to touch the rest of WebCore after
+    // detach is called.
+    virtual void detach();
+
+    // stopParsing() is used when a load is canceled/stopped.
+    // stopParsing() is currently different from detach(), but shouldn't be.
+    // It should NOT be ok to call any methods on DocumentParser after either
+    // detach() or stopParsing() but right now only detach() will ASSERT.
+    virtual void stopParsing() { m_parserStopped = true; }
+
 protected:
     DocumentParser(Document*);
 
     // The parser has buffers, so parsing may continue even after
     // it stops receiving data. We use m_parserStopped to stop the parser
     // even when it has buffered data.
+    // FIXME: m_document = 0 could be changed to mean "parser stopped".
     bool m_parserStopped;
 
     // Every DocumentParser needs a pointer back to the document.
+    // m_document will be 0 after the parser is stopped.
     Document* m_document;
 };
 
index 2eb3d0a..9eea9b6 100644 (file)
 namespace WebCore {
 
 class RawDataDocumentParser : public DocumentParser {
-public:
+protected:
     RawDataDocumentParser(Document* document)
         : DocumentParser(document)
     {
     }
 
-protected:
     virtual void finish()
     {
         if (!m_parserStopped)
index 3d2f324..a3828d5 100644 (file)
@@ -233,6 +233,11 @@ void XMLDocumentParser::end()
 
 void XMLDocumentParser::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 Document::finishParsing unconditionally
+    // which in turn calls m_parser->finish().
+
     if (m_parserPaused)
         m_finishCalled = true;
     else
index 0b2c63b..cdc770e 100644 (file)
@@ -72,8 +72,15 @@ namespace WebCore {
 
     class XMLDocumentParser : public ScriptableDocumentParser, public CachedResourceClient {
     public:
-        XMLDocumentParser(Document*, FrameView* = 0);
-        XMLDocumentParser(DocumentFragment*, Element*, FragmentScriptingPermission);
+        static PassRefPtr<XMLDocumentParser> create(Document* document, FrameView* view)
+        {
+            return adoptRef(new XMLDocumentParser(document, view));
+        }
+        static PassRefPtr<XMLDocumentParser> create(DocumentFragment* fragment, Element* element, FragmentScriptingPermission permission)
+        {
+            return adoptRef(new XMLDocumentParser(fragment, element, permission));
+        }
+
         ~XMLDocumentParser();
 
         // Exposed for callbacks:
@@ -98,6 +105,9 @@ namespace WebCore {
         virtual int columnNumber() const;
 
     private:
+        XMLDocumentParser(Document*, FrameView* = 0);
+        XMLDocumentParser(DocumentFragment*, Element*, FragmentScriptingPermission);
+
         // From DocumentParser
         virtual void insert(const SegmentedString&);
         virtual void append(const SegmentedString&);
index 1309827..861853c 100644 (file)
@@ -1378,22 +1378,24 @@ bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragm
     if (!chunk.length())
         return true;
 
-    XMLDocumentParser parser(fragment, parent, scriptingPermission);
+    RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, parent, scriptingPermission);
 
     CString chunkAsUtf8 = chunk.utf8();
-    parser.initializeParserContext(chunkAsUtf8.data());
+    parser->initializeParserContext(chunkAsUtf8.data());
 
-    xmlParseContent(parser.context());
+    xmlParseContent(parser->context());
 
-    parser.endDocument();
+    parser->endDocument();
 
     // Check if all the chunk has been processed.
-    long bytesProcessed = xmlByteConsumed(parser.context());
+    long bytesProcessed = xmlByteConsumed(parser->context());
     if (bytesProcessed == -1 || ((unsigned long)bytesProcessed) != chunkAsUtf8.length())
         return false;
 
+    parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
+
     // No error if the chunk is well formed or it is not but we have no error.
-    return parser.context()->wellFormed || xmlCtxtGetLastError(parser.context()) == 0;
+    return parser->context()->wellFormed || !xmlCtxtGetLastError(parser->context());
 }
 
 // --------------------------------
index 715856c..148cff0 100644 (file)
@@ -259,13 +259,14 @@ bool XMLDocumentParser::parseDocumentFragment(const String& chunk, DocumentFragm
     if (!chunk.length())
         return true;
 
-    XMLDocumentParser parser(fragment, parent, scriptingPermission);
-
-    parser.append(String("<qxmlstreamdummyelement>"));
-    parser.append(chunk);
-    parser.append(String("</qxmlstreamdummyelement>"));
-    parser.finish();
-    return !parser.hasError();
+    RefPtr<XMLDocumentParser> parser = XMLDocumentParser::create(fragment, parent, scriptingPermission);
+
+    parser->append(String("<qxmlstreamdummyelement>"));
+    parser->append(chunk);
+    parser->append(String("</qxmlstreamdummyelement>"));
+    parser->finish();
+    parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
+    return !parser->hasError();
 }
 
 // --------------------------------
index a25c7d9..9e43b70 100644 (file)
@@ -146,6 +146,11 @@ HTMLConstructionSite::~HTMLConstructionSite()
 {
 }
 
+void HTMLConstructionSite::detach()
+{
+    m_document = 0;
+}
+
 void HTMLConstructionSite::setForm(HTMLFormElement* form)
 {
     // This method should only be needed for HTMLTreeBuilder in the fragment case.
@@ -160,6 +165,7 @@ PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm()
 
 void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded()
 {
+    ASSERT(m_document);
     if (m_document->frame() && !m_isParsingFragment)
         m_document->frame()->loader()->dispatchDocumentElementAvailable();
 }
index 16ba56b..c195a65 100644 (file)
@@ -45,6 +45,8 @@ public:
     HTMLConstructionSite(Document*, FragmentScriptingPermission, bool isParsingFragment);
     ~HTMLConstructionSite();
 
+    void detach();
+
     void insertDoctype(AtomicHTMLToken&);
     void insertComment(AtomicHTMLToken&);
     void insertCommentOnDocument(AtomicHTMLToken&);
index 5798783..986c51d 100644 (file)
@@ -282,14 +282,14 @@ void HTMLDocument::releaseEvents()
 {
 }
 
-DocumentParser* HTMLDocument::createParser()
+PassRefPtr<DocumentParser> HTMLDocument::createParser()
 {
     bool reportErrors = false;
 #if ENABLE(INSPECTOR)
     if (Page* page = this->page())
         reportErrors = page->inspectorController()->windowVisible();
 #endif
-    return new HTMLDocumentParser(this, reportErrors);
+    return HTMLDocumentParser::create(this, reportErrors);
 }
 
 // --------------------------------------------------------------------------
index eeabda4..f9744c7 100644 (file)
@@ -88,7 +88,7 @@ private:
     virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
 
     virtual bool isFrameSet() const;
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
     virtual void determineParseMode();
 
     void addItemToMap(HashCountedSet<AtomicStringImpl*>&, const AtomicString&);
index 28b3198..0a47643 100644 (file)
@@ -119,10 +119,21 @@ HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* cont
 
 HTMLDocumentParser::~HTMLDocumentParser()
 {
-    // FIXME: We'd like to ASSERT that normal operation of this class clears
-    // out any delayed actions, but we can't because we're unceremoniously
-    // deleted.  If there were a required call to some sort of cancel function,
-    // then we could ASSERT some invariants here.
+    ASSERT(!m_parserScheduler);
+    ASSERT(!m_writeNestingLevel);
+    ASSERT(!m_preloadScanner);
+}
+
+void HTMLDocumentParser::detach()
+{
+    DocumentParser::detach();
+    if (m_scriptRunner)
+        m_scriptRunner->detach();
+    m_treeBuilder->detach();
+    // FIXME: It seems wrong that we would have a preload scanner here.
+    // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do.
+    m_preloadScanner.clear();
+    m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
 }
 
 void HTMLDocumentParser::stopParsing()
@@ -158,6 +169,10 @@ bool HTMLDocumentParser::isScheduledForResume() const
 // Used by HTMLParserScheduler
 void HTMLDocumentParser::resumeParsingAfterYield()
 {
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     // We should never be here unless we can pump immediately.  Call pumpTokenizer()
     // directly so that ASSERTS will fire if we're wrong.
     pumpTokenizer(AllowYield);
@@ -178,9 +193,12 @@ bool HTMLDocumentParser::runScriptsForPausedTreeBuilder()
 
 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
 {
+    ASSERT(m_document);
     ASSERT(!m_parserStopped);
     ASSERT(!m_treeBuilder->isPaused());
     ASSERT(!isScheduledForResume());
+    // ASSERT that this object is both attached to the Document and protected.
+    ASSERT(refCount() >= 2);
 
     // We tell the InspectorTimelineAgent about every pump, even if we
     // end up pumping nothing.  It can filter out empty pumps itself.
@@ -188,13 +206,17 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
 
     HTMLParserScheduler::PumpSession session;
     // FIXME: This loop body has is now too long and needs cleanup.
-    while (mode == ForceSynchronous || (!m_parserStopped && m_parserScheduler->shouldContinueParsing(session))) {
+    while (mode == ForceSynchronous || m_parserScheduler->shouldContinueParsing(session)) {
         if (!m_tokenizer->nextToken(m_input.current(), m_token))
             break;
 
         m_treeBuilder->constructTreeFromToken(m_token);
         m_token.clear();
 
+        // JavaScript may have stopped or detached the parser.
+        if (!m_document || m_parserStopped)
+            return;
+
         // The parser will pause itself when waiting on a script to load or run.
         if (!m_treeBuilder->isPaused())
             continue;
@@ -202,10 +224,19 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
         // If we're paused waiting for a script, we try to execute scripts before continuing.
         bool shouldContinueParsing = runScriptsForPausedTreeBuilder();
         m_treeBuilder->setPaused(!shouldContinueParsing);
+
+        // JavaScript may have stopped or detached the parser.
+        if (!m_document || m_parserStopped)
+            return;
+
         if (!shouldContinueParsing)
             break;
     }
 
+    // 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);
+
     if (isWaitingForScripts()) {
         ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
         if (!m_preloadScanner) {
@@ -247,6 +278,10 @@ void HTMLDocumentParser::insert(const SegmentedString& source)
     if (m_parserStopped)
         return;
 
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     {
         NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
 
@@ -264,6 +299,10 @@ void HTMLDocumentParser::append(const SegmentedString& source)
     if (m_parserStopped)
         return;
 
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     {
         NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
 
@@ -286,7 +325,13 @@ void HTMLDocumentParser::append(const SegmentedString& source)
 
 void HTMLDocumentParser::end()
 {
+    ASSERT(m_document);
     ASSERT(!isScheduledForResume());
+
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     // NOTE: This pump should only ever emit buffered character tokens,
     // so ForceSynchronous vs. AllowYield should be meaningless.
     pumpTokenizerIfPossible(ForceSynchronous);
@@ -309,6 +354,10 @@ void HTMLDocumentParser::attemptToEnd()
 
 void HTMLDocumentParser::endIfDelayed()
 {
+    // If we've already been detached, don't bother ending.
+    if (!m_document)
+        return;
+
     if (!m_endWasDelayed || shouldDelayEnd())
         return;
 
@@ -318,6 +367,11 @@ void HTMLDocumentParser::endIfDelayed()
 
 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 Document::finishParsing unconditionally
+    // which in turn calls m_parser->finish().
+
     // 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().
@@ -398,6 +452,10 @@ bool HTMLDocumentParser::shouldLoadExternalScriptFromSrc(const AtomicString& src
 
 void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource)
 {
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     ASSERT(m_scriptRunner);
     ASSERT(!inScriptExecution());
     ASSERT(m_treeBuilder->isPaused());
@@ -420,6 +478,11 @@ void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
     // is a re-entrant call from encountering a </ style> tag.
     if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
         return;
+
+    // pumpTokenizer can cause this parser to be detached from the Document,
+    // but we need to ensure it isn't deleted yet.
+    RefPtr<HTMLDocumentParser> protect(this);
+
     ASSERT(!m_scriptRunner->isExecutingScript());
     ASSERT(m_treeBuilder->isPaused());
     // Note: We only ever wait on one script at a time, so we always know this
@@ -438,10 +501,11 @@ ScriptController* HTMLDocumentParser::script() const
 
 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
 {
-    HTMLDocumentParser parser(fragment, contextElement, scriptingPermission);
-    parser.insert(source); // Use insert() so that the parser will not yield.
-    parser.finish();
-    ASSERT(!parser.processingData()); // Make sure we're done. <rdar://problem/3963151>
+    RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, scriptingPermission);
+    parser->insert(source); // Use insert() so that the parser will not yield.
+    parser->finish();
+    ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151>
+    parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
 }
 
 }
index d35cfaf..ee7439d 100644 (file)
@@ -52,9 +52,15 @@ class ScriptSourceCode;
 
 class HTMLDocumentParser :  public ScriptableDocumentParser, HTMLScriptRunnerHost, CachedResourceClient {
 public:
-    // FIXME: These constructors should be made private and replaced by create() methods.
-    HTMLDocumentParser(HTMLDocument*, bool reportErrors);
-    HTMLDocumentParser(DocumentFragment*, Element* contextElement, FragmentScriptingPermission);
+    static PassRefPtr<HTMLDocumentParser> create(HTMLDocument* document, bool reportErrors)
+    {
+        return adoptRef(new HTMLDocumentParser(document, reportErrors));
+    }
+    static PassRefPtr<HTMLDocumentParser> create(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission permission)
+    {
+        return adoptRef(new HTMLDocumentParser(fragment, contextElement, permission));
+    }
+
     virtual ~HTMLDocumentParser();
 
     // Exposed for HTMLParserScheduler
@@ -66,8 +72,12 @@ protected:
     virtual void insert(const SegmentedString&);
     virtual void finish();
 
+    HTMLDocumentParser(HTMLDocument*, bool reportErrors);
+    HTMLDocumentParser(DocumentFragment*, Element* contextElement, FragmentScriptingPermission);
+
 private:
     // DocumentParser
+    virtual void detach();
     virtual bool hasInsertionPoint();
     virtual void append(const SegmentedString&);
     virtual bool finishWasCalled();
index 0d603ed..6d470a0 100644 (file)
@@ -77,6 +77,11 @@ HTMLScriptRunner::~HTMLScriptRunner()
         stopWatchingForLoad(m_parsingBlockingScript);
 }
 
+void HTMLScriptRunner::detach()
+{
+    m_document = 0;
+}
+
 static KURL documentURLForScriptExecution(Document* document)
 {
     if (!document || !document->frame())
@@ -119,6 +124,7 @@ bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script)
 
 void HTMLScriptRunner::executeParsingBlockingScript()
 {
+    ASSERT(m_document);
     ASSERT(!m_scriptNestingLevel);
     ASSERT(m_document->haveStylesheetsLoaded());
     ASSERT(isPendingScriptReady(m_parsingBlockingScript));
@@ -152,6 +158,7 @@ void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendi
 
 void HTMLScriptRunner::executeScript(Element* element, const ScriptSourceCode& sourceCode) const
 {
+    ASSERT(m_document);
     ScriptElement* scriptElement = toScriptElement(element);
     ASSERT(scriptElement);
     if (!scriptElement->shouldExecuteAsJavaScript())
@@ -222,6 +229,7 @@ bool HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript
 
 bool HTMLScriptRunner::executeScriptsWaitingForStylesheets()
 {
+    ASSERT(m_document);
     // Callers should check hasScriptsWaitingForStylesheets() before calling
     // to prevent parser or script re-entry during </style> parsing.
     ASSERT(hasScriptsWaitingForStylesheets());
@@ -269,6 +277,7 @@ bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Elemen
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#running-a-script
 void HTMLScriptRunner::runScript(Element* script, int startingLineNumber)
 {
+    ASSERT(m_document);
     ASSERT(!haveParsingBlockingScript());
     {
         InsertionPointRecord insertionPointRecord(m_host->inputStream());
index 981d433..262f1a2 100644 (file)
@@ -45,6 +45,8 @@ public:
     HTMLScriptRunner(Document*, HTMLScriptRunnerHost*);
     ~HTMLScriptRunner();
 
+    void detach();
+
     // Processes the passed in script and any pending scripts if possible.
     bool execute(PassRefPtr<Element> scriptToProcess, int scriptStartLine);
 
index fd0b62e..78055f7 100644 (file)
@@ -426,6 +426,16 @@ HTMLTreeBuilder::~HTMLTreeBuilder()
 {
 }
 
+void HTMLTreeBuilder::detach()
+{
+    // This call makes little sense in fragment mode, but for consistency
+    // DocumentParser expects detach() to always be called before it's destroyed.
+    m_document = 0;
+    // HTMLConstructionSite might be on the callstack when detach() is called
+    // otherwise we'd just call m_tree.clear() here instead.
+    m_tree.detach();
+}
+
 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
     : m_fragment(0)
     , m_contextElement(0)
@@ -538,6 +548,7 @@ HTMLTokenizer::State HTMLTreeBuilder::adjustedLexerState(HTMLTokenizer::State st
 
 void HTMLTreeBuilder::passTokenToLegacyParser(HTMLToken& token)
 {
+    ASSERT(m_document);
     if (token.type() == HTMLToken::DOCTYPE) {
         DoctypeToken doctypeToken;
         doctypeToken.m_name.append(token.name().data(), token.name().size());
@@ -2935,6 +2946,7 @@ void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
 
 void HTMLTreeBuilder::finished()
 {
+    ASSERT(m_document);
     // We should call m_document->finishedParsing() here, except
     // m_legacyTreeBuilder->finished() does it for us.
     if (m_legacyTreeBuilder) {
@@ -2947,7 +2959,7 @@ void HTMLTreeBuilder::finished()
         return;
     }
 
-    // Warning, this may delete the parser, so don't try to do anything else after this.
+    // Warning, this may detach the parser. Do not do anything else after this.
     m_document->finishedParsing();
 }
 
index 24cb542..0f6e837 100644 (file)
@@ -57,6 +57,8 @@ public:
     HTMLTreeBuilder(HTMLTokenizer*, DocumentFragment*, Element* contextElement, FragmentScriptingPermission);
     ~HTMLTreeBuilder();
 
+    void detach();
+
     void setPaused(bool paused) { m_isPaused = paused; }
     bool isPaused() const { return m_isPaused; }
 
index e0e71ee..b307ea2 100644 (file)
@@ -53,7 +53,7 @@ HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, co
     setUsesBeforeAfterRules(true);
 }
 
-DocumentParser* HTMLViewSourceDocument::createParser()
+PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser()
 {
     // Use HTMLDocumentParser if applicable, otherwise use TextDocumentParser.
     if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type)
@@ -61,7 +61,7 @@ DocumentParser* HTMLViewSourceDocument::createParser()
         || m_type == "application/vnd.wap.xhtml+xml"
 #endif
         )
-        return new HTMLViewSourceParser(this);
+        return HTMLViewSourceParser::create(this);
 
     return createTextDocumentParser(this);
 }
index 8805848..f459016 100644 (file)
@@ -49,7 +49,7 @@ private:
     HTMLViewSourceDocument(Frame*, const KURL&, const String& mimeType);
 
     // Returns HTMLViewSourceParser or TextDocumentParser based on m_type.
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 
     void processDoctypeToken(const String& source, HTMLToken&);
     void processTagToken(const String& source, HTMLToken&);
index 3da4c23..2d69fe6 100644 (file)
 
 namespace WebCore {
 
+HTMLViewSourceParser::HTMLViewSourceParser(HTMLViewSourceDocument* document)
+    : DecodedDataDocumentParser(document)
+{
+}
+
 HTMLViewSourceParser::~HTMLViewSourceParser()
 {
 }
index 2571301..af333d8 100644 (file)
@@ -45,15 +45,15 @@ class ScriptSourceCode;
 
 class HTMLViewSourceParser :  public DecodedDataDocumentParser {
 public:
-    // FIXME: Make private with a create method.
-    HTMLViewSourceParser(HTMLViewSourceDocument* document)
-        : DecodedDataDocumentParser(document)
+    static PassRefPtr<HTMLViewSourceParser> create(HTMLViewSourceDocument* document)
     {
+        return adoptRef(new HTMLViewSourceParser(document));
     }
-
     virtual ~HTMLViewSourceParser();
 
 private:
+    HTMLViewSourceParser(HTMLViewSourceDocument*);
+
     // DocumentParser
     virtual void insert(const SegmentedString&);
     virtual void append(const SegmentedString&);
index 5c7102c..6475ea9 100644 (file)
@@ -50,7 +50,10 @@ using namespace HTMLNames;
     
 class FTPDirectoryDocumentParser : public HTMLDocumentParser {
 public:
-    FTPDirectoryDocumentParser(HTMLDocument*);
+    static PassRefPtr<FTPDirectoryDocumentParser> create(HTMLDocument* document)
+    {
+        return adoptRef(new FTPDirectoryDocumentParser(document));
+    }
 
     virtual void append(const SegmentedString&);
     virtual void finish();
@@ -70,6 +73,8 @@ public:
     }
         
 private:
+    FTPDirectoryDocumentParser(HTMLDocument*);
+
     // The parser will attempt to load the document template specified via the preference
     // Failing that, it will fall back and create the basic document which will have a minimal
     // table for presenting the FTP directory in a useful manner
@@ -436,9 +441,9 @@ FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame, const KURL& url)
 #endif
 }
 
-DocumentParser* FTPDirectoryDocument::createParser()
+PassRefPtr<DocumentParser> FTPDirectoryDocument::createParser()
 {
-    return new FTPDirectoryDocumentParser(this);
+    return FTPDirectoryDocumentParser::create(this);
 }
 
 }
index 920f870..e7e52f7 100644 (file)
@@ -40,7 +40,7 @@ public:
 
 private:
     FTPDirectoryDocument(Frame*, const KURL&);
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 };
     
 } // namespace WebCore
index b1e33f4..e5e70c7 100644 (file)
@@ -73,17 +73,22 @@ private:
     
 class ImageDocumentParser : public RawDataDocumentParser {
 public:
-    ImageDocumentParser(ImageDocument* document)
-        : RawDataDocumentParser(document)
+    static PassRefPtr<ImageDocumentParser> create(ImageDocument* document)
     {
+        return adoptRef(new ImageDocumentParser(document));
     }
 
     ImageDocument* document() const
     {
         return static_cast<ImageDocument*>(m_document);
     }
-
+    
 private:
+    ImageDocumentParser(ImageDocument* document)
+        : RawDataDocumentParser(document)
+    {
+    }
+
     virtual void appendBytes(DocumentWriter*, const char*, int, bool);
     virtual void finish();
 };
@@ -175,9 +180,9 @@ ImageDocument::ImageDocument(Frame* frame, const KURL& url)
     setParseMode(Compat);
 }
     
-DocumentParser* ImageDocument::createParser()
+PassRefPtr<DocumentParser> ImageDocument::createParser()
 {
-    return new ImageDocumentParser(this);
+    return ImageDocumentParser::create(this);
 }
 
 void ImageDocument::createDocumentStructure()
index e85b3ab..5d00bd6 100644 (file)
@@ -49,7 +49,7 @@ public:
 private:
     ImageDocument(Frame*, const KURL&);
 
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
     virtual bool isImageDocument() const { return true; }
     
     void createDocumentStructure();
index 97e1775..7e28d02 100644 (file)
@@ -47,13 +47,18 @@ using namespace HTMLNames;
 // FIXME: Share more code with PluginDocumentParser.
 class MediaDocumentParser : public RawDataDocumentParser {
 public:
+    static PassRefPtr<MediaDocumentParser> create(MediaDocument* document)
+    {
+        return adoptRef(new MediaDocumentParser(document));
+    }
+    
+private:
     MediaDocumentParser(Document* document)
         : RawDataDocumentParser(document)
         , m_mediaElement(0)
     {
     }
 
-private:
     virtual void appendBytes(DocumentWriter*, const char*, int, bool);
 
     void createDocumentStructure();
@@ -113,9 +118,9 @@ MediaDocument::~MediaDocument()
     ASSERT(!m_replaceMediaElementTimer.isActive());
 }
 
-DocumentParser* MediaDocument::createParser()
+PassRefPtr<DocumentParser> MediaDocument::createParser()
 {
-    return new MediaDocumentParser(this);
+    return MediaDocumentParser::create(this);
 }
 
 void MediaDocument::defaultEventHandler(Event* event)
index 5a8ec52..2d81296 100644 (file)
@@ -46,7 +46,7 @@ private:
     MediaDocument(Frame*, const KURL&);
 
     virtual bool isMediaDocument() const { return true; }        
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 
     virtual void defaultEventHandler(Event*);
 
index cca6894..54e686c 100644 (file)
@@ -43,15 +43,20 @@ using namespace HTMLNames;
 // FIXME: Share more code with MediaDocumentParser.
 class PluginDocumentParser : public RawDataDocumentParser {
 public:
+    static PassRefPtr<PluginDocumentParser> create(PluginDocument* document)
+    {
+        return adoptRef(new PluginDocumentParser(document));
+    }
+
+    static Widget* pluginWidgetFromDocument(Document*);
+    
+private:
     PluginDocumentParser(Document* document)
         : RawDataDocumentParser(document)
         , m_embedElement(0)
     {
     }
 
-    static Widget* pluginWidgetFromDocument(Document*);
-
-private:
     virtual void appendBytes(DocumentWriter*, const char*, int, bool);
 
     void createDocumentStructure();
@@ -130,9 +135,9 @@ PluginDocument::PluginDocument(Frame* frame, const KURL& url)
     setParseMode(Compat);
 }
     
-DocumentParser* PluginDocument::createParser()
+PassRefPtr<DocumentParser> PluginDocument::createParser()
 {
-    return new PluginDocumentParser(this);
+    return PluginDocumentParser::create(this);
 }
 
 Widget* PluginDocument::pluginWidget()
index 53dde65..3bb5d99 100644 (file)
@@ -46,7 +46,7 @@ public:
 private:
     PluginDocument(Frame*, const KURL&);
 
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 };
     
 }
index fb0ab94..262d318 100644 (file)
@@ -32,12 +32,17 @@ namespace WebCore {
 
 class SinkDocumentParser : public RawDataDocumentParser {
 public:
+    static PassRefPtr<SinkDocumentParser> create(SinkDocument* document)
+    {
+        return adoptRef(new SinkDocumentParser(document));
+    }
+    
+private:
     SinkDocumentParser(SinkDocument* document)
         : RawDataDocumentParser(document)
     {
     }
 
-private:
     // Ignore all data.
     virtual void appendBytes(DocumentWriter*, const char*, int, bool) { }
 };
@@ -48,9 +53,9 @@ SinkDocument::SinkDocument(Frame* frame, const KURL& url)
     setParseMode(Compat);
 }
 
-DocumentParser* SinkDocument::createParser()
+PassRefPtr<DocumentParser> SinkDocument::createParser()
 {
-    return new SinkDocumentParser(this);
+    return SinkDocumentParser::create(this);
 }
 
 } // namespace WebCore
index 61930d4..50152ff 100644 (file)
@@ -40,7 +40,7 @@ public:
 private:
     SinkDocument(Frame*, const KURL&);
     
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 };
 
 
index 6b53084..5e2b774 100644 (file)
@@ -42,11 +42,22 @@ using namespace HTMLNames;
 // which started the Tokenizer in the PlainText state.
 class TextDocumentParser : public DecodedDataDocumentParser {
 public:
-    TextDocumentParser(Document*);
+    static PassRefPtr<TextDocumentParser> create(Document* document)
+    {
+        return adoptRef(new TextDocumentParser(document));
+    }
+
+    static PassRefPtr<TextDocumentParser> create(HTMLViewSourceDocument* document)
+    {
+        return adoptRef(new TextDocumentParser(document));
+    }
+
     virtual ~TextDocumentParser();
+    
+private:
+    TextDocumentParser(Document*);
     TextDocumentParser(HTMLViewSourceDocument*);
 
-private:
     virtual void insert(const SegmentedString&);
     virtual void append(const SegmentedString&);
     virtual void finish();
@@ -193,14 +204,14 @@ TextDocument::TextDocument(Frame* frame, const KURL& url)
 {
 }
 
-DocumentParser* TextDocument::createParser()
+PassRefPtr<DocumentParser> TextDocument::createParser()
 {
-    return new TextDocumentParser(this);
+    return TextDocumentParser::create(this);
 }
 
-DocumentParser* createTextDocumentParser(HTMLViewSourceDocument* document)
+PassRefPtr<DocumentParser> createTextDocumentParser(HTMLViewSourceDocument* document)
 {
-    return new TextDocumentParser(document);
+    return TextDocumentParser::create(document);
 }
 
 }
index 8f58b69..d5bf153 100644 (file)
@@ -41,10 +41,10 @@ public:
 private:
     TextDocument(Frame*, const KURL&);
     
-    virtual DocumentParser* createParser();
+    virtual PassRefPtr<DocumentParser> createParser();
 };
 
-DocumentParser* createTextDocumentParser(HTMLViewSourceDocument*);
+PassRefPtr<DocumentParser> createTextDocumentParser(HTMLViewSourceDocument*);
 
 }