Resync WPT shadow DOM tests
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Oct 2019 02:00:13 +0000 (02:00 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 4 Oct 2019 02:00:13 +0000 (02:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=202497

Rubber-stamped by Sam Weinig.

LayoutTests/imported/w3c:

Sync shadow DOM tests as of e33c216e635b4615e4332953fcb758f698ecd510. Also added enough support for
sending key events so that sequential focusing tests would work.

* web-platform-tests/resources/testdriver-vendor.js:
* web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint-expected.txt:
* web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html:
* web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element.html:
* web-platform-tests/shadow-dom/Element-interface-attachShadow-expected.txt:
* web-platform-tests/shadow-dom/Element-interface-attachShadow.html:
* web-platform-tests/shadow-dom/META.yml: Added.
* web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt: Added.
* web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html: Added.
* web-platform-tests/shadow-dom/directionality-002.tentative-expected.html: Added.
* web-platform-tests/shadow-dom/directionality-002.tentative.html: Added.
* web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html: Added.
* web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one-expected.txt: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html: Added.
* web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html: Added.
* web-platform-tests/shadow-dom/focus/resources/shadow-utils.js: Added.
(prepareDOM):
(setTabIndex):
(removeTabIndex):
(resetFocus):
(navigateFocusForward):
(async.assertFocusOrder):
* web-platform-tests/shadow-dom/focus/resources/w3c-import.log: Added.
* web-platform-tests/shadow-dom/focus/w3c-import.log: Added.
* web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt: Added.
* web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html: Added.
* web-platform-tests/shadow-dom/resources/shadow-dom.js:
(createTestTree):
* web-platform-tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html:
* web-platform-tests/shadow-dom/w3c-import.log:

LayoutTests:

Added a failing expectation. Also skipped focus tests on iOS for now since sending tab key via UIController
doesn't seem to move the focus on iOS somehow even though manual testing seems to work.

* TestExpectations:
* platform/ios/TestExpectations:
* /platform/mac-highsierra-wk1/imported/w3c/web-platform-tests/mathml/relations/html5-tree/tabindex-002-expected.txt: Added.

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

43 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/resources/testdriver-vendor.js
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint-expected.txt
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element-expected.txt
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element.html
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow-expected.txt
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Element-interface-attachShadow.html
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/META.yml [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative-expected.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/shadow-utils.js [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/w3c-import.log [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/w3c-import.log [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/resources/shadow-dom.js
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
LayoutTests/imported/w3c/web-platform-tests/shadow-dom/w3c-import.log
LayoutTests/platform/ios/TestExpectations
LayoutTests/platform/mac-highsierra-wk1/imported/w3c/web-platform-tests/mathml/relations/html5-tree/tabindex-002-expected.txt [new file with mode: 0644]

index 956cdd4..f187a77 100644 (file)
@@ -1,3 +1,17 @@
+2019-10-03  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Resync WPT shadow DOM tests
+        https://bugs.webkit.org/show_bug.cgi?id=202497
+
+        Rubber-stamped by Sam Weinig.
+
+        Added a failing expectation. Also skipped focus tests on iOS for now since sending tab key via UIController
+        doesn't seem to move the focus on iOS somehow even though manual testing seems to work.
+
+        * TestExpectations:
+        * platform/ios/TestExpectations:
+        * /platform/mac-highsierra-wk1/imported/w3c/web-platform-tests/mathml/relations/html5-tree/tabindex-002-expected.txt: Added.
+
 2019-10-03  Chris Dumez  <cdumez@apple.com>
 
         Allow pages using FontFaceSet to enter the back/forward cache
index b7f89aa..61862e3 100644 (file)
@@ -263,6 +263,8 @@ imported/w3c/web-platform-tests/html/browsers/history/the-location-interface/loc
 imported/w3c/web-platform-tests/html/browsers/history/the-location-interface/location-protocol-setter-non-broken-weird.html [ DumpJSConsoleLogInStdErr ]
 imported/w3c/web-platform-tests/service-workers/service-worker/fetch-cors-xhr.https.html [ DumpJSConsoleLogInStdErr ]
 
+webkit.org/b/202495 imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative.html [ ImageOnlyFailure ]
+
 # Newly imported WPT tests that are timing out.
 imported/w3c/web-platform-tests/html/browsers/history/the-location-interface/location-origin-idna.sub.window.html [ Skip ]
 imported/w3c/web-platform-tests/html/browsers/history/the-location-interface/location_assign.html [ Skip ]
index de3be02..cae7c79 100644 (file)
@@ -1,3 +1,58 @@
+2019-10-03  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Resync WPT shadow DOM tests
+        https://bugs.webkit.org/show_bug.cgi?id=202497
+
+        Rubber-stamped by Sam Weinig.
+
+        Sync shadow DOM tests as of e33c216e635b4615e4332953fcb758f698ecd510. Also added enough support for
+        sending key events so that sequential focusing tests would work.
+
+        * web-platform-tests/resources/testdriver-vendor.js:
+        * web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint-expected.txt:
+        * web-platform-tests/shadow-dom/DocumentOrShadowRoot-prototype-elementFromPoint.html:
+        * web-platform-tests/shadow-dom/Element-interface-attachShadow-custom-element.html:
+        * web-platform-tests/shadow-dom/Element-interface-attachShadow-expected.txt:
+        * web-platform-tests/shadow-dom/Element-interface-attachShadow.html:
+        * web-platform-tests/shadow-dom/META.yml: Added.
+        * web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt: Added.
+        * web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html: Added.
+        * web-platform-tests/shadow-dom/directionality-002.tentative-expected.html: Added.
+        * web-platform-tests/shadow-dom/directionality-002.tentative.html: Added.
+        * web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one-expected.txt: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html: Added.
+        * web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html: Added.
+        * web-platform-tests/shadow-dom/focus/resources/shadow-utils.js: Added.
+        (prepareDOM):
+        (setTabIndex):
+        (removeTabIndex):
+        (resetFocus):
+        (navigateFocusForward):
+        (async.assertFocusOrder):
+        * web-platform-tests/shadow-dom/focus/resources/w3c-import.log: Added.
+        * web-platform-tests/shadow-dom/focus/w3c-import.log: Added.
+        * web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt: Added.
+        * web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html: Added.
+        * web-platform-tests/shadow-dom/resources/shadow-dom.js:
+        (createTestTree):
+        * web-platform-tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html:
+        * web-platform-tests/shadow-dom/w3c-import.log:
+
 2019-10-03  Joonghun Park  <jh718.park@samsung.com>
 
         Unreviewed. Move below's shape-outside-foo wpt tests to its proper place.
index 5adc030..dc5bb1a 100644 (file)
@@ -136,6 +136,48 @@ function dispatchTouchActions(actions, options = { insertPauseAfterPointerUp: fa
 if (window.test_driver_internal === undefined)
     window.test_driver_internal = { };
 
+window.test_driver_internal.send_keys = function(element, keys)
+{
+    if (!window.eventSender)
+        return Promise.reject(new Error("window.eventSender is undefined."));
+
+    // https://seleniumhq.github.io/selenium/docs/api/py/webdriver/selenium.webdriver.common.keys.html
+    // FIXME: Add more cases.
+    // FIXME: Add the support for modifier keys.
+    const SeleniumCharCodeToEventSenderKey = {
+        0xE003: 'delete',
+        0xE004: '\t',
+        0xE00D: ' ',
+        0xE012: 'leftArrow',
+        0xE013: 'upArrow',
+        0xE014: 'rightArrow',
+        0xE015: 'downArrow',
+    };
+
+    function convertSeleniumKeyCode(key)
+    {
+        const code = key.charCodeAt(0);
+        return SeleniumCharCodeToEventSenderKey[code] || key;
+    }
+
+    const keyList = [];
+    for (const key of keys)
+        keyList.push(convertSeleniumKeyCode(key));
+
+    if (testRunner.isIOSFamily && testRunner.isWebKit2) {
+        return new Promise((resolve) => {
+            testRunner.runUIScript(`
+                const keyList = JSON.parse('${JSON.stringify(keyList)}');
+                for (const key of keyList)
+                    uiController.keyDown(key);`, resolve);
+        });
+    }
+
+    for (const key of keyList)
+        eventSender.keyDown(key);
+    return Promise.resolve();
+}
+
 window.test_driver_internal.action_sequence = function(sources)
 {
     // https://w3c.github.io/webdriver/#processing-actions    
index 37b12fd..9653383 100644 (file)
@@ -1,3 +1,4 @@
+foo
 
 PASS document.elementFromPoint and shadow.ElementFromPoint must return the shadow host of the hit-tested text node when the hit-tested text node is a direct child of the root and the host has display: inline 
 PASS document.elementFromPoint and shadow.ElementFromPoint must return the shadow host of the hit-tested text node when the hit-tested text node is a direct child of the root and the host has display: block 
@@ -39,4 +40,5 @@ PASS document.elementsFromPoint must return the shadow host and its ancestors an
 PASS document.elementsFromPoint must return the shadow host and its ancestors and shadowRoot.elementsFromPoint must return the slot parent of the fallback text and its non-shadow ancestors when the hit-tested text node is a fallback content and the host has display: block 
 PASS document.elementsFromPoint must return the shadow host and its ancestors and shadowRoot.elementsFromPoint must return the slot parent of the fallback text and its non-shadow ancestors when the hit-tested text node is a fallback content and the host has display: inline-block 
 FAIL shadowRoot.elementsFromPoint must behave the same with document.elementsFromPoint regarding HTML element assert_array_equals: lengths differ, expected 5 got 4
+FAIL elementsFromPoint should return all elements under a point, even when context object is not connected assert_equals: expected 4 but got 3
 
index 83cbc3d..6893ad3 100644 (file)
@@ -250,7 +250,30 @@ test(function () {
 
 container.innerHTML = '';
 
-    </script>
-  </body>
+test(function(t) {
+  const container = document.createElement("div");
+  const span = document.createElement("span");
+  span.innerText = "foo";
+  document.body.appendChild(container);
+  container.appendChild(span);
+
+  let elements = document.elementsFromPoint(...pointInElement(span));
+  assert_equals(elements.length, 4);
+  assert_equals(elements[0], span);
+  assert_equals(elements[1], container);
+  assert_equals(elements[2].nodeName, 'BODY');
+  assert_equals(elements[3].nodeName, 'HTML');
+
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  elements = shadowRoot.elementsFromPoint(...pointInElement(span));
+  assert_equals(elements.length, 4);
+  assert_equals(elements[0], span);
+  assert_equals(elements[1], container);
+  assert_equals(elements[2].nodeName, 'BODY');
+  assert_equals(elements[3].nodeName, 'HTML');
+}, "elementsFromPoint should return all elements under a point, even when context object is not connected");
+</script>
+</body>
 </html>
 
index 3ebd606..420da6b 100644 (file)
@@ -1,4 +1,12 @@
 
 PASS Element.attachShadow must create an instance of ShadowRoot for autonomous custom elements 
+PASS Element.attachShadow must create an instance of ShadowRoot for undefined autonomous custom elements 
+FAIL Element.attachShadow for an autonomous custom element with disabledFeatures=["shadow"] should throw a NotSupportedError assert_throws: No definition, host function "() => {
+    element.attachShadow({mode: 'closed'});
+  }" threw object "InvalidStateError: The object is in an invalid state." that is not a DOMException NotSupportedError: property "code" is equal to 11, expected 9
+FAIL Element.attachShadow for a customized built-in element with disabledFeatures=["shadow"] should throw a NotSupportedError assert_throws: No definition, host. function "() => {
+    element.attachShadow({mode: 'closed'});
+  }" threw object "InvalidStateError: The object is in an invalid state." that is not a DOMException NotSupportedError: property "code" is equal to 11, expected 9
+PASS Element.attachShadow for a custom element with disabledFeatures=["SHADOW"] should not throw a NotSupportedError 
 PASS Element.attachShadow must throw a NotSupportedError for customized built-in elements 
 
index b59460e..123d9c1 100644 (file)
@@ -14,6 +14,78 @@ test(() => {
   assert_true(document.createElement('my-custom').attachShadow({mode: "open"}) instanceof ShadowRoot);
 }, 'Element.attachShadow must create an instance of ShadowRoot for autonomous custom elements');
 
+test(() => {
+  assert_true(document.createElement('undefined-custom').attachShadow({mode: 'open'}) instanceof ShadowRoot);
+}, 'Element.attachShadow must create an instance of ShadowRoot for undefined autonomous custom elements');
+
+test(() => {
+  class ShadowDisabledElement extends HTMLElement {
+    static get disabledFeatures() { return ['shadow']; }
+  }
+
+  // No definition. attachShadow() should succeed.
+  let element = document.createElement('shadow-disabled-element');
+  element.attachShadow({mode: 'closed'});
+
+  // No definition and it's already a host.
+  assert_throws('NotSupportedError', () => {
+    element.attachShadow({mode: 'closed'});
+  }, 'No definition, host');
+
+  // The element has a definition, and it's already a host.
+  customElements.define('shadow-disabled-element', ShadowDisabledElement);
+  assert_throws('NotSupportedError', () => {
+    element.attachShadow({mode: 'closed'});
+  }, 'Definition, host');
+
+  // The element has a definition, and it's not a host.
+  assert_throws('NotSupportedError', () => {
+    document.createElement('shadow-disabled-element').attachShadow({mode: 'closed'});
+  }, 'Definition, not a host');
+}, 'Element.attachShadow for an autonomous custom element with ' +
+   'disabledFeatures=["shadow"] should throw a NotSupportedError');
+
+test(() => {
+  class ShadowDisabledHeadingElement extends HTMLHeadingElement {
+    static get disabledFeatures() { return ['shadow']; }
+  }
+
+  // No definition. attachShadow() should succeed.
+  let element = document.createElement('h2',
+      {is: 'shadow-disabled-heading-element'});
+  element.attachShadow({mode: 'closed'});
+
+  // No definition and it's already a host.
+  assert_throws('NotSupportedError', () => {
+    element.attachShadow({mode: 'closed'});
+  }, 'No definition, host.');
+
+  // The element has a definition, and it's already a host.
+  customElements.define('shadow-disabled-heading-element',
+      ShadowDisabledHeadingElement, {extends: 'h2'});
+  assert_throws('NotSupportedError', () => {
+    element.attachShadow({mode: 'closed'});
+  }, 'Definition, host');
+
+  // The element has a definition, and it's not a host.
+  let h2 = document.createElement('h2', {is: 'shadow-disabled-heading-element'});
+  assert_throws('NotSupportedError', () => {
+    h2.attachShadow({mode: 'closed'});
+  }, 'Definition, not a host');
+}, 'Element.attachShadow for a customized built-in element with ' +
+   'disabledFeatures=["shadow"] should throw a NotSupportedError');
+
+test(() => {
+  class CapitalShadowDisabledElement extends HTMLElement {
+    static get disabledFeatures() { return ['shadow']; }
+  }
+
+  customElements.define('capital-shadow-disabled-element', CapitalShadowDisabledElement);
+
+  // Test fails if this throws
+  document.createElement('capital-shadow-disabled-element').attachShadow({mode: 'open'});
+}, 'Element.attachShadow for a custom element with disabledFeatures=["SHADOW"] should not throw a NotSupportedError');
+
 class MyCustomizedBuiltinElement extends HTMLInputElement {
 }
 
index 0d40c7c..edc89e5 100644 (file)
@@ -3,6 +3,10 @@ PASS Check the existence of Element.attachShadow
 PASS Nodes other than Element should not have attachShadow 
 PASS Element.attachShadow must throw a TypeError if mode is not "open" or "closed" 
 PASS Element.attachShadow must create an instance of ShadowRoot 
-PASS Element.attachShadow must throw a InvalidStateError if the context object already hosts a shadow tree 
+FAIL Element.attachShadow must throw a NotSupportedError if the context object already hosts a shadow tree assert_throws: Calling attachShadow({mode: "open"}) twice on the same element must throw function "function () {
+        var div = document.createElement('div');
+        div.attachShadow({mode: "open"});
+        div.attachShadow({mode: "open"});
+    }" threw object "InvalidStateError: The object is in an invalid state." ("InvalidStateError") expected object "[object Object]" ("NotSupportedError")
 PASS Element.attachShadow must throw a NotSupportedError for non-safelisted elements 
 
index fbf20e8..304b64c 100644 (file)
@@ -53,30 +53,30 @@ test(function () {
 }, 'Element.attachShadow must create an instance of ShadowRoot');
 
 test(function () {
-    assert_throws({'name': 'InvalidStateError'}, function () {
+    assert_throws({'name': 'NotSupportedError'}, function () {
         var div = document.createElement('div');
         div.attachShadow({mode: "open"});
         div.attachShadow({mode: "open"});
     }, 'Calling attachShadow({mode: "open"}) twice on the same element must throw');
 
-    assert_throws({'name': 'InvalidStateError'}, function () {
+    assert_throws({'name': 'NotSupportedError'}, function () {
         var div = document.createElement('div');
         div.attachShadow({mode: "closed"});
         div.attachShadow({mode: "closed"});
     }, 'Calling attachShadow({mode: "closed"}) twice on the same element must throw');
 
-    assert_throws({'name': 'InvalidStateError'}, function () {
+    assert_throws({'name': 'NotSupportedError'}, function () {
         var div = document.createElement('div');
         div.attachShadow({mode: "open"});
         div.attachShadow({mode: "closed"});
     }, 'Calling attachShadow({mode: "closed"}) after attachShadow({mode: "open"}) on the same element must throw');
 
-    assert_throws({'name': 'InvalidStateError'}, function () {
+    assert_throws({'name': 'NotSupportedError'}, function () {
         var div = document.createElement('div');
         div.attachShadow({mode: "closed"});
         div.attachShadow({mode: "open"});
     }, 'Calling attachShadow({mode: "open"}) after attachShadow({mode: "closed"}) on the same element must throw');
-}, 'Element.attachShadow must throw a InvalidStateError if the context object already hosts a shadow tree');
+}, 'Element.attachShadow must throw a NotSupportedError if the context object already hosts a shadow tree');
 
 test(function () {
     for (var elementName of ATTACHSHADOW_DISALLOWED_ELEMENTS) {
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/META.yml b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/META.yml
new file mode 100644 (file)
index 0000000..8fb0a9b
--- /dev/null
@@ -0,0 +1,6 @@
+spec: https://dom.spec.whatwg.org/#shadow-trees
+suggested_reviewers:
+  - kojiishi
+  - rniwa
+  - takayoshikochi
+  - hayatoito
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees-expected.txt
new file mode 100644 (file)
index 0000000..c5f120a
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS Capturing event listeners should be invoked before bubbling event listeners on the target without shadow trees 
+PASS Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a shadow tree 
+PASS Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a doubly nested shadow tree 
+PASS Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched via a slot 
+PASS Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a shadow tree which passes through another shadow tree 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html
new file mode 100644 (file)
index 0000000..bcb4ee6
--- /dev/null
@@ -0,0 +1,191 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Shadow DOM: Capturing event listeners should be invoked before bubbling event listeners</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/shadow-dom.js"></script>
+<script>
+
+function attachEventListeners(eventType, tree) {
+    const eventLogs = [];
+    const makeComposedPathResult = (event) => event.composedPath().map((node) => node.id)
+    for (const id in tree) {
+        const node = tree[id];
+        node.addEventListener(eventType, event => eventLogs.push(
+            ['bubbling', event.eventPhase, event.target.id, event.currentTarget.id, makeComposedPathResult(event)]), {capture: false});
+        node.addEventListener(eventType, event => eventLogs.push(
+            ['capturing', event.eventPhase, event.target.id, event.currentTarget.id, makeComposedPathResult(event)]), {capture: true});
+    }
+    return eventLogs;
+}
+
+</script>
+</head>
+<body>
+
+<div id="test1">
+    <div id="parent">
+        <div id="target"></div>
+    </div>
+</div>
+<script>
+test(() => {
+    const tree = createTestTree(document.getElementById('test1'));
+    const logs = attachEventListeners('my-event', tree);
+    tree.target.dispatchEvent(new Event('my-event', { bubbles: true, composed: true }));
+
+    const composedPath = ['target', 'parent', 'test1'];
+    assert_object_equals(logs, [
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'test1', composedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'parent', composedPath],
+        ['capturing', Event.AT_TARGET, 'target', 'target', composedPath],
+        ['bubbling', Event.AT_TARGET, 'target', 'target', composedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'parent', composedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'test1', composedPath],
+    ]);
+}, 'Capturing event listeners should be invoked before bubbling event listeners on the target without shadow trees');
+</script>
+
+<div id="test2">
+    <div id="host">
+        <template id="shadowRoot" data-mode="closed">
+            <div id="target"></div>
+        </template>
+    </div>
+</div>
+<script>
+test(() => {
+    const tree = createTestTree(document.getElementById('test2'));
+    const logs = attachEventListeners('my-event', tree);
+    tree.target.dispatchEvent(new Event('my-event', { bubbles: true, composed: true }));
+
+    const innerComposedPath = ['target', 'shadowRoot', 'host', 'test2'];
+    const outerComposedPath = ['host', 'test2'];
+    assert_object_equals(logs, [
+        ['capturing', Event.CAPTURING_PHASE, 'host', 'test2', outerComposedPath],
+        ['capturing', Event.AT_TARGET, 'host', 'host', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'shadowRoot', innerComposedPath],
+        ['capturing', Event.AT_TARGET, 'target', 'target', innerComposedPath],
+        ['bubbling', Event.AT_TARGET, 'target', 'target', innerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'shadowRoot', innerComposedPath],
+        ['bubbling', Event.AT_TARGET, 'host', 'host', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'host', 'test2', outerComposedPath],
+    ]);
+}, 'Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a shadow tree');
+</script>
+
+<div id="test3">
+    <div id="outerHost">
+        <template id="outerShadowRoot" data-mode="closed">
+            <div id="innerHost">
+                <template id="innerShadowRoot" data-mode="closed">
+                    <div id="target"></div>
+                </template>
+            </div>
+        </template>
+    </div>
+</div>
+<script>
+test(() => {
+    const tree = createTestTree(document.getElementById('test3'));
+    const logs = attachEventListeners('my-event', tree);
+    tree.target.dispatchEvent(new Event('my-event', { bubbles: true, composed: true }));
+
+    const innerShadowComposedPath = ['target', 'innerShadowRoot', 'innerHost', 'outerShadowRoot', 'outerHost', 'test3'];
+    const outerShadowComposedPath = ['innerHost', 'outerShadowRoot', 'outerHost', 'test3'];
+    const outerComposedPath = ['outerHost', 'test3'];
+    assert_object_equals(logs, [
+        ['capturing', Event.CAPTURING_PHASE, 'outerHost', 'test3', outerComposedPath],
+        ['capturing', Event.AT_TARGET, 'outerHost', 'outerHost', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'innerHost', 'outerShadowRoot', outerShadowComposedPath],
+        ['capturing', Event.AT_TARGET, 'innerHost', 'innerHost', outerShadowComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'innerShadowRoot', innerShadowComposedPath],
+        ['capturing', Event.AT_TARGET, 'target', 'target', innerShadowComposedPath],
+
+        ['bubbling', Event.AT_TARGET, 'target', 'target', innerShadowComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'innerShadowRoot', innerShadowComposedPath],
+        ['bubbling', Event.AT_TARGET, 'innerHost', 'innerHost', outerShadowComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'innerHost', 'outerShadowRoot', outerShadowComposedPath],
+        ['bubbling', Event.AT_TARGET, 'outerHost', 'outerHost', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'outerHost', 'test3', outerComposedPath],
+    ]);
+}, 'Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a doubly nested shadow tree');
+</script>
+
+<div id="test4">
+    <div id="host">
+        <template id="shadowRoot" data-mode="closed">
+            <slot id="slot"></slot>
+        </template>
+        <div id="target"></div>
+    </div>
+</div>
+<script>
+test(() => {
+    const tree = createTestTree(document.getElementById('test4'));
+    const logs = attachEventListeners('my-event', tree);
+    tree.target.dispatchEvent(new Event('my-event', { bubbles: true, composed: true }));
+
+    const innerComposedPath = ['target', 'slot', 'shadowRoot', 'host', 'test4'];
+    const outerComposedPath = ['target', 'host', 'test4'];
+    assert_object_equals(logs, [
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'test4', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'host', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'shadowRoot', innerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'slot', innerComposedPath],
+        ['capturing', Event.AT_TARGET, 'target', 'target', outerComposedPath],
+
+        ['bubbling', Event.AT_TARGET, 'target', 'target', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'slot', innerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'shadowRoot', innerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'host', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'test4', outerComposedPath],
+    ]);
+}, 'Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched via a slot');
+</script>
+
+<div id="test5">
+    <div id="upperHost">
+        <template id="upperShadowRoot" data-mode="closed">
+            <slot id="upperSlot"></slot>
+        </template>
+        <div id="lowerHost">
+            <template id="lowerShadowRoot" data-mode="closed">
+                <div id="target"></div>
+            </template>
+        </div>
+    </div>
+</div>
+<script>
+test(() => {
+    const tree = createTestTree(document.getElementById('test5'));
+    const logs = attachEventListeners('my-event', tree);
+    tree.target.dispatchEvent(new Event('my-event', { bubbles: true, composed: true }));
+
+    const lowerComposedPath = ['target', 'lowerShadowRoot', 'lowerHost', 'upperHost', 'test5'];
+    const upperComposedPath = ['lowerHost', 'upperSlot', 'upperShadowRoot', 'upperHost', 'test5'];
+    const outerComposedPath = ['lowerHost', 'upperHost', 'test5'];
+    assert_object_equals(logs, [
+        ['capturing', Event.CAPTURING_PHASE, 'lowerHost', 'test5', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'lowerHost', 'upperHost', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'lowerHost', 'upperShadowRoot', upperComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'lowerHost', 'upperSlot', upperComposedPath],
+        ['capturing', Event.AT_TARGET, 'lowerHost', 'lowerHost', outerComposedPath],
+        ['capturing', Event.CAPTURING_PHASE, 'target', 'lowerShadowRoot', lowerComposedPath],
+        ['capturing', Event.AT_TARGET, 'target', 'target', lowerComposedPath],
+
+        ['bubbling', Event.AT_TARGET, 'target', 'target', lowerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'target', 'lowerShadowRoot', lowerComposedPath],
+        ['bubbling', Event.AT_TARGET, 'lowerHost', 'lowerHost', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'lowerHost', 'upperSlot', upperComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'lowerHost', 'upperShadowRoot', upperComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'lowerHost', 'upperHost', outerComposedPath],
+        ['bubbling', Event.BUBBLING_PHASE, 'lowerHost', 'test5', outerComposedPath],
+    ]);
+}, 'Capturing event listeners should be invoked before bubbling event listeners when an event is dispatched inside a shadow tree which passes through another shadow tree');
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative-expected.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative-expected.html
new file mode 100644 (file)
index 0000000..454b2fe
--- /dev/null
@@ -0,0 +1,6 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<body dir="rtl">
+<div style="width: 100px; height: 100px; background: green"></div>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative.html
new file mode 100644 (file)
index 0000000..7ff0361
--- /dev/null
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Directionality is properly computed for slotted children</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1562425">
+<link rel="match" href="directionality-002-ref.html">
+<style>
+  .slotted {
+    width: 100px;
+    height: 100px;
+    background: red;
+  }
+  .slotted:dir(rtl) {
+    background: green;
+  }
+</style>
+<body dir="rtl">
+<script>
+  let root = document.createElement("div")
+  let slotted = document.createElement("div");
+  slotted.className = "slotted";
+  root.appendChild(slotted);
+  root.attachShadow({ mode: "open" }).appendChild(document.createElement("slot"));
+  document.body.appendChild(root);
+</script>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement-expected.txt
new file mode 100644 (file)
index 0000000..4e45db0
--- /dev/null
@@ -0,0 +1,8 @@
+
+PASS activeElement on document & shadow root when focused element is in the shadow tree 
+PASS activeElement on document & shadow root when focused element is in the document 
+PASS activeElement on document & shadow root when focused element is slotted 
+PASS activeElement on a neighboring host when focused element is in another shadow tree 
+PASS activeElement when focused element is in a nested shadow tree 
+PASS activeElement when focused element is in a parent shadow tree 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html
new file mode 100644 (file)
index 0000000..20456b0
--- /dev/null
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: DocumentOrShadowRoot.activeElement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<body>
+<script>
+function createChildAndFocus(focusParent) {
+  const focused = document.createElement("div");
+  focused.tabIndex = 0;
+  focusParent.appendChild(focused);
+  focused.focus();
+  return focused;
+}
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+}, "activeElement on document & shadow root when focused element is in the shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+
+  const focused = createChildAndFocus(document.body);
+  assert_equals(document.activeElement, focused);
+  assert_equals(shadowRoot.activeElement, null);
+}, "activeElement on document & shadow root when focused element is in the document");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(document.createElement("slot"));
+  document.body.appendChild(host);
+
+  // Child of |host|, will be slotted to the slot in |shadowRoot|.
+  const focused = createChildAndFocus(host);
+  assert_equals(document.activeElement, focused);
+  assert_equals(shadowRoot.activeElement, null);
+}, "activeElement on document & shadow root when focused element is slotted");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const neighborHost = document.createElement("div");
+  const neighborShadowRoot = neighborHost.attachShadow({ mode: "open" });
+  document.body.appendChild(neighborHost);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+  assert_equals(neighborShadowRoot.activeElement, null);
+}, "activeElement on a neighboring host when focused element is in another shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const nestedHost = document.createElement("div");
+  const nestedShadowRoot = nestedHost.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(nestedHost);
+
+  const focused = createChildAndFocus(nestedShadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, nestedHost);
+  assert_equals(nestedShadowRoot.activeElement, focused);
+}, "activeElement when focused element is in a nested shadow tree");
+
+test(() => {
+  const host = document.createElement("div");
+  const shadowRoot = host.attachShadow({ mode: "open" });
+  document.body.appendChild(host);
+  const nestedHost = document.createElement("div");
+  const nestedShadowRoot = nestedHost.attachShadow({ mode: "open" });
+  shadowRoot.appendChild(nestedHost);
+
+  const focused = createChildAndFocus(shadowRoot);
+  assert_equals(document.activeElement, host);
+  assert_equals(shadowRoot.activeElement, focused);
+  assert_equals(nestedShadowRoot.activeElement, null);
+}, "activeElement when focused element is in a parent shadow tree");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus-expected.txt
new file mode 100644 (file)
index 0000000..aee9f0b
--- /dev/null
@@ -0,0 +1,12 @@
+foo
+foo
+
+FAIL :focus applies to host with delegatesFocus=true when the shadow root's descendant has focus assert_true: host matches :focus expected true got false
+FAIL :focus applies to host with delegatesFocus=true when slotted element has focus assert_true: host matches :focus expected true got false
+FAIL :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=true is focused assert_true: host of nested shadow tree matches focus expected true got false
+FAIL :focus applies to host with delegatesFocus=true when an element in a nested shadow tree with delegatesFocus=false is focused assert_true: host of nested shadow tree matches focus expected true got false
+FAIL :focus applies to host with delegatesFocus=false when the shadow root's descendant has focus assert_true: host matches :focus expected true got false
+FAIL :focus applies to host with delegatesFocus=false when slotted element has focus assert_true: host matches :focus expected true got false
+FAIL :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=true is focused assert_true: host of nested shadow tree matches focus expected true got false
+FAIL :focus applies to host with delegatesFocus=false when an element in a nested shadow tree with delegatesFocus=false is focused assert_true: host of nested shadow tree matches focus expected true got false
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html
new file mode 100644 (file)
index 0000000..3860452
--- /dev/null
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8" />
+  <title>CSS Test (Selectors): :focus behavior with shadow hosts & delegatesFocus </title>
+  <link rel="author" title="Rakina Zata Amni" href="rakina@chromium.org" />
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/semantics-other.html#selector-focus" />
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+  <script src="resources/shadow-utils.js"></script>
+</head>
+
+<body>
+<script>
+function createFocusableDiv() {
+  const div = document.createElement("div");
+  div.innerText = "foo";
+  div.tabIndex = 0;
+  return div;
+}
+
+function createShadowHost(delegatesFocus, container) {
+  const host = document.createElement("div");
+  host.attachShadow({ mode: "open", delegatesFocus: delegatesFocus });
+  container.appendChild(host);
+  return host;
+}
+
+const delegatesFocusValues = [true, false];
+
+for (const delegatesFocus of delegatesFocusValues) {
+  test(() => {
+    resetFocus();
+    const host = createShadowHost(delegatesFocus, document.body);
+    const shadowChild = createFocusableDiv();
+    host.shadowRoot.appendChild(shadowChild);
+
+    shadowChild.focus();
+    assert_true(shadowChild.matches(":focus"), "element in shadow tree matches :focus");
+    assert_true(host.matches(":focus"), "host matches :focus");
+  }, `:focus applies to host with delegatesFocus=${delegatesFocus} when the shadow root's descendant has focus`);
+
+  test(() => {
+    resetFocus();
+    const host = createShadowHost(delegatesFocus, document.body);
+    const slotted = createFocusableDiv();
+    host.shadowRoot.appendChild(document.createElement("slot"));
+    host.appendChild(slotted);
+
+    slotted.focus();
+    assert_true(slotted.matches(":focus"), "slotted element matches :focus");
+    assert_true(host.matches(":focus"), "host matches :focus");
+  }, `:focus applies to host with delegatesFocus=${delegatesFocus} when slotted element has focus`);
+
+  for (const nestedDelegatesFocus of delegatesFocusValues) {
+    test(() => {
+      resetFocus();
+      const host = createShadowHost(delegatesFocus, document.body);
+      const nestedHost = createShadowHost(nestedDelegatesFocus, host.shadowRoot);
+      const nestedShadowChild = createFocusableDiv();
+      nestedHost.shadowRoot.appendChild(nestedShadowChild);
+      nestedShadowChild.focus();
+      assert_true(nestedShadowChild.matches(":focus"), "element in nested shadow tree matches :focus");
+      assert_true(nestedHost.matches(":focus"), "host of nested shadow tree matches focus");
+      assert_true(host.matches(":focus"), "topmost host matches focus");
+}, `:focus applies to host with delegatesFocus=${delegatesFocus} when an element in a nested shadow tree with delegatesFocus=${nestedDelegatesFocus} is focused`);
+  }
+}
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative-expected.txt
new file mode 100644 (file)
index 0000000..8fff3aa
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all elements in shadow tree has negative tabindex 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html
new file mode 100644 (file)
index 0000000..ab25ea8
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and negative tabindex in shadow scope</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=-1>
+//      <slot #slotAbove tabindex=-1>
+//        (slotted) <div #slottedAbove tabindex=-1>
+//      <slot #slotBelow tabindex=-1>
+//        (slotted) <div #slottedBelow tabindex=-1>
+//      <div #belowSlot tabindex=-1>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, -1);
+  setTabIndex([aboveHost, host, belowHost], 0);
+  resetFocus();
+  return assertFocusOrder([aboveHost, host, belowHost]);
+}, "Order when all elements in shadow tree has negative tabindex");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one-expected.txt
new file mode 100644 (file)
index 0000000..400e2b8
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all tabindex=0, except for one slot that has tabindex=1 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html
new file mode 100644 (file)
index 0000000..3c9e708
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0 except for one of the slot</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=1>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  slotBelow.tabIndex = 1;
+  resetFocus();
+  // Focus should move first according to flat tree order to #aboveHost and #host, then into #host's focus scope.
+  // It will then move to #slottedBelow because #slotBelow has tabindex=1 (though we actually won't focus on the slot),
+  // and then back to #host's focus scope again, finally getting out to the document focus scope.
+  return assertFocusOrder([aboveHost, host, slottedBelow, aboveSlot, slottedAbove, belowSlot, belowHost]);
+}, "Order when all tabindex=0, except for one slot that has tabindex=1");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex-expected.txt
new file mode 100644 (file)
index 0000000..9b94a81
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order with various tabindex values 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html
new file mode 100644 (file)
index 0000000..875e5b6
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom with varying tabindex values</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=3>
+// <div #host tabindex=3>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=2>
+//      <slot #slotAbove tabindex=1>
+//        (slotted) <div #slottedAbove tabindex=4>
+//      <slot #slotBelow tabindex=1>
+//        (slotted) <div #slottedBelow tabindex=4>
+//      <div #belowSlot tabindex=2>
+// <div #belowHost tabindex=3>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex([slotAbove, slotBelow], 1);
+  setTabIndex([aboveSlot, belowSlot], 2);
+  setTabIndex([aboveHost, host, belowHost], 3);
+  setTabIndex([slottedAbove, slottedBelow], 4);
+  resetFocus();
+  return assertFocusOrder([aboveHost, host, slottedAbove, slottedBelow, aboveSlot, belowSlot, belowHost]);
+}, "Order with various tabindex values");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-expected.txt
new file mode 100644 (file)
index 0000000..60fd1c5
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all tabindex=0 is and delegatesFocus = false 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative-expected.txt
new file mode 100644 (file)
index 0000000..9f61c8c
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all tabindex=0 except for host, which has tabindex=-1 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html
new file mode 100644 (file)
index 0000000..b491c7d
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and negative host tabindex</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=-1>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  host.tabIndex = -1;
+  resetFocus();
+  // Focus willl only move within the focus navigation scope of the document (not going to get into #host).
+  return assertFocusOrder([aboveHost, belowHost]);
+}, "Order when all tabindex=0 except for host, which has tabindex=-1");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set-expected.txt
new file mode 100644 (file)
index 0000000..64bc9ee
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all tabindex=0 except host (tabindex not set) 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html
new file mode 100644 (file)
index 0000000..f257261
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and non-focusable host</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  removeTabIndex([host]);
+  resetFocus();
+  // Focus should move in flat tree order since every one of them has tabindex ==0,
+  // but doesn't include #slot since it's not rendered and #host since its tabindex is not set
+  // (but #host is considered as 0 in focus scope navigation, keeping the flat tree order for the shadow root's descendants).
+  return assertFocusOrder(elementsInFlatTreeOrder.filter(el => (el !== slotAbove && el !== slotBelow && el !== host)));
+}, "Order when all tabindex=0 except host (tabindex not set)");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one-expected.txt
new file mode 100644 (file)
index 0000000..6fe8f73
--- /dev/null
@@ -0,0 +1,7 @@
+aboveHost
+slotted below
+slotted above
+belowHost
+
+PASS Order when all tabindex=0 except for host, which has tabindex=1 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html
new file mode 100644 (file)
index 0000000..1aa5292
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0 except host (tabindex=1)</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=1>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  host.tabIndex = 1;
+  resetFocus();
+  // Focus should move first to #host because it has tabindex=1, and then to the contents of its scope
+  // (e.g. the contents of its shadow tree) in flat tree order, and then outside to the document scope
+  // again (aboveHost & belowHost).
+  return assertFocusOrder([host, aboveSlot, slottedAbove, slottedBelow, belowSlot, aboveHost, belowHost]);
+}, "Order when all tabindex=0 except for host, which has tabindex=1");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html
new file mode 100644 (file)
index 0000000..d8b12ed
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTML Test: focus - the sequential focus navigation order with shadow dom and all tabindex=0</title>
+<link rel="help" href="https://html.spec.whatwg.org/multipage/interaction.html#sequential-focus-navigation">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/shadow-utils.js"></script>
+<body>
+<script>
+// Structure:
+// <div #aboveHost tabindex=0>
+// <div #host tabindex=0>
+//    #shadowRoot
+//      <div #aboveSlot tabindex=0>
+//      <slot #slotAbove tabindex=0>
+//        (slotted) <div #slottedAbove tabindex=0>
+//      <slot #slotBelow tabindex=0>
+//        (slotted) <div #slottedBelow tabindex=0>
+//      <div #belowSlot tabindex=0>
+// <div #belowHost tabindex=0>
+
+promise_test(() => {
+  let elementsInFlatTreeOrder;
+  let [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost] =
+    elementsInFlatTreeOrder = prepareDOM(document.body, false);
+  setTabIndex(elementsInFlatTreeOrder, 0);
+  resetFocus();
+  // Focus should move in flat tree order since every one of them has tabindex==0,
+  // but doesn't include slots.
+  return assertFocusOrder(elementsInFlatTreeOrder.filter(el => (el !== slotAbove && el !== slotBelow)));
+}, "Order when all tabindex=0 is and delegatesFocus = false");
+</script>
+</body>
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/shadow-utils.js b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/shadow-utils.js
new file mode 100644 (file)
index 0000000..8033ce0
--- /dev/null
@@ -0,0 +1,80 @@
+// Structure:
+// <div #aboveHost>
+// <div #host>
+//    #shadowRoot
+//      <div #aboveSlot>
+//      <slot #slotAbove>
+//        (slotted) <div #slottedAbove>
+//      <slot #slotBelow>
+//        (slotted) <div #slottedBelow>
+//      <div #belowSlot>
+// <div #belowHost>
+function prepareDOM(container, delegatesFocus) {
+
+  const aboveHost = document.createElement("div");
+  aboveHost.innerText = "aboveHost";
+  const host = document.createElement("div");
+  host.id = "host";
+  const slottedBelow = document.createElement("div");
+  slottedBelow.innerText = "slotted below";
+  slottedBelow.slot = "below";
+  const slottedAbove = document.createElement("div");
+  slottedAbove.innerText = "slotted above";
+  slottedAbove.slot = "above";
+
+  const belowHost = document.createElement("div");
+  belowHost.innerText = "belowHost";
+  container.appendChild(aboveHost);
+  container.appendChild(host);
+  container.appendChild(belowHost);
+  host.appendChild(slottedBelow);
+  host.appendChild(slottedAbove);
+  const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: delegatesFocus});
+  const aboveSlot = document.createElement("div");
+  aboveSlot.innerText = "aboveSlot";
+
+  const slotAbove = document.createElement("slot");
+  slotAbove.name = "above";
+  const slotBelow = document.createElement("slot");
+  slotBelow.name = "below";
+
+  const belowSlot = document.createElement("div");
+  belowSlot.innerText = "belowSlot";
+  shadowRoot.appendChild(aboveSlot);
+  shadowRoot.appendChild(slotAbove);
+  shadowRoot.appendChild(slotBelow);
+  shadowRoot.appendChild(belowSlot);
+
+  return [aboveHost, host, aboveSlot, slotAbove, slottedAbove, slotBelow, slottedBelow, belowSlot, belowHost];
+}
+
+function setTabIndex(elements, value) {
+  for (const el of elements) {
+    el.tabIndex = value;
+  }
+}
+
+function removeTabIndex(elements) {
+  for (const el of elements) {
+    el.removeAttribute("tabindex");
+  }
+}
+
+function resetFocus(root = document) {
+  if (root.activeElement)
+    root.activeElement.blur();
+}
+
+function navigateFocusForward() {
+  // TAB = '\ue004'
+  return test_driver.send_keys(document.body, "\ue004");
+}
+
+async function assertFocusOrder(expectedOrder) {
+  const shadowRoot = document.getElementById("host").shadowRoot;
+  for (const el of expectedOrder) {
+    await navigateFocusForward();
+    const focused = shadowRoot.activeElement ? shadowRoot.activeElement : document.activeElement;
+    assert_equals(focused, el);
+  }
+}
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/w3c-import.log b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/w3c-import.log
new file mode 100644 (file)
index 0000000..00c77c8
--- /dev/null
@@ -0,0 +1,17 @@
+The tests in this directory were imported from the W3C repository.
+Do NOT modify these tests directly in WebKit.
+Instead, create a pull request on the WPT github:
+       https://github.com/web-platform-tests/wpt
+
+Then run the Tools/Scripts/import-w3c-tests in WebKit to reimport
+
+Do NOT modify or remove this file.
+
+------------------------------------------------------------------------
+Properties requiring vendor prefixes:
+None
+Property values requiring vendor prefixes:
+None
+------------------------------------------------------------------------
+List of files:
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/resources/shadow-utils.js
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/w3c-import.log b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/w3c-import.log
new file mode 100644 (file)
index 0000000..f04ab0b
--- /dev/null
@@ -0,0 +1,25 @@
+The tests in this directory were imported from the W3C repository.
+Do NOT modify these tests directly in WebKit.
+Instead, create a pull request on the WPT github:
+       https://github.com/web-platform-tests/wpt
+
+Then run the Tools/Scripts/import-w3c-tests in WebKit to reimport
+
+Do NOT modify or remove this file.
+
+------------------------------------------------------------------------
+Properties requiring vendor prefixes:
+None
+Property values requiring vendor prefixes:
+None
+------------------------------------------------------------------------
+List of files:
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/DocumentOrShadowRoot-activeElement.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-selector-delegatesFocus.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-negative.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-slot-one.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-varying-tabindex.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-negative.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-not-set.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero-host-one.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/focus/focus-tabindex-order-shadow-zero.html
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries-expected.txt
new file mode 100644 (file)
index 0000000..df4a285
--- /dev/null
@@ -0,0 +1,18 @@
+
+PASS offsetParent must return the offset parent in the same shadow tree of open mode 
+PASS offsetParent must return the offset parent in the same shadow tree of closed mode 
+PASS offsetParent must return the offset parent in the same shadow tree of open mode even when nested 
+PASS offsetParent must return the offset parent in the same shadow tree of closed mode even when nested 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
+PASS offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode 
+PASS offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of open mode 
+PASS offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of closed mode 
+PASS offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of open mode did not have any offset parent 
+PASS offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of closed mode did not have any offset parent 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in open mode which is not assigned to any slot 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in closed mode which is not assigned to any slot 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in open mode which is not in the flat tree 
+PASS offsetParent must return null on a child element of a shadow host for the shadow tree in closed mode which is not in the flat tree 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html b/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html
new file mode 100644 (file)
index 0000000..5491e21
--- /dev/null
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="offsetParent should only return nodes that are shadow including ancestor">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-htmlelement-offsetparent">
+<link rel="help" href="https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/event-path-test-helpers.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<div id="container" style="position: relative"></div>
+<script>
+
+const container = document.getElementById('container');
+
+function testOffsetParentInShadowTree(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        container.appendChild(host);
+        this.add_cleanup(() => host.remove());
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<div id="relativeParent" style="position: relative; padding-left: 100px; padding-top: 70px;"><div id="target"></div></div>';
+        const relativeParent = shadowRoot.getElementById('relativeParent');
+
+        assert_true(relativeParent instanceof HTMLDivElement);
+        const target = shadowRoot.getElementById('target');
+        assert_equals(target.offsetParent, relativeParent);
+        assert_equals(target.offsetLeft, 100);
+        assert_equals(target.offsetTop, 70);
+    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode`);
+}
+
+testOffsetParentInShadowTree('open');
+testOffsetParentInShadowTree('closed');
+
+function testOffsetParentInNestedShadowTrees(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<section id="outerParent" style="position: absolute; top: 50px; left: 50px;"></section>';
+
+        const innerHost = document.createElement('div');
+        outerShadow.firstChild.appendChild(innerHost);
+        const innerShadow = innerHost.attachShadow({mode});
+        innerShadow.innerHTML = '<div id="innerParent" style="position: relative; padding-left: 60px; padding-top: 40px;"><div id="target"></div></div>';
+        const innerParent = innerShadow.getElementById('innerParent');
+
+        const target = innerShadow.getElementById('target');
+        assert_true(innerParent instanceof HTMLDivElement);
+        assert_equals(target.offsetParent, innerParent);
+        assert_equals(target.offsetLeft, 60);
+        assert_equals(target.offsetTop, 40);
+
+        outerHost.remove();
+    }, `offsetParent must return the offset parent in the same shadow tree of ${mode} mode even when nested`);
+}
+
+testOffsetParentInNestedShadowTrees('open');
+testOffsetParentInNestedShadowTrees('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideOffsetParent(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        host.innerHTML = '<div id="target"></div>'
+        container.appendChild(host);
+        this.add_cleanup(() => host.remove());
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<div style="position: relative; padding-left: 85px; padding-top: 45px;"><slot></slot></div>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 85);
+        assert_equals(target.offsetTop, 45);
+    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideOffsetParent('open');
+testOffsetParentOnElementAssignedToSlotInsideOffsetParent('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents(mode) {
+    test(function () {
+        const host = document.createElement('div');
+        host.innerHTML = '<div id="target" style="border:solid 1px blue;">hi</div>';
+        const previousBlock = document.createElement('div');
+        previousBlock.style.height = '12px';
+        container.append(previousBlock, host);
+        this.add_cleanup(() => { container.innerHTML = ''; });
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<section style="position: relative; margin-left: 20px; margin-top: 100px; background: #ccc"><div style="position: absolute; top: 10px; left: 10px;"><slot></slot></div></section>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 30);
+        assert_equals(target.offsetTop, 122);
+    }, `offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('open');
+testOffsetParentOnElementAssignedToSlotInsideNestedOffsetParents('closed');
+
+function testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        outerHost.innerHTML = '<div id="target"></div>';
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<section style="position: absolute; top: 40px; left: 50px;"><div id="innerHost"><slot></slot></div></section>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div style="position: absolute; top: 200px; margin-left: 100px;"><slot></slot></div>';
+
+        const target = outerHost.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 150);
+        assert_equals(target.offsetTop, 240);
+        outerHost.remove();
+    }, `offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of ${mode} mode`);
+}
+
+testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('open');
+testOffsetParentOnElementAssignedToSlotInsideNestedShadowTrees('closed');
+
+function testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<div id="innerHost"><div id="target"></div></div>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div style="position: absolute; top: 23px; left: 24px;"><slot></slot></div>';
+
+        const target = outerShadow.querySelector('#target');
+        assert_equals(target.offsetParent, container);
+        assert_equals(target.offsetLeft, 24);
+        assert_equals(target.offsetTop, 23);
+    }, `offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of ${mode} mode did not have any offset parent`);
+}
+
+testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('open');
+testOffsetParentOnElementInsideShadowTreeWithoutOffsetParent('closed');
+
+function testOffsetParentOnUnassignedChild(mode) {
+    test(function () {
+        const host = document.createElement('section');
+        host.innerHTML = '<div id="target"></div>';
+        this.add_cleanup(() => host.remove());
+        container.appendChild(host);
+        const shadowRoot = host.attachShadow({mode});
+        shadowRoot.innerHTML = '<section style="position: absolute; top: 50px; left: 50px;">content</section>';
+        const target = host.querySelector('#target');
+        assert_equals(target.offsetParent, null);
+        assert_equals(target.offsetLeft, 0);
+        assert_equals(target.offsetTop, 0);
+    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not assigned to any slot`);
+}
+
+testOffsetParentOnUnassignedChild('open');
+testOffsetParentOnUnassignedChild('closed');
+
+function testOffsetParentOnAssignedChildNotInFlatTree(mode) {
+    test(function () {
+        const outerHost = document.createElement('section');
+        outerHost.innerHTML = '<div id="target"></div>';
+        container.appendChild(outerHost);
+        this.add_cleanup(() => outerHost.remove());
+        const outerShadow = outerHost.attachShadow({mode});
+        outerShadow.innerHTML = '<div id="innerHost"><div style="position: absolute; top: 50px; left: 50px;"><slot></slot></div></div>';
+
+        const innerShadow = outerShadow.getElementById('innerHost').attachShadow({mode});
+        innerShadow.innerHTML = '<div>content</div>';
+
+        const target = outerHost.querySelector('#target');
+        assert_equals(target.offsetParent, null);
+        assert_equals(target.offsetLeft, 0);
+        assert_equals(target.offsetTop, 0);
+    }, `offsetParent must return null on a child element of a shadow host for the shadow tree in ${mode} mode which is not in the flat tree`);
+}
+
+testOffsetParentOnAssignedChildNotInFlatTree('open');
+testOffsetParentOnAssignedChildNotInFlatTree('closed');
+
+</script>
+</body>
+</html>
index 192ad45..81b9a3d 100644 (file)
@@ -21,13 +21,7 @@ function createTestTree(node) {
   function attachShadowFromTemplate(template) {
     let parent = template.parentNode;
     parent.removeChild(template);
-    let shadowRoot;
-    if (template.getAttribute('data-mode') === 'v0') {
-      // For legacy Shadow DOM
-      shadowRoot = parent.createShadowRoot();
-    } else {
-      shadowRoot = parent.attachShadow({mode: template.getAttribute('data-mode')});
-    }
+    let shadowRoot = parent.attachShadow({mode: template.getAttribute('data-mode')});
     let id = template.id;
     if (id) {
       shadowRoot.id = id;
index c87932e..b4ecc16 100644 (file)
@@ -5,6 +5,7 @@
 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
 <meta name="assert" content="An element inside a shadow tree should not be the indicated part of the document even if its ID is exactly equal to the decoded fragid or its name attribute is exactly equal to the fragid">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#scroll-to-the-fragment-identifier">
+<meta name="viewport" content="width=device-width,initial-scale=1">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
index e83a8d3..9dc2e71 100644 (file)
@@ -29,8 +29,11 @@ List of files:
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Range-prototype-insertNode.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/ShadowRoot-interface.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/Slotable-interface.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/capturing-and-bubbling-event-listeners-across-shadow-trees.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-001.tentative-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-001.tentative.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative-expected.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/directionality-002.tentative.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/event-composed-path-after-dom-mutation.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/event-composed-path-with-related-target.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/event-composed-path.html
@@ -49,6 +52,7 @@ List of files:
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/layout-slot-no-longer-assigned.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/layout-slot-no-longer-fallback-expected.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/layout-slot-no-longer-fallback.html
+/LayoutTests/imported/w3c/web-platform-tests/shadow-dom/offsetParent-across-shadow-boundaries.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/slotchange-customelements.html
 /LayoutTests/imported/w3c/web-platform-tests/shadow-dom/slotchange-event.html
index 87e2ba5..4ea3ace 100644 (file)
@@ -805,6 +805,7 @@ webkit.org/b/159240 fast/events/remove-focus-navigation-starting-point-crash.htm
 webkit.org/b/164888 fast/shadow-dom/focus-navigation-out-of-slot.html [ Skip ]
 webkit.org/b/164888 fast/shadow-dom/focus-navigation-passes-shadow-host.html [ Skip ]
 webkit.org/b/164888 fast/shadow-dom/focus-navigation-passes-svg-use-element.html [ Skip ]
+webkit.org/b/202497 imported/w3c/web-platform-tests/shadow-dom/focus/ [ Skip ]
 
 # This test needs to be rewritten to use runUIScript to work on iOS
 webkit.org/b/152993 http/tests/contentdispositionattachmentsandbox/form-submission-disabled.html [ Skip ]
diff --git a/LayoutTests/platform/mac-highsierra-wk1/imported/w3c/web-platform-tests/mathml/relations/html5-tree/tabindex-002-expected.txt b/LayoutTests/platform/mac-highsierra-wk1/imported/w3c/web-platform-tests/mathml/relations/html5-tree/tabindex-002-expected.txt
new file mode 100644 (file)
index 0000000..7dd5225
--- /dev/null
@@ -0,0 +1,14 @@
+
+FAIL Elements with different tabindex must be focused sequentially when pressing 'Tab' keys assert_array_equals: property 6, expected "text6" but got "text11"
+tabindex(omitted)
+tabindex(empty)
+tabindex(a)
+tabindex(-1)
+tabindex(0)
+tabindex(href)
+tabindex(3)
+tabindex(2)
+tabindex(2)
+tabindex(2)
+tabindex(1)
+