browser_rules_keybindings.js (9573B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test keyboard navigation in the rule view 7 8 add_task(async function () { 9 await pushPref("devtools.inspector.rule-view.focusNextOnEnter", false); 10 const tab = await addTab(`data:text/html;charset=utf-8, 11 <style>h1 {}</style> 12 <h1>Some header text</h1>`); 13 let { inspector, view } = await openRuleView(); 14 await selectNode("h1", inspector); 15 16 info("Getting the ruleclose brace element for the `h1` rule"); 17 const brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1]; 18 19 info("Focus the new property editable field to create a color property"); 20 const ruleEditor = getRuleViewRuleEditor(view, 1); 21 await focusNewRuleViewProperty(ruleEditor); 22 EventUtils.sendString("color"); 23 24 info("Typing ENTER to focus the next field: property value"); 25 let onFocus = once(brace.parentNode, "focus", true); 26 let onRuleViewChanged = view.once("ruleview-changed"); 27 28 EventUtils.sendKey("Return"); 29 30 await onFocus; 31 await onRuleViewChanged; 32 ok(true, "The value field was focused"); 33 34 info("Entering a property value"); 35 EventUtils.sendString("tomato"); 36 37 info("Typing Tab again should focus a new property name"); 38 onFocus = once(brace.parentNode, "focus", true); 39 onRuleViewChanged = view.once("ruleview-changed"); 40 EventUtils.sendKey("Tab"); 41 await onFocus; 42 await onRuleViewChanged; 43 ok(true, "The new property name field was focused"); 44 45 info( 46 "Filling new property name with background-color and hit Tab to focus value input" 47 ); 48 EventUtils.sendString("background-color"); 49 onRuleViewChanged = view.once("ruleview-changed"); 50 EventUtils.sendKey("Tab"); 51 await onRuleViewChanged; 52 53 ok(true, "The value field was focused"); 54 55 info("Entering a background color value"); 56 EventUtils.sendString("gold"); 57 58 info("Typing Enter should close the input and focus the value span"); 59 onRuleViewChanged = view.once("ruleview-changed"); 60 EventUtils.sendKey("Return"); 61 await onRuleViewChanged; 62 63 info("Wait until the swatch for the color is created"); 64 const colorSwatchEl = await waitFor(() => 65 getRuleViewProperty( 66 view, 67 "h1", 68 "background-color" 69 )?.valueSpan?.querySelector(".inspector-colorswatch") 70 ); 71 72 is( 73 view.styleDocument.activeElement.textContent, 74 "gold", 75 "Value span is focused after pressing Enter" 76 ); 77 78 info("Type Tab should focus the color swatch"); 79 EventUtils.sendKey("Tab"); 80 is( 81 view.styleDocument.activeElement, 82 colorSwatchEl, 83 "Focused was moved to color swatch" 84 ); 85 86 info("Press Shift Tab"); 87 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 88 is( 89 view.styleDocument.activeElement.textContent, 90 "gold", 91 "Focus is moved back to property value" 92 ); 93 94 info("Press Shift Tab again"); 95 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 96 is( 97 view.styleDocument.activeElement.textContent, 98 "background-color", 99 "Focus is moved back to property name" 100 ); 101 102 info("Press Shift Tab once more"); 103 EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); 104 ok( 105 view.styleDocument.activeElement.matches( 106 "input[type=checkbox].ruleview-enableproperty" 107 ), 108 "Focus is moved to the prop toggle checkbox" 109 ); 110 const toggleEl = view.styleDocument.activeElement; 111 ok(toggleEl.checked, "Checkbox is checked by default"); 112 is( 113 toggleEl.getAttribute("title"), 114 "Enable background-color property", 115 "checkbox has expected label" 116 ); 117 118 info("Press Space to uncheck checkbox"); 119 let onRuleViewRefreshed = view.once("ruleview-changed"); 120 EventUtils.sendKey("Space"); 121 await onRuleViewRefreshed; 122 ok(!toggleEl.checked, "Checkbox is now unchecked"); 123 124 info("Press Space to check checkbox back"); 125 onRuleViewRefreshed = view.once("ruleview-changed"); 126 EventUtils.sendKey("Space"); 127 await onRuleViewRefreshed; 128 ok(toggleEl.checked, "Checkbox is checked again"); 129 130 info("Re-start the toolbox"); 131 await gDevTools.closeToolboxForTab(tab); 132 ({ view } = await openRuleView()); 133 }); 134 135 // The `element` have specific behavior, so we want to test that keyboard navigation 136 // also works fine on them. 137 138 add_task(async function testKeyboardNavigationInElementRule() { 139 await pushPref("devtools.inspector.rule-view.focusNextOnEnter", false); 140 await addTab("data:text/html;charset=utf-8,<h1>Some header text</h1>"); 141 const { inspector, view } = await openRuleView(); 142 await selectNode("h1", inspector); 143 144 info("Getting the ruleclose brace element"); 145 const brace = view.styleDocument.querySelector(".ruleview-ruleclose"); 146 147 info("Focus the new property editable field to create a color property"); 148 const ruleEditor = getRuleViewRuleEditor(view, 0); 149 let editor = await focusNewRuleViewProperty(ruleEditor); 150 editor.input.value = "color"; 151 152 info("Typing ENTER to focus the next field: property value"); 153 let onFocus = once(brace.parentNode, "focus", true); 154 let onRuleViewChanged = view.once("ruleview-changed"); 155 let onStyleAttributeMutation = waitForStyleAttributeMutation(view, `color: `); 156 157 EventUtils.sendKey("Return"); 158 159 await onFocus; 160 await onRuleViewChanged; 161 await onStyleAttributeMutation; 162 ok(true, "The value field was focused"); 163 164 info("Entering a property value"); 165 onStyleAttributeMutation = waitForStyleAttributeMutation( 166 view, 167 `color: green;` 168 ); 169 editor = getCurrentInplaceEditor(view); 170 editor.input.value = "green"; 171 172 info("Typing Tab again should focus a new property name"); 173 onFocus = once(brace.parentNode, "focus", true); 174 onRuleViewChanged = view.once("ruleview-changed"); 175 EventUtils.sendKey("Tab"); 176 await onFocus; 177 await onRuleViewChanged; 178 await onStyleAttributeMutation; 179 ok(true, "The new property name field was focused"); 180 181 info( 182 "Filling new property name with background-color and hit Tab to focus value input" 183 ); 184 185 EventUtils.sendString("background-color"); 186 187 onRuleViewChanged = view.once("ruleview-changed"); 188 onStyleAttributeMutation = waitForStyleAttributeMutation( 189 view, 190 `background-color:` 191 ); 192 EventUtils.sendKey("Tab"); 193 await onRuleViewChanged; 194 await onStyleAttributeMutation; 195 196 ok(true, "The value field was focused"); 197 198 info("Entering a background color value"); 199 onStyleAttributeMutation = waitForStyleAttributeMutation( 200 view, 201 `background-color: tomato;` 202 ); 203 204 EventUtils.sendString("tomato", view.styleWindow); 205 206 info("Typing Enter should close the input and focus the value span"); 207 const onValueDone = view.once("ruleview-changed"); 208 // The element rule is reset when a property is added, which impacts how we deal 209 // with the focused element. 210 const onRuleEditorFocusReset = view.once("rule-editor-focus-reset"); 211 EventUtils.sendKey("Return"); 212 213 await onValueDone; 214 await onRuleEditorFocusReset; 215 await onStyleAttributeMutation; 216 217 is( 218 view.styleDocument.activeElement, 219 getRuleViewProperty(view, "element", "background-color").valueSpan, 220 `background-color value span ("tomato") is focused after pressing Enter` 221 ); 222 is( 223 view.styleDocument.activeElement.textContent, 224 "tomato", 225 `focused element has expected text` 226 ); 227 }); 228 229 // Test keyboard navigation in the rule view when 230 // devtools.inspector.rule-view.focusNextOnEnter is set to true 231 232 add_task(async function () { 233 await pushPref("devtools.inspector.rule-view.focusNextOnEnter", true); 234 await addTab(`data:text/html;charset=utf-8, 235 <style>h1 {}</style> 236 <h1>Some header text</h1>`); 237 const { inspector, view } = await openRuleView(); 238 await selectNode("h1", inspector); 239 240 info("Getting the ruleclose brace element for the `h1` rule"); 241 const brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1]; 242 243 info("Focus the new property editable field to create a color property"); 244 const ruleEditor = getRuleViewRuleEditor(view, 1); 245 await focusNewRuleViewProperty(ruleEditor); 246 EventUtils.sendString("color"); 247 248 info("Typing ENTER to focus the next field: property value"); 249 let onFocus = once(brace.parentNode, "focus", true); 250 let onRuleViewChanged = view.once("ruleview-changed"); 251 252 EventUtils.sendKey("Return"); 253 254 await onFocus; 255 await onRuleViewChanged; 256 ok(true, "The value field was focused"); 257 258 info("Entering a property value"); 259 EventUtils.sendString("tomato"); 260 261 info("Typing Enter again should focus a new property name"); 262 onFocus = once(brace.parentNode, "focus", true); 263 onRuleViewChanged = view.once("ruleview-changed"); 264 EventUtils.sendKey("Return"); 265 await onFocus; 266 await onRuleViewChanged; 267 268 const activeElement = view.styleDocument.activeElement; 269 is( 270 `${activeElement.tagName}${[...activeElement.classList] 271 .map(cls => `.${cls}`) 272 .join("")}`, 273 "input.styleinspector-propertyeditor", 274 "The new property name field was focused" 275 ); 276 }); 277 278 function waitForStyleAttributeMutation(view, expectedAttributeValue) { 279 return new Promise(r => { 280 view.inspector.walker.on( 281 "mutations", 282 function onWalkerMutations(mutations) { 283 // Wait until we receive a mutation which updates the style attribute 284 // with the expected value. 285 const receivedLastMutation = mutations.find( 286 mut => 287 mut.attributeName === "style" && 288 mut.newValue.includes(expectedAttributeValue) 289 ); 290 if (receivedLastMutation) { 291 view.inspector.walker.off("mutations", onWalkerMutations); 292 r(); 293 } 294 } 295 ); 296 }); 297 } 298 299 function getCurrentInplaceEditor(view) { 300 return inplaceEditor(view.styleDocument.activeElement); 301 }