tizen beta release
[framework/web/webkit-efl.git] / LayoutTests / fast / dom / shadow / shadow-boundary-events.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <script src="../../js/resources/js-test-pre.js"></script>
5 <script src="resources/create-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 shadow DOM boundary is not crossed during event propagation. Can only run within DRT.");
13
14 function moveMouseOver(element)
15 {
16     if (!window.eventSender || !window.internals)
17         return;
18
19     var defaultPaddingSize = 20;
20     var x = element.offsetLeft + element.offsetWidth / 2;
21     var y;
22     if (element.hasChildNodes() || window.internals.shadowRoot(element))
23         y = element.offsetTop + defaultPaddingSize;
24     else
25         y = element.offsetTop + element.offsetHeight / 2;
26     eventSender.mouseMoveTo(x, y);
27 }
28
29 var eventRecords = {};
30
31 function clearEventRecords()
32 {
33     eventRecords = {};
34 }
35
36 function dispatchedEvent(eventType)
37 {
38     var events = eventRecords[eventType];
39     if (!events)
40         return [];
41     return events;
42 }
43
44 function recordEvent(event)
45 {
46     var eventType = event.type
47     if (!eventRecords[eventType]) {
48         eventRecords[eventType] = []
49     }
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.
54     var eventString = '';
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);
63 }
64
65 function getElementInShadow(path)
66 {
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]);
72     }
73     return element;
74 }
75
76 function prepareDomTree(parent)
77 {
78     parent.appendChild(
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}))))));
91
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
104     }
105 }
106
107 function moveMouse(oldElementId, newElementId, message)
108 {
109     debug('\n' + message + '\n' + 'Moving mouse from ' + oldElementId + ' to ' + newElementId);
110     moveMouseOver(getElementInShadow(oldElementId));
111     clearEventRecords();
112     moveMouseOver(getElementInShadow(newElementId));
113 }
114
115 function moveFocus(oldElementId, newElementId, message)
116 {
117     debug('\n' + message + '\n' + 'Moving focus from ' + oldElementId + ' to ' + newElementId);
118     getElementInShadow(oldElementId).focus();
119     clearEventRecords();
120     getElementInShadow(newElementId).focus();
121 }
122
123 function test()
124 {
125     if (window.layoutTestController)
126         layoutTestController.dumpAsText();
127     prepareDomTree(document.getElementById('sandbox'));
128
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)"]');
134
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)"]');
139
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)"]');
144
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)"]');
149
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)"]');
154
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)"]');
159
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)"]');
164
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)"]');
169
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")', '[]');
174
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")', '[]');
179
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)"]');
184
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)"]');
190
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)"]');
195
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.
198
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)"]');
204
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)"]');
209 }
210
211 test();
212 </script>
213 <script src="../../js/resources/js-test-post.js"></script>
214 </body>
215 </html>