- 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());
}