toolkit/content/tests/chrome/popup_trigger.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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;
     8 function cacheEvent(modifiers)
     9 {
    10   var cachedEvent = null;
    12   var mouseFn = function(event) {
    13     cachedEvent = event;
    14   }
    16   window.addEventListener("mousedown", mouseFn, false);
    17   synthesizeMouse(document.documentElement, 0, 0, modifiers);
    18   window.removeEventListener("mousedown", mouseFn, false);
    20   return cachedEvent;
    21 }
    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   }
    29   gMenuPopup = document.getElementById("thepopup");
    30   gTrigger = document.getElementById("trigger");
    32   gIsMenu = gTrigger.boxObject instanceof Components.interfaces.nsIMenuBoxObject;
    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 });
    41   startPopupTests(popupTests);
    42 }
    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");
    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);
    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");
   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     }
   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 }
   852 ];

mercurial