document.currentScript must be null when we're executing a script inside a shadow...
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 May 2016 18:19:44 +0000 (18:19 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 2 May 2016 18:19:44 +0000 (18:19 +0000)
https://bugs.webkit.org/show_bug.cgi?id=157245

Reviewed by Darin Adler.

Source/WebCore:

Fix the bug by not setting currentScript as spec'ed in HTML5 specification:
https://html.spec.whatwg.org/multipage/dom.html#dom-document-currentscript
https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
as of 3dc763829ca1598427b588cf08830c1e2af5a05c

New behavior matches that of Google Chrome Canary.

Test: fast/shadow-dom/Document-prototype-currentScript.html

* dom/CurrentScriptIncrementer.h:
(WebCore::CurrentScriptIncrementer::CurrentScriptIncrementer):
(WebCore::CurrentScriptIncrementer::~CurrentScriptIncrementer):
* dom/ScriptElement.cpp:
(WebCore::ScriptElement::executeScript):

LayoutTests:

Add a W3C style testharness.js test.

* fast/shadow-dom/Document-prototype-currentScript-expected.txt: Added.
* fast/shadow-dom/Document-prototype-currentScript.html: Added.
* fast/shadow-dom/resources/Document-prototype-currentScript-helper.js: Added.

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

LayoutTests/ChangeLog
LayoutTests/fast/shadow-dom/Document-prototype-currentScript-expected.txt [new file with mode: 0644]
LayoutTests/fast/shadow-dom/Document-prototype-currentScript.html [new file with mode: 0644]
LayoutTests/fast/shadow-dom/resources/Document-prototype-currentScript-helper.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/CurrentScriptIncrementer.h
Source/WebCore/dom/ScriptElement.cpp

index aef7bdd..00f6229 100644 (file)
@@ -1,3 +1,16 @@
+2016-05-01  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.currentScript must be null when we're executing a script inside a shadow tree
+        https://bugs.webkit.org/show_bug.cgi?id=157245
+
+        Reviewed by Darin Adler.
+
+        Add a W3C style testharness.js test.
+
+        * fast/shadow-dom/Document-prototype-currentScript-expected.txt: Added.
+        * fast/shadow-dom/Document-prototype-currentScript.html: Added.
+        * fast/shadow-dom/resources/Document-prototype-currentScript-helper.js: Added.
+
 2016-04-29  Alex Christensen  <achristensen@webkit.org>
 
         Do not reuse cache entries with conditional headers
diff --git a/LayoutTests/fast/shadow-dom/Document-prototype-currentScript-expected.txt b/LayoutTests/fast/shadow-dom/Document-prototype-currentScript-expected.txt
new file mode 100644 (file)
index 0000000..f10b8b7
--- /dev/null
@@ -0,0 +1,10 @@
+
+PASS document.currentScript must not to be set to a script element in a shadow tree in open mode 
+PASS document.currentScript must not to be set to a script element in a shadow tree in closed mode 
+PASS document.currentScript must be set to a script element that loads an external script in a document tree 
+PASS document.currentScript must be set to a script element that loads an external script in a document tree 
+PASS document.currentScript must not be set to a script element that loads an external script in an open shadow tree 
+PASS document.currentScript must not be set to a script element that loads an external script in a closed shadow tree 
+PASS document.currentScript must be set to a script element that loads an external script that was in an open shadow tree and then removed 
+PASS document.currentScript must be set to a script element that loads an external script that was in a closed shadow tree and then removed 
+
diff --git a/LayoutTests/fast/shadow-dom/Document-prototype-currentScript.html b/LayoutTests/fast/shadow-dom/Document-prototype-currentScript.html
new file mode 100644 (file)
index 0000000..2ff352b
--- /dev/null
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>DOM and Shadow DOM: Document.prototype.adoptNode</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="If the script element is in a document, then set the script element's node document's currentScript attribute to the script element.">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script id="outerScriptElement">
+
+var outerScriptElement = document.currentScript;
+
+function testInlineScript(mode)
+{
+    test(function () {
+        var host = document.createElement('div');
+        var shadowRoot = host.attachShadow({mode: mode});
+        var scriptElement = document.createElement('script');
+        scriptElement.textContent = 'assert_equals(document.currentScript, outerScriptElement)';
+        shadowRoot.appendChild(scriptElement);
+
+        assert_equals(document.currentScript, outerScriptElement,
+            'document.currentScript must be set to the currently excusing script element in a document tree before executing a script in a shadow tree');
+        document.body.appendChild(host);
+        assert_equals(document.currentScript, outerScriptElement,
+            'document.currentScript must be set to the currently excusing script element in a document tree after executing a script in a shadow tree');
+
+    }, 'document.currentScript must not to be set to a script element in a shadow tree in ' + mode + ' mode');
+}
+
+testInlineScript('open');
+testInlineScript('closed');
+
+var executeExternalScript = null;
+var testedScriptElement = null;
+function executeNextTest()
+{
+    var testCase = asyncScriptTests.shift();
+    var mode = testCase.mode;
+    var remove = testCase.mode;
+    if (!testCase)
+        return;
+
+    testCase.test.step(function () {
+        testedScriptElement = document.createElement('script');
+        testedScriptElement.src = 'resources/Document-prototype-currentScript-helper.js';
+
+        if (mode !== null) {
+            var host = document.createElement('div');
+            var shadowRoot = host.attachShadow({mode: mode});
+            shadowRoot.appendChild(testedScriptElement);
+            document.body.appendChild(host);
+        } else {
+            document.body.appendChild(testedScriptElement);
+        }
+
+        if (testCase.remove)
+            testedScriptElement.parentNode.removeChild(testedScriptElement);
+    });
+
+    executeExternalScript = function () {
+        testCase.test.step(function () {
+            assert_equals(document.currentScript, testCase.expected());
+        });
+        testCase.test.done();
+        setTimeout(executeNextTest, 1);
+    }
+}
+
+var asyncScriptTests = [
+    {
+        test: async_test('document.currentScript must be set to a script element that loads an external script in a document tree'),
+        mode: null, remove: false, expected: function () { return testedScriptElement; }},
+    {
+        test: async_test('document.currentScript must be set to a script element that loads an external script in a document tree'),
+        mode: null, remove: true, expected: function () { return testedScriptElement; }},
+    {
+        test: async_test('document.currentScript must not be set to a script element that loads an external script in an open shadow tree'),
+        mode: 'open', remove: false, expected: function () { return null; }},
+    {
+        test: async_test('document.currentScript must not be set to a script element that loads an external script in a closed shadow tree'),
+        mode: 'closed', remove: false, expected: function () { return null; }},
+    {
+        test: async_test('document.currentScript must be set to a script element that loads an external script that was in an open shadow tree and then removed'),
+        mode: 'open', remove: true, expected: function () { return testedScriptElement; }},
+    {
+        test: async_test('document.currentScript must be set to a script element that loads an external script that was in a closed shadow tree and then removed'),
+        mode: 'closed', remove: true, expected: function () { return testedScriptElement; }},
+];
+
+executeNextTest();
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/shadow-dom/resources/Document-prototype-currentScript-helper.js b/LayoutTests/fast/shadow-dom/resources/Document-prototype-currentScript-helper.js
new file mode 100644 (file)
index 0000000..c25693c
--- /dev/null
@@ -0,0 +1 @@
+executeExternalScript();
index 1aebef9..4bfd2f1 100644 (file)
@@ -1,3 +1,25 @@
+2016-05-01  Ryosuke Niwa  <rniwa@webkit.org>
+
+        document.currentScript must be null when we're executing a script inside a shadow tree
+        https://bugs.webkit.org/show_bug.cgi?id=157245
+
+        Reviewed by Darin Adler.
+
+        Fix the bug by not setting currentScript as spec'ed in HTML5 specification:
+        https://html.spec.whatwg.org/multipage/dom.html#dom-document-currentscript
+        https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
+        as of 3dc763829ca1598427b588cf08830c1e2af5a05c
+
+        New behavior matches that of Google Chrome Canary.
+
+        Test: fast/shadow-dom/Document-prototype-currentScript.html
+
+        * dom/CurrentScriptIncrementer.h:
+        (WebCore::CurrentScriptIncrementer::CurrentScriptIncrementer):
+        (WebCore::CurrentScriptIncrementer::~CurrentScriptIncrementer):
+        * dom/ScriptElement.cpp:
+        (WebCore::ScriptElement::executeScript):
+
 2016-04-29  Alex Christensen  <achristensen@webkit.org>
 
         Do not reuse cache entries with conditional headers
index 0523caa..e70c2d1 100644 (file)
@@ -37,23 +37,23 @@ namespace WebCore {
 class CurrentScriptIncrementer {
     WTF_MAKE_NONCOPYABLE(CurrentScriptIncrementer);
 public:
-    CurrentScriptIncrementer(Document& document, Element* element)
+    CurrentScriptIncrementer(Document& document, Element& element)
         : m_document(document)
-        , m_isHTMLScriptElement(is<HTMLScriptElement>(*element))
+        , m_isHTMLScriptElementOutsideShadowTree(is<HTMLScriptElement>(element) && !element.isInShadowTree())
     {
-        if (m_isHTMLScriptElement)
-            m_document.pushCurrentScript(downcast<HTMLScriptElement>(element));
+        if (m_isHTMLScriptElementOutsideShadowTree)
+            m_document.pushCurrentScript(&downcast<HTMLScriptElement>(element));
     }
 
     ~CurrentScriptIncrementer()
     {
-        if (m_isHTMLScriptElement)
+        if (m_isHTMLScriptElementOutsideShadowTree)
             m_document.popCurrentScript();
     }
 
 private:
     Document& m_document;
-    bool m_isHTMLScriptElement;
+    bool m_isHTMLScriptElementOutsideShadowTree;
 };
 
 }
index f9c70e3..2713392 100644 (file)
@@ -312,7 +312,7 @@ void ScriptElement::executeScript(const ScriptSourceCode& sourceCode)
     Ref<Document> document(m_element.document());
     if (Frame* frame = document->frame()) {
         IgnoreDestructiveWriteCountIncrementer ignoreDesctructiveWriteCountIncrementer(m_isExternalScript ? document.ptr() : nullptr);
-        CurrentScriptIncrementer currentScriptIncrementer(document, &m_element);
+        CurrentScriptIncrementer currentScriptIncrementer(document, m_element);
 
         // Create a script from the script element node, using the script
         // block's source and the script block's type.