toolkit/content/tests/widgets/popup_shared.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.

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

mercurial