|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?> |
|
3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> |
|
4 |
|
5 <window title="Context Menu on List Tests" |
|
6 onload="setTimeout(startTest, 0);" |
|
7 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
8 |
|
9 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> |
|
10 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> |
|
11 |
|
12 <spacer height="5"/> |
|
13 |
|
14 <hbox style="padding-left: 10px;"> |
|
15 <spacer width="5"/> |
|
16 <richlistbox id="list" context="themenu" style="padding: 0;" oncontextmenu="checkContextMenu(event)"> |
|
17 <richlistitem id="item1" style="padding-top: 3px; margin: 0;"><button label="One"/></richlistitem> |
|
18 <richlistitem id="item2" height="22"><checkbox label="Checkbox"/></richlistitem> |
|
19 <richlistitem id="item3"><button label="Three"/></richlistitem> |
|
20 <richlistitem id="item4"><checkbox label="Four"/></richlistitem> |
|
21 </richlistbox> |
|
22 |
|
23 <tree id="tree" rows="5" flex="1" context="themenu" style="-moz-appearance: none; border: 0"> |
|
24 <treecols> |
|
25 <treecol label="Name" flex="1"/> |
|
26 <splitter class="tree-splitter"/> |
|
27 <treecol label="Moons"/> |
|
28 </treecols> |
|
29 <treechildren id="treechildren"> |
|
30 <treeitem> |
|
31 <treerow> |
|
32 <treecell label="Mercury"/> |
|
33 <treecell label="0"/> |
|
34 </treerow> |
|
35 </treeitem> |
|
36 <treeitem> |
|
37 <treerow> |
|
38 <treecell label="Venus"/> |
|
39 <treecell label="0"/> |
|
40 </treerow> |
|
41 </treeitem> |
|
42 <treeitem> |
|
43 <treerow> |
|
44 <treecell label="Earth"/> |
|
45 <treecell label="1"/> |
|
46 </treerow> |
|
47 </treeitem> |
|
48 <treeitem> |
|
49 <treerow> |
|
50 <treecell label="Mars"/> |
|
51 <treecell label="2"/> |
|
52 </treerow> |
|
53 </treeitem> |
|
54 </treechildren> |
|
55 </tree> |
|
56 |
|
57 <menu id="menu" label="Menu"> |
|
58 <menupopup id="menupopup" onpopupshown="menuTests()" onpopuphidden="nextTest()" |
|
59 oncontextmenu="checkContextMenuForMenu(event)"> |
|
60 <menuitem id="menu1" label="Menu 1"/> |
|
61 <menuitem id="menu2" label="Menu 2"/> |
|
62 <menuitem id="menu3" label="Menu 3"/> |
|
63 </menupopup> |
|
64 </menu> |
|
65 |
|
66 </hbox> |
|
67 |
|
68 <menupopup id="themenu" onpopupshowing="if (gTestId == -1) event.preventDefault()" |
|
69 onpopupshown="checkPopup()" onpopuphidden="setTimeout(nextTest, 0);"> |
|
70 <menuitem label="Item"/> |
|
71 </menupopup> |
|
72 |
|
73 <script class="testbody" type="application/javascript"> |
|
74 <![CDATA[ |
|
75 |
|
76 SimpleTest.waitForExplicitFinish(); |
|
77 |
|
78 var gTestId = -1; |
|
79 var gTestElement = "list"; |
|
80 var gSelectionStep = 0; |
|
81 var gContextMenuFired = false; |
|
82 |
|
83 function startTest() |
|
84 { |
|
85 // first, check if the richlistbox selection changes on a contextmenu mouse event |
|
86 var element = $("list"); |
|
87 synthesizeMouse(element.getItemAtIndex(3), 7, 1, { type : "mousedown", button: 2, ctrlKey: true }); |
|
88 synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); |
|
89 |
|
90 gSelectionStep++; |
|
91 synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2, ctrlKey: true, shiftKey: true }); |
|
92 synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); |
|
93 |
|
94 gSelectionStep++; |
|
95 synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2 }); |
|
96 synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); |
|
97 |
|
98 $("menu").open = true; |
|
99 } |
|
100 |
|
101 function menuTests() |
|
102 { |
|
103 gSelectionStep = 0; |
|
104 var element = $("menu"); |
|
105 synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); |
|
106 is(gContextMenuFired, true, "context menu fired when menu open"); |
|
107 |
|
108 gSelectionStep = 1; |
|
109 $("menu").boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject).activeChild = $("menu2"); |
|
110 synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); |
|
111 |
|
112 $("menu").open = false; |
|
113 } |
|
114 |
|
115 function nextTest() |
|
116 { |
|
117 gTestId++; |
|
118 if (gTestId > 2) { |
|
119 if (gTestElement == "list") { |
|
120 gTestElement = "tree"; |
|
121 gTestId = 0; |
|
122 } |
|
123 else { |
|
124 SimpleTest.finish(); |
|
125 return; |
|
126 } |
|
127 } |
|
128 var element = $(gTestElement); |
|
129 element.focus(); |
|
130 if (gTestId == 0) { |
|
131 if (gTestElement == "list") |
|
132 element.selectedIndex = 2; |
|
133 element.currentIndex = 2; |
|
134 synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); |
|
135 } |
|
136 else if (gTestId == 1) { |
|
137 synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); |
|
138 } |
|
139 else { |
|
140 element.currentIndex = -1; |
|
141 element.selectedIndex = -1; |
|
142 synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); |
|
143 } |
|
144 } |
|
145 |
|
146 // This is nasty so I'd better explain what's going on. |
|
147 // The basic problem is that the synthetic mouse coordinate generated |
|
148 // by DOMWindowUtils.sendMouseEvent and also the synthetic mouse coordinate |
|
149 // generated internally when contextmenu events are redirected to the focused |
|
150 // element are rounded to the nearest device pixel. But this rounding is done |
|
151 // while the coordinates are relative to the nearest widget. When this test |
|
152 // is run in the mochitest harness, the nearest widget is the main mochitest |
|
153 // window, and our document can have a fractional position within that |
|
154 // mochitest window. So when we round coordinates for comparison in this |
|
155 // test, we need to do so very carefully, especially if the target element |
|
156 // also has a fractional position within our document. |
|
157 // |
|
158 // For example, if the y-offset of our containing IFRAME is 100.4px, |
|
159 // and the offset of our expected point is 10.3px in our document, the actual |
|
160 // mouse event is dispatched to round(110.7) == 111px. This comes back |
|
161 // with a clientY of round(111 - 100.4) == round(10.6) == 11. This is not |
|
162 // equal to round(10.3) as you might expect. |
|
163 |
|
164 function isRoundedX(a, b, msg) |
|
165 { |
|
166 is(Math.round(a + mozInnerScreenX), Math.round(b + mozInnerScreenX), msg); |
|
167 } |
|
168 |
|
169 function isRoundedY(a, b, msg) |
|
170 { |
|
171 is(Math.round(a + mozInnerScreenY), Math.round(b + mozInnerScreenY), msg); |
|
172 } |
|
173 |
|
174 function checkContextMenu(event) |
|
175 { |
|
176 var rect = $(gTestElement).getBoundingClientRect(); |
|
177 |
|
178 var frombase = (gTestId == -1 || gTestId == 1); |
|
179 if (!frombase) |
|
180 rect = event.originalTarget.getBoundingClientRect(); |
|
181 var left = frombase ? rect.left + 7 : rect.left; |
|
182 var top = frombase ? rect.top + 4 : rect.bottom; |
|
183 |
|
184 isRoundedX(event.clientX, left, gTestElement + " clientX " + gSelectionStep + " " + gTestId + "," + frombase); |
|
185 isRoundedY(event.clientY, top, gTestElement + " clientY " + gSelectionStep + " " + gTestId); |
|
186 ok(event.screenX > left, gTestElement + " screenX " + gSelectionStep + " " + gTestId); |
|
187 ok(event.screenY > top, gTestElement + " screenY " + gSelectionStep + " " + gTestId); |
|
188 |
|
189 // context menu from mouse click |
|
190 switch (gTestId) { |
|
191 case -1: |
|
192 var expected = gSelectionStep == 2 ? 1 : (navigator.platform.indexOf("Mac") >= 0 ? 3 : 0); |
|
193 is($(gTestElement).selectedIndex, expected, "index after click " + gSelectionStep); |
|
194 break; |
|
195 case 0: |
|
196 if (gTestElement == "list") |
|
197 is(event.originalTarget, $("item3"), "list selection target"); |
|
198 else |
|
199 is(event.originalTarget, $("treechildren"), "tree selection target"); |
|
200 break; |
|
201 case 1: |
|
202 is(event.originalTarget.id, $("item1").id, "list mouse selection target"); |
|
203 break; |
|
204 case 2: |
|
205 is(event.originalTarget, $("list"), "list no selection target"); |
|
206 break; |
|
207 } |
|
208 } |
|
209 |
|
210 function checkContextMenuForMenu(event) |
|
211 { |
|
212 gContextMenuFired = true; |
|
213 |
|
214 var popuprect = (gSelectionStep ? $("menu2") : $("menupopup")).getBoundingClientRect(); |
|
215 is(event.clientX, Math.round(popuprect.left), "menu left " + gSelectionStep); |
|
216 // the clientY is off by one sometimes on Windows (when loaded in the testing iframe |
|
217 // but not when loaded separately) so just check for both cases for now |
|
218 ok(event.clientY == Math.round(popuprect.bottom) || |
|
219 event.clientY - 1 == Math.round(popuprect.bottom), "menu top " + gSelectionStep); |
|
220 } |
|
221 |
|
222 function checkPopup() |
|
223 { |
|
224 var menurect = $("themenu").getBoundingClientRect(); |
|
225 |
|
226 if (gTestId == 0) { |
|
227 if (gTestElement == "list") { |
|
228 var itemrect = $("item3").getBoundingClientRect(); |
|
229 isRoundedX(menurect.left, itemrect.left + 2, |
|
230 "list selection keyboard left"); |
|
231 isRoundedY(menurect.top, itemrect.bottom + 2, |
|
232 "list selection keyboard top"); |
|
233 } |
|
234 else { |
|
235 var tree = $("tree"); |
|
236 var bodyrect = $("treechildren").getBoundingClientRect(); |
|
237 isRoundedX(menurect.left, bodyrect.left + 2, |
|
238 "tree selection keyboard left"); |
|
239 isRoundedY(menurect.top, bodyrect.top + |
|
240 tree.treeBoxObject.rowHeight * 3 + 2, |
|
241 "tree selection keyboard top"); |
|
242 } |
|
243 } |
|
244 else if (gTestId == 1) { |
|
245 // activating a context menu with the mouse from position (7, 1). |
|
246 // Add 2 pixels to these values as context menus are offset by 2 pixels |
|
247 // so that they don't appear exactly only the menu making them easier to |
|
248 // dismiss. See nsXULPopupListener. |
|
249 var elementrect = $(gTestElement).getBoundingClientRect(); |
|
250 isRoundedX(menurect.left, elementrect.left + 9, |
|
251 gTestElement + " mouse left"); |
|
252 isRoundedY(menurect.top, elementrect.top + 6, |
|
253 gTestElement + " mouse top"); |
|
254 } |
|
255 else { |
|
256 var elementrect = $(gTestElement).getBoundingClientRect(); |
|
257 isRoundedX(menurect.left, elementrect.left + 2, |
|
258 gTestElement + " no selection keyboard left"); |
|
259 isRoundedY(menurect.top, elementrect.bottom + 2, |
|
260 gTestElement + " no selection keyboard top"); |
|
261 } |
|
262 |
|
263 $("themenu").hidePopup(); |
|
264 } |
|
265 |
|
266 ]]> |
|
267 </script> |
|
268 |
|
269 <body xmlns="http://www.w3.org/1999/xhtml"> |
|
270 <p id="display"> |
|
271 </p> |
|
272 <div id="content" style="display: none"> |
|
273 </div> |
|
274 <pre id="test"> |
|
275 </pre> |
|
276 </body> |
|
277 |
|
278 </window> |