|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?> |
|
3 |
|
4 <window title="Large Menu Tests" |
|
5 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
6 |
|
7 <script type="application/javascript" |
|
8 src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> |
|
9 |
|
10 <!-- |
|
11 This test checks that a large menu is displayed with arrow buttons |
|
12 and is on the screen. |
|
13 --> |
|
14 |
|
15 <script> |
|
16 <![CDATA[ |
|
17 |
|
18 var gOverflowed = false, gUnderflowed = false; |
|
19 var gContextMenuTests = false; |
|
20 var gScreenY = -1; |
|
21 var gTestIndex = 0; |
|
22 var gTests = ["open normal", "open flipped position", "open with scrolling", |
|
23 "open after scrolling", "open small again", |
|
24 "menu movement", "panel movement", |
|
25 "context menu enough space below", |
|
26 "context menu more space above", |
|
27 "context menu too big either side", |
|
28 "context menu larger than screen"]; |
|
29 |
|
30 function getScreenXY(element) |
|
31 { |
|
32 var screenX, screenY; |
|
33 var mouseFn = function(event) { |
|
34 screenX = event.screenX - 1; |
|
35 screenY = event.screenY - 1; |
|
36 } |
|
37 |
|
38 // a hacky way to get the screen position of an element without using the box object |
|
39 window.addEventListener("mousedown", mouseFn, false); |
|
40 synthesizeMouse(element, 1, 1, { }); |
|
41 window.removeEventListener("mousedown", mouseFn, false); |
|
42 |
|
43 return [screenX, screenY]; |
|
44 } |
|
45 |
|
46 function hidePopup() { |
|
47 window.requestAnimationFrame( |
|
48 function() { |
|
49 setTimeout( |
|
50 function() { |
|
51 document.getElementById("popup").hidePopup(); |
|
52 }, 0); |
|
53 }); |
|
54 } |
|
55 |
|
56 function runTests() |
|
57 { |
|
58 [, gScreenY] = getScreenXY(document.documentElement); |
|
59 nextTest(); |
|
60 } |
|
61 |
|
62 function nextTest() |
|
63 { |
|
64 gOverflowed = false, gUnderflowed = false; |
|
65 |
|
66 var y = screen.height; |
|
67 if (gTestIndex == 1) // open flipped position test: |
|
68 y -= 100; |
|
69 else |
|
70 y /= 2; |
|
71 |
|
72 var popup = document.getElementById("popup"); |
|
73 if (gTestIndex == 2) { |
|
74 // add some more menuitems so that scrolling will be necessary |
|
75 for (var t = 1; t <= 30; t++) { |
|
76 var menu = document.createElement("menuitem"); |
|
77 menu.setAttribute("label", "More" + t); |
|
78 popup.appendChild(menu); |
|
79 } |
|
80 } |
|
81 else if (gTestIndex == 4) { |
|
82 for (var t = 1; t <= 30; t++) |
|
83 popup.removeChild(popup.lastChild); |
|
84 } |
|
85 |
|
86 window.requestAnimationFrame(function() { |
|
87 setTimeout( |
|
88 function() { |
|
89 popup.openPopupAtScreen(100, y, false); |
|
90 }, 0); |
|
91 }); |
|
92 } |
|
93 |
|
94 function popupShown() |
|
95 { |
|
96 if (gTests[gTestIndex] == "menu movement") |
|
97 return testPopupMovement(); |
|
98 |
|
99 if (gContextMenuTests) |
|
100 return contextMenuPopupShown(); |
|
101 |
|
102 var popup = document.getElementById("popup"); |
|
103 var rect = popup.getBoundingClientRect(); |
|
104 var sbo = document.getAnonymousNodes(popup)[0].scrollBoxObject; |
|
105 var expectedScrollPos = 0; |
|
106 |
|
107 if (gTestIndex == 0) { |
|
108 // the popup should be in the center of the screen |
|
109 // note that if the height is odd, the y-offset will have been rounded |
|
110 // down when we pass the fractional value to openPopupAtScreen above. |
|
111 is(Math.round(rect.top) + gScreenY, Math.floor(screen.height / 2), |
|
112 gTests[gTestIndex] + " top"); |
|
113 ok(Math.round(rect.bottom) + gScreenY < screen.height, |
|
114 gTests[gTestIndex] + " bottom"); |
|
115 ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow") |
|
116 } |
|
117 else if (gTestIndex == 1) { |
|
118 // the popup was supposed to open 100 pixels from the bottom, but that |
|
119 // would put it off screen so it should be flipped to have its bottom |
|
120 // edge 100 pixels from the bottom |
|
121 ok(Math.round(rect.top) + gScreenY >= screen.top, gTests[gTestIndex] + " top"); |
|
122 is(Math.round(rect.bottom) + gScreenY, screen.height - 100, |
|
123 gTests[gTestIndex] + " bottom"); |
|
124 ok(!gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow") |
|
125 } |
|
126 else if (gTestIndex == 2) { |
|
127 // the popup is too large so ensure that it is on screen |
|
128 ok(Math.round(rect.top) + gScreenY >= screen.top, gTests[gTestIndex] + " top"); |
|
129 ok(Math.round(rect.bottom) + gScreenY <= screen.height, gTests[gTestIndex] + " bottom"); |
|
130 ok(gOverflowed && !gUnderflowed, gTests[gTestIndex] + " overflow") |
|
131 |
|
132 sbo.scrollTo(0, 40); |
|
133 expectedScrollPos = 40; |
|
134 } |
|
135 else if (gTestIndex == 3) { |
|
136 expectedScrollPos = 40; |
|
137 } |
|
138 else if (gTestIndex == 4) { |
|
139 // note that if the height is odd, the y-offset will have been rounded |
|
140 // down when we pass the fractional value to openPopupAtScreen above. |
|
141 is(Math.round(rect.top) + gScreenY, Math.floor(screen.height / 2), |
|
142 gTests[gTestIndex] + " top"); |
|
143 ok(Math.round(rect.bottom) + gScreenY < screen.height, |
|
144 gTests[gTestIndex] + " bottom"); |
|
145 ok(!gOverflowed && gUnderflowed, gTests[gTestIndex] + " overflow") |
|
146 } |
|
147 |
|
148 var sx = { }, sy = { }; |
|
149 sbo.getPosition(sx, sy); |
|
150 is(sy.value, expectedScrollPos, "menu scroll position"); |
|
151 |
|
152 hidePopup(); |
|
153 } |
|
154 |
|
155 function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); } |
|
156 function ok(v, n) { window.opener.wrappedJSObject.SimpleTest.ok(v,n); } |
|
157 |
|
158 var oldx, oldy, waitSteps = 0; |
|
159 function moveWindowTo(x, y, callback, arg) |
|
160 { |
|
161 if (!waitSteps) { |
|
162 oldx = window.screenX; |
|
163 oldy = window.screenY; |
|
164 window.moveTo(x, y); |
|
165 |
|
166 waitSteps++; |
|
167 setTimeout(moveWindowTo, 100, x, y, callback, arg); |
|
168 return; |
|
169 } |
|
170 |
|
171 if (window.screenX == oldx && window.screenY == oldy) { |
|
172 if (waitSteps++ > 10) { |
|
173 ok(false, "Window never moved properly to " + x + "," + y); |
|
174 window.opener.wrappedJSObject.SimpleTest.finish(); |
|
175 window.close(); |
|
176 } |
|
177 |
|
178 setTimeout(moveWindowTo, 100, x, y, callback, arg); |
|
179 } |
|
180 else { |
|
181 waitSteps = 0; |
|
182 callback(arg); |
|
183 } |
|
184 } |
|
185 |
|
186 function popupHidden() |
|
187 { |
|
188 gTestIndex++; |
|
189 if (gTestIndex == gTests.length) { |
|
190 window.opener.wrappedJSObject.SimpleTest.finish(); |
|
191 window.close(); |
|
192 } |
|
193 else if (gTests[gTestIndex] == "context menu enough space below") { |
|
194 gContextMenuTests = true; |
|
195 moveWindowTo(window.screenX, screen.availTop + 10, |
|
196 function () synthesizeMouse(document.getElementById("label"), 4, 4, { type: "contextmenu", button: 2 })); |
|
197 } |
|
198 else if (gTests[gTestIndex] == "menu movement") { |
|
199 document.getElementById("popup").openPopup( |
|
200 document.getElementById("label"), "after_start", 0, 0, false, false); |
|
201 } |
|
202 else if (gTests[gTestIndex] == "panel movement") { |
|
203 document.getElementById("panel").openPopup( |
|
204 document.getElementById("label"), "after_start", 0, 0, false, false); |
|
205 } |
|
206 else if (gContextMenuTests) { |
|
207 contextMenuPopupHidden(); |
|
208 } |
|
209 else { |
|
210 nextTest(); |
|
211 } |
|
212 } |
|
213 |
|
214 function contextMenuPopupShown() |
|
215 { |
|
216 var popup = document.getElementById("popup"); |
|
217 var rect = popup.getBoundingClientRect(); |
|
218 var labelrect = document.getElementById("label").getBoundingClientRect(); |
|
219 |
|
220 is(rect.left, labelrect.left + 6, gTests[gTestIndex] + " left"); |
|
221 switch (gTests[gTestIndex]) { |
|
222 case "context menu enough space below": |
|
223 is(rect.top, labelrect.top + 6, gTests[gTestIndex] + " top"); |
|
224 break; |
|
225 case "context menu more space above": |
|
226 is(rect.top, labelrect.top - rect.height + 2, gTests[gTestIndex] + " top"); |
|
227 break; |
|
228 case "context menu too big either side": |
|
229 [, gScreenY] = getScreenXY(document.documentElement); |
|
230 // compare against the available size as well as the total size, as some |
|
231 // platforms allow the menu to overlap os chrome and others do not |
|
232 var pos = (screen.availTop + screen.availHeight - rect.height) - gScreenY; |
|
233 var availPos = (screen.top + screen.height - rect.height) - gScreenY; |
|
234 ok(rect.top == pos || rect.top == availPos, |
|
235 gTests[gTestIndex] + " top"); |
|
236 break; |
|
237 case "context menu larger than screen": |
|
238 ok(rect.top == -(gScreenY - screen.availTop) || rect.top == -(gScreenY - screen.top), gTests[gTestIndex] + " top"); |
|
239 break; |
|
240 } |
|
241 |
|
242 hidePopup(); |
|
243 } |
|
244 |
|
245 function contextMenuPopupHidden() |
|
246 { |
|
247 var screenAvailBottom = screen.availTop + screen.availHeight; |
|
248 |
|
249 if (gTests[gTestIndex] == "context menu more space above") { |
|
250 moveWindowTo(window.screenX, screenAvailBottom - 80, nextContextMenuTest, -1); |
|
251 } |
|
252 else if (gTests[gTestIndex] == "context menu too big either side") { |
|
253 moveWindowTo(window.screenX, screenAvailBottom / 2 - 80, nextContextMenuTest, screenAvailBottom / 2 + 120); |
|
254 } |
|
255 else if (gTests[gTestIndex] == "context menu larger than screen") { |
|
256 nextContextMenuTest(screen.availHeight + 80); |
|
257 } |
|
258 } |
|
259 |
|
260 function nextContextMenuTest(desiredHeight) |
|
261 { |
|
262 if (desiredHeight >= 0) { |
|
263 var popup = document.getElementById("popup"); |
|
264 var height = popup.getBoundingClientRect().height; |
|
265 var itemheight = document.getElementById("firstitem").getBoundingClientRect().height; |
|
266 while (height < desiredHeight) { |
|
267 var menu = document.createElement("menuitem"); |
|
268 menu.setAttribute("label", "Item"); |
|
269 popup.appendChild(menu); |
|
270 height += itemheight; |
|
271 } |
|
272 } |
|
273 |
|
274 synthesizeMouse(document.getElementById("label"), 4, 4, { type: "contextmenu", button: 2 }); |
|
275 } |
|
276 |
|
277 function testPopupMovement() |
|
278 { |
|
279 var button = document.getElementById("label"); |
|
280 var isPanelTest = (gTests[gTestIndex] == "panel movement"); |
|
281 var popup = document.getElementById(isPanelTest ? "panel" : "popup"); |
|
282 |
|
283 var screenX, screenY, buttonScreenX, buttonScreenY; |
|
284 var rect = popup.getBoundingClientRect(); |
|
285 |
|
286 var overlapOSChrome = (navigator.platform.indexOf("Mac") == -1); |
|
287 popup.moveTo(1, 1); |
|
288 [screenX, screenY] = getScreenXY(popup); |
|
289 |
|
290 var expectedx = 1, expectedy = 1; |
|
291 if (!isPanelTest && !overlapOSChrome) { |
|
292 if (screen.availLeft >= 1) expectedx = screen.availLeft; |
|
293 if (screen.availTop >= 1) expectedy = screen.availTop; |
|
294 } |
|
295 is(screenX, expectedx, gTests[gTestIndex] + " (1, 1) x"); |
|
296 is(screenY, expectedy, gTests[gTestIndex] + " (1, 1) y"); |
|
297 |
|
298 popup.moveTo(100, 8000); |
|
299 if (isPanelTest) { |
|
300 expectedy = 8000; |
|
301 } |
|
302 else { |
|
303 expectedy = (overlapOSChrome ? screen.height + screen.top : screen.availHeight + screen.availTop) - |
|
304 Math.round(rect.height); |
|
305 } |
|
306 |
|
307 [screenX, screenY] = getScreenXY(popup); |
|
308 is(screenX, 100, gTests[gTestIndex] + " (100, 8000) x"); |
|
309 is(screenY, expectedy, gTests[gTestIndex] + " (100, 8000) y"); |
|
310 |
|
311 popup.moveTo(6000, 100); |
|
312 |
|
313 if (isPanelTest) { |
|
314 expectedx = 6000; |
|
315 } |
|
316 else { |
|
317 expectedx = (overlapOSChrome ? screen.width + screen.left : screen.availWidth + screen.availLeft) - |
|
318 Math.round(rect.width); |
|
319 } |
|
320 |
|
321 [screenX, screenY] = getScreenXY(popup); |
|
322 is(screenX, expectedx, gTests[gTestIndex] + " (6000, 100) x"); |
|
323 is(screenY, 100, gTests[gTestIndex] + " (6000, 100) y"); |
|
324 |
|
325 is(popup.left, "", gTests[gTestIndex] + " left is empty after moving"); |
|
326 is(popup.top, "", gTests[gTestIndex] + " top is empty after moving"); |
|
327 popup.setAttribute("left", "80"); |
|
328 popup.setAttribute("top", "82"); |
|
329 [screenX, screenY] = getScreenXY(popup); |
|
330 is(screenX, 80, gTests[gTestIndex] + " set left and top x"); |
|
331 is(screenY, 82, gTests[gTestIndex] + " set left and top y"); |
|
332 popup.moveTo(95, 98); |
|
333 [screenX, screenY] = getScreenXY(popup); |
|
334 is(screenX, 95, gTests[gTestIndex] + " move after set left and top x"); |
|
335 is(screenY, 98, gTests[gTestIndex] + " move after set left and top y"); |
|
336 is(popup.left, "95", gTests[gTestIndex] + " left is set after moving"); |
|
337 is(popup.top, "98", gTests[gTestIndex] + " top is set after moving"); |
|
338 popup.removeAttribute("left"); |
|
339 popup.removeAttribute("top"); |
|
340 |
|
341 popup.moveTo(-1, -1); |
|
342 [screenX, screenY] = getScreenXY(popup); |
|
343 [buttonScreenX, buttonScreenY] = getScreenXY(button); |
|
344 is(screenX, buttonScreenX, gTests[gTestIndex] + " original x"); |
|
345 is(screenY, buttonScreenY + button.getBoundingClientRect().height, gTests[gTestIndex] + " original y"); |
|
346 |
|
347 popup.hidePopup(); |
|
348 } |
|
349 |
|
350 window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTests, window); |
|
351 |
|
352 ]]> |
|
353 </script> |
|
354 |
|
355 <button id="label" label="OK" context="popup"/> |
|
356 <menupopup id="popup" onpopupshown="popupShown();" onpopuphidden="popupHidden();" |
|
357 onoverflow="gOverflowed = true" onunderflow="gUnderflowed = true;"> |
|
358 <menuitem id="firstitem" label="1"/> |
|
359 <menuitem label="2"/> |
|
360 <menuitem label="3"/> |
|
361 <menuitem label="4"/> |
|
362 <menuitem label="5"/> |
|
363 <menuitem label="6"/> |
|
364 <menuitem label="7"/> |
|
365 <menuitem label="8"/> |
|
366 <menuitem label="9"/> |
|
367 <menuitem label="10"/> |
|
368 <menuitem label="11"/> |
|
369 <menuitem label="12"/> |
|
370 <menuitem label="13"/> |
|
371 <menuitem label="14"/> |
|
372 <menuitem label="15"/> |
|
373 </menupopup> |
|
374 |
|
375 <panel id="panel" onpopupshown="testPopupMovement();" onpopuphidden="popupHidden();" style="margin: 0"> |
|
376 <button label="OK"/> |
|
377 </panel> |
|
378 |
|
379 </window> |