https://bugs.webkit.org/show_bug.cgi?id=161651
Reviewed by Ryosuke Niwa.
Source/WebCore:
This patch cleans up XML document script handling by using PendingScript.
Previously, we directly used CachedScript. But it is not good since we
have PendingScript wrapper.
We also disable ES6 modules for non HTML document. While ES6 modules tag
requires "defer" semantics, "defer" semantics is not implemented in non
HTML documents. And ES6 module tag is only specified in whatwg HTML spec.
* dom/LoadableClassicScript.cpp:
(WebCore::LoadableClassicScript::execute):
* dom/ScriptElement.cpp:
(WebCore::ScriptElement::determineScriptType):
(WebCore::ScriptElement::prepareScript):
(WebCore::ScriptElement::executeClassicScript):
(WebCore::ScriptElement::executePendingScript):
(WebCore::ScriptElement::executeScript): Deleted.
(WebCore::ScriptElement::executeScriptForScriptRunner): Deleted.
* dom/ScriptElement.h:
* dom/ScriptRunner.cpp:
(WebCore::ScriptRunner::timerFired):
* html/parser/HTMLDocumentParser.cpp:
* html/parser/HTMLScriptRunner.cpp:
(WebCore::HTMLScriptRunner::executePendingScriptAndDispatchEvent):
(WebCore::HTMLScriptRunner::runScript):
* xml/parser/XMLDocumentParser.cpp:
(WebCore::XMLDocumentParser::notifyFinished):
* xml/parser/XMLDocumentParser.h:
* xml/parser/XMLDocumentParserLibxml2.cpp:
(WebCore::XMLDocumentParser::XMLDocumentParser):
(WebCore::XMLDocumentParser::~XMLDocumentParser):
(WebCore::XMLDocumentParser::endElementNs):
LayoutTests:
Add tests that ensure modules are not executed in XHTML documents.
* js/dom/modules/module-inline-dynamic-in-xhtml-expected.txt: Added.
* js/dom/modules/module-inline-dynamic-in-xhtml.xhtml: Added.
* js/dom/modules/module-inline-simple-in-xhtml-expected.txt: Added.
* js/dom/modules/module-inline-simple-in-xhtml.xhtml: Added.
* js/dom/modules/module-src-dynamic-in-xhtml-expected.txt: Added.
* js/dom/modules/module-src-dynamic-in-xhtml.xhtml: Added.
* js/dom/modules/module-src-simple-in-xhtml-expected.txt: Added.
* js/dom/modules/module-src-simple-in-xhtml.xhtml: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@208840
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2016-11-16 Yusuke Suzuki <utatane.tea@gmail.com>
+
+ [WebCore] Clean up script loading code in XML
+ https://bugs.webkit.org/show_bug.cgi?id=161651
+
+ Reviewed by Ryosuke Niwa.
+
+ Add tests that ensure modules are not executed in XHTML documents.
+
+ * js/dom/modules/module-inline-dynamic-in-xhtml-expected.txt: Added.
+ * js/dom/modules/module-inline-dynamic-in-xhtml.xhtml: Added.
+ * js/dom/modules/module-inline-simple-in-xhtml-expected.txt: Added.
+ * js/dom/modules/module-inline-simple-in-xhtml.xhtml: Added.
+ * js/dom/modules/module-src-dynamic-in-xhtml-expected.txt: Added.
+ * js/dom/modules/module-src-dynamic-in-xhtml.xhtml: Added.
+ * js/dom/modules/module-src-simple-in-xhtml-expected.txt: Added.
+ * js/dom/modules/module-src-simple-in-xhtml.xhtml: Added.
+
2016-11-16 Ryosuke Niwa <rniwa@webkit.org>
REGRESSION(r208082): 1% Speedometer regression on iOS
--- /dev/null
+Test dynamically added inlined module does not work in XHTML document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Module is not executed yet.
+PASS window.exportedCocoa is undefined
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+description('Test dynamically added inlined module does not work in XHTML document.');
+
+// Module will be executed asynchronously.
+window.jsTestIsAsync = true;
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+<script>
+debug('Module is not executed yet.');
+(function () {
+ var element = document.createElement("script");
+ element.textContent = `
+ import Cocoa from "./script-tests/module-inline-dynamic.js";
+ var cocoa = new Cocoa();
+
+ debug("Module execution is confined in the module environment.");
+ shouldBeEqualToString("typeof cocoa", "undefined");
+
+ window.exportedCocoa = cocoa;
+ shouldBeEqualToString("typeof exportedCocoa", "object");
+ shouldBeEqualToString("exportedCocoa.taste()", "awesome");
+ finishJSTest();
+ `;
+ element.type = "module";
+ document.body.appendChild(element);
+ setTimeout(function () {
+ shouldBe(`window.exportedCocoa`, `undefined`);
+ finishJSTest();
+ }, 100);
+} ());
+</script>
+</body>
+</html>
--- /dev/null
+Test inlined module does not work in XHTML document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Module is not executed yet.
+PASS window.exportedCocoa is undefined
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+description('Test inlined module does not work in XHTML document.');
+
+// Module will be executed asynchronously.
+window.jsTestIsAsync = true;
+</script>
+<script>
+debug('Module is not executed yet.');
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+<script type="module">
+import Cocoa from "./script-tests/module-inline-simple.js";
+var cocoa = new Cocoa();
+
+debug("Module execution is confined in the module environment.");
+shouldBeEqualToString("typeof cocoa", "undefined");
+
+window.exportedCocoa = cocoa;
+shouldBeEqualToString("typeof exportedCocoa", "object");
+shouldBeEqualToString("exportedCocoa.taste()", "awesome");
+finishJSTest();
+</script>
+<script>
+window.addEventListener('load', function () {
+ shouldBe(`window.exportedCocoa`, `undefined`);
+ finishJSTest();
+});
+</script>
+</body>
+</html>
--- /dev/null
+Test dynamically added module with "src" attribute does not work in XHTML document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Module is not executed yet.
+PASS window.exportedCocoa is undefined
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+description('Test dynamically added module with "src" attribute does not work in XHTML document.');
+
+// Module will be executed asynchronously.
+window.jsTestIsAsync = true;
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+<script>
+debug('Module is not executed yet.');
+(function () {
+ var element = document.createElement('script');
+ element.type = 'module';
+ element.src = './script-tests/module-src-dynamic.js';
+ document.body.appendChild(element);
+ setTimeout(function () {
+ shouldBe(`window.exportedCocoa`, `undefined`);
+ finishJSTest();
+ }, 100);
+}());
+</script>
+</body>
+</html>
--- /dev/null
+Test module with "src" attribute does not work in XHTML document.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Module is not executed yet.
+PASS window.exportedCocoa is undefined
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script src="../../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+description('Test module with "src" attribute does not work in XHTML document.');
+
+// Module will be executed asynchronously.
+window.jsTestIsAsync = true;
+</script>
+<script>
+debug('Module is not executed yet.');
+</script>
+<script src="../../../resources/js-test-post.js"></script>
+<script type="module" src="./script-tests/module-src-simple.js"></script>
+<script>
+window.addEventListener('load', function () {
+ shouldBe(`window.exportedCocoa`, `undefined`);
+ finishJSTest();
+});
+</script>
+</body>
+</html>
+2016-11-16 Yusuke Suzuki <utatane.tea@gmail.com>
+
+ [WebCore] Clean up script loading code in XML
+ https://bugs.webkit.org/show_bug.cgi?id=161651
+
+ Reviewed by Ryosuke Niwa.
+
+ This patch cleans up XML document script handling by using PendingScript.
+ Previously, we directly used CachedScript. But it is not good since we
+ have PendingScript wrapper.
+
+ We also disable ES6 modules for non HTML document. While ES6 modules tag
+ requires "defer" semantics, "defer" semantics is not implemented in non
+ HTML documents. And ES6 module tag is only specified in whatwg HTML spec.
+
+ * dom/LoadableClassicScript.cpp:
+ (WebCore::LoadableClassicScript::execute):
+ * dom/ScriptElement.cpp:
+ (WebCore::ScriptElement::determineScriptType):
+ (WebCore::ScriptElement::prepareScript):
+ (WebCore::ScriptElement::executeClassicScript):
+ (WebCore::ScriptElement::executePendingScript):
+ (WebCore::ScriptElement::executeScript): Deleted.
+ (WebCore::ScriptElement::executeScriptForScriptRunner): Deleted.
+ * dom/ScriptElement.h:
+ * dom/ScriptRunner.cpp:
+ (WebCore::ScriptRunner::timerFired):
+ * html/parser/HTMLDocumentParser.cpp:
+ * html/parser/HTMLScriptRunner.cpp:
+ (WebCore::HTMLScriptRunner::executePendingScriptAndDispatchEvent):
+ (WebCore::HTMLScriptRunner::runScript):
+ * xml/parser/XMLDocumentParser.cpp:
+ (WebCore::XMLDocumentParser::notifyFinished):
+ * xml/parser/XMLDocumentParser.h:
+ * xml/parser/XMLDocumentParserLibxml2.cpp:
+ (WebCore::XMLDocumentParser::XMLDocumentParser):
+ (WebCore::XMLDocumentParser::~XMLDocumentParser):
+ (WebCore::XMLDocumentParser::endElementNs):
+
2016-11-16 Chris Dumez <cdumez@apple.com>
Add Node::isDescendantOf() overload that takes in a reference
void LoadableClassicScript::execute(ScriptElement& scriptElement)
{
ASSERT(!error());
- scriptElement.executeScript(ScriptSourceCode(m_cachedScript.get(), JSC::SourceProviderSourceType::Program));
+ scriptElement.executeClassicScript(ScriptSourceCode(m_cachedScript.get(), JSC::SourceProviderSourceType::Program));
}
}
if (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))
return ScriptType::Classic;
+ // FIXME: XHTML spec defines "defer" attribute. But WebKit does not implement it for a long time.
+ // And module tag also uses defer attribute semantics. We disable script type="module" for non HTML document.
+ // Once "defer" is implemented, we can reconsider enabling modules in XHTML.
+ // https://bugs.webkit.org/show_bug.cgi?id=123387
+ if (!m_element.document().isHTMLDocument())
+ return Nullopt;
+
auto* settings = m_element.document().settings();
if (!settings || !settings->es6ModulesEnabled())
return Nullopt;
} else {
ASSERT(scriptType == ScriptType::Classic);
TextPosition position = document.isInDocumentWrite() ? TextPosition() : scriptStartPosition;
- executeScript(ScriptSourceCode(scriptContent(), document.url(), position, JSC::SourceProviderSourceType::Program));
+ executeClassicScript(ScriptSourceCode(scriptContent(), document.url(), position, JSC::SourceProviderSourceType::Program));
}
return true;
return document.cachedResourceLoader().requestScript(WTFMove(request));
}
-void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
+void ScriptElement::executeClassicScript(const ScriptSourceCode& sourceCode)
{
ASSERT(m_alreadyStarted);
}
}
-void ScriptElement::executeScriptForScriptRunner(PendingScript& pendingScript)
+void ScriptElement::executePendingScript(PendingScript& pendingScript)
{
if (auto* loadableScript = pendingScript.loadableScript())
executeScriptAndDispatchEvent(*loadableScript);
else {
ASSERT(!pendingScript.error());
- JSC::SourceProviderSourceType sourceType = scriptType() == ScriptType::Module ? JSC::SourceProviderSourceType::Module : JSC::SourceProviderSourceType::Program;
- executeScript(ScriptSourceCode(scriptContent(), m_element.document().url(), pendingScript.startingPosition(), sourceType));
+ ASSERT_WITH_MESSAGE(scriptType() == ScriptType::Classic, "Module script always have a loadableScript pointer.");
+ executeClassicScript(ScriptSourceCode(scriptContent(), m_element.document().url(), pendingScript.startingPosition(), JSC::SourceProviderSourceType::Program));
dispatchLoadEvent();
}
}
String scriptCharset() const { return m_characterEncoding; }
WEBCORE_EXPORT String scriptContent() const;
- void executeScript(const ScriptSourceCode&);
+ void executeClassicScript(const ScriptSourceCode&);
void executeModuleScript(CachedModuleScript&);
- void executeScriptForScriptRunner(PendingScript&);
+ void executePendingScript(PendingScript&);
// XML parser calls these
virtual void dispatchLoadEvent() = 0;
auto* scriptElement = toScriptElementIfPossible(&script->element());
ASSERT(scriptElement);
ASSERT(script->needsLoading());
- scriptElement->executeScriptForScriptRunner(*script);
+ scriptElement->executePendingScript(*script);
m_document.decrementLoadEventDelayCount();
}
}
#include "config.h"
#include "HTMLDocumentParser.h"
-#include "CachedScript.h"
#include "DocumentFragment.h"
#include "Frame.h"
#include "HTMLDocument.h"
if (auto* scriptElement = toScriptElementIfPossible(&pendingScript->element())) {
NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel);
- scriptElement->executeScriptForScriptRunner(*pendingScript);
+ scriptElement->executePendingScript(*pendingScript);
}
ASSERT(!isExecutingScript());
}
if (m_scriptNestingLevel == 1)
m_parserBlockingScript = PendingScript::create(*script, scriptStartPosition);
else
- scriptElement->executeScript(ScriptSourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition));
+ scriptElement->executeClassicScript(ScriptSourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition));
} else
requestParsingBlockingScript(script);
}
#include "XMLDocumentParser.h"
#include "CDATASection.h"
-#include "CachedScript.h"
#include "Comment.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "HTMLNames.h"
#include "HTMLStyleElement.h"
#include "ImageLoader.h"
+#include "PendingScript.h"
#include "ProcessingInstruction.h"
#include "ResourceError.h"
#include "ResourceRequest.h"
m_xmlErrors->insertErrorMessageBlock();
}
-void XMLDocumentParser::notifyFinished(CachedResource& unusedResource)
+void XMLDocumentParser::notifyFinished(PendingScript& pendingScript)
{
- ASSERT_UNUSED(unusedResource, &unusedResource == m_pendingScript);
- ASSERT(m_pendingScript->accessCount() > 0);
-
- // FIXME: Support ES6 modules in XML document.
- // https://bugs.webkit.org/show_bug.cgi?id=161651
- ScriptSourceCode sourceCode(m_pendingScript.get(), JSC::SourceProviderSourceType::Program);
- bool errorOccurred = m_pendingScript->errorOccurred();
- bool wasCanceled = m_pendingScript->wasCanceled();
-
- m_pendingScript->removeClient(*this);
- m_pendingScript = nullptr;
-
- RefPtr<Element> e = m_scriptElement;
- m_scriptElement = nullptr;
-
- ScriptElement* scriptElement = toScriptElementIfPossible(e.get());
- ASSERT(scriptElement);
+ ASSERT(&pendingScript == m_pendingScript.get());
// JavaScript can detach this parser, make sure it's kept alive even if detached.
Ref<XMLDocumentParser> protectedThis(*this);
-
- if (errorOccurred)
- scriptElement->dispatchErrorEvent();
- else if (!wasCanceled) {
- scriptElement->executeScript(sourceCode);
- scriptElement->dispatchLoadEvent();
- }
- m_scriptElement = nullptr;
+ m_pendingScript = nullptr;
+ pendingScript.clearClient();
+
+ auto& scriptElement = *toScriptElementIfPossible(&pendingScript.element());
+ scriptElement.executePendingScript(pendingScript);
if (!isDetached() && !m_requestingScript)
resumeParsing();
#pragma once
-#include "CachedResourceClient.h"
-#include "CachedResourceHandle.h"
#include "FragmentScriptingPermission.h"
+#include "PendingScriptClient.h"
#include "ScriptableDocumentParser.h"
#include "SegmentedString.h"
#include "XMLErrors.h"
namespace WebCore {
class ContainerNode;
-class CachedScript;
class CachedResourceLoader;
class DocumentFragment;
class Document;
class Element;
class FrameView;
class PendingCallbacks;
+class PendingScript;
class Text;
class XMLParserContext : public RefCounted<XMLParserContext> {
xmlParserCtxtPtr m_context;
};
- class XMLDocumentParser final : public ScriptableDocumentParser, public CachedResourceClient {
+ class XMLDocumentParser final : public ScriptableDocumentParser, public PendingScriptClient {
WTF_MAKE_FAST_ALLOCATED;
public:
static Ref<XMLDocumentParser> create(Document& document, FrameView* view)
TextPosition textPosition() const override;
bool shouldAssociateConsoleMessagesWithTextPosition() const override;
- // from CachedResourceClient
- void notifyFinished(CachedResource&) final;
+ void notifyFinished(PendingScript&) final;
void end();
std::unique_ptr<XMLErrors> m_xmlErrors;
- CachedResourceHandle<CachedScript> m_pendingScript;
- RefPtr<Element> m_scriptElement;
+ RefPtr<PendingScript> m_pendingScript;
TextPosition m_scriptStartPosition;
bool m_parsingFragment;
#include "XMLDocumentParser.h"
#include "CDATASection.h"
-#include "CachedScript.h"
#include "Comment.h"
#include "CachedResourceLoader.h"
#include "Document.h"
#include "HTMLTemplateElement.h"
#include "LoadableClassicScript.h"
#include "Page.h"
+#include "PendingScript.h"
#include "ProcessingInstruction.h"
#include "ResourceError.h"
#include "ResourceRequest.h"
, m_parserPaused(false)
, m_requestingScript(false)
, m_finishCalled(false)
- , m_pendingScript(nullptr)
, m_scriptStartPosition(TextPosition::belowRangePosition())
, m_parsingFragment(false)
{
, m_parserPaused(false)
, m_requestingScript(false)
, m_finishCalled(false)
- , m_pendingScript(0)
, m_scriptStartPosition(TextPosition::belowRangePosition())
, m_parsingFragment(true)
{
// FIXME: m_pendingScript handling should be moved into XMLDocumentParser.cpp!
if (m_pendingScript)
- m_pendingScript->removeClient(*this);
+ m_pendingScript->clearClient();
}
void XMLDocumentParser::doWrite(const String& parseString)
// the libxml2 and Qt XMLDocumentParser implementations.
if (scriptElement->readyToBeParserExecuted())
- scriptElement->executeScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
- else if (scriptElement->willBeParserExecuted() && scriptElement->loadableScript() && is<LoadableClassicScript>(*scriptElement->loadableScript())) {
- // FIXME: Allow "module" scripts for XML documents.
- // https://bugs.webkit.org/show_bug.cgi?id=161651
- m_pendingScript = &downcast<LoadableClassicScript>(*scriptElement->loadableScript()).cachedScript();
- m_scriptElement = &element;
- m_pendingScript->addClient(*this);
-
- // m_pendingScript will be 0 if script was already loaded and addClient() executed it.
+ scriptElement->executeClassicScript(ScriptSourceCode(scriptElement->scriptContent(), document()->url(), m_scriptStartPosition));
+ else if (scriptElement->willBeParserExecuted() && scriptElement->loadableScript()) {
+ m_pendingScript = PendingScript::create(element, *scriptElement->loadableScript());
+ m_pendingScript->setClient(this);
+
+ // m_pendingScript will be nullptr if script was already loaded and setClient() executed it.
if (m_pendingScript)
pauseParsing();
- } else
- m_scriptElement = nullptr;
+ }
// JavaScript may have detached the parser
if (isDetached())