Sat, 03 Jan 2015 20:18:00 +0100
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 /*
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 }