/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; // Test keyboard navigation in the rule view add_task(async function () { await pushPref("devtools.inspector.rule-view.focusNextOnEnter", false); const tab = await addTab(`data:text/html;charset=utf-8,

Some header text

`); let { inspector, view } = await openRuleView(); await selectNode("h1", inspector); info("Getting the ruleclose brace element for the `h1` rule"); const brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1]; info("Focus the new property editable field to create a color property"); const ruleEditor = getRuleViewRuleEditorAt(view, 1); await focusNewRuleViewProperty(ruleEditor); EventUtils.sendString("color"); info("Typing ENTER to focus the next field: property value"); let onFocus = once(brace.parentNode, "focus", true); let onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Return"); await onFocus; await onRuleViewChanged; ok(true, "The value field was focused"); info("Entering a property value"); EventUtils.sendString("tomato"); info("Typing Tab again should focus a new property name"); onFocus = once(brace.parentNode, "focus", true); onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Tab"); await onFocus; await onRuleViewChanged; ok(true, "The new property name field was focused"); info( "Filling new property name with background-color and hit Tab to focus value input" ); EventUtils.sendString("background-color"); onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Tab"); await onRuleViewChanged; ok(true, "The value field was focused"); info("Entering a background color value"); EventUtils.sendString("gold"); info("Typing Enter should close the input and focus the value span"); onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Return"); await onRuleViewChanged; info("Wait until the swatch for the color is created"); const colorSwatchEl = await waitFor(() => getRuleViewProperty( view, "h1", "background-color" )?.valueSpan?.querySelector(".inspector-colorswatch") ); is( view.styleDocument.activeElement.textContent, "gold", "Value span is focused after pressing Enter" ); info("Type Tab should focus the color swatch"); EventUtils.sendKey("Tab"); is( view.styleDocument.activeElement, colorSwatchEl, "Focused was moved to color swatch" ); info("Press Shift Tab"); EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); is( view.styleDocument.activeElement.textContent, "gold", "Focus is moved back to property value" ); info("Press Shift Tab again"); EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); is( view.styleDocument.activeElement.textContent, "background-color", "Focus is moved back to property name" ); info("Press Shift Tab once more"); EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); ok( view.styleDocument.activeElement.matches( "input[type=checkbox].ruleview-enableproperty" ), "Focus is moved to the prop toggle checkbox" ); const toggleEl = view.styleDocument.activeElement; ok(toggleEl.checked, "Checkbox is checked by default"); is( toggleEl.getAttribute("title"), "Enable background-color property", "checkbox has expected label" ); info("Press Space to uncheck checkbox"); let onRuleViewRefreshed = view.once("ruleview-changed"); EventUtils.sendKey("Space"); await onRuleViewRefreshed; ok(!toggleEl.checked, "Checkbox is now unchecked"); info("Press Space to check checkbox back"); onRuleViewRefreshed = view.once("ruleview-changed"); EventUtils.sendKey("Space"); await onRuleViewRefreshed; ok(toggleEl.checked, "Checkbox is checked again"); info("Re-start the toolbox"); await gDevTools.closeToolboxForTab(tab); ({ view } = await openRuleView()); }); // The `element` have specific behavior, so we want to test that keyboard navigation // also works fine on them. add_task(async function testKeyboardNavigationInElementRule() { await pushPref("devtools.inspector.rule-view.focusNextOnEnter", false); await addTab("data:text/html;charset=utf-8,

Some header text

"); const { inspector, view } = await openRuleView(); await selectNode("h1", inspector); info("Getting the ruleclose brace element"); const brace = view.styleDocument.querySelector(".ruleview-ruleclose"); info("Focus the new property editable field to create a color property"); const ruleEditor = getRuleViewRuleEditorAt(view, 0); let editor = await focusNewRuleViewProperty(ruleEditor); editor.input.value = "color"; info("Typing ENTER to focus the next field: property value"); let onFocus = once(brace.parentNode, "focus", true); let onRuleViewChanged = view.once("ruleview-changed"); let onStyleAttributeMutation = waitForStyleAttributeMutation(view, `color: `); EventUtils.sendKey("Return"); await onFocus; await onRuleViewChanged; await onStyleAttributeMutation; ok(true, "The value field was focused"); info("Entering a property value"); onStyleAttributeMutation = waitForStyleAttributeMutation( view, `color: green;` ); editor = getCurrentInplaceEditor(view); editor.input.value = "green"; info("Typing Tab again should focus a new property name"); onFocus = once(brace.parentNode, "focus", true); onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Tab"); await onFocus; await onRuleViewChanged; await onStyleAttributeMutation; ok(true, "The new property name field was focused"); info( "Filling new property name with background-color and hit Tab to focus value input" ); EventUtils.sendString("background-color"); onRuleViewChanged = view.once("ruleview-changed"); onStyleAttributeMutation = waitForStyleAttributeMutation( view, `background-color:` ); EventUtils.sendKey("Tab"); await onRuleViewChanged; await onStyleAttributeMutation; ok(true, "The value field was focused"); info("Entering a background color value"); onStyleAttributeMutation = waitForStyleAttributeMutation( view, `background-color: tomato;` ); EventUtils.sendString("tomato", view.styleWindow); info("Typing Enter should close the input and focus the value span"); const onValueDone = view.once("ruleview-changed"); // The element rule is reset when a property is added, which impacts how we deal // with the focused element. const onRuleEditorFocusReset = view.once("rule-editor-focus-reset"); EventUtils.sendKey("Return"); await onValueDone; await onRuleEditorFocusReset; await onStyleAttributeMutation; is( view.styleDocument.activeElement, getRuleViewProperty(view, "element", "background-color").valueSpan, `background-color value span ("tomato") is focused after pressing Enter` ); is( view.styleDocument.activeElement.textContent, "tomato", `focused element has expected text` ); }); // Test keyboard navigation in the rule view when // devtools.inspector.rule-view.focusNextOnEnter is set to true add_task(async function () { await pushPref("devtools.inspector.rule-view.focusNextOnEnter", true); await addTab(`data:text/html;charset=utf-8,

Some header text

`); const { inspector, view } = await openRuleView(); await selectNode("h1", inspector); info("Getting the ruleclose brace element for the `h1` rule"); const brace = view.styleDocument.querySelectorAll(".ruleview-ruleclose")[1]; info("Focus the new property editable field to create a color property"); const ruleEditor = getRuleViewRuleEditorAt(view, 1); await focusNewRuleViewProperty(ruleEditor); EventUtils.sendString("color"); info("Typing ENTER to focus the next field: property value"); let onFocus = once(brace.parentNode, "focus", true); let onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Return"); await onFocus; await onRuleViewChanged; ok(true, "The value field was focused"); info("Entering a property value"); EventUtils.sendString("tomato"); info("Typing Enter again should focus a new property name"); onFocus = once(brace.parentNode, "focus", true); onRuleViewChanged = view.once("ruleview-changed"); EventUtils.sendKey("Return"); await onFocus; await onRuleViewChanged; const activeElement = view.styleDocument.activeElement; is( `${activeElement.tagName}${[...activeElement.classList] .map(cls => `.${cls}`) .join("")}`, "input.styleinspector-propertyeditor", "The new property name field was focused" ); }); function waitForStyleAttributeMutation(view, expectedAttributeValue) { return new Promise(r => { view.inspector.walker.on( "mutations", function onWalkerMutations(mutations) { // Wait until we receive a mutation which updates the style attribute // with the expected value. const receivedLastMutation = mutations.find( mut => mut.attributeName === "style" && mut.newValue.includes(expectedAttributeValue) ); if (receivedLastMutation) { view.inspector.walker.off("mutations", onWalkerMutations); r(); } } ); }); } function getCurrentInplaceEditor(view) { return inplaceEditor(view.styleDocument.activeElement); }