Title: [205695] trunk
Revision
205695
Author
[email protected]
Date
2016-09-08 22:20:17 -0700 (Thu, 08 Sep 2016)

Log Message

ScriptRunner should be driven by PendingScript rather than ScriptElement
https://bugs.webkit.org/show_bug.cgi?id=161726

Reviewed by Ryosuke Niwa.

Source/WebCore:

Tests: js/dom/document-write-in-error-event.html
       js/dom/document-write-in-load-event.html

ScriptRunner is driven by ScriptElement::notifyFinished. While ScriptRunner is driven by this,
HTMLScriptRunner does not use it. Instead, HTMLScriptRunner uses PendingScriptClient. As a result,
ScriptElement::notifyFinished is used only when the script is annotated with "defer" or "async"
while all the external script will load the LoadableScript. It is confusing.
This patch removes ScriptElement::notifyFinished and use PendingScript's observability
in ScriptRunner instead.

This patch also fixes the behavior about ignore-destructive-writes counter[1]. When dispatching
the load and error events, this ignore-destructive-writes counter should not be incremeneted by
this execution. The added tests ensure this behavior.

[1]: https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block

* dom/ScriptElement.cpp: Drop LoadableScriptClient interface.
(WebCore::ScriptElement::prepareScript): Do not use addClient. ScriptRunner use PendingScript::{setClient,clearClient} instead.
(WebCore::ScriptElement::executeScriptForScriptRunner): IgnoreDestructiveWriteCountIncrementer will be done in ScriptElement::executeScript.
So no need to do it here, that's duplicated.
(WebCore::ScriptElement::~ScriptElement): Deleted. ScriptElement does not use addClient/removeClient.
(WebCore::ScriptElement::stopLoadRequest): Deleted.
(WebCore::ScriptElement::executeScriptForHTMLScriptRunner): Deleted. executeScriptForHTMLScriptRunner and executeScriptForScriptRunner are
merged into executeScriptForRunner.
(WebCore::ScriptElement::notifyFinished): Deleted.
* dom/ScriptElement.h:
(WebCore::ScriptElement::~ScriptElement):
(WebCore::ScriptElement::willExecuteInOrder): Used in ScriptRunner to determine whether the script is async or defer.
(WebCore::ScriptElement::willExecuteWhenDocumentFinishedParsing): Deleted.
* dom/ScriptRunner.cpp:
(WebCore::ScriptRunner::~ScriptRunner): HashSet's iterator will return `const PendingScript&`.
Another option is using HashSet<RefPtr<PendingScript>>. Here, we use a little bit weired const_cast.
(WebCore::ScriptRunner::queueScriptForExecution): Use PendingScript::setClient to wait loading.
(WebCore::ScriptRunner::notifyFinished): Notify the script ready here.
(WebCore::ScriptRunner::timerFired): Use executeScriptForScriptRunner.
(WebCore::ScriptRunner::notifyScriptReady): Deleted.
* dom/ScriptRunner.h:
* html/parser/HTMLScriptRunner.cpp:
(WebCore::HTMLScriptRunner::executePendingScriptAndDispatchEvent): Use executeScriptForScriptRunner.

LayoutTests:

* js/dom/document-write-in-error-event-expected.txt: Added.
* js/dom/document-write-in-error-event.html: Added.
* js/dom/document-write-in-load-event-expected.txt: Added.
* js/dom/document-write-in-load-event.html: Added.
* js/dom/script-tests/dummy.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (205694 => 205695)


--- trunk/LayoutTests/ChangeLog	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/LayoutTests/ChangeLog	2016-09-09 05:20:17 UTC (rev 205695)
@@ -1,3 +1,16 @@
+2016-09-08  Yusuke Suzuki  <[email protected]>
+
+        ScriptRunner should be driven by PendingScript rather than ScriptElement
+        https://bugs.webkit.org/show_bug.cgi?id=161726
+
+        Reviewed by Ryosuke Niwa.
+
+        * js/dom/document-write-in-error-event-expected.txt: Added.
+        * js/dom/document-write-in-error-event.html: Added.
+        * js/dom/document-write-in-load-event-expected.txt: Added.
+        * js/dom/document-write-in-load-event.html: Added.
+        * js/dom/script-tests/dummy.js: Added.
+
 2016-09-08  Chris Dumez  <[email protected]>
 
         Add support for HTMLIframeElement.allowFullscreen attribute

Added: trunk/LayoutTests/js/dom/document-write-in-error-event-expected.txt (0 => 205695)


--- trunk/LayoutTests/js/dom/document-write-in-error-event-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/dom/document-write-in-error-event-expected.txt	2016-09-09 05:20:17 UTC (rev 205695)
@@ -0,0 +1,4 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/dom/document-write-in-error-event.html (0 => 205695)


--- trunk/LayoutTests/js/dom/document-write-in-error-event.html	                        (rev 0)
+++ trunk/LayoutTests/js/dom/document-write-in-error-event.html	2016-09-09 05:20:17 UTC (rev 205695)
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src=""
+</head>
+<body _onload_="loaded()">
+<script>
+    window.jsTestIsAsync = true;
+    function loaded() {
+        let script = document.createElement("script");
+        script.src = ""
+        script.addEventListener('error', function () {
+            document.write("<script>finishJSTest()<\/script>");
+        }, false);
+        document.body.appendChild(script);
+    }
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/dom/document-write-in-load-event-expected.txt (0 => 205695)


--- trunk/LayoutTests/js/dom/document-write-in-load-event-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/js/dom/document-write-in-load-event-expected.txt	2016-09-09 05:20:17 UTC (rev 205695)
@@ -0,0 +1,4 @@
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/js/dom/document-write-in-load-event.html (0 => 205695)


--- trunk/LayoutTests/js/dom/document-write-in-load-event.html	                        (rev 0)
+++ trunk/LayoutTests/js/dom/document-write-in-load-event.html	2016-09-09 05:20:17 UTC (rev 205695)
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src=""
+</head>
+<body _onload_="loaded()">
+<script>
+    window.jsTestIsAsync = true;
+    function loaded() {
+        let script = document.createElement("script");
+        script.src = ""
+        script.addEventListener('load', function () {
+            document.write("<script>finishJSTest()<\/script>");
+        }, false);
+        document.body.appendChild(script);
+    }
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/js/dom/script-tests/dummy.js (0 => 205695)


--- trunk/LayoutTests/js/dom/script-tests/dummy.js	                        (rev 0)
+++ trunk/LayoutTests/js/dom/script-tests/dummy.js	2016-09-09 05:20:17 UTC (rev 205695)
@@ -0,0 +1 @@
+// Do nothing.

Modified: trunk/Source/WebCore/ChangeLog (205694 => 205695)


--- trunk/Source/WebCore/ChangeLog	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/ChangeLog	2016-09-09 05:20:17 UTC (rev 205695)
@@ -1,5 +1,52 @@
 2016-09-08  Yusuke Suzuki  <[email protected]>
 
+        ScriptRunner should be driven by PendingScript rather than ScriptElement
+        https://bugs.webkit.org/show_bug.cgi?id=161726
+
+        Reviewed by Ryosuke Niwa.
+
+        Tests: js/dom/document-write-in-error-event.html
+               js/dom/document-write-in-load-event.html
+
+        ScriptRunner is driven by ScriptElement::notifyFinished. While ScriptRunner is driven by this,
+        HTMLScriptRunner does not use it. Instead, HTMLScriptRunner uses PendingScriptClient. As a result,
+        ScriptElement::notifyFinished is used only when the script is annotated with "defer" or "async"
+        while all the external script will load the LoadableScript. It is confusing.
+        This patch removes ScriptElement::notifyFinished and use PendingScript's observability
+        in ScriptRunner instead.
+
+        This patch also fixes the behavior about ignore-destructive-writes counter[1]. When dispatching
+        the load and error events, this ignore-destructive-writes counter should not be incremeneted by
+        this execution. The added tests ensure this behavior.
+
+        [1]: https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
+
+        * dom/ScriptElement.cpp: Drop LoadableScriptClient interface.
+        (WebCore::ScriptElement::prepareScript): Do not use addClient. ScriptRunner use PendingScript::{setClient,clearClient} instead.
+        (WebCore::ScriptElement::executeScriptForScriptRunner): IgnoreDestructiveWriteCountIncrementer will be done in ScriptElement::executeScript.
+        So no need to do it here, that's duplicated.
+        (WebCore::ScriptElement::~ScriptElement): Deleted. ScriptElement does not use addClient/removeClient.
+        (WebCore::ScriptElement::stopLoadRequest): Deleted.
+        (WebCore::ScriptElement::executeScriptForHTMLScriptRunner): Deleted. executeScriptForHTMLScriptRunner and executeScriptForScriptRunner are
+        merged into executeScriptForRunner.
+        (WebCore::ScriptElement::notifyFinished): Deleted.
+        * dom/ScriptElement.h:
+        (WebCore::ScriptElement::~ScriptElement):
+        (WebCore::ScriptElement::willExecuteInOrder): Used in ScriptRunner to determine whether the script is async or defer.
+        (WebCore::ScriptElement::willExecuteWhenDocumentFinishedParsing): Deleted.
+        * dom/ScriptRunner.cpp:
+        (WebCore::ScriptRunner::~ScriptRunner): HashSet's iterator will return `const PendingScript&`.
+        Another option is using HashSet<RefPtr<PendingScript>>. Here, we use a little bit weired const_cast.
+        (WebCore::ScriptRunner::queueScriptForExecution): Use PendingScript::setClient to wait loading.
+        (WebCore::ScriptRunner::notifyFinished): Notify the script ready here.
+        (WebCore::ScriptRunner::timerFired): Use executeScriptForScriptRunner.
+        (WebCore::ScriptRunner::notifyScriptReady): Deleted.
+        * dom/ScriptRunner.h:
+        * html/parser/HTMLScriptRunner.cpp:
+        (WebCore::HTMLScriptRunner::executePendingScriptAndDispatchEvent): Use executeScriptForScriptRunner.
+
+2016-09-08  Yusuke Suzuki  <[email protected]>
+
         [WTF] HashTable's rehash is not compatible to Ref<T> and ASan
         https://bugs.webkit.org/show_bug.cgi?id=161763
 

Modified: trunk/Source/WebCore/dom/ScriptElement.cpp (205694 => 205695)


--- trunk/Source/WebCore/dom/ScriptElement.cpp	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/dom/ScriptElement.cpp	2016-09-09 05:20:17 UTC (rev 205695)
@@ -75,11 +75,6 @@
         m_startLineNumber = m_element.document().scriptableDocumentParser()->textPosition().m_line;
 }
 
-ScriptElement::~ScriptElement()
-{
-    stopLoadRequest();
-}
-
 bool ScriptElement::shouldCallFinishedInsertingSubtree(ContainerNode& insertionPoint)
 {
     return insertionPoint.inDocument() && !m_parserInserted;
@@ -245,11 +240,9 @@
         ASSERT(m_loadableScript);
         m_willExecuteInOrder = true;
         document.scriptRunner()->queueScriptForExecution(this, *m_loadableScript, ScriptRunner::IN_ORDER_EXECUTION);
-        m_loadableScript->addClient(*this);
     } else if (hasSourceAttribute()) {
         ASSERT(m_loadableScript);
         m_element.document().scriptRunner()->queueScriptForExecution(this, *m_loadableScript, ScriptRunner::ASYNC_EXECUTION);
-        m_loadableScript->addClient(*this);
     } else {
         // Reset line numbering for nested writes.
         TextPosition position = document.isInDocumentWrite() ? TextPosition() : scriptStartPosition;
@@ -336,15 +329,6 @@
     }
 }
 
-void ScriptElement::stopLoadRequest()
-{
-    if (m_loadableScript) {
-        if (!m_willBeParserExecuted)
-            m_loadableScript->removeClient(*this);
-        m_loadableScript = nullptr;
-    }
-}
-
 void ScriptElement::executeScriptAndDispatchEvent(LoadableScript& loadableScript)
 {
     if (Optional<LoadableScript::Error> error = loadableScript.wasErrored()) {
@@ -358,16 +342,8 @@
     }
 }
 
-void ScriptElement::executeScriptForScriptRunner(LoadableScript& loadableScript)
+void ScriptElement::executeScriptForScriptRunner(PendingScript& pendingScript)
 {
-    ASSERT(!m_willBeParserExecuted);
-    executeScriptAndDispatchEvent(loadableScript);
-    loadableScript.removeClient(*this);
-}
-
-void ScriptElement::executeScriptForHTMLScriptRunner(PendingScript& pendingScript)
-{
-    IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(&m_element.document());
     if (auto* loadableScript = pendingScript.loadableScript())
         executeScriptAndDispatchEvent(*loadableScript);
     else {
@@ -377,25 +353,6 @@
     }
 }
 
-void ScriptElement::notifyFinished(LoadableScript&)
-{
-    ASSERT(!m_willBeParserExecuted);
-
-    // LoadableScript possibly invokes this notifyFinished() more than
-    // once because ScriptElement doesn't unsubscribe itself from
-    // LoadableScript here and does it in execute() instead.
-    // We use m_loadableScript to check if this function is already called.
-    if (!m_loadableScript)
-        return;
-
-    if (m_willExecuteInOrder)
-        m_element.document().scriptRunner()->notifyScriptReady(this, ScriptRunner::IN_ORDER_EXECUTION);
-    else
-        m_element.document().scriptRunner()->notifyScriptReady(this, ScriptRunner::ASYNC_EXECUTION);
-
-    m_loadableScript = nullptr;
-}
-
 bool ScriptElement::ignoresLoadRequest() const
 {
     return m_alreadyStarted || m_isExternalScript || m_parserInserted || !m_element.inDocument();

Modified: trunk/Source/WebCore/dom/ScriptElement.h (205694 => 205695)


--- trunk/Source/WebCore/dom/ScriptElement.h	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/dom/ScriptElement.h	2016-09-09 05:20:17 UTC (rev 205695)
@@ -38,9 +38,9 @@
 class ScriptElement;
 class ScriptSourceCode;
 
-class ScriptElement : private LoadableScriptClient {
+class ScriptElement {
 public:
-    virtual ~ScriptElement();
+    virtual ~ScriptElement() { }
 
     Element& element() { return m_element; }
     const Element& element() const { return m_element; }
@@ -52,8 +52,7 @@
     WEBCORE_EXPORT String scriptContent() const;
     void executeScript(const ScriptSourceCode&);
 
-    void executeScriptForScriptRunner(LoadableScript&);
-    void executeScriptForHTMLScriptRunner(PendingScript&);
+    void executeScriptForScriptRunner(PendingScript&);
 
     // XML parser calls these
     virtual void dispatchLoadEvent() = 0;
@@ -63,6 +62,7 @@
     bool willBeParserExecuted() const { return m_willBeParserExecuted; }
     bool readyToBeParserExecuted() const { return m_readyToBeParserExecuted; }
     bool willExecuteWhenDocumentFinishedParsing() const { return m_willExecuteWhenDocumentFinishedParsing; }
+    bool willExecuteInOrder() const { return m_willExecuteInOrder; }
     LoadableScript* loadableScript() { return m_loadableScript.get(); }
 
 protected:
@@ -92,10 +92,7 @@
     CachedResourceHandle<CachedScript> requestScriptWithCache(const URL&, const String& nonceAttribute, const String& crossoriginAttribute);
 
     bool requestClassicScript(const String& sourceURL);
-    void stopLoadRequest();
 
-    void notifyFinished(LoadableScript&) override;
-
     virtual String sourceAttributeValue() const = 0;
     virtual String charsetAttributeValue() const = 0;
     virtual String typeAttributeValue() const = 0;

Modified: trunk/Source/WebCore/dom/ScriptRunner.cpp (205694 => 205695)


--- trunk/Source/WebCore/dom/ScriptRunner.cpp	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/dom/ScriptRunner.cpp	2016-09-09 05:20:17 UTC (rev 205695)
@@ -40,12 +40,20 @@
 
 ScriptRunner::~ScriptRunner()
 {
-    for (size_t i = 0; i < m_scriptsToExecuteSoon.size(); ++i)
+    for (auto& pendingScript : m_scriptsToExecuteSoon) {
+        UNUSED_PARAM(pendingScript);
         m_document.decrementLoadEventDelayCount();
-    for (size_t i = 0; i < m_scriptsToExecuteInOrder.size(); ++i)
+    }
+    for (auto& pendingScript : m_scriptsToExecuteInOrder) {
+        if (pendingScript->watchingForLoad())
+            pendingScript->clearClient();
         m_document.decrementLoadEventDelayCount();
-    for (unsigned i = 0; i < m_pendingAsyncScripts.size(); ++i)
+    }
+    for (auto& pendingScript : m_pendingAsyncScripts) {
+        if (pendingScript->watchingForLoad())
+            const_cast<PendingScript&>(pendingScript.get()).clearClient();
         m_document.decrementLoadEventDelayCount();
+    }
 }
 
 void ScriptRunner::queueScriptForExecution(ScriptElement* scriptElement, LoadableScript& loadableScript, ExecutionType executionType)
@@ -57,15 +65,17 @@
 
     m_document.incrementLoadEventDelayCount();
 
+    Ref<PendingScript> pendingScript = PendingScript::create(element, loadableScript);
     switch (executionType) {
     case ASYNC_EXECUTION:
-        m_pendingAsyncScripts.add(scriptElement, PendingScript::create(element, loadableScript));
+        m_pendingAsyncScripts.add(pendingScript.copyRef());
         break;
 
     case IN_ORDER_EXECUTION:
-        m_scriptsToExecuteInOrder.append(PendingScript::create(element, loadableScript));
+        m_scriptsToExecuteInOrder.append(pendingScript.copyRef());
         break;
     }
+    pendingScript->setClient(this);
 }
 
 void ScriptRunner::suspend()
@@ -79,18 +89,17 @@
         m_timer.startOneShot(0);
 }
 
-void ScriptRunner::notifyScriptReady(ScriptElement* scriptElement, ExecutionType executionType)
+void ScriptRunner::notifyFinished(PendingScript& pendingScript)
 {
-    switch (executionType) {
-    case ASYNC_EXECUTION:
-        ASSERT(m_pendingAsyncScripts.contains(scriptElement));
-        m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(scriptElement)->ptr());
-        break;
-
-    case IN_ORDER_EXECUTION:
+    auto* scriptElement = toScriptElementIfPossible(&pendingScript.element());
+    ASSERT(scriptElement);
+    if (scriptElement->willExecuteInOrder())
         ASSERT(!m_scriptsToExecuteInOrder.isEmpty());
-        break;
+    else {
+        ASSERT(m_pendingAsyncScripts.contains(pendingScript));
+        m_scriptsToExecuteSoon.append(m_pendingAsyncScripts.take(pendingScript)->ptr());
     }
+    pendingScript.clearClient();
     m_timer.startOneShot(0);
 }
 
@@ -116,7 +125,7 @@
         auto* scriptElement = toScriptElementIfPossible(&script->element());
         ASSERT(scriptElement);
         ASSERT(script->needsLoading());
-        scriptElement->executeScriptForScriptRunner(*script->loadableScript());
+        scriptElement->executeScriptForScriptRunner(*script);
         m_document.decrementLoadEventDelayCount();
     }
 }

Modified: trunk/Source/WebCore/dom/ScriptRunner.h (205694 => 205695)


--- trunk/Source/WebCore/dom/ScriptRunner.h	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/dom/ScriptRunner.h	2016-09-09 05:20:17 UTC (rev 205695)
@@ -25,8 +25,9 @@
 
 #pragma once
 
+#include "PendingScriptClient.h"
 #include "Timer.h"
-#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/Vector.h>
 
@@ -37,7 +38,7 @@
 class ScriptElement;
 class LoadableScript;
 
-class ScriptRunner {
+class ScriptRunner : private PendingScriptClient {
     WTF_MAKE_NONCOPYABLE(ScriptRunner); WTF_MAKE_FAST_ALLOCATED;
 public:
     explicit ScriptRunner(Document&);
@@ -53,10 +54,12 @@
 private:
     void timerFired();
 
+    void notifyFinished(PendingScript&) override;
+
     Document& m_document;
     Vector<Ref<PendingScript>> m_scriptsToExecuteInOrder;
     Vector<RefPtr<PendingScript>> m_scriptsToExecuteSoon; // http://www.whatwg.org/specs/web-apps/current-work/#set-of-scripts-that-will-execute-as-soon-as-possible
-    HashMap<ScriptElement*, Ref<PendingScript>> m_pendingAsyncScripts;
+    HashSet<Ref<PendingScript>> m_pendingAsyncScripts;
     Timer m_timer;
 };
 

Modified: trunk/Source/WebCore/html/parser/HTMLScriptRunner.cpp (205694 => 205695)


--- trunk/Source/WebCore/html/parser/HTMLScriptRunner.cpp	2016-09-09 05:11:25 UTC (rev 205694)
+++ trunk/Source/WebCore/html/parser/HTMLScriptRunner.cpp	2016-09-09 05:20:17 UTC (rev 205695)
@@ -121,7 +121,7 @@
 
     if (auto* scriptElement = toScriptElementIfPossible(&pendingScript->element())) {
         NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
-        scriptElement->executeScriptForHTMLScriptRunner(*pendingScript);
+        scriptElement->executeScriptForScriptRunner(*pendingScript);
     }
     ASSERT(!isExecutingScript());
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to