[EFL][WK2] Refactor Ewk_Favicon code and stop relying on internal C++ API
[WebKit-https.git] / LayoutTests / fast / dom / shadow / shadow-dom-event-dispatching.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <script src="../../js/resources/js-test-pre.js"></script>
5 <script src="resources/shadow-dom.js"></script>
6 </head>
7 <body>
8 <p id="description"></p>
9 <div id="sandbox"></div>
10 <pre id="console"></pre>
11 <script>
12 description("Tests to ensure that event dispatching behaves as the Shadow DOM spec describes.");
13
14 var defaultPaddingSize = 40;
15
16 function moveMouseOver(element)
17 {
18     if (!window.eventSender || !window.internals)
19         return;
20
21     var x = element.offsetLeft + element.offsetWidth / 2;
22     var y;
23     if (element.hasChildNodes() || window.internals.shadowRoot(element))
24         y = element.offsetTop + defaultPaddingSize / 2;
25     else
26         y = element.offsetTop + element.offsetHeight / 2;
27     eventSender.mouseMoveTo(x, y);
28 }
29
30 var eventRecords = {};
31
32 function clearEventRecords()
33 {
34     eventRecords = {};
35 }
36
37 function dispatchedEvent(eventType)
38 {
39     var events = eventRecords[eventType];
40     if (!events)
41         return [];
42     return events;
43 }
44
45 function recordEvent(event)
46 {
47     var eventType = event.type
48     if (!eventRecords[eventType]) {
49         eventRecords[eventType] = []
50     }
51     var eventString = '';
52     if (event.currentTarget)
53         eventString += ' @' + event.currentTarget.id;
54     if (event.target)
55         eventString += ' (target: ' + event.target.id + ')';
56     if (event.relatedTarget)
57         eventString += ' (related: ' + event.relatedTarget.id + ')';
58     if (event.eventPhase == 1)
59         eventString += '(capturing phase)';
60     if (event.target && event.currentTarget && event.target.id == event.currentTarget.id)
61         shouldBe("event.eventPhase", "2", true);
62     eventRecords[eventType].push(eventString);
63 }
64
65 function dumpNode(node)
66 {
67     var output = node.nodeName + "\t";
68     if (node.id)
69         output += ' id=' + node.id;
70     if (node.className)
71         output += ' class=' + node.className;
72     return output;
73 }
74
75 function dumpComposedShadowTree(node, indent)
76 {
77     indent = indent || "";
78     var output = indent + dumpNode(node) + "\n";
79     var child;
80     for (child = internals.firstChildByWalker(node); child; child = internals.nextSiblingByWalker(child))
81          output += dumpComposedShadowTree(child, indent + "\t");
82     return output;
83 }
84
85 function addEventListeners(nodes)
86 {
87     for (var i = 0; i < nodes.length; ++i) {
88         var node = getNodeInShadowTreeStack(nodes[i]);
89         node.addEventListener('mouseover', recordEvent, false);
90         node.addEventListener('mouseout', recordEvent, false);
91         node.addEventListener('click', recordEvent, false);
92         // <content> might be an inactive insertion point, so style it also.
93         if (node.tagName == 'DIV' || node.tagName == 'DETAILS' || node.tagName == 'SUMMARY' || node.tagName == 'CONTENT')
94             node.setAttribute('style', 'padding-top: ' + defaultPaddingSize + 'px;');
95     }
96 }
97
98 function debugDispatchedEvent(eventType)
99 {
100     debug('\n  ' + eventType);
101     var events = dispatchedEvent(eventType);
102     for (var i = 0; i < events.length; ++i)
103         debug('    ' + events[i])
104 }
105
106 function moveMouse(oldElementId, newElementId)
107 {
108     clearEventRecords();
109     debug('\n' + 'Moving mouse from ' + oldElementId + ' to ' + newElementId);
110     moveMouseOver(getNodeInShadowTreeStack(oldElementId));
111
112     clearEventRecords();
113     moveMouseOver(getNodeInShadowTreeStack(newElementId));
114
115     debugDispatchedEvent('mouseout');
116     debugDispatchedEvent('mouseover');
117 }
118
119 var sandbox = document.getElementById('sandbox');
120
121 function showSandboxTree()
122 {
123     var sandbox = document.getElementById('sandbox');
124     sandbox.offsetLeft;
125     debug('\n\nComposed Shadow Tree will be:\n' + dumpComposedShadowTree(sandbox));
126 }
127
128 function testEventsOnDistributedChild()
129 {
130     sandbox.innerHTML = '';
131     sandbox.appendChild(
132         createDOM('div', {'id': 'top'},
133                   createDOM('div', {'id': 'shadow-host'},
134                             createShadowRoot(
135                                 createDOM('content', {'id': 'content', 'select': '#distributed-light-child',}),
136                                 createDOM('div', {'id': 'shadow-root-child'})),
137                             createDOM('div', {'id': 'distributed-light-child'}),
138                             createDOM('div', {'id': 'non-distributed-light-child'}))));
139
140     addEventListeners(['top', 'shadow-host', 'shadow-host/', 'shadow-host/content', 'shadow-host/shadow-root-child',
141                        'distributed-light-child', 'non-distributed-light-child']);
142     getNodeInShadowTreeStack('shadow-host/').id = 'shadow-root';
143     showSandboxTree();
144
145     moveMouse('shadow-host', 'shadow-host/shadow-root-child');
146     moveMouse('shadow-host/shadow-root-child', 'shadow-host');
147
148     moveMouse('shadow-host', 'distributed-light-child');
149     moveMouse('distributed-light-child', 'shadow-host');
150
151     moveMouse('shadow-host/shadow-root-child', 'distributed-light-child');
152     moveMouse('distributed-light-child', 'shadow-host/shadow-root-child');
153 }
154
155 function testEventsOnDetailsSummary()
156 {
157     sandbox.innerHTML = '';
158     sandbox.appendChild(
159         createDOM('div', {'id': 'top'},
160                   // 'details/summary' elements use Shadow DOM in its implementation.
161                   createDOM('details', {'id': 'details', 'open': true},
162                             createDOM('summary', {'id': 'summary'}))));
163
164     addEventListeners(['top', 'details', 'summary']);
165     showSandboxTree();
166
167     moveMouse('details', 'summary');
168     moveMouse('summary', 'details');
169 }
170
171 function testEventsOnNestedShadowRoots()
172 {
173     sandbox.innerHTML = '';
174     sandbox.appendChild(
175         createDOM('div', {'id': 'top'},
176                   createDOM('div', {'id': 'A'},
177                             createDOM('div', {'id': 'B'},
178                                       createShadowRoot(
179                                           createDOM('div', {'id': 'G'},
180                                                     createShadowRoot(
181                                                         createDOM('div', {'id': 'J'},
182                                                                   createShadowRoot(
183                                                                       createDOM('content', {'id': 'N', 'select': '#C'})),
184                                                                   createDOM('content', {'id': 'K', 'select': '#C'})),
185                                                         createDOM('div', {'id': 'L'},
186                                                                   createShadowRoot(
187                                                                       createDOM('content', {'id': 'O', 'select': '#E'})),
188                                                                   createDOM('content', {'id': 'M', 'select': '#E'}))),
189                                                     createDOM('content', {'id': 'H', 'select': '#C'}),
190                                                     createDOM('content', {'id': 'I', 'select': '#E'}))),
191                                       createDOM('div', {'id': 'C'},
192                                                 createDOM('div', {'id': 'D'})),
193                                       createDOM('div', {'id': 'E'},
194                                                 createDOM('div', {'id': 'F'}))))));
195
196     addEventListeners(['top', 'A', 'B', 'C', 'D', 'E', 'F', 'B/', 'B/G', 'B/H', 'B/I', 'B/G/', 'B/G/J', 'B/G/K', 'B/G/L', 'B/G/M',
197                        'B/G/J/', 'B/G/J/N', 'B/G/L/', 'B/G/L/O']);
198     getNodeInShadowTreeStack('B/').id = 'shadow-root-B';
199     getNodeInShadowTreeStack('B/G/').id = 'shadow-root-G';
200     getNodeInShadowTreeStack('B/G/J/').id = 'shadow-root-J';
201     getNodeInShadowTreeStack('B/G/L/').id = 'shadow-root-L';
202     showSandboxTree();
203
204     moveMouse('F', 'D');
205     moveMouse('B/G/L', 'D');
206     moveMouse('B/G/L', 'B/G/J');
207     moveMouse('A', 'D');
208     moveMouse('D', 'A');
209 }
210
211 function testEventsOnSVGInShadowSubtree()
212 {
213     // Makes sure that <svg> in shadow DOM subtree, which is not supported at this time, does not crash.
214     sandbox.innerHTML = '';
215     sandbox.appendChild(
216         createDOM('div', {'id': 'top'},
217                   createDOM('div', {'id': 'shadow-host'},
218                             createShadowRoot())));
219     var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
220     svg.id = 'svg-in-shadow-tree';
221     getNodeInShadowTreeStack('shadow-host/').appendChild(svg);
222
223     addEventListeners(['top', 'shadow-host/', 'shadow-host/svg-in-shadow-tree']);
224     getNodeInShadowTreeStack('shadow-host/').id = 'shadow-root';
225     showSandboxTree();
226
227     moveMouse('shadow-host/svg-in-shadow-tree', 'top');
228 }
229
230 function testEventsOnTextNodeOfShadowRoot()
231 {
232     sandbox.innerHTML = '';
233     sandbox.appendChild(
234         createDOM('div', {'id': 'top'},
235                   createDOM('div', {'id': 'shadow-host'},
236                             createShadowRoot())));
237     var shadowRoot = getNodeInShadowTreeStack('shadow-host/');
238     shadowRoot.id = 'shadow-root';
239     shadowRoot.innerHTML = 'Text Nodes';
240     addEventListeners(['top', 'shadow-host', 'shadow-host/']);
241     showSandboxTree();
242
243     // Calculates the position of the text node in the shadow root.
244     var host = document.getElementById('shadow-host');
245     var x = host.offsetLeft + 5;
246     var y = host.offsetTop + defaultPaddingSize + 5;
247     debug('\n' + 'Moving mouse from a direct child text node of the shadow root to top');
248     eventSender.mouseMoveTo(x, y);
249     clearEventRecords();
250     moveMouseOver(document.getElementById('top'));
251     debugDispatchedEvent('mouseout');
252     debugDispatchedEvent('mouseover');
253 }
254
255 function testEventsOnDistributedTextNode()
256 {
257     // Makes sure an insertion point can receive a event when a distributed text node is clicked.
258     sandbox.innerHTML = '';
259     sandbox.appendChild(
260         createDOM('div', {'id': 'top'},
261                   createDOM('div', {'id': 'shadow-host'},
262                             createShadowRoot(
263                                 createDOM('content', {'id': 'content'})),
264                             document.createTextNode('Text Node'))));
265     var shadowRoot = getNodeInShadowTreeStack('shadow-host/').id = 'shadow-root';
266     addEventListeners(['top', 'shadow-host', 'shadow-host/', 'shadow-host/content']);
267     showSandboxTree();
268
269     // Calculates the position of the text node.
270     var host = document.getElementById('shadow-host');
271     var x = host.offsetLeft + 5;
272     var y = host.offsetTop + defaultPaddingSize + 5;
273     debug('\n' + 'Moving mouse from a distributed text node to top');
274     eventSender.mouseMoveTo(x, y);
275     clearEventRecords();
276     moveMouseOver(document.getElementById('top'));
277     debugDispatchedEvent('mouseout');
278     debugDispatchedEvent('mouseover');
279 }
280
281 function testEventsOnChildOfInactiveContent()
282 {
283     sandbox.innerHTML = '';
284     sandbox.appendChild(
285         createDOM('div', {'id': 'top'},
286                   createDOM('div', {'id': 'A'}),
287                   createDOM('div', {'id': 'B'},
288                             createShadowRoot(
289                                 createDOM('content', {'id': 'active-content', 'select': '#parent-of-inactive-content'})),
290                             createDOM('div', {'id': 'parent-of-inactive-content'},
291                                       createDOM('content', {'id': 'inactive-content'},
292                                                 createDOM('div', {'id': 'child-of-inactive-content'}))))));
293
294     addEventListeners(['top', 'A', 'B', 'B/', 'B/active-content',
295                        'parent-of-inactive-content', 'inactive-content', 'child-of-inactive-content']);
296     getNodeInShadowTreeStack('B/').id = 'shadow-root-B';
297     showSandboxTree();
298     moveMouse('A', 'child-of-inactive-content');
299 }
300
301 function testEventsOnMultipleShadowRoots()
302 {
303     sandbox.innerHTML = '';
304     sandbox.appendChild(
305         createDOM('div', {'id': 'top'},
306                   createDOM('div', {'id': 'A'},
307                             createShadowRoot(
308                                 createDOM('content', {'id': 'C'}),
309                                 createDOM('div', {'id': 'D'})),
310                             createShadowRoot(
311                                 createDOM('shadow', {'id': 'E'}),
312                                 createDOM('div', {'id': 'F'})),
313                             createDOM('div', {'id': 'B'}))));
314
315     addEventListeners(['top', 'A', 'B', 'A/', 'A/C', 'A/D', 'A//', 'A//E', 'A//F']);
316     getNodeInShadowTreeStack('A/').id = 'older-shadow-root';
317     getNodeInShadowTreeStack('A//').id = 'younger-shadow-root';
318     showSandboxTree();
319
320     moveMouse('B', 'A');
321     moveMouse('A/D', 'A//F');
322     moveMouse('B', 'A//F');
323 }
324
325 function testEventsOnNonDistributedNodes()
326 {
327     sandbox.innerHTML = '';
328     sandbox.appendChild(
329         createDOM('div', {'id': 'top'},
330                   createDOM('div', {'id': 'A'},
331                             createShadowRoot(
332                                 createDOM('div', {'id': 'B'},
333                                           createDOM('div', {'id': 'C'}))),
334                             createShadowRoot(
335                                 createDOM('div', {'id': 'D'})),
336                             createDOM('div', {'id': 'non-distributed-node'}))));
337
338     addEventListeners(['top', 'A', 'A/', 'A/B', 'A/C', 'A//', 'A//D', 'non-distributed-node']);
339     getNodeInShadowTreeStack('A/').id = 'orphaned-shadow-root';
340     getNodeInShadowTreeStack('A//').id = 'youngest-shadow-root';
341     parent.offsetLeft;
342     showSandboxTree();
343
344     clearEventRecords();
345     debug('\nClick C');
346     getNodeInShadowTreeStack('A/C').click();
347     debugDispatchedEvent('click');
348
349     clearEventRecords();
350     debug('\nClick non-distributed-node');
351     document.getElementById('non-distributed-node').click();
352     debugDispatchedEvent('click');
353 }
354
355 function testEventsOnFallbackElements()
356 {
357     sandbox.innerHTML = '';
358     sandbox.appendChild(
359         createDOM('div', {'id': 'top'},
360                   createDOM('div', {'id': 'A'},
361                             createShadowRoot(
362                                 createDOM('content', {'id': 'content1', 'select': '#none'},
363                                           createDOM('div', {'id': 'used-fallback'})),
364                                 createDOM('content', {'id': 'content2'},
365                                           createDOM('div', {'id': 'non-used-fallback'}))),
366                             createDOM('div', {'id': 'B'}))));
367
368     addEventListeners(['top', 'A', 'B', 'A/', 'A/content1', 'A/used-fallback', 'A/content2', 'A/non-used-fallback']);
369     getNodeInShadowTreeStack('A/').id = 'shadow-root';
370     showSandboxTree();
371
372     moveMouse('A/used-fallback', 'A');
373     moveMouse('A', 'A/used-fallback');
374
375     clearEventRecords();
376     debug('\nClick non-used-fallback node');
377     getNodeInShadowTreeStack('A/non-used-fallback').click();
378     debugDispatchedEvent('click');
379 }
380
381 function testEventsFiredManually()
382 {
383     sandbox.innerHTML = '';
384     sandbox.appendChild(
385         createDOM('div', {'id': 'top'},
386                   createDOM('div', {'id': 'host'},
387                             createShadowRoot(
388                                 createDOM('div', {'id': 'div1'}),
389                                 createDOM('div', {'id': 'div2'})))));
390
391     addEventListeners(['top', 'host', 'host/', 'host/div1', 'host/div2']);
392     getNodeInShadowTreeStack('host/').id = 'shadow-root';
393     showSandboxTree();
394
395     var div1 = getNodeInShadowTreeStack('host/div1');
396     var div2 = getNodeInShadowTreeStack('host/div2');
397
398     clearEventRecords();
399     var event = document.createEvent("MouseEvents");
400     event.initMouseEvent("mouseover", true, false, window,
401                          0, 10, 10, 10, 10, false, false, false, false, 0, div1);
402     div2.dispatchEvent(event);
403
404     debugDispatchedEvent('mouseout');
405     debugDispatchedEvent('mouseover');
406 }
407
408 function testEventsFiredManuallyWithRelatedTargetSameToTarget()
409 {
410     sandbox.innerHTML = '';
411     sandbox.appendChild(
412         createDOM('div', {'id': 'top'},
413                   createDOM('div', {'id': 'host'},
414                             createShadowRoot(
415                                 createDOM('div', {'id': 'div1'})))));
416
417     addEventListeners(['top', 'host', 'host/', 'host/div1']);
418     getNodeInShadowTreeStack('host/').id = 'shadow-root';
419     showSandboxTree();
420
421     var div1 = getNodeInShadowTreeStack('host/div1');
422
423     clearEventRecords();
424     var event = document.createEvent("MouseEvents");
425     event.initMouseEvent("mouseover", true, false, window,
426                          0, 10, 10, 10, 10, false, false, false, false, 0, div1);
427     div1.dispatchEvent(event);
428
429     debugDispatchedEvent('mouseout');
430     debugDispatchedEvent('mouseover');
431 }
432
433 function test()
434 {
435     if (window.testRunner)
436         testRunner.dumpAsText();
437
438     testEventsOnDistributedChild();
439     testEventsOnDetailsSummary();
440     testEventsOnNestedShadowRoots();
441     testEventsOnSVGInShadowSubtree();
442     testEventsOnTextNodeOfShadowRoot();
443     testEventsOnDistributedTextNode();
444     testEventsOnChildOfInactiveContent();
445     testEventsOnMultipleShadowRoots();
446     testEventsOnNonDistributedNodes();
447     testEventsOnFallbackElements();
448     testEventsFiredManually();
449     testEventsFiredManuallyWithRelatedTargetSameToTarget();
450 }
451
452 test();
453 </script>
454 <script src="../../js/resources/js-test-post.js"></script>
455 </body>
456 </html>