|
1 var gMenuPopup = null; |
|
2 var gTrigger = null; |
|
3 var gIsMenu = false; |
|
4 var gScreenX = -1, gScreenY = -1; |
|
5 var gCachedEvent = null; |
|
6 var gCachedEvent2 = null; |
|
7 |
|
8 function cacheEvent(modifiers) |
|
9 { |
|
10 var cachedEvent = null; |
|
11 |
|
12 var mouseFn = function(event) { |
|
13 cachedEvent = event; |
|
14 } |
|
15 |
|
16 window.addEventListener("mousedown", mouseFn, false); |
|
17 synthesizeMouse(document.documentElement, 0, 0, modifiers); |
|
18 window.removeEventListener("mousedown", mouseFn, false); |
|
19 |
|
20 return cachedEvent; |
|
21 } |
|
22 |
|
23 function runTests() |
|
24 { |
|
25 if (screen.height < 768) { |
|
26 ok(false, "popup tests are likely to fail for screen heights less than 768 pixels"); |
|
27 } |
|
28 |
|
29 gMenuPopup = document.getElementById("thepopup"); |
|
30 gTrigger = document.getElementById("trigger"); |
|
31 |
|
32 gIsMenu = gTrigger.boxObject instanceof Components.interfaces.nsIMenuBoxObject; |
|
33 |
|
34 // a hacky way to get the screen position of the document. Cache the event |
|
35 // so that we can use it in calls to openPopup. |
|
36 gCachedEvent = cacheEvent({ shiftKey: true }); |
|
37 gScreenX = gCachedEvent.screenX; |
|
38 gScreenY = gCachedEvent.screenY; |
|
39 gCachedEvent2 = cacheEvent({ altKey: true, ctrlKey: true, shiftKey: true, metaKey: true }); |
|
40 |
|
41 startPopupTests(popupTests); |
|
42 } |
|
43 |
|
44 var popupTests = [ |
|
45 { |
|
46 testname: "mouse click on trigger", |
|
47 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
48 test: function() { |
|
49 // for menus, no trigger will be set. For non-menus using the popup |
|
50 // attribute, the trigger will be set to the node with the popup attribute |
|
51 gExpectedTriggerNode = gIsMenu ? "notset" : gTrigger; |
|
52 synthesizeMouse(gTrigger, 4, 4, { }); |
|
53 }, |
|
54 result: function (testname) { |
|
55 gExpectedTriggerNode = null; |
|
56 // menus are the anchor but non-menus are opened at screen coordinates |
|
57 is(gMenuPopup.anchorNode, gIsMenu ? gTrigger : null, testname + " anchorNode"); |
|
58 // menus are opened internally, but non-menus have a mouse event which |
|
59 // triggered them |
|
60 is(gMenuPopup.triggerNode, gIsMenu ? null : gTrigger, testname + " triggerNode"); |
|
61 is(document.popupNode, gIsMenu ? null : gTrigger, testname + " document.popupNode"); |
|
62 is(document.tooltipNode, null, testname + " document.tooltipNode"); |
|
63 // check to ensure the popup node for a different document isn't used |
|
64 if (window.opener) |
|
65 is(window.opener.document.popupNode, null, testname + " opener.document.popupNode"); |
|
66 |
|
67 // this will be used in some tests to ensure the size doesn't change |
|
68 var popuprect = gMenuPopup.getBoundingClientRect(); |
|
69 gPopupWidth = Math.round(popuprect.width); |
|
70 gPopupHeight = Math.round(popuprect.height); |
|
71 |
|
72 checkActive(gMenuPopup, "", testname); |
|
73 checkOpen("trigger", testname); |
|
74 // if a menu, the popup should be opened underneath the menu in the |
|
75 // 'after_start' position, otherwise it is opened at the mouse position |
|
76 if (gIsMenu) |
|
77 compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname); |
|
78 } |
|
79 }, |
|
80 { |
|
81 // check that pressing cursor down while there is no selection |
|
82 // highlights the first item |
|
83 testname: "cursor down no selection", |
|
84 events: [ "DOMMenuItemActive item1" ], |
|
85 test: function() { synthesizeKey("VK_DOWN", { }); }, |
|
86 result: function(testname) { checkActive(gMenuPopup, "item1", testname); } |
|
87 }, |
|
88 { |
|
89 // check that pressing cursor up wraps and highlights the last item |
|
90 testname: "cursor up wrap", |
|
91 events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive last" ], |
|
92 test: function() { synthesizeKey("VK_UP", { }); }, |
|
93 result: function(testname) { |
|
94 checkActive(gMenuPopup, "last", testname); |
|
95 } |
|
96 }, |
|
97 { |
|
98 // check that pressing cursor down wraps and highlights the first item |
|
99 testname: "cursor down wrap", |
|
100 events: [ "DOMMenuItemInactive last", "DOMMenuItemActive item1" ], |
|
101 test: function() { synthesizeKey("VK_DOWN", { }); }, |
|
102 result: function(testname) { checkActive(gMenuPopup, "item1", testname); } |
|
103 }, |
|
104 { |
|
105 // check that pressing cursor down highlights the second item |
|
106 testname: "cursor down", |
|
107 events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive item2" ], |
|
108 test: function() { synthesizeKey("VK_DOWN", { }); }, |
|
109 result: function(testname) { checkActive(gMenuPopup, "item2", testname); } |
|
110 }, |
|
111 { |
|
112 // check that pressing cursor up highlights the second item |
|
113 testname: "cursor up", |
|
114 events: [ "DOMMenuItemInactive item2", "DOMMenuItemActive item1" ], |
|
115 test: function() { synthesizeKey("VK_UP", { }); }, |
|
116 result: function(testname) { checkActive(gMenuPopup, "item1", testname); } |
|
117 }, |
|
118 { |
|
119 // cursor left should not do anything |
|
120 testname: "cursor left", |
|
121 test: function() { synthesizeKey("VK_LEFT", { }); }, |
|
122 result: function(testname) { checkActive(gMenuPopup, "item1", testname); } |
|
123 }, |
|
124 { |
|
125 // cursor right should not do anything |
|
126 testname: "cursor right", |
|
127 test: function() { synthesizeKey("VK_RIGHT", { }); }, |
|
128 result: function(testname) { checkActive(gMenuPopup, "item1", testname); } |
|
129 }, |
|
130 { |
|
131 // check cursor down when a disabled item exists in the menu |
|
132 testname: "cursor down disabled", |
|
133 events: function() { |
|
134 // On Windows, disabled items are included when navigating, but on |
|
135 // other platforms, disabled items are skipped over |
|
136 if (navigator.platform.indexOf("Win") == 0) |
|
137 return [ "DOMMenuItemInactive item1", "DOMMenuItemActive item2" ]; |
|
138 else |
|
139 return [ "DOMMenuItemInactive item1", "DOMMenuItemActive amenu" ]; |
|
140 }, |
|
141 test: function() { |
|
142 document.getElementById("item2").disabled = true; |
|
143 synthesizeKey("VK_DOWN", { }); |
|
144 } |
|
145 }, |
|
146 { |
|
147 // check cursor up when a disabled item exists in the menu |
|
148 testname: "cursor up disabled", |
|
149 events: function() { |
|
150 if (navigator.platform.indexOf("Win") == 0) |
|
151 return [ "DOMMenuItemInactive item2", "DOMMenuItemActive amenu", |
|
152 "DOMMenuItemInactive amenu", "DOMMenuItemActive item2", |
|
153 "DOMMenuItemInactive item2", "DOMMenuItemActive item1" ]; |
|
154 else |
|
155 return [ "DOMMenuItemInactive amenu", "DOMMenuItemActive item1" ]; |
|
156 }, |
|
157 test: function() { |
|
158 if (navigator.platform.indexOf("Win") == 0) |
|
159 synthesizeKey("VK_DOWN", { }); |
|
160 synthesizeKey("VK_UP", { }); |
|
161 if (navigator.platform.indexOf("Win") == 0) |
|
162 synthesizeKey("VK_UP", { }); |
|
163 } |
|
164 }, |
|
165 { |
|
166 testname: "mouse click outside", |
|
167 events: [ "popuphiding thepopup", "popuphidden thepopup", |
|
168 "DOMMenuItemInactive item1", "DOMMenuInactive thepopup" ], |
|
169 test: function() { |
|
170 gMenuPopup.hidePopup(); |
|
171 // XXXndeakin event simulation fires events outside of the platform specific |
|
172 // widget code so the popup capturing isn't handled. Thus, the menu won't |
|
173 // rollup this way. |
|
174 // synthesizeMouse(gTrigger, 0, -12, { }); |
|
175 }, |
|
176 result: function(testname, step) { |
|
177 is(gMenuPopup.anchorNode, null, testname + " anchorNode"); |
|
178 is(gMenuPopup.triggerNode, null, testname + " triggerNode"); |
|
179 is(document.popupNode, null, testname + " document.popupNode"); |
|
180 checkClosed("trigger", testname); |
|
181 } |
|
182 }, |
|
183 { |
|
184 // these tests check to ensure that passing an anchor and position |
|
185 // puts the popup in the right place |
|
186 testname: "open popup anchored", |
|
187 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
188 autohide: "thepopup", |
|
189 steps: ["before_start", "before_end", "after_start", "after_end", |
|
190 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", |
|
191 "topleft topleft", "topcenter topleft", "topright topleft", |
|
192 "leftcenter topright", "rightcenter topright", |
|
193 "bottomleft bottomleft", "bottomcenter bottomleft", "bottomright bottomleft", |
|
194 "topleft bottomright", "bottomcenter bottomright", "rightcenter topright"], |
|
195 test: function(testname, step) { |
|
196 gExpectedTriggerNode = "notset"; |
|
197 gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); |
|
198 }, |
|
199 result: function(testname, step) { |
|
200 // no triggerNode because it was opened without passing an event |
|
201 gExpectedTriggerNode = null; |
|
202 is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode"); |
|
203 is(gMenuPopup.triggerNode, null, testname + " triggerNode"); |
|
204 is(document.popupNode, null, testname + " document.popupNode"); |
|
205 compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname); |
|
206 } |
|
207 }, |
|
208 { |
|
209 // these tests check the same but with a 10 pixel margin on the popup |
|
210 testname: "open popup anchored with margin", |
|
211 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
212 autohide: "thepopup", |
|
213 steps: ["before_start", "before_end", "after_start", "after_end", |
|
214 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", |
|
215 "topleft topleft", "topcenter topleft", "topright topleft", |
|
216 "leftcenter topright", "rightcenter topright", |
|
217 "bottomleft bottomleft", "bottomcenter bottomleft", "bottomright bottomleft", |
|
218 "topleft bottomright", "bottomcenter bottomright", "rightcenter topright"], |
|
219 test: function(testname, step) { |
|
220 gMenuPopup.setAttribute("style", "margin: 10px;"); |
|
221 gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); |
|
222 }, |
|
223 result: function(testname, step) { |
|
224 var rightmod = step == "before_end" || step == "after_end" || |
|
225 step == "start_before" || step == "start_after" || |
|
226 step.match(/topright$/) || step.match(/bottomright$/); |
|
227 var bottommod = step == "before_start" || step == "before_end" || |
|
228 step == "start_after" || step == "end_after" || |
|
229 step.match(/bottomleft$/) || step.match(/bottomright$/); |
|
230 compareEdge(gTrigger, gMenuPopup, step, rightmod ? -10 : 10, bottommod ? -10 : 10, testname); |
|
231 gMenuPopup.removeAttribute("style"); |
|
232 } |
|
233 }, |
|
234 { |
|
235 // these tests check the same but with a -8 pixel margin on the popup |
|
236 testname: "open popup anchored with negative margin", |
|
237 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
238 autohide: "thepopup", |
|
239 steps: ["before_start", "before_end", "after_start", "after_end", |
|
240 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], |
|
241 test: function(testname, step) { |
|
242 gMenuPopup.setAttribute("style", "margin: -8px;"); |
|
243 gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); |
|
244 }, |
|
245 result: function(testname, step) { |
|
246 var rightmod = step == "before_end" || step == "after_end" || |
|
247 step == "start_before" || step == "start_after"; |
|
248 var bottommod = step == "before_start" || step == "before_end" || |
|
249 step == "start_after" || step == "end_after"; |
|
250 compareEdge(gTrigger, gMenuPopup, step, rightmod ? 8 : -8, bottommod ? 8 : -8, testname); |
|
251 gMenuPopup.removeAttribute("style"); |
|
252 } |
|
253 }, |
|
254 { |
|
255 testname: "open popup with large positive margin", |
|
256 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
257 autohide: "thepopup", |
|
258 steps: ["before_start", "before_end", "after_start", "after_end", |
|
259 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], |
|
260 test: function(testname, step) { |
|
261 gMenuPopup.setAttribute("style", "margin: 1000px;"); |
|
262 gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); |
|
263 }, |
|
264 result: function(testname, step) { |
|
265 var popuprect = gMenuPopup.getBoundingClientRect(); |
|
266 // as there is more room on the 'end' or 'after' side, popups will always |
|
267 // appear on the right or bottom corners, depending on which side they are |
|
268 // allowed to be flipped by. |
|
269 var expectedleft = step == "before_end" || step == "after_end" ? |
|
270 0 : Math.round(window.innerWidth - gPopupWidth); |
|
271 var expectedtop = step == "start_after" || step == "end_after" ? |
|
272 0 : Math.round(window.innerHeight - gPopupHeight); |
|
273 is(Math.round(popuprect.left), expectedleft, testname + " x position " + step); |
|
274 is(Math.round(popuprect.top), expectedtop, testname + " y position " + step); |
|
275 gMenuPopup.removeAttribute("style"); |
|
276 } |
|
277 }, |
|
278 { |
|
279 testname: "open popup with large negative margin", |
|
280 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
281 autohide: "thepopup", |
|
282 steps: ["before_start", "before_end", "after_start", "after_end", |
|
283 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap"], |
|
284 test: function(testname, step) { |
|
285 gMenuPopup.setAttribute("style", "margin: -1000px;"); |
|
286 gMenuPopup.openPopup(gTrigger, step, 0, 0, false, false); |
|
287 }, |
|
288 result: function(testname, step) { |
|
289 var popuprect = gMenuPopup.getBoundingClientRect(); |
|
290 // using negative margins causes the reverse of positive margins, and |
|
291 // popups will appear on the left or top corners. |
|
292 var expectedleft = step == "before_end" || step == "after_end" ? |
|
293 Math.round(window.innerWidth - gPopupWidth) : 0; |
|
294 var expectedtop = step == "start_after" || step == "end_after" ? |
|
295 Math.round(window.innerHeight - gPopupHeight) : 0; |
|
296 is(Math.round(popuprect.left), expectedleft, testname + " x position " + step); |
|
297 is(Math.round(popuprect.top), expectedtop, testname + " y position " + step); |
|
298 gMenuPopup.removeAttribute("style"); |
|
299 } |
|
300 }, |
|
301 { |
|
302 testname: "popup with unknown step", |
|
303 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
304 autohide: "thepopup", |
|
305 test: function() { |
|
306 gMenuPopup.openPopup(gTrigger, "other", 0, 0, false, false); |
|
307 }, |
|
308 result: function (testname) { |
|
309 var triggerrect = gMenuPopup.getBoundingClientRect(); |
|
310 var popuprect = gMenuPopup.getBoundingClientRect(); |
|
311 is(Math.round(popuprect.left), triggerrect.left, testname + " x position "); |
|
312 is(Math.round(popuprect.top), triggerrect.top, testname + " y position "); |
|
313 } |
|
314 }, |
|
315 { |
|
316 // these tests check to ensure that the position attribute can be used |
|
317 // to set the position of a popup instead of passing it as an argument |
|
318 testname: "open popup anchored with attribute", |
|
319 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
320 autohide: "thepopup", |
|
321 steps: ["before_start", "before_end", "after_start", "after_end", |
|
322 "start_before", "start_after", "end_before", "end_after", "after_pointer", "overlap", |
|
323 "topcenter topleft", "topright bottomright", "leftcenter topright"], |
|
324 test: function(testname, step) { |
|
325 gMenuPopup.setAttribute("position", step); |
|
326 gMenuPopup.openPopup(gTrigger, "", 0, 0, false, false); |
|
327 }, |
|
328 result: function(testname, step) { compareEdge(gTrigger, gMenuPopup, step, 0, 0, testname); } |
|
329 }, |
|
330 { |
|
331 // this test checks to ensure that the attributes override flag to openPopup |
|
332 // can be used to override the popup's position. This test also passes an |
|
333 // event to openPopup to check the trigger node. |
|
334 testname: "open popup anchored with override", |
|
335 events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], |
|
336 test: function(testname, step) { |
|
337 // attribute overrides the position passed in |
|
338 gMenuPopup.setAttribute("position", "end_after"); |
|
339 gExpectedTriggerNode = gCachedEvent.target; |
|
340 gMenuPopup.openPopup(gTrigger, "before_start", 0, 0, false, true, gCachedEvent); |
|
341 }, |
|
342 result: function(testname, step) { |
|
343 gExpectedTriggerNode = null; |
|
344 is(gMenuPopup.anchorNode, gTrigger, testname + " anchorNode"); |
|
345 is(gMenuPopup.triggerNode, gCachedEvent.target, testname + " triggerNode"); |
|
346 is(document.popupNode, gCachedEvent.target, testname + " document.popupNode"); |
|
347 compareEdge(gTrigger, gMenuPopup, "end_after", 0, 0, testname); |
|
348 } |
|
349 }, |
|
350 { |
|
351 testname: "close popup with escape", |
|
352 events: [ "popuphiding thepopup", "popuphidden thepopup", |
|
353 "DOMMenuInactive thepopup", ], |
|
354 test: function(testname, step) { |
|
355 synthesizeKey("VK_ESCAPE", { }); |
|
356 checkClosed("trigger", testname); |
|
357 } |
|
358 }, |
|
359 { |
|
360 // check that offsets may be supplied to the openPopup method |
|
361 testname: "open popup anchored with offsets", |
|
362 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
363 autohide: "thepopup", |
|
364 test: function(testname, step) { |
|
365 // attribute is empty so does not override |
|
366 gMenuPopup.setAttribute("position", ""); |
|
367 gMenuPopup.openPopup(gTrigger, "before_start", 5, 10, true, true); |
|
368 }, |
|
369 result: function(testname, step) { compareEdge(gTrigger, gMenuPopup, "before_start", 5, 10, testname); } |
|
370 }, |
|
371 { |
|
372 // these tests check to ensure that passing an anchor and position |
|
373 // puts the popup in the right place |
|
374 testname: "show popup anchored", |
|
375 condition: function() { |
|
376 // only perform this test for popups not in a menu, such as those using |
|
377 // the popup attribute, as the showPopup implementation in popup.xml |
|
378 // calls openMenu if the popup is inside a menu |
|
379 return !gIsMenu; |
|
380 }, |
|
381 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
382 autohide: "thepopup", |
|
383 steps: [["topleft", "topleft"], |
|
384 ["topleft", "topright"], ["topleft", "bottomleft"], |
|
385 ["topright", "topleft"], ["topright", "bottomright"], |
|
386 ["bottomleft", "bottomright"], ["bottomleft", "topleft"], |
|
387 ["bottomright", "bottomleft"], ["bottomright", "topright"]], |
|
388 test: function(testname, step) { |
|
389 // the attributes should be ignored |
|
390 gMenuPopup.setAttribute("popupanchor", "topright"); |
|
391 gMenuPopup.setAttribute("popupalign", "bottomright"); |
|
392 gMenuPopup.setAttribute("position", "end_after"); |
|
393 gMenuPopup.showPopup(gTrigger, -1, -1, "popup", step[0], step[1]); |
|
394 }, |
|
395 result: function(testname, step) { |
|
396 var pos = convertPosition(step[0], step[1]); |
|
397 compareEdge(gTrigger, gMenuPopup, pos, 0, 0, testname); |
|
398 gMenuPopup.removeAttribute("popupanchor"); |
|
399 gMenuPopup.removeAttribute("popupalign"); |
|
400 gMenuPopup.removeAttribute("position"); |
|
401 } |
|
402 }, |
|
403 { |
|
404 testname: "show popup with position", |
|
405 condition: function() { return !gIsMenu; }, |
|
406 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
407 autohide: "thepopup", |
|
408 test: function(testname, step) { |
|
409 gMenuPopup.showPopup(gTrigger, gScreenX + 60, gScreenY + 15, |
|
410 "context", "topleft", "bottomright"); |
|
411 }, |
|
412 result: function(testname, step) { |
|
413 var rect = gMenuPopup.getBoundingClientRect(); |
|
414 ok(true, gScreenX + "," + gScreenY); |
|
415 is(rect.left, 60, testname + " left"); |
|
416 is(rect.top, 15, testname + " top"); |
|
417 ok(rect.right, testname + " right is " + rect.right); |
|
418 ok(rect.bottom, testname + " bottom is " + rect.bottom); |
|
419 } |
|
420 }, |
|
421 { |
|
422 // if no anchor is supplied to openPopup, it should be opened relative |
|
423 // to the viewport. |
|
424 testname: "open popup unanchored", |
|
425 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
426 test: function(testname, step) { gMenuPopup.openPopup(null, "after_start", 6, 8, false); }, |
|
427 result: function(testname, step) { |
|
428 var rect = gMenuPopup.getBoundingClientRect(); |
|
429 ok(rect.left == 6 && rect.top == 8 && rect.right && rect.bottom, testname); |
|
430 } |
|
431 }, |
|
432 { |
|
433 testname: "activate menuitem with mouse", |
|
434 events: [ "DOMMenuInactive thepopup", "command item3", |
|
435 "popuphiding thepopup", "popuphidden thepopup", |
|
436 "DOMMenuItemInactive item3" ], |
|
437 test: function(testname, step) { |
|
438 var item3 = document.getElementById("item3"); |
|
439 synthesizeMouse(item3, 4, 4, { }); |
|
440 }, |
|
441 result: function(testname, step) { checkClosed("trigger", testname); } |
|
442 }, |
|
443 { |
|
444 testname: "close popup", |
|
445 condition: function() { return false; }, |
|
446 events: [ "popuphiding thepopup", "popuphidden thepopup", |
|
447 "DOMMenuInactive thepopup" ], |
|
448 test: function(testname, step) { gMenuPopup.hidePopup(); } |
|
449 }, |
|
450 { |
|
451 testname: "open popup at screen", |
|
452 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
453 test: function(testname, step) { |
|
454 gExpectedTriggerNode = "notset"; |
|
455 gMenuPopup.openPopupAtScreen(gScreenX + 24, gScreenY + 20, false); |
|
456 }, |
|
457 result: function(testname, step) { |
|
458 gExpectedTriggerNode = null; |
|
459 is(gMenuPopup.anchorNode, null, testname + " anchorNode"); |
|
460 is(gMenuPopup.triggerNode, null, testname + " triggerNode"); |
|
461 is(document.popupNode, null, testname + " document.popupNode"); |
|
462 var rect = gMenuPopup.getBoundingClientRect(); |
|
463 is(rect.left, 24, testname + " left"); |
|
464 is(rect.top, 20, testname + " top"); |
|
465 ok(rect.right, testname + " right is " + rect.right); |
|
466 ok(rect.bottom, testname + " bottom is " + rect.bottom); |
|
467 } |
|
468 }, |
|
469 { |
|
470 // check that pressing a menuitem's accelerator selects it. Note that |
|
471 // the menuitem with the M accesskey overrides the earlier menuitem that |
|
472 // begins with M. |
|
473 testname: "menuitem accelerator", |
|
474 events: [ "DOMMenuItemActive amenu", "DOMMenuItemInactive amenu", |
|
475 "DOMMenuInactive thepopup", |
|
476 "command amenu", "popuphiding thepopup", "popuphidden thepopup", |
|
477 "DOMMenuItemInactive amenu" |
|
478 ], |
|
479 test: function() { synthesizeKey("M", { }); }, |
|
480 result: function(testname) { checkClosed("trigger", testname); } |
|
481 }, |
|
482 { |
|
483 testname: "open context popup at screen", |
|
484 events: [ "popupshowing thepopup 0010", "popupshown thepopup" ], |
|
485 test: function(testname, step) { |
|
486 gExpectedTriggerNode = gCachedEvent.target; |
|
487 gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent); |
|
488 }, |
|
489 result: function(testname, step) { |
|
490 gExpectedTriggerNode = null; |
|
491 is(gMenuPopup.anchorNode, null, testname + " anchorNode"); |
|
492 is(gMenuPopup.triggerNode, gCachedEvent.target, testname + " triggerNode"); |
|
493 is(document.popupNode, gCachedEvent.target, testname + " document.popupNode"); |
|
494 |
|
495 var childframe = document.getElementById("childframe"); |
|
496 if (childframe) { |
|
497 for (var t = 0; t < 2; t++) { |
|
498 var child = childframe.contentDocument; |
|
499 var evt = child.createEvent("Event"); |
|
500 evt.initEvent("click", true, true); |
|
501 child.documentElement.dispatchEvent(evt); |
|
502 is(child.documentElement.getAttribute("data"), "xnull", |
|
503 "cannot get popupNode from other document"); |
|
504 child.documentElement.setAttribute("data", "none"); |
|
505 // now try again with document.popupNode set explicitly |
|
506 document.popupNode = gCachedEvent.target; |
|
507 } |
|
508 } |
|
509 |
|
510 var rect = gMenuPopup.getBoundingClientRect(); |
|
511 is(rect.left, 10, testname + " left"); |
|
512 is(rect.top, 18, testname + " top"); |
|
513 ok(rect.right, testname + " right is " + rect.right); |
|
514 ok(rect.bottom, testname + " bottom is " + rect.bottom); |
|
515 } |
|
516 }, |
|
517 { |
|
518 // pressing a letter that doesn't correspond to an accelerator, but does |
|
519 // correspond to the first letter in a menu's label. The menu should not |
|
520 // close because there is more than one item corresponding to that letter |
|
521 testname: "menuitem with non accelerator", |
|
522 events: [ "DOMMenuItemActive one" ], |
|
523 test: function() { synthesizeKey("O", { }); }, |
|
524 result: function(testname) { |
|
525 checkOpen("trigger", testname); |
|
526 checkActive(gMenuPopup, "one", testname); |
|
527 } |
|
528 }, |
|
529 { |
|
530 // pressing the letter again should select the next one that starts with |
|
531 // that letter |
|
532 testname: "menuitem with non accelerator again", |
|
533 events: [ "DOMMenuItemInactive one", "DOMMenuItemActive submenu" ], |
|
534 test: function() { synthesizeKey("O", { }); }, |
|
535 result: function(testname) { |
|
536 // 'submenu' is a menu but it should not be open |
|
537 checkOpen("trigger", testname); |
|
538 checkClosed("submenu", testname); |
|
539 checkActive(gMenuPopup, "submenu", testname); |
|
540 } |
|
541 }, |
|
542 { |
|
543 // open the submenu with the cursor right key |
|
544 testname: "open submenu with cursor right", |
|
545 events: [ "popupshowing submenupopup", "DOMMenuItemActive submenuitem", |
|
546 "popupshown submenupopup" ], |
|
547 test: function() { synthesizeKey("VK_RIGHT", { }); }, |
|
548 result: function(testname) { |
|
549 checkOpen("trigger", testname); |
|
550 checkOpen("submenu", testname); |
|
551 checkActive(gMenuPopup, "submenu", testname); |
|
552 checkActive(document.getElementById("submenupopup"), "submenuitem", testname); |
|
553 } |
|
554 }, |
|
555 { |
|
556 // close the submenu with the cursor left key |
|
557 testname: "close submenu with cursor left", |
|
558 events: [ "popuphiding submenupopup", "popuphidden submenupopup", |
|
559 "DOMMenuItemInactive submenuitem", "DOMMenuInactive submenupopup", |
|
560 "DOMMenuItemActive submenu" ], |
|
561 test: function() { synthesizeKey("VK_LEFT", { }); }, |
|
562 result: function(testname) { |
|
563 checkOpen("trigger", testname); |
|
564 checkClosed("submenu", testname); |
|
565 checkActive(gMenuPopup, "submenu", testname); |
|
566 checkActive(document.getElementById("submenupopup"), "", testname); |
|
567 } |
|
568 }, |
|
569 { |
|
570 // open the submenu with the enter key |
|
571 testname: "open submenu with enter", |
|
572 events: [ "popupshowing submenupopup", "DOMMenuItemActive submenuitem", |
|
573 "popupshown submenupopup" ], |
|
574 test: function() { synthesizeKey("VK_RETURN", { }); }, |
|
575 result: function(testname) { |
|
576 checkOpen("trigger", testname); |
|
577 checkOpen("submenu", testname); |
|
578 checkActive(gMenuPopup, "submenu", testname); |
|
579 checkActive(document.getElementById("submenupopup"), "submenuitem", testname); |
|
580 } |
|
581 }, |
|
582 { |
|
583 // close the submenu with the escape key |
|
584 testname: "close submenu with escape", |
|
585 events: [ "popuphiding submenupopup", "popuphidden submenupopup", |
|
586 "DOMMenuItemInactive submenuitem", "DOMMenuInactive submenupopup", |
|
587 "DOMMenuItemActive submenu" ], |
|
588 test: function() { synthesizeKey("VK_ESCAPE", { }); }, |
|
589 result: function(testname) { |
|
590 checkOpen("trigger", testname); |
|
591 checkClosed("submenu", testname); |
|
592 checkActive(gMenuPopup, "submenu", testname); |
|
593 checkActive(document.getElementById("submenupopup"), "", testname); |
|
594 } |
|
595 }, |
|
596 { |
|
597 // pressing the letter again when the next item is disabled should still |
|
598 // select the disabled item on Windows, but select the next item on other |
|
599 // platforms |
|
600 testname: "menuitem with non accelerator disabled", |
|
601 events: function() { |
|
602 if (navigator.platform.indexOf("Win") == 0) |
|
603 return [ "DOMMenuItemInactive submenu", "DOMMenuItemActive other", |
|
604 "DOMMenuItemInactive other", "DOMMenuItemActive item1" ]; |
|
605 else |
|
606 return [ "DOMMenuItemInactive submenu", "DOMMenuItemActive last", |
|
607 "DOMMenuItemInactive last", "DOMMenuItemActive item1" ]; |
|
608 }, |
|
609 test: function() { synthesizeKey("O", { }); synthesizeKey("F", { }); }, |
|
610 result: function(testname) { |
|
611 checkActive(gMenuPopup, "item1", testname); |
|
612 } |
|
613 }, |
|
614 { |
|
615 // pressing a letter that doesn't correspond to an accelerator nor the |
|
616 // first letter of a menu. This should have no effect. |
|
617 testname: "menuitem with keypress no accelerator found", |
|
618 test: function() { synthesizeKey("G", { }); }, |
|
619 result: function(testname) { |
|
620 checkOpen("trigger", testname); |
|
621 checkActive(gMenuPopup, "item1", testname); |
|
622 } |
|
623 }, |
|
624 { |
|
625 // when only one menuitem starting with that letter exists, it should be |
|
626 // selected and the menu closed |
|
627 testname: "menuitem with non accelerator single", |
|
628 events: [ "DOMMenuItemInactive item1", "DOMMenuItemActive amenu", |
|
629 "DOMMenuItemInactive amenu", "DOMMenuInactive thepopup", |
|
630 "command amenu", "popuphiding thepopup", "popuphidden thepopup", |
|
631 "DOMMenuItemInactive amenu", |
|
632 ], |
|
633 test: function() { synthesizeKey("M", { }); }, |
|
634 result: function(testname) { |
|
635 checkClosed("trigger", testname); |
|
636 checkActive(gMenuPopup, "", testname); |
|
637 } |
|
638 }, |
|
639 { |
|
640 testname: "open context popup at screen with all modifiers set", |
|
641 events: [ "popupshowing thepopup 1111", "popupshown thepopup" ], |
|
642 autohide: "thepopup", |
|
643 test: function(testname, step) { |
|
644 gMenuPopup.openPopupAtScreen(gScreenX + 8, gScreenY + 16, true, gCachedEvent2); |
|
645 } |
|
646 }, |
|
647 { |
|
648 testname: "open popup with open property", |
|
649 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
650 test: function(testname, step) { openMenu(gTrigger); }, |
|
651 result: function(testname, step) { |
|
652 checkOpen("trigger", testname); |
|
653 if (gIsMenu) |
|
654 compareEdge(gTrigger, gMenuPopup, "after_start", 0, 0, testname); |
|
655 } |
|
656 }, |
|
657 { |
|
658 testname: "open submenu with open property", |
|
659 events: [ "popupshowing submenupopup", "DOMMenuItemActive submenu", |
|
660 "popupshown submenupopup" ], |
|
661 test: function(testname, step) { openMenu(document.getElementById("submenu")); }, |
|
662 result: function(testname, step) { |
|
663 checkOpen("trigger", testname); |
|
664 checkOpen("submenu", testname); |
|
665 // XXXndeakin |
|
666 // getBoundingClientRect doesn't seem to working right for submenus |
|
667 // so disable this test for now |
|
668 // compareEdge(document.getElementById("submenu"), |
|
669 // document.getElementById("submenupopup"), "end_before", 0, 0, testname); |
|
670 } |
|
671 }, |
|
672 { |
|
673 testname: "hidePopup hides entire chain", |
|
674 events: [ "popuphiding submenupopup", "popuphidden submenupopup", |
|
675 "popuphiding thepopup", "popuphidden thepopup", |
|
676 "DOMMenuInactive submenupopup", |
|
677 "DOMMenuItemInactive submenu", "DOMMenuItemInactive submenu", |
|
678 "DOMMenuInactive thepopup", ], |
|
679 test: function() { gMenuPopup.hidePopup(); }, |
|
680 result: function(testname, step) { |
|
681 checkClosed("trigger", testname); |
|
682 checkClosed("submenu", testname); |
|
683 } |
|
684 }, |
|
685 { |
|
686 testname: "open submenu with open property without parent open", |
|
687 test: function(testname, step) { openMenu(document.getElementById("submenu")); }, |
|
688 result: function(testname, step) { |
|
689 checkClosed("trigger", testname); |
|
690 checkClosed("submenu", testname); |
|
691 } |
|
692 }, |
|
693 { |
|
694 testname: "open popup with open property and position", |
|
695 condition: function() { return gIsMenu; }, |
|
696 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
697 test: function(testname, step) { |
|
698 gMenuPopup.setAttribute("position", "before_start"); |
|
699 openMenu(gTrigger); |
|
700 }, |
|
701 result: function(testname, step) { |
|
702 compareEdge(gTrigger, gMenuPopup, "before_start", 0, 0, testname); |
|
703 } |
|
704 }, |
|
705 { |
|
706 testname: "close popup with open property", |
|
707 condition: function() { return gIsMenu; }, |
|
708 events: [ "popuphiding thepopup", "popuphidden thepopup", |
|
709 "DOMMenuInactive thepopup" ], |
|
710 test: function(testname, step) { closeMenu(gTrigger, gMenuPopup); }, |
|
711 result: function(testname, step) { checkClosed("trigger", testname); } |
|
712 }, |
|
713 { |
|
714 testname: "open popup with open property, position, anchor and alignment", |
|
715 condition: function() { return gIsMenu; }, |
|
716 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
717 autohide: "thepopup", |
|
718 test: function(testname, step) { |
|
719 gMenuPopup.setAttribute("position", "start_after"); |
|
720 gMenuPopup.setAttribute("popupanchor", "topright"); |
|
721 gMenuPopup.setAttribute("popupalign", "bottomright"); |
|
722 openMenu(gTrigger); |
|
723 }, |
|
724 result: function(testname, step) { |
|
725 compareEdge(gTrigger, gMenuPopup, "start_after", 0, 0, testname); |
|
726 } |
|
727 }, |
|
728 { |
|
729 testname: "open popup with open property, anchor and alignment", |
|
730 condition: function() { return gIsMenu; }, |
|
731 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
732 autohide: "thepopup", |
|
733 test: function(testname, step) { |
|
734 gMenuPopup.removeAttribute("position"); |
|
735 gMenuPopup.setAttribute("popupanchor", "bottomright"); |
|
736 gMenuPopup.setAttribute("popupalign", "topright"); |
|
737 openMenu(gTrigger); |
|
738 }, |
|
739 result: function(testname, step) { |
|
740 compareEdge(gTrigger, gMenuPopup, "after_end", 0, 0, testname); |
|
741 gMenuPopup.removeAttribute("popupanchor"); |
|
742 gMenuPopup.removeAttribute("popupalign"); |
|
743 } |
|
744 }, |
|
745 { |
|
746 testname: "focus and cursor down on trigger", |
|
747 condition: function() { return gIsMenu; }, |
|
748 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
749 autohide: "thepopup", |
|
750 test: function(testname, step) { |
|
751 gTrigger.focus(); |
|
752 synthesizeKey("VK_DOWN", { altKey: (navigator.platform.indexOf("Mac") == -1) }); |
|
753 }, |
|
754 result: function(testname, step) { |
|
755 checkOpen("trigger", testname); |
|
756 checkActive(gMenuPopup, "", testname); |
|
757 } |
|
758 }, |
|
759 { |
|
760 testname: "focus and cursor up on trigger", |
|
761 condition: function() { return gIsMenu; }, |
|
762 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
763 test: function(testname, step) { |
|
764 gTrigger.focus(); |
|
765 synthesizeKey("VK_UP", { altKey: (navigator.platform.indexOf("Mac") == -1) }); |
|
766 }, |
|
767 result: function(testname, step) { |
|
768 checkOpen("trigger", testname); |
|
769 checkActive(gMenuPopup, "", testname); |
|
770 } |
|
771 }, |
|
772 { |
|
773 testname: "select and enter on menuitem", |
|
774 condition: function() { return gIsMenu; }, |
|
775 events: [ "DOMMenuItemActive item1", "DOMMenuItemInactive item1", |
|
776 "DOMMenuInactive thepopup", "command item1", |
|
777 "popuphiding thepopup", "popuphidden thepopup", |
|
778 "DOMMenuItemInactive item1" ], |
|
779 test: function(testname, step) { |
|
780 synthesizeKey("VK_DOWN", { }); |
|
781 synthesizeKey("VK_RETURN", { }); |
|
782 }, |
|
783 result: function(testname, step) { checkClosed("trigger", testname); } |
|
784 }, |
|
785 { |
|
786 testname: "focus trigger and key to open", |
|
787 condition: function() { return gIsMenu; }, |
|
788 events: [ "popupshowing thepopup", "popupshown thepopup" ], |
|
789 autohide: "thepopup", |
|
790 test: function(testname, step) { |
|
791 gTrigger.focus(); |
|
792 synthesizeKey((navigator.platform.indexOf("Mac") == -1) ? "VK_F4" : " ", { }); |
|
793 }, |
|
794 result: function(testname, step) { |
|
795 checkOpen("trigger", testname); |
|
796 checkActive(gMenuPopup, "", testname); |
|
797 } |
|
798 }, |
|
799 { |
|
800 // the menu should only open when the meta or alt key is not pressed |
|
801 testname: "focus trigger and key wrong modifier", |
|
802 condition: function() { return gIsMenu; }, |
|
803 test: function(testname, step) { |
|
804 gTrigger.focus(); |
|
805 if (navigator.platform.indexOf("Mac") == -1) |
|
806 synthesizeKey("", { metaKey: true }); |
|
807 else |
|
808 synthesizeKey("VK_F4", { altKey: true }); |
|
809 }, |
|
810 result: function(testname, step) { |
|
811 checkClosed("trigger", testname); |
|
812 } |
|
813 }, |
|
814 { |
|
815 testname: "mouse click on disabled menu", |
|
816 condition: function() { return gIsMenu; }, |
|
817 test: function(testname, step) { |
|
818 gTrigger.setAttribute("disabled", "true"); |
|
819 synthesizeMouse(gTrigger, 4, 4, { }); |
|
820 }, |
|
821 result: function(testname, step) { |
|
822 checkClosed("trigger", testname); |
|
823 gTrigger.removeAttribute("disabled"); |
|
824 } |
|
825 }, |
|
826 { |
|
827 // openPopup should open the menu synchronously, however popupshown |
|
828 // is fired asynchronously |
|
829 testname: "openPopup synchronous", |
|
830 events: [ "popupshowing thepopup", "popupshowing submenupopup", |
|
831 "popupshown thepopup", "DOMMenuItemActive submenu", |
|
832 "popupshown submenupopup" ], |
|
833 test: function(testname, step) { |
|
834 gMenuPopup.openPopup(gTrigger, "after_start", 0, 0, false, true); |
|
835 document.getElementById("submenupopup"). |
|
836 openPopup(gTrigger, "end_before", 0, 0, false, true); |
|
837 checkOpen("trigger", testname); |
|
838 checkOpen("submenu", testname); |
|
839 } |
|
840 }, |
|
841 { |
|
842 // remove the content nodes for the popup |
|
843 testname: "remove content", |
|
844 test: function(testname, step) { |
|
845 var submenupopup = document.getElementById("submenupopup"); |
|
846 submenupopup.parentNode.removeChild(submenupopup); |
|
847 var popup = document.getElementById("thepopup"); |
|
848 popup.parentNode.removeChild(popup); |
|
849 } |
|
850 } |
|
851 |
|
852 ]; |