toolkit/content/tests/widgets/popup_shared.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /*
     2  * This script is used for menu and popup tests. Call startPopupTests to start
     3  * the tests, passing an array of tests as an argument. Each test is an object
     4  * with the following properties:
     5  *   testname - name of the test
     6  *   test - function to call to perform the test
     7  *   events - a list of events that are expected to be fired in sequence
     8  *            as a result of calling the 'test' function. This list should be
     9  *            an array of strings of the form "eventtype targetid" where
    10  *            'eventtype' is the event type and 'targetid' is the id of
    11  *            target of the event. This function will be passed two
    12  *            arguments, the testname and the step argument.
    13  *            Alternatively, events may be a function which returns the array
    14  *            of events. This can be used when the events vary per platform.
    15  *   result - function to call after all the events have fired to check
    16  *            for additional results. May be null. This function will be
    17  *            passed two arguments, the testname and the step argument.
    18  *   steps - optional array of values. The test will be repeated for
    19  *           each step, passing each successive value within the array to
    20  *           the test and result functions
    21  *   autohide - if set, should be set to the id of a popup to hide after
    22  *              the test is complete. This is a convenience for some tests.
    23  *   condition - an optional function which, if it returns false, causes the
    24  *               test to be skipped.
    25  *   end - used for debugging. Set to true to stop the tests after running
    26  *         this one.
    27  */
    29 const menuactiveAttribute = "_moz-menuactive";
    31 var gPopupTests = null;
    32 var gTestIndex = -1;
    33 var gTestStepIndex = 0;
    34 var gTestEventIndex = 0;
    35 var gAutoHide = false;
    36 var gExpectedEventDetails = null;
    37 var gExpectedTriggerNode = null;
    38 var gWindowUtils;
    39 var gPopupWidth = -1, gPopupHeight = -1;
    41 function startPopupTests(tests)
    42 {
    43   document.addEventListener("popupshowing", eventOccurred, false);
    44   document.addEventListener("popupshown", eventOccurred, false);
    45   document.addEventListener("popuphiding", eventOccurred, false);
    46   document.addEventListener("popuphidden", eventOccurred, false);
    47   document.addEventListener("command", eventOccurred, false);
    48   document.addEventListener("DOMMenuItemActive", eventOccurred, false);
    49   document.addEventListener("DOMMenuItemInactive", eventOccurred, false);
    50   document.addEventListener("DOMMenuInactive", eventOccurred, false);
    51   document.addEventListener("DOMMenuBarActive", eventOccurred, false);
    52   document.addEventListener("DOMMenuBarInactive", eventOccurred, false);
    54   gPopupTests = tests;
    55   gWindowUtils = SpecialPowers.getDOMWindowUtils(window);
    57   goNext();
    58 }
    60 function finish()
    61 {
    62   if (window.opener) {
    63     window.close();
    64     window.opener.SimpleTest.finish();
    65     return;
    66   }
    67   SimpleTest.finish();
    68   return;
    69 }
    71 function ok(condition, message) {
    72   if (window.opener)
    73     window.opener.SimpleTest.ok(condition, message);
    74   else
    75     SimpleTest.ok(condition, message);
    76 }
    78 function is(left, right, message) {
    79   if (window.opener)
    80     window.opener.SimpleTest.is(left, right, message);
    81   else
    82     SimpleTest.is(left, right, message);
    83 }
    85 function disableNonTestMouse(aDisable) {
    86   gWindowUtils.disableNonTestMouseEvents(aDisable);
    87 }
    89 function eventOccurred(event)
    90 {
    91   if (gPopupTests.length <= gTestIndex) {
    92     ok(false, "Extra " + event.type + " event fired");
    93     return;
    94   }
    96   var test = gPopupTests[gTestIndex];
    97   if ("autohide" in test && gAutoHide) {
    98     if (event.type == "DOMMenuInactive") {
    99       gAutoHide = false;
   100       setTimeout(goNextStep, 0);
   101     }
   102     return;
   103   }
   105   var events = test.events;
   106   if (typeof events == "function")
   107     events = events();
   108   if (events) {
   109     if (events.length <= gTestEventIndex) {
   110       ok(false, "Extra " + event.type + " event fired for " + event.target.id +
   111                   " " +gPopupTests[gTestIndex].testname);
   112       return;
   113     }
   115     var eventitem = events[gTestEventIndex].split(" ");
   116     var matches;
   117     if (eventitem[1] == "#tooltip") {
   118       is(event.originalTarget.localName, "tooltip",
   119          test.testname + " event.originalTarget.localName is 'tooltip'");
   120       is(event.originalTarget.getAttribute("default"), "true",
   121          test.testname + " event.originalTarget default attribute is 'true'");
   122       matches = event.originalTarget.localName == "tooltip" &&
   123           event.originalTarget.getAttribute("default") == "true";
   124     } else {
   125       is(event.type, eventitem[0],
   126          test.testname + " event type " + event.type + " fired");
   127       is(event.target.id, eventitem[1],
   128          test.testname + " event target ID " + event.target.id);
   129       matches = eventitem[0] == event.type && eventitem[1] == event.target.id;
   130     }
   132     var modifiersMask = eventitem[2];
   133     if (modifiersMask) {
   134       var m = "";
   135       m += event.altKey ? '1' : '0';
   136       m += event.ctrlKey ? '1' : '0';
   137       m += event.shiftKey ? '1' : '0';
   138       m += event.metaKey ? '1' : '0';
   139       is(m, modifiersMask, test.testname + " modifiers mask matches");
   140     }
   142     var expectedState;
   143     switch (event.type) {
   144       case "popupshowing": expectedState = "showing"; break;
   145       case "popupshown": expectedState = "open"; break;
   146       case "popuphiding": expectedState = "hiding"; break;
   147       case "popuphidden": expectedState = "closed"; break;
   148     }
   150     if (gExpectedTriggerNode && event.type == "popupshowing") {
   151       if (gExpectedTriggerNode == "notset") // check against null instead
   152         gExpectedTriggerNode = null;
   154       is(event.originalTarget.triggerNode, gExpectedTriggerNode, test.testname + " popupshowing triggerNode");
   155       var isTooltip = (event.target.localName == "tooltip");
   156       is(document.popupNode, isTooltip ? null : gExpectedTriggerNode,
   157          test.testname + " popupshowing document.popupNode");
   158       is(document.tooltipNode, isTooltip ? gExpectedTriggerNode : null,
   159          test.testname + " popupshowing document.tooltipNode");
   160     }
   162     if (expectedState)
   163       is(event.originalTarget.state, expectedState,
   164          test.testname + " " + event.type + " state");
   166     if (matches) {
   167       gTestEventIndex++
   168       if (events.length <= gTestEventIndex)
   169         setTimeout(checkResult, 0);
   170     }
   171   }
   172 }
   174 function checkResult()
   175 {
   176   var step = null;
   177   var test = gPopupTests[gTestIndex];
   178   if ("steps" in test)
   179     step = test.steps[gTestStepIndex];
   181   if ("result" in test)
   182     test.result(test.testname, step);
   184   if ("autohide" in test) {
   185     gAutoHide = true;
   186     document.getElementById(test.autohide).hidePopup();
   187     return;
   188   }
   190   goNextStep();
   191 }
   193 function goNextStep()
   194 {
   195   gTestEventIndex = 0;
   197   var step = null;
   198   var test = gPopupTests[gTestIndex];
   199   if ("steps" in test) {
   200     gTestStepIndex++;
   201     step = test.steps[gTestStepIndex];
   202     if (gTestStepIndex < test.steps.length) {
   203       test.test(test.testname, step);
   204       return;
   205     }
   206   }
   208   goNext();
   209 }
   211 function goNext()
   212 {
   213   // We want to continue after the next animation frame so that
   214   // we're in a stable state and don't get spurious mouse events at unexpected targets.
   215   window.requestAnimationFrame(
   216     function() {
   217       setTimeout(goNextStepSync, 0);
   218     }
   219   );
   220 }
   222 function goNextStepSync()
   223 {
   224   if (gTestIndex >= 0 && "end" in gPopupTests[gTestIndex] && gPopupTests[gTestIndex].end) {
   225     finish();
   226     return;
   227   }
   229   gTestIndex++;
   230   gTestStepIndex = 0;
   231   if (gTestIndex < gPopupTests.length) {
   232     var test = gPopupTests[gTestIndex];
   233     // Set the location hash so it's easy to see which test is running
   234     document.location.hash = test.testname;
   236     // skip the test if the condition returns false
   237     if ("condition" in test && !test.condition()) {
   238       goNext();
   239       return;
   240     }
   242     // start with the first step if there are any
   243     var step = null;
   244     if ("steps" in test)
   245       step = test.steps[gTestStepIndex];
   247     test.test(test.testname, step);
   249     // no events to check for so just check the result
   250     if (!("events" in test))
   251       checkResult();
   252   }
   253   else {
   254     finish();
   255   }
   256 }
   258 function openMenu(menu)
   259 {
   260   if ("open" in menu) {
   261     menu.open = true;
   262   }
   263   else {
   264     var bo = menu.boxObject;
   265     if (bo instanceof SpecialPowers.Ci.nsIMenuBoxObject)
   266       bo.openMenu(true);
   267     else
   268       synthesizeMouse(menu, 4, 4, { });
   269   }
   270 }
   272 function closeMenu(menu, popup)
   273 {
   274   if ("open" in menu) {
   275     menu.open = false;
   276   }
   277   else {
   278     var bo = menu.boxObject;
   279     if (bo instanceof SpecialPowers.Ci.nsIMenuBoxObject)
   280       bo.openMenu(false);
   281     else
   282       popup.hidePopup();
   283   }
   284 }
   286 function checkActive(popup, id, testname)
   287 {
   288   var activeok = true;
   289   var children = popup.childNodes;
   290   for (var c = 0; c < children.length; c++) {
   291     var child = children[c];
   292     if ((id == child.id && child.getAttribute(menuactiveAttribute) != "true") ||
   293         (id != child.id && child.hasAttribute(menuactiveAttribute) != "")) {
   294       activeok = false;
   295       break;
   296     }
   297   }
   298   ok(activeok, testname + " item " + (id ? id : "none") + " active");
   299 }
   301 function checkOpen(menuid, testname)
   302 {
   303   var menu = document.getElementById(menuid);
   304   if ("open" in menu)
   305     ok(menu.open, testname + " " + menuid + " menu is open");
   306   else if (menu.boxObject instanceof SpecialPowers.Ci.nsIMenuBoxObject)
   307     ok(menu.getAttribute("open") == "true", testname + " " + menuid + " menu is open");
   308 }
   310 function checkClosed(menuid, testname)
   311 {
   312   var menu = document.getElementById(menuid);
   313   if ("open" in menu)
   314     ok(!menu.open, testname + " " + menuid + " menu is open");
   315   else if (menu.boxObject instanceof SpecialPowers.Ci.nsIMenuBoxObject)
   316     ok(!menu.hasAttribute("open"), testname + " " + menuid + " menu is closed");
   317 }
   319 function convertPosition(anchor, align)
   320 {
   321   if (anchor == "topleft" && align == "topleft") return "overlap";
   322   if (anchor == "topleft" && align == "topright") return "start_before";
   323   if (anchor == "topleft" && align == "bottomleft") return "before_start";
   324   if (anchor == "topright" && align == "topleft") return "end_before";
   325   if (anchor == "topright" && align == "bottomright") return "before_end";
   326   if (anchor == "bottomleft" && align == "bottomright") return "start_after";
   327   if (anchor == "bottomleft" && align == "topleft") return "after_start";
   328   if (anchor == "bottomright" && align == "bottomleft") return "end_after";
   329   if (anchor == "bottomright" && align == "topright") return "after_end";
   330   return "";
   331 }
   333 /*
   334  * When checking position of the bottom or right edge of the popup's rect,
   335  * use this instead of strict equality check of rounded values,
   336  * because we snap the top/left edges to pixel boundaries,
   337  * which can shift the bottom/right up to 0.5px from its "ideal" location,
   338  * and could cause it to round differently. (See bug 622507.)
   339  */
   340 function isWithinHalfPixel(a, b)
   341 {
   342   return Math.abs(a - b) <= 0.5;
   343 }
   345 function compareEdge(anchor, popup, edge, offsetX, offsetY, testname)
   346 {
   347   testname += " " + edge;
   349   checkOpen(anchor.id, testname);
   351   var anchorrect = anchor.getBoundingClientRect();
   352   var popuprect = popup.getBoundingClientRect();
   353   var check1 = false, check2 = false;
   355   if (gPopupWidth == -1) {
   356     ok((Math.round(popuprect.right) - Math.round(popuprect.left)) &&
   357        (Math.round(popuprect.bottom) - Math.round(popuprect.top)),
   358        testname + " size");
   359   }
   360   else {
   361     is(Math.round(popuprect.width), gPopupWidth, testname + " width");
   362     is(Math.round(popuprect.height), gPopupHeight, testname + " height");
   363   }
   365   var spaceIdx = edge.indexOf(" ");
   366   if (spaceIdx > 0) {
   367     let cornerX, cornerY;
   368     let [anchor, align] = edge.split(" ");
   369     switch (anchor) {
   370       case "topleft": cornerX = anchorrect.left; cornerY = anchorrect.top; break;
   371       case "topcenter": cornerX = anchorrect.left + anchorrect.width / 2; cornerY = anchorrect.top; break;
   372       case "topright": cornerX = anchorrect.right; cornerY = anchorrect.top; break;
   373       case "leftcenter": cornerX = anchorrect.left; cornerY = anchorrect.top + anchorrect.height / 2; break;
   374       case "rightcenter": cornerX = anchorrect.right; cornerY = anchorrect.top + anchorrect.height / 2; break;
   375       case "bottomleft": cornerX = anchorrect.left; cornerY = anchorrect.bottom; break;
   376       case "bottomcenter": cornerX = anchorrect.left + anchorrect.width / 2; cornerY = anchorrect.bottom; break;
   377       case "bottomright": cornerX = anchorrect.right; cornerY = anchorrect.bottom; break;
   378     }
   380     switch (align) {
   381       case "topleft": cornerX += offsetX; cornerY += offsetY; break;
   382       case "topright": cornerX += -popuprect.width + offsetX; cornerY += offsetY; break;
   383       case "bottomleft": cornerX += offsetX; cornerY += -popuprect.height + offsetY; break;
   384       case "bottomright": cornerX += -popuprect.width + offsetX; cornerY += -popuprect.height + offsetY; break;
   385     }
   387     is(Math.round(popuprect.left), Math.round(cornerX), testname + " x position");
   388     is(Math.round(popuprect.top), Math.round(cornerY), testname + " y position");
   389     return;
   390   }
   392   if (edge == "after_pointer") {
   393     is(Math.round(popuprect.left), Math.round(anchorrect.left) + offsetX, testname + " x position");
   394     is(Math.round(popuprect.top), Math.round(anchorrect.top) + offsetY + 21, testname + " y position");
   395     return;
   396   }
   398   if (edge == "overlap") {
   399     ok(Math.round(anchorrect.left) + offsetY == Math.round(popuprect.left) &&
   400        Math.round(anchorrect.top) + offsetY == Math.round(popuprect.top),
   401        testname + " position");
   402     return;
   403   }
   405   if (edge.indexOf("before") == 0)
   406     check1 = isWithinHalfPixel(anchorrect.top + offsetY, popuprect.bottom);
   407   else if (edge.indexOf("after") == 0)
   408     check1 = (Math.round(anchorrect.bottom) + offsetY == Math.round(popuprect.top));
   409   else if (edge.indexOf("start") == 0)
   410     check1 = isWithinHalfPixel(anchorrect.left + offsetX, popuprect.right);
   411   else if (edge.indexOf("end") == 0)
   412     check1 = (Math.round(anchorrect.right) + offsetX == Math.round(popuprect.left));
   414   if (0 < edge.indexOf("before"))
   415     check2 = (Math.round(anchorrect.top) + offsetY == Math.round(popuprect.top));
   416   else if (0 < edge.indexOf("after"))
   417     check2 = isWithinHalfPixel(anchorrect.bottom + offsetY, popuprect.bottom);
   418   else if (0 < edge.indexOf("start"))
   419     check2 = (Math.round(anchorrect.left) + offsetX == Math.round(popuprect.left));
   420   else if (0 < edge.indexOf("end"))
   421     check2 = isWithinHalfPixel(anchorrect.right + offsetX, popuprect.right);
   423   ok(check1 && check2, testname + " position");
   424 }

mercurial