test_focusrings.xhtml (8137B)
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 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> 5 6 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 7 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 8 <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> 9 10 <html:style xmlns:html="http://www.w3.org/1999/xhtml" type="text/css"> 11 * { outline: none; } 12 #b3:focus-visible { outline: auto } 13 #l1:focus-visible, #l3:focus-visible, #b1:focus-visible { outline: 2px solid red; } 14 #l2:focus, #b2:focus { outline: 2px solid red; } 15 </html:style> 16 17 <script> 18 <![CDATA[ 19 20 SimpleTest.waitForExplicitFinish(); 21 22 const kIsMac = navigator.platform.includes("Mac"); 23 24 function snapShot(element) { 25 var rect = element.getBoundingClientRect(); 26 adjustedRect = { left: rect.left - 6, top: rect.top - 6, 27 width: rect.width + 12, height: rect.height + 12 } 28 return SpecialPowers.snapshotRect(window, adjustedRect, "transparent"); 29 } 30 31 async function initTest() { 32 if (kIsMac) { 33 // FIXME: Historically this pref was only respected on mac, 34 // and the test uses that assumption. 35 // 36 // Ideally it should be refactored to test with the different 37 // values of the pref, independently of the platform. 38 await SpecialPowers.pushPrefEnv({ 39 "set": [ 40 ['accessibility.mouse_focuses_formcontrol', 0], 41 ] 42 }); 43 } 44 45 runTest(); 46 } 47 48 function hasOutline(element) { 49 let cs = getComputedStyle(element); 50 return cs.outlineWidth != "0px" && cs.outlineStyle != "none"; 51 } 52 53 function checkRing(element, width, message) { 54 const expectOutline = width != "0px"; 55 is(hasOutline(element), expectOutline, message); 56 if (expectOutline) { 57 is(getComputedStyle(element).outlineWidth, width, message); 58 } 59 } 60 61 function runTest() 62 { 63 // Make sure our snapshots don't depend on mouse position. 64 synthesizeMouse(document.documentElement, 0, 0, { type: "mousemove" }); 65 66 var isWinOrLinux = navigator.platform.includes("Win") || navigator.platform.includes("Linux"); 67 68 function checkFocus(element, visible, testid) 69 { 70 is(hasOutline(element), visible, testid); 71 } 72 73 // make sure that a focus ring appears on the focused button 74 if (kIsMac) { 75 var focusedButton = $("b3"); 76 const retBefore = compareSnapshots(snapShot(focusedButton), snapShot($("b2")), true); 77 ok(retBefore[0], `unfocused shows no ring,\nRESULT:\n${retBefore[1]}, \nREFERENCE:\n${retBefore[2]}`); 78 focusedButton.focus(); 79 const retAfter = compareSnapshots(snapShot(focusedButton), snapShot($("b2")), false); 80 ok(retAfter[0], `focus shows ring,\nRESULT:\n${retAfter[1]}, \nREFERENCE:\n${retAfter[2]}`); 81 } 82 83 checkFocus($("l1"), false, "initial appearance"); 84 85 // we can't really test the situation on Windows where a dialog doesn't show 86 // focus rings until a key is pressed, as the default state depends on what 87 // kind of real user input, mouse or key, was last entered. But we can handle 88 // the test regardless of which user input last occurred. 89 $("l1").focus(); 90 var expectedVisible = !isWinOrLinux || hasOutline($("l1")); 91 testHTMLElements(htmlElements, isWinOrLinux && !expectedVisible); 92 93 $("l1").focus(); 94 checkFocus($("l1"), expectedVisible, "appearance on list after focus() with :moz-focusring"); 95 $("l2").focus(); 96 97 checkFocus($("l2"), true, "appearance on list after focus() with :focus"); 98 99 ok(!hasOutline($("l1")), "appearance on previous list after focus() with :focus"); 100 101 synthesizeMouse($("l1"), 4, 4, { }); 102 checkFocus($("l1"), false, "appearance on list after mouse focus with :moz-focusring"); 103 synthesizeMouse($("l2"), 4, 4, { }); 104 checkFocus($("l2"), true, "appearance on list after mouse focus with :focus"); 105 106 synthesizeMouse($("b1"), 4, 4, { }); 107 checkFocus($("b1"), false, "appearance on button after mouse focus with :moz-focusring"); 108 109 synthesizeMouse($("b2"), 4, 4, { }); 110 checkFocus($("b2"), !kIsMac, "appearance on button after mouse focus with :focus"); 111 112 // after a key is pressed, the focus ring will always be visible 113 $("l2").focus(); 114 synthesizeKey("KEY_Tab"); 115 checkFocus($("l3"), true, "appearance on list after tab focus"); 116 117 if (kIsMac) { 118 SpecialPowers.pushPrefEnv({"set": [['accessibility.mouse_focuses_formcontrol', 1]]}, testMacFocusesFormControl); 119 } 120 else { 121 SimpleTest.finish(); 122 } 123 } 124 125 async function testMacFocusesFormControl() 126 { 127 await SimpleTest.promiseFocus(window); 128 testHTMLElements(htmlElementsMacPrefSet, false); 129 SimpleTest.finish(); 130 } 131 132 var htmlElements = [ 133 "<button id='elem'>Button</button>", 134 "<input id='elem' type='button'>", 135 "<input id='elem' type='checkbox'>", 136 "<input id='elem' class='canfocus'>", 137 "<input id='elem' type='password' class='canfocus'>", 138 "<textarea id='elem' class='canfocus'></textarea>", 139 "<select id='elem' class='canfocus'><option>One</select>", 140 "<select id='elem' rows='5' class='canfocus'><option>One</select>", 141 "<div id='elem' tabindex='0' class='canfocus' style='width: 10px; height: 10px;'></div>", 142 "<a href='about:blank' class='canfocus' onclick='return false;'>about:blank</a>", 143 ]; 144 145 var htmlElementsMacPrefSet = [ 146 "<button id='elem' class='canfocus'>Button</button>", 147 "<input id='elem' class='canfocus'>", 148 "<input id='elem' type='button' class='canfocus'>", 149 "<input id='elem' type='checkbox' class='canfocus'>", 150 ]; 151 152 function createElement(str) { 153 let doc = new DOMParser().parseFromSafeString(`<html><body>${str}</body></html>`, "text/html"); 154 return doc.body.firstChild; 155 } 156 157 function testHTMLElements(list, expectedNoRingsOnWin) { 158 var childwin = frames[0]; 159 var childdoc = childwin.document; 160 var container = childdoc.getElementById("container"); 161 for (var e = 0; e < list.length; e++) { 162 // Using innerHTML would be sanitized, so use the DOM parser instead to 163 // create the elements. 164 var elem = childdoc.adoptNode(createElement(list[e])); 165 container.appendChild(elem); 166 167 var shouldFocus = !kIsMac || (elem.className == "canfocus"); 168 var ringSize = (shouldFocus ? (expectedNoRingsOnWin ? 2 : 1) : 0) + "px"; 169 170 var expectedMatchWithMouse = shouldFocus && !expectedNoRingsOnWin; 171 var mouseRingSize = ringSize; 172 if (shouldFocus) { 173 var textControl = (function() { 174 if (elem.localName == "textarea") 175 return true; 176 if (elem.localName == "input") 177 return elem.type == "text" || elem.type == "password"; 178 return false; 179 }()); 180 expectedMatchWithMouse = textControl; 181 mouseRingSize = textControl ? "1px" : "2px"; 182 } 183 184 if (elem.localName == "a") { 185 mouseRingSize = ringSize = "0px"; 186 expectedMatchWithMouse = expectedMatchWithMouse && !kIsMac; 187 } 188 189 synthesizeMouse(elem, 8, 8, { }, childwin); 190 is(childdoc.activeElement, shouldFocus ? elem : childdoc.body, "mouse click on " + list[e]); 191 is(elem.matches(":focus-visible"), expectedMatchWithMouse, "mouse click on " + list[e] + " selector"); 192 checkRing(elem, mouseRingSize, "mouse click on " + list[e] + " ring"); 193 194 if (childdoc.activeElement) 195 childdoc.activeElement.blur(); 196 197 ringSize = mouseRingSize; 198 if (kIsMac && !shouldFocus && elem.localName != "a") { 199 ringSize = "1px"; 200 } 201 202 elem.focus(); 203 is(childdoc.activeElement, elem, "focus() on " + list[e]); 204 checkRing(elem, ringSize, "focus() on " + list[e] + " ring"); 205 206 childdoc.activeElement.blur(); 207 208 // Clear out the container for the next test. 209 while (container.firstChild) { 210 container.firstChild.remove(); 211 } 212 } 213 } 214 215 SimpleTest.waitForFocus(initTest); 216 217 ]]> 218 </script> 219 220 <richlistbox id="l1" class="plain" height="20"/> 221 <richlistbox id="l2" class="plain" height="20"/> 222 <richlistbox id="l3" class="plain" height="20"/> 223 <button id="b1" label="Button"/> 224 <button id="b2" label="Button"/> 225 <button id="b3" label="Button"/> 226 227 <iframe id="child" src="file_focusrings.html"/> 228 229 <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> 230 231 </window>