4 <script src="../../js/resources/js-test-pre.js"></script>
5 <script src="resources/create-dom.js"></script>
8 <p id="description"></p>
9 <div id="sandbox"></div>
10 <pre id="console"></pre>
12 description("Tests to ensure that shadow DOM boundary is not crossed during event propagation. Can only run within DRT.");
14 function moveMouseOver(element)
16 if (!window.eventSender || !window.internals)
19 var defaultPaddingSize = 20;
20 var x = element.offsetLeft + element.offsetWidth / 2;
22 if (element.hasChildNodes() || window.internals.shadowRoot(element))
23 y = element.offsetTop + defaultPaddingSize;
25 y = element.offsetTop + element.offsetHeight / 2;
26 eventSender.mouseMoveTo(x, y);
29 var eventRecords = {};
31 function clearEventRecords()
36 function dispatchedEvent(eventType)
38 var events = eventRecords[eventType];
44 function recordEvent(event)
46 var eventType = event.type
47 if (!eventRecords[eventType]) {
48 eventRecords[eventType] = []
50 // Records each event in the following format per event type:
51 // eventRecords[eventType] = ['target.id(<-relatedTarget.id)(@currentTarget.id)',,,]
52 // * RelatedTarget and currentTarget may be omitted if they are not defined.
53 // A new event is pushed back to the array of its event type.
55 eventString += event.target.id;
56 if (event.relatedTarget)
57 eventString += '(<-' + event.relatedTarget.id + ')';
58 if (event.currentTarget)
59 eventString += '(@' + event.currentTarget.id + ')';
60 if (event.eventPhase == 1)
61 eventString += '(capturing phase)';
62 eventRecords[eventType].push(eventString);
65 function getElementInShadow(path)
67 var ids = path.split('/');
68 var element = document.getElementById(ids[0]);
69 for (var i = 1; element != null && i < ids.length; ++i) {
70 var shadowRoot = internals.shadowRoot(element);
71 element = internals.getElementByIdInShadowRoot(shadowRoot, ids[i]);
76 function prepareDomTree(parent)
79 createDom('div', {'id': 'divA', 'style': 'padding-top: 40px'},
80 createDom('div', {'id': 'divB', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
81 createDom('div', {'id': 'divC', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
82 createShadow('div', {'id': 'shadowD', 'style': 'padding-top: 40px'},
83 createDom('div', {'id': 'divE', 'style': 'padding-top: 40px'},
84 createShadow('div', {'id': 'shadowF', 'style': 'padding-top: 40px'},
85 createShadow('div', {'id': 'shadowG', 'style': 'padding-top: 40px'},
86 createDom('div', {'id': 'divH', 'style': 'width: 40px; height: 40px', 'tabindex': 0}),
87 createDom('div', {'id': 'divI', 'style': 'width: 40px; height: 40px', 'tabindex': 0})))),
88 createDom('div', {'id': 'divJ', 'style': 'padding-top: 40px'},
89 createShadow('div', {'id': 'shadowK', 'style': 'padding-top: 40px'},
90 createDom('div', {'id': 'divL', 'style': 'width: 40px; height: 40px', 'tabindex': 0}))))));
92 var ids = ['divA', 'divB', 'divC',
93 'shadowD', 'shadowD/divE', 'shadowD/shadowF', 'shadowD/shadowF/shadowG',
94 'shadowD/shadowF/shadowG/divH', 'shadowD/shadowF/shadowG/divI',
95 'shadowD/divJ', 'shadowD/shadowK', 'shadowD/shadowK/divL'];
96 for (var i = 0; i < ids.length; ++i) {
97 var element = getElementInShadow(ids[i]);
98 element.addEventListener('mouseover', recordEvent, false);
99 element.addEventListener('mouseout', recordEvent, false);
100 element.addEventListener('focusin', recordEvent, false);
101 element.addEventListener('focusout', recordEvent, false);
102 element.addEventListener('focus', recordEvent, true); // capturing phase
103 element.addEventListener('blur', recordEvent, true); // capturing phase
107 function moveMouse(oldElementId, newElementId, message)
109 debug('\n' + message + '\n' + 'Moving mouse from ' + oldElementId + ' to ' + newElementId);
110 moveMouseOver(getElementInShadow(oldElementId));
112 moveMouseOver(getElementInShadow(newElementId));
115 function moveFocus(oldElementId, newElementId, message)
117 debug('\n' + message + '\n' + 'Moving focus from ' + oldElementId + ' to ' + newElementId);
118 getElementInShadow(oldElementId).focus();
120 getElementInShadow(newElementId).focus();
125 if (window.layoutTestController)
126 layoutTestController.dumpAsText();
127 prepareDomTree(document.getElementById('sandbox'));
129 // Test for mouseover/mouseout events.
130 moveMouse('divB', 'divC',
131 'Move mouse from a node to its sibling node. All nodes are outside of shadow boundary.');
132 shouldBe('dispatchedEvent("mouseover")', '["divC(<-divB)(@divC)", "divC(<-divB)(@divA)"]');
133 shouldBe('dispatchedEvent("mouseout")', '["divB(<-divC)(@divB)", "divB(<-divC)(@divA)"]');
135 moveMouse('divB', 'divA',
136 'Target is an ancestor of relatedTarget. All nodes are outside of shadow boundary.');
137 shouldBe('dispatchedEvent("mouseover")', '["divA(<-divB)(@divA)"]');
138 shouldBe('dispatchedEvent("mouseout")', '["divB(<-divA)(@divB)", "divB(<-divA)(@divA)"]');
140 moveMouse('divA', 'divB',
141 'RelatedTarget is an ancestor of target. All nodes are outside of shadow boundary.');
142 shouldBe('dispatchedEvent("mouseover")', '["divB(<-divA)(@divB)", "divB(<-divA)(@divA)"]');
143 shouldBe('dispatchedEvent("mouseout")', '["divA(<-divB)(@divA)"]');
145 moveMouse('shadowD/shadowF/shadowG/divH', 'shadowD/shadowF/shadowG/divI',
146 'Both target and relatedTarget are immediate children of the same shadow root.');
147 shouldBe('dispatchedEvent("mouseover")', '["divI(<-divH)(@divI)"]');
148 shouldBe('dispatchedEvent("mouseout")', '["divH(<-divI)(@divH)"]');
150 moveMouse('shadowD/shadowF/shadowG/divI', 'shadowD/divE',
151 'Target is an ancestor of relatedTarget.');
152 shouldBe('dispatchedEvent("mouseover")', '["divE(<-shadowF)(@divE)"]');
153 shouldBe('dispatchedEvent("mouseout")', '["divI(<-divE)(@divI)", "shadowG(<-divE)(@shadowG)", "shadowF(<-divE)(@shadowF)", "shadowF(<-divE)(@divE)"]');
155 moveMouse('shadowD/shadowF/shadowG/divI', 'shadowD/shadowF',
156 'Target (shadow host) is an ancestor of relatedTarget.');
157 shouldBe('dispatchedEvent("mouseover")', '[]');
158 shouldBe('dispatchedEvent("mouseout")', '["divI(<-shadowF)(@divI)", "shadowG(<-shadowF)(@shadowG)"]');
160 moveMouse('shadowD/shadowF/shadowG', 'shadowD',
161 'Target (shadow host) is an ancestor of relatedTarget (shadow host).');
162 shouldBe('dispatchedEvent("mouseover")', '[]');
163 shouldBe('dispatchedEvent("mouseout")', '["shadowG(<-shadowD)(@shadowG)", "shadowF(<-shadowD)(@shadowF)", "shadowF(<-shadowD)(@divE)"]');
165 moveMouse('shadowD/divE', 'shadowD/shadowF/shadowG/divI',
166 'RelatedTarget is ancestor of target.');
167 shouldBe('dispatchedEvent("mouseover")', '["divI(<-divE)(@divI)", "shadowG(<-divE)(@shadowG)", "shadowF(<-divE)(@shadowF)", "shadowF(<-divE)(@divE)"]');
168 shouldBe('dispatchedEvent("mouseout")', '["divE(<-shadowF)(@divE)"]');
170 moveMouse('shadowD/shadowF', 'shadowD/shadowF/shadowG/divI',
171 'RelatedTarget (shadow host) is ancestor of target.');
172 shouldBe('dispatchedEvent("mouseover")', '["divI(<-shadowF)(@divI)", "shadowG(<-shadowF)(@shadowG)"]');
173 shouldBe('dispatchedEvent("mouseout")', '[]');
175 moveMouse('shadowD', 'shadowD/shadowF/shadowG',
176 'RelatedTarget (shadow host) is an ancestor of target (shadow host).');
177 shouldBe('dispatchedEvent("mouseover")', '["shadowG(<-shadowD)(@shadowG)", "shadowF(<-shadowD)(@shadowF)", "shadowF(<-shadowD)(@divE)"]');
178 shouldBe('dispatchedEvent("mouseout")', '[]');
180 moveMouse('shadowD/shadowF/shadowG/divH', 'shadowD/shadowK/divL',
181 'Target and relatedTarget exist in separated subtree, crossing shadow boundaries. Making sure that event is not dispatched beyond the lowest common boundary.');
182 shouldBe('dispatchedEvent("mouseover")', '["divL(<-shadowF)(@divL)", "shadowK(<-shadowF)(@shadowK)", "shadowK(<-shadowF)(@divJ)"]');
183 shouldBe('dispatchedEvent("mouseout")', '["divH(<-shadowK)(@divH)", "shadowG(<-shadowK)(@shadowG)", "shadowF(<-shadowK)(@shadowF)", "shadowF(<-shadowK)(@divE)"]');
185 // Test for focusin/focusout events.
186 moveFocus('divB', 'divC',
187 'Move focus from a node to its sibling node. All nodes are outside of shadow boundary.');
188 shouldBe('dispatchedEvent("focusin")', '["divC(@divC)", "divC(@divA)"]');
189 shouldBe('dispatchedEvent("focusout")', '["divB(@divB)", "divB(@divA)"]');
191 moveFocus('shadowD/shadowF/shadowG/divH', 'shadowD/shadowK/divL',
192 'Old focused node and new focused node exist in separated subtrees, crossing shadow boundaries. Making sure that an event is not dispatched beyond the lowest common boundary.');
193 shouldBe('dispatchedEvent("focusin")', '["divL(@divL)", "shadowK(@shadowK)", "shadowK(@divJ)"]');
194 shouldBe('dispatchedEvent("focusout")', '["divH(@divH)", "shadowG(@shadowG)", "shadowF(@shadowF)", "shadowF(@divE)"]');
196 // Omitted test cases where either a oldFocusedNode or newFocusedNode is an ancestor of the other.
197 // Due to a focus transfer mechanism on shadow hosts, a focused node should be a leaf node in general.
199 // Test for focus/blur events. Event listners should be registerd on captureing phase.
200 moveFocus('divB', 'divC',
201 'Move focus from a node to its sibling node. All nodes are outside of shadow boundary.');
202 shouldBe('dispatchedEvent("focus")', '["divC(@divA)(capturing phase)", "divC(@divC)"]');
203 shouldBe('dispatchedEvent("blur")', '["divB(@divA)(capturing phase)", "divB(@divB)"]');
205 moveFocus('shadowD/shadowF/shadowG/divH', 'shadowD/shadowK/divL',
206 'Old focused node and new focused node exist in separated subtrees, crossing shadow boundaries. Making sure that an event is not dispatched beyond the lowest common boundary.');
207 shouldBe('dispatchedEvent("focus")', '["shadowK(@divJ)(capturing phase)", "shadowK(@shadowK)(capturing phase)", "divL(@divL)"]');
208 shouldBe('dispatchedEvent("blur")', '["shadowF(@divE)(capturing phase)", "shadowF(@shadowF)(capturing phase)", "shadowG(@shadowG)(capturing phase)", "divH(@divH)"]');
213 <script src="../../js/resources/js-test-post.js"></script>