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