toolkit/content/tests/chrome/popup_trigger.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:420e9f1155a5
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 ];

mercurial