1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/content/tests/chrome/test_contextmenu_list.xul Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,278 @@ 1.4 +<?xml version="1.0"?> 1.5 +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> 1.6 +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> 1.7 + 1.8 +<window title="Context Menu on List Tests" 1.9 + onload="setTimeout(startTest, 0);" 1.10 + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 1.11 + 1.12 + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 1.13 + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 1.14 + 1.15 +<spacer height="5"/> 1.16 + 1.17 +<hbox style="padding-left: 10px;"> 1.18 + <spacer width="5"/> 1.19 + <richlistbox id="list" context="themenu" style="padding: 0;" oncontextmenu="checkContextMenu(event)"> 1.20 + <richlistitem id="item1" style="padding-top: 3px; margin: 0;"><button label="One"/></richlistitem> 1.21 + <richlistitem id="item2" height="22"><checkbox label="Checkbox"/></richlistitem> 1.22 + <richlistitem id="item3"><button label="Three"/></richlistitem> 1.23 + <richlistitem id="item4"><checkbox label="Four"/></richlistitem> 1.24 + </richlistbox> 1.25 + 1.26 + <tree id="tree" rows="5" flex="1" context="themenu" style="-moz-appearance: none; border: 0"> 1.27 + <treecols> 1.28 + <treecol label="Name" flex="1"/> 1.29 + <splitter class="tree-splitter"/> 1.30 + <treecol label="Moons"/> 1.31 + </treecols> 1.32 + <treechildren id="treechildren"> 1.33 + <treeitem> 1.34 + <treerow> 1.35 + <treecell label="Mercury"/> 1.36 + <treecell label="0"/> 1.37 + </treerow> 1.38 + </treeitem> 1.39 + <treeitem> 1.40 + <treerow> 1.41 + <treecell label="Venus"/> 1.42 + <treecell label="0"/> 1.43 + </treerow> 1.44 + </treeitem> 1.45 + <treeitem> 1.46 + <treerow> 1.47 + <treecell label="Earth"/> 1.48 + <treecell label="1"/> 1.49 + </treerow> 1.50 + </treeitem> 1.51 + <treeitem> 1.52 + <treerow> 1.53 + <treecell label="Mars"/> 1.54 + <treecell label="2"/> 1.55 + </treerow> 1.56 + </treeitem> 1.57 + </treechildren> 1.58 + </tree> 1.59 + 1.60 + <menu id="menu" label="Menu"> 1.61 + <menupopup id="menupopup" onpopupshown="menuTests()" onpopuphidden="nextTest()" 1.62 + oncontextmenu="checkContextMenuForMenu(event)"> 1.63 + <menuitem id="menu1" label="Menu 1"/> 1.64 + <menuitem id="menu2" label="Menu 2"/> 1.65 + <menuitem id="menu3" label="Menu 3"/> 1.66 + </menupopup> 1.67 + </menu> 1.68 + 1.69 +</hbox> 1.70 + 1.71 +<menupopup id="themenu" onpopupshowing="if (gTestId == -1) event.preventDefault()" 1.72 + onpopupshown="checkPopup()" onpopuphidden="setTimeout(nextTest, 0);"> 1.73 + <menuitem label="Item"/> 1.74 +</menupopup> 1.75 + 1.76 +<script class="testbody" type="application/javascript"> 1.77 +<![CDATA[ 1.78 + 1.79 +SimpleTest.waitForExplicitFinish(); 1.80 + 1.81 +var gTestId = -1; 1.82 +var gTestElement = "list"; 1.83 +var gSelectionStep = 0; 1.84 +var gContextMenuFired = false; 1.85 + 1.86 +function startTest() 1.87 +{ 1.88 + // first, check if the richlistbox selection changes on a contextmenu mouse event 1.89 + var element = $("list"); 1.90 + synthesizeMouse(element.getItemAtIndex(3), 7, 1, { type : "mousedown", button: 2, ctrlKey: true }); 1.91 + synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); 1.92 + 1.93 + gSelectionStep++; 1.94 + synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2, ctrlKey: true, shiftKey: true }); 1.95 + synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); 1.96 + 1.97 + gSelectionStep++; 1.98 + synthesizeMouse(element.getItemAtIndex(1), 7, 1, { type : "mousedown", button: 2 }); 1.99 + synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); 1.100 + 1.101 + $("menu").open = true; 1.102 +} 1.103 + 1.104 +function menuTests() 1.105 +{ 1.106 + gSelectionStep = 0; 1.107 + var element = $("menu"); 1.108 + synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); 1.109 + is(gContextMenuFired, true, "context menu fired when menu open"); 1.110 + 1.111 + gSelectionStep = 1; 1.112 + $("menu").boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject).activeChild = $("menu2"); 1.113 + synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); 1.114 + 1.115 + $("menu").open = false; 1.116 +} 1.117 + 1.118 +function nextTest() 1.119 +{ 1.120 + gTestId++; 1.121 + if (gTestId > 2) { 1.122 + if (gTestElement == "list") { 1.123 + gTestElement = "tree"; 1.124 + gTestId = 0; 1.125 + } 1.126 + else { 1.127 + SimpleTest.finish(); 1.128 + return; 1.129 + } 1.130 + } 1.131 + var element = $(gTestElement); 1.132 + element.focus(); 1.133 + if (gTestId == 0) { 1.134 + if (gTestElement == "list") 1.135 + element.selectedIndex = 2; 1.136 + element.currentIndex = 2; 1.137 + synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); 1.138 + } 1.139 + else if (gTestId == 1) { 1.140 + synthesizeMouse(element, 7, 4, { type : "contextmenu", button: 2 }); 1.141 + } 1.142 + else { 1.143 + element.currentIndex = -1; 1.144 + element.selectedIndex = -1; 1.145 + synthesizeMouse(element, 0, 0, { type : "contextmenu", button: 0 }); 1.146 + } 1.147 +} 1.148 + 1.149 +// This is nasty so I'd better explain what's going on. 1.150 +// The basic problem is that the synthetic mouse coordinate generated 1.151 +// by DOMWindowUtils.sendMouseEvent and also the synthetic mouse coordinate 1.152 +// generated internally when contextmenu events are redirected to the focused 1.153 +// element are rounded to the nearest device pixel. But this rounding is done 1.154 +// while the coordinates are relative to the nearest widget. When this test 1.155 +// is run in the mochitest harness, the nearest widget is the main mochitest 1.156 +// window, and our document can have a fractional position within that 1.157 +// mochitest window. So when we round coordinates for comparison in this 1.158 +// test, we need to do so very carefully, especially if the target element 1.159 +// also has a fractional position within our document. 1.160 +// 1.161 +// For example, if the y-offset of our containing IFRAME is 100.4px, 1.162 +// and the offset of our expected point is 10.3px in our document, the actual 1.163 +// mouse event is dispatched to round(110.7) == 111px. This comes back 1.164 +// with a clientY of round(111 - 100.4) == round(10.6) == 11. This is not 1.165 +// equal to round(10.3) as you might expect. 1.166 + 1.167 +function isRoundedX(a, b, msg) 1.168 +{ 1.169 + is(Math.round(a + mozInnerScreenX), Math.round(b + mozInnerScreenX), msg); 1.170 +} 1.171 + 1.172 +function isRoundedY(a, b, msg) 1.173 +{ 1.174 + is(Math.round(a + mozInnerScreenY), Math.round(b + mozInnerScreenY), msg); 1.175 +} 1.176 + 1.177 +function checkContextMenu(event) 1.178 +{ 1.179 + var rect = $(gTestElement).getBoundingClientRect(); 1.180 + 1.181 + var frombase = (gTestId == -1 || gTestId == 1); 1.182 + if (!frombase) 1.183 + rect = event.originalTarget.getBoundingClientRect(); 1.184 + var left = frombase ? rect.left + 7 : rect.left; 1.185 + var top = frombase ? rect.top + 4 : rect.bottom; 1.186 + 1.187 + isRoundedX(event.clientX, left, gTestElement + " clientX " + gSelectionStep + " " + gTestId + "," + frombase); 1.188 + isRoundedY(event.clientY, top, gTestElement + " clientY " + gSelectionStep + " " + gTestId); 1.189 + ok(event.screenX > left, gTestElement + " screenX " + gSelectionStep + " " + gTestId); 1.190 + ok(event.screenY > top, gTestElement + " screenY " + gSelectionStep + " " + gTestId); 1.191 + 1.192 + // context menu from mouse click 1.193 + switch (gTestId) { 1.194 + case -1: 1.195 + var expected = gSelectionStep == 2 ? 1 : (navigator.platform.indexOf("Mac") >= 0 ? 3 : 0); 1.196 + is($(gTestElement).selectedIndex, expected, "index after click " + gSelectionStep); 1.197 + break; 1.198 + case 0: 1.199 + if (gTestElement == "list") 1.200 + is(event.originalTarget, $("item3"), "list selection target"); 1.201 + else 1.202 + is(event.originalTarget, $("treechildren"), "tree selection target"); 1.203 + break; 1.204 + case 1: 1.205 + is(event.originalTarget.id, $("item1").id, "list mouse selection target"); 1.206 + break; 1.207 + case 2: 1.208 + is(event.originalTarget, $("list"), "list no selection target"); 1.209 + break; 1.210 + } 1.211 +} 1.212 + 1.213 +function checkContextMenuForMenu(event) 1.214 +{ 1.215 + gContextMenuFired = true; 1.216 + 1.217 + var popuprect = (gSelectionStep ? $("menu2") : $("menupopup")).getBoundingClientRect(); 1.218 + is(event.clientX, Math.round(popuprect.left), "menu left " + gSelectionStep); 1.219 + // the clientY is off by one sometimes on Windows (when loaded in the testing iframe 1.220 + // but not when loaded separately) so just check for both cases for now 1.221 + ok(event.clientY == Math.round(popuprect.bottom) || 1.222 + event.clientY - 1 == Math.round(popuprect.bottom), "menu top " + gSelectionStep); 1.223 +} 1.224 + 1.225 +function checkPopup() 1.226 +{ 1.227 + var menurect = $("themenu").getBoundingClientRect(); 1.228 + 1.229 + if (gTestId == 0) { 1.230 + if (gTestElement == "list") { 1.231 + var itemrect = $("item3").getBoundingClientRect(); 1.232 + isRoundedX(menurect.left, itemrect.left + 2, 1.233 + "list selection keyboard left"); 1.234 + isRoundedY(menurect.top, itemrect.bottom + 2, 1.235 + "list selection keyboard top"); 1.236 + } 1.237 + else { 1.238 + var tree = $("tree"); 1.239 + var bodyrect = $("treechildren").getBoundingClientRect(); 1.240 + isRoundedX(menurect.left, bodyrect.left + 2, 1.241 + "tree selection keyboard left"); 1.242 + isRoundedY(menurect.top, bodyrect.top + 1.243 + tree.treeBoxObject.rowHeight * 3 + 2, 1.244 + "tree selection keyboard top"); 1.245 + } 1.246 + } 1.247 + else if (gTestId == 1) { 1.248 + // activating a context menu with the mouse from position (7, 1). 1.249 + // Add 2 pixels to these values as context menus are offset by 2 pixels 1.250 + // so that they don't appear exactly only the menu making them easier to 1.251 + // dismiss. See nsXULPopupListener. 1.252 + var elementrect = $(gTestElement).getBoundingClientRect(); 1.253 + isRoundedX(menurect.left, elementrect.left + 9, 1.254 + gTestElement + " mouse left"); 1.255 + isRoundedY(menurect.top, elementrect.top + 6, 1.256 + gTestElement + " mouse top"); 1.257 + } 1.258 + else { 1.259 + var elementrect = $(gTestElement).getBoundingClientRect(); 1.260 + isRoundedX(menurect.left, elementrect.left + 2, 1.261 + gTestElement + " no selection keyboard left"); 1.262 + isRoundedY(menurect.top, elementrect.bottom + 2, 1.263 + gTestElement + " no selection keyboard top"); 1.264 + } 1.265 + 1.266 + $("themenu").hidePopup(); 1.267 +} 1.268 + 1.269 +]]> 1.270 +</script> 1.271 + 1.272 +<body xmlns="http://www.w3.org/1999/xhtml"> 1.273 +<p id="display"> 1.274 +</p> 1.275 +<div id="content" style="display: none"> 1.276 +</div> 1.277 +<pre id="test"> 1.278 +</pre> 1.279 +</body> 1.280 + 1.281 +</window>