| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>Shadow DOM: Extensions to Event Interface</title> | |
| <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> | |
| <meta name="assert" content="Event interface must have composedPath() as a method"> | |
| <link rel="help" href="http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-event-interface"> | |
| <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> | |
| <script> | |
| test(function () { | |
| var event = new Event('my-event'); | |
| assert_array_equals(event.composedPath(), []); | |
| }, 'composedPath() must return an empty array when the event has not been dispatched'); | |
| test(function () { | |
| var event = new Event('my-event'); | |
| document.body.dispatchEvent(event); | |
| assert_array_equals(event.composedPath(), []); | |
| }, 'composedPath() must return an empty array when the event is no longer dispatched'); | |
| test(function () { | |
| var event = new Event('my-event'); | |
| assert_false(event.composed); | |
| }, 'composed on EventInit must default to false'); | |
| test(function () { | |
| var event = new Event('my-event', {composed: true}); | |
| assert_true(event.composed); | |
| event = new Event('my-event', {composed: false}); | |
| assert_false(event.composed); | |
| }, 'composed on EventInit must set the composed flag'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) *: indicates start digit: event path order | |
| A (4) --------------------------- A-SR (3) | |
| + B ------------ B-SR + A1 (2) --- A1-SR (1) | |
| + C + B1 --- B1-SR + A2-S + A1a (*; 0) | |
| + D --- D-SR + B1a + B1b --- B1b-SR | |
| + D1 + B1c-S + B1b1 | |
| + B1b2 | |
| */ | |
| function testComposedEvent(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| var log = dispatchEventWithEventLog(nodes, nodes.A1a, new Event('my-event', {composed: true, bubbles: true})); | |
| var expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR', 'A']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], expectedPath); | |
| assert_array_equals(log.pathAtTargets[1], expectedPath); | |
| assert_array_equals(log.pathAtTargets[2], mode == 'open' ? expectedPath : ['A1', 'A-SR', 'A'], | |
| 'composedPath must only contain unclosed nodes of the current target.'); | |
| }, 'The event must propagate out of ' + mode + ' mode shadow boundaries when the composed flag is set'); | |
| } | |
| testComposedEvent('open'); | |
| testComposedEvent('closed'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) *: indicates start digit: event path order | |
| A ------------------------------- A-SR | |
| + B ------------ B-SR + A1 --- A1-SR (1) | |
| + C + B1 --- B1-SR + A2-S + A1a (*; 0) | |
| + D --- D-SR + B1a + B1b --- B1b-SR | |
| + D1 + B1c-S + B1b1 | |
| + B1b2 | |
| */ | |
| function testNonComposedEvent(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| var log = dispatchEventWithEventLog(nodes, nodes.A1a, new Event('my-event', {composed: false, bubbles: true})); | |
| var expectedPath = ['A1a', 'A1-SR']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], expectedPath); | |
| assert_array_equals(log.pathAtTargets[1], expectedPath); | |
| }, 'The event must not propagate out of ' + mode + ' mode shadow boundaries when the composed flag is unset'); | |
| } | |
| testNonComposedEvent('open'); | |
| testNonComposedEvent('closed'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) relatedTarget: [~] *: indicates start digit: event path order | |
| A ------------------------------- A-SR | |
| + B ------------ B-SR + A1 ----------- A1-SR (1) | |
| + C + B1 --- B1-SR + A2-S [*; 0-1] + A1a (*; 0) | |
| + D --- D-SR + B1a + B1b --- B1b-SR | |
| + D1 + B1c-S + B1b1 | |
| + B1b2 | |
| */ | |
| function testNonComposedEventWithRelatedTarget(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| var log = dispatchEventWithEventLog(nodes, nodes.A1a, new MouseEvent('foo', {composed: false, bubbles: true, relatedTarget: nodes['A2-S']})); | |
| var expectedPath = ['A1a', 'A1-SR']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], expectedPath); | |
| assert_array_equals(log.pathAtTargets[1], expectedPath); | |
| assert_array_equals(log.relatedTargets, ['A2-S', 'A2-S']); | |
| }, 'The event must not propagate out of ' + mode + ' mode shadow boundaries when the composed flag is unset on an event with relatedTarget'); | |
| } | |
| testNonComposedEventWithRelatedTarget('open'); | |
| testNonComposedEventWithRelatedTarget('closed'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) relatedTarget: [~] *: indicates start digit: event path order | |
| A ------------------------------------------------ A-SR | |
| + B ------------ B-SR (4) + A1 --- A1-SR | |
| + C + B1 (3) [0,3-4] --- B1-SR (2) + A2-S + A1a | |
| + D --- D-SR + B1a (*; 0) + B1b [1-2] --- B1b-SR | |
| + D1 + B1c-S (1) + B1b1 | |
| + B1b2 [*] | |
| */ | |
| function testScopedEventWithUnscopedRelatedTargetThroughSlot(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| var log = dispatchEventWithEventLog(nodes, nodes.B1a, new MouseEvent('foo', {scoped: true, relatedTargetScoped: false, bubbles: true, relatedTarget: nodes['B1b2']})); | |
| var expectedPath = ['B1a', 'B1c-S', 'B1-SR', 'B1', 'B-SR']; | |
| var pathExposedToB1a = ['B1a', 'B1', 'B-SR']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.pathAtTargets[1], expectedPath); | |
| assert_array_equals(log.pathAtTargets[2], expectedPath); | |
| assert_array_equals(log.pathAtTargets[3], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.pathAtTargets[4], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.relatedTargets, ['B1', 'B1b', 'B1b', 'B1', 'B1']); | |
| }, 'The event must not propagate out of ' + mode + ' mode shadow tree of the target but must propagate out of inner shadow trees when the scoped flag is set'); | |
| } | |
| testScopedEventWithUnscopedRelatedTargetThroughSlot('open'); | |
| testScopedEventWithUnscopedRelatedTargetThroughSlot('closed'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) relatedTarget: [~] *: indicates start digit: event path order | |
| A ------------------------------- A-SR (3) | |
| + B ------------ B-SR + A1 (2) ------- A1-SR (1) | |
| + C + B1 --- B1-SR + A2-S [*; 0-3] + A1a (*; 0) | |
| + D --- D-SR + B1a + B1b --- B1b-SR | |
| + D1 + B1c-S + B1b1 | |
| + B1b2 | |
| */ | |
| function testComposedEventWithRelatedTarget(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| log = dispatchEventWithEventLog(nodes, nodes.A1a, new MouseEvent('foo', {composed: true, bubbles: true, relatedTarget: nodes['A2-S']})); | |
| var expectedPath = ['A1a', 'A1-SR', 'A1', 'A-SR']; | |
| var pathExposedToA1 = ['A1', 'A-SR']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], expectedPath); | |
| assert_array_equals(log.pathAtTargets[1], expectedPath); | |
| assert_array_equals(log.pathAtTargets[2], mode == 'open' ? expectedPath : pathExposedToA1); | |
| assert_array_equals(log.pathAtTargets[3], mode == 'open' ? expectedPath : pathExposedToA1); | |
| assert_array_equals(log.relatedTargets, ['A2-S', 'A2-S', 'A2-S', 'A2-S']); | |
| }, 'The event must propagate out of ' + mode + ' mode shadow tree in which the relative target and the relative related target are the same'); | |
| } | |
| testComposedEventWithRelatedTarget('open'); | |
| testComposedEventWithRelatedTarget('closed'); | |
| /* | |
| -SR: ShadowRoot -S: Slot target: (~) relatedTarget: [~] *: indicates start digit: event path order | |
| A (8) [0-5,8] ---------------------------------------- A-SR (7) | |
| + B (5) ------- B-SR (4) + A1 [6,7] --- A1-SR | |
| + C + B1 (3) ------- B1-SR (2) + A2-S (6) + A1a [*] | |
| + D --- D-SR + B1a (*; 0) + B1b ------- B1b-SR | |
| + D1 + B1c-S (1) + B1b1 | |
| + B1b2 | |
| */ | |
| function testComposedEventThroughSlot(mode) { | |
| test(function () { | |
| var nodes = createFixedTestTree(mode); | |
| log = dispatchEventWithEventLog(nodes, nodes.B1a, new MouseEvent('foo', {composed: true, bubbles: true, relatedTarget: nodes.A1a})); | |
| var expectedPath = ['B1a', 'B1c-S', 'B1-SR', 'B1', 'B-SR', 'B', 'A2-S', 'A-SR', 'A']; | |
| var expectedRelatedTarget = ['A', 'A', 'A', 'A', 'A', 'A', 'A1', 'A1', 'A']; | |
| var pathExposedToB1a = ['B1a', 'B1', 'B-SR', 'B', 'A']; | |
| var pathExposedToB1cS = ['B1a', 'B1c-S', 'B1-SR', 'B1', 'B-SR', 'B', 'A']; | |
| var pathExposedToB = [ 'B', 'A']; | |
| var pathExposedToA1 = [ 'B', 'A2-S', 'A-SR', 'A']; | |
| assert_array_equals(log.eventPath, expectedPath); | |
| assert_equals(log.eventPath.length, log.pathAtTargets.length); | |
| assert_array_equals(log.pathAtTargets[0], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.pathAtTargets[1], mode == 'open' ? expectedPath : pathExposedToB1cS); | |
| assert_array_equals(log.pathAtTargets[2], mode == 'open' ? expectedPath : pathExposedToB1cS); | |
| assert_array_equals(log.pathAtTargets[3], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.pathAtTargets[4], mode == 'open' ? expectedPath : pathExposedToB1a); | |
| assert_array_equals(log.pathAtTargets[5], mode == 'open' ? expectedPath : pathExposedToB); | |
| assert_array_equals(log.pathAtTargets[6], mode == 'open' ? expectedPath : pathExposedToA1); | |
| assert_array_equals(log.pathAtTargets[7], mode == 'open' ? expectedPath : pathExposedToA1); | |
| assert_array_equals(log.pathAtTargets[8], mode == 'open' ? expectedPath : pathExposedToB); | |
| assert_array_equals(log.relatedTargets, expectedRelatedTarget); | |
| }, 'composedPath() must contain and only contain the unclosed nodes of target in ' + mode + ' mode shadow trees'); | |
| } | |
| testComposedEventThroughSlot('open'); | |
| testComposedEventThroughSlot('closed'); | |
| </script> | |
| </body> | |
| </html> |