diff --git a/devtools/client/inspector/index.xhtml b/devtools/client/inspector/index.xhtml --- a/devtools/client/inspector/index.xhtml +++ b/devtools/client/inspector/index.xhtml @@ -102,10 +102,11 @@
diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js --- a/devtools/client/inspector/inspector.js +++ b/devtools/client/inspector/inspector.js @@ -1583,11 +1583,11 @@ menu.append(new MenuItem({ type: "separator", })); // Set the pseudo classes - for (const name of ["hover", "active", "focus"]) { + for (const name of ["hover", "active", "focus", "focus-within"]) { const menuitem = new MenuItem({ id: "node-menu-pseudo-" + name, label: name, type: "checkbox", click: this.togglePseudoClass.bind(this, ":" + name), diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js --- a/devtools/client/inspector/rules/rules.js +++ b/devtools/client/inspector/rules/rules.js @@ -137,10 +137,11 @@ this.classPanel = doc.getElementById("ruleview-class-panel"); this.classToggle = doc.getElementById("class-panel-toggle"); this.hoverCheckbox = doc.getElementById("pseudo-hover-toggle"); this.activeCheckbox = doc.getElementById("pseudo-active-toggle"); this.focusCheckbox = doc.getElementById("pseudo-focus-toggle"); + this.focusWithinCheckbox = doc.getElementById("pseudo-focus-within-toggle"); this.searchClearButton.hidden = true; this.shortcuts = new KeyShortcuts({ window: this.styleWindow }); this._onShortcut = this._onShortcut.bind(this); @@ -156,10 +157,11 @@ this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel); this.classToggle.addEventListener("click", this._onToggleClassPanel); this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass); this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass); this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass); + this.focusWithinCheckbox.addEventListener("click", this._onTogglePseudoClass); if (flags.testing) { // In tests, we start listening immediately to avoid having to simulate a mousemove. this.highlighters.addToView(this); } else { @@ -776,20 +778,22 @@ this.pseudoClassToggle.removeEventListener("click", this._onTogglePseudoClassPanel); this.classToggle.removeEventListener("click", this._onToggleClassPanel); this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass); + this.focusWithinCheckbox.removeEventListener("click", this._onTogglePseudoClass); this.searchField = null; this.searchClearButton = null; this.pseudoClassPanel = null; this.pseudoClassToggle = null; this.classPanel = null; this.classToggle = null; this.hoverCheckbox = null; this.activeCheckbox = null; this.focusCheckbox = null; + this.focusWithinCheckbox = null; this.inspector = null; this.styleDocument = null; this.styleWindow = null; @@ -925,20 +929,22 @@ */ clearPseudoClassPanel: function() { this.hoverCheckbox.checked = this.hoverCheckbox.disabled = false; this.activeCheckbox.checked = this.activeCheckbox.disabled = false; this.focusCheckbox.checked = this.focusCheckbox.disabled = false; + this.focusWithinCheckbox.checked = this.focusWithinCheckbox.disabled = false; }, /** * Update the pseudo class options for the currently highlighted element. */ refreshPseudoClassPanel: function() { if (!this._elementStyle || !this.inspector.selection.isElementNode()) { this.hoverCheckbox.disabled = true; this.activeCheckbox.disabled = true; this.focusCheckbox.disabled = true; + this.focusWithinCheckbox.disabled = true; return; } for (const pseudoClassLock of this._elementStyle.element.pseudoClassLocks) { switch (pseudoClassLock) { @@ -952,10 +958,14 @@ } case ":focus": { this.focusCheckbox.checked = true; break; } + case ":focus-within": { + this.focusWithinCheckbox.checked = true; + break; + } } } }, _populate: function() { @@ -1567,19 +1577,21 @@ this.pseudoClassToggle.classList.add("checked"); this.hoverCheckbox.setAttribute("tabindex", "0"); this.activeCheckbox.setAttribute("tabindex", "0"); this.focusCheckbox.setAttribute("tabindex", "0"); + this.focusWithinCheckbox.setAttribute("tabindex", "0"); this.pseudoClassPanel.hidden = false; }, hidePseudoClassPanel: function() { this.pseudoClassToggle.classList.remove("checked"); this.hoverCheckbox.setAttribute("tabindex", "-1"); this.activeCheckbox.setAttribute("tabindex", "-1"); this.focusCheckbox.setAttribute("tabindex", "-1"); + this.focusWithinCheckbox.setAttribute("tabindex", "-1"); this.pseudoClassPanel.hidden = true; }, /** diff --git a/devtools/client/inspector/rules/test/browser_rules_add-rule-pseudo-class.js b/devtools/client/inspector/rules/test/browser_rules_add-rule-pseudo-class.js --- a/devtools/client/inspector/rules/test/browser_rules_add-rule-pseudo-class.js +++ b/devtools/client/inspector/rules/test/browser_rules_add-rule-pseudo-class.js @@ -14,11 +14,14 @@ [":hover"], [":hover", ":active"], [":hover", ":active", ":focus"], [":active"], [":active", ":focus"], - [":focus"] + [":focus"], + [":focus-within"], + [":focus-within", ":hover"], + [":focus-within", ":hover", ":active"], ]; add_task(async function() { await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); const {inspector, view} = await openRuleView(); @@ -55,18 +58,23 @@ break; case ":focus": view.focusCheckbox.click(); await inspector.once("rule-view-refreshed"); break; + case ":focus-within": + view.focusWithinCheckbox.click(); + await inspector.once("rule-view-refreshed"); + break; } } } async function resetPseudoLocks(inspector, view) { if (!view.hoverCheckbox.checked && !view.activeCheckbox.checked && - !view.focusCheckbox.checked) { + !view.focusCheckbox.checked && + !view.focusWithinCheckbox.checked) { return; } if (view.hoverCheckbox.checked) { view.hoverCheckbox.click(); await inspector.once("rule-view-refreshed"); @@ -77,6 +85,10 @@ } if (view.focusCheckbox.checked) { view.focusCheckbox.click(); await inspector.once("rule-view-refreshed"); } + if (view.focusWithinCheckbox.checked) { + view.focusWithinCheckbox.click(); + await inspector.once("rule-view-refreshed"); + } } diff --git a/devtools/client/inspector/rules/test/browser_rules_pseudo_lock_options.js b/devtools/client/inspector/rules/test/browser_rules_pseudo_lock_options.js --- a/devtools/client/inspector/rules/test/browser_rules_pseudo_lock_options.js +++ b/devtools/client/inspector/rules/test/browser_rules_pseudo_lock_options.js @@ -18,10 +18,13 @@ color: yellow; } div:focus { color: green; } + div:focus-within { + color: papayawhip; + }
test div
`; add_task(async function() { @@ -49,17 +52,23 @@ await togglePseudoClass(inspector, view.focusCheckbox); await assertPseudoAdded(inspector, view, ":focus", 3, 1); await togglePseudoClass(inspector, view.focusCheckbox); await assertPseudoRemoved(inspector, view, 2); + await togglePseudoClass(inspector, view.focusWithinCheckbox); + await assertPseudoAdded(inspector, view, ":focus-within", 3, 1); + await togglePseudoClass(inspector, view.focusWithinCheckbox); + await assertPseudoRemoved(inspector, view, 2); + info("Toggle all pseudo lock and check that the pseudo lock is added"); await togglePseudoClass(inspector, view.hoverCheckbox); await togglePseudoClass(inspector, view.activeCheckbox); await togglePseudoClass(inspector, view.focusCheckbox); - await assertPseudoAdded(inspector, view, ":focus", 5, 1); - await assertPseudoAdded(inspector, view, ":active", 5, 2); - await assertPseudoAdded(inspector, view, ":hover", 5, 3); + await assertPseudoAdded(inspector, view, ":focus-within", 6, 1); + await assertPseudoAdded(inspector, view, ":focus", 6, 1); + await assertPseudoAdded(inspector, view, ":active", 6, 2); + await assertPseudoAdded(inspector, view, ":hover", 6, 3); await togglePseudoClass(inspector, view.hoverCheckbox); await togglePseudoClass(inspector, view.activeCheckbox); await togglePseudoClass(inspector, view.focusCheckbox); await assertPseudoRemoved(inspector, view, 2); @@ -69,10 +78,12 @@ ":hover checkbox is unchecked and disabled"); ok(!view.activeCheckbox.checked && view.activeCheckbox.disabled, ":active checkbox is unchecked and disabled"); ok(!view.focusCheckbox.checked && view.focusCheckbox.disabled, ":focus checkbox is unchecked and disabled"); + ok(!view.focusWithinCheckbox.checked && view.focusWithinCheckbox.disabled, + ":focus-within checkbox is unchecked and disabled"); info("Toggle the pseudo class panel close"); view.pseudoClassToggle.click(); await assertPseudoPanelClosed(view); }); @@ -106,17 +117,20 @@ ok(!view.pseudoClassPanel.hidden, "Pseudo Class Panel Opened"); ok(!view.hoverCheckbox.disabled, ":hover checkbox is not disabled"); ok(!view.activeCheckbox.disabled, ":active checkbox is not disabled"); ok(!view.focusCheckbox.disabled, ":focus checkbox is not disabled"); + ok(!view.focusWithinCheckbox.disabled, ":focus-within checkbox is not disabled"); is(view.hoverCheckbox.getAttribute("tabindex"), "0", ":hover checkbox has a tabindex of 0"); is(view.activeCheckbox.getAttribute("tabindex"), "0", ":active checkbox has a tabindex of 0"); is(view.focusCheckbox.getAttribute("tabindex"), "0", ":focus checkbox has a tabindex of 0"); + is(view.focusWithinCheckbox.getAttribute("tabindex"), "0", + ":focus-within checkbox has a tabindex of 0"); } function assertPseudoPanelClosed(view) { info("Check the closed state of the pseudo clas panel"); @@ -126,6 +140,8 @@ ":hover checkbox has a tabindex of -1"); is(view.activeCheckbox.getAttribute("tabindex"), "-1", ":active checkbox has a tabindex of -1"); is(view.focusCheckbox.getAttribute("tabindex"), "-1", ":focus checkbox has a tabindex of -1"); + is(view.focusWithinCheckbox.getAttribute("tabindex"), "-1", + ":focus-within checkbox has a tabindex of -1"); } diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js --- a/devtools/client/inspector/rules/views/rule-editor.js +++ b/devtools/client/inspector/rules/views/rule-editor.js @@ -433,11 +433,11 @@ break; case SELECTOR_ELEMENT: selectorClass = "ruleview-selector"; break; case SELECTOR_PSEUDO_CLASS: - selectorClass = [":active", ":focus", ":hover"].some( + selectorClass = [":active", ":focus", ":focus-within", ":hover"].some( pseudo => selectorText.value === pseudo) ? "ruleview-selector-pseudo-class-lock" : "ruleview-selector-pseudo-class"; break; default: diff --git a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js --- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js +++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js @@ -31,10 +31,11 @@ "node-menu-copyimagedatauri", "node-menu-delete", "node-menu-pseudo-hover", "node-menu-pseudo-active", "node-menu-pseudo-focus", + "node-menu-pseudo-focus-within", "node-menu-scrollnodeintoview", "node-menu-screenshotnode", "node-menu-add-attribute", "node-menu-copy-attribute", "node-menu-edit-attribute", diff --git a/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js b/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js --- a/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js +++ b/devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js @@ -7,11 +7,11 @@ // that these items actually work const TEST_URI = "data:text/html;charset=UTF-8," + "pseudo-class lock node menu tests" + "
test div
"; -const PSEUDOS = ["hover", "active", "focus"]; +const PSEUDOS = ["hover", "active", "focus", "focus-within"]; add_task(async function() { const {inspector, testActor} = await openInspectorForURL(TEST_URI); await selectNode("div", inspector); diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css --- a/devtools/client/themes/rules.css +++ b/devtools/client/themes/rules.css @@ -53,10 +53,11 @@ } .ruleview-reveal-panel { display: flex; overflow: hidden; + flex-wrap: wrap; } .ruleview-reveal-panel[hidden] { display: none; } diff --git a/devtools/server/actors/highlighters/box-model.js b/devtools/server/actors/highlighters/box-model.js --- a/devtools/server/actors/highlighters/box-model.js +++ b/devtools/server/actors/highlighters/box-model.js @@ -26,11 +26,11 @@ const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"]; const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"]; // Width of boxmodelhighlighter guides const GUIDE_STROKE_WIDTH = 1; // FIXME: add ":visited" and ":link" after bug 713106 is fixed -const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; +const PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-within"]; /** * The BoxModelHighlighter draws the box model regions on top of a node. * If the node is a block box, then each region will be displayed as 1 polygon. * If the node is an inline box though, each region may be represented by 1 or diff --git a/devtools/server/actors/inspector/node.js b/devtools/server/actors/inspector/node.js --- a/devtools/server/actors/inspector/node.js +++ b/devtools/server/actors/inspector/node.js @@ -32,11 +32,11 @@ loader.lazyRequireGetter(this, "EventParsers", "devtools/server/actors/inspector/event-parsers", true); const SUBGRID_ENABLED = Services.prefs.getBoolPref("layout.css.grid-template-subgrid-value.enabled"); -const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; +const PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-within"]; const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog"; const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20; /** * Server side of the node actor. diff --git a/devtools/server/actors/inspector/walker.js b/devtools/server/actors/inspector/walker.js --- a/devtools/server/actors/inspector/walker.js +++ b/devtools/server/actors/inspector/walker.js @@ -1165,11 +1165,11 @@ /** * Add a pseudo-class lock to a node. * * @param NodeActor node * @param string pseudo - * A pseudoclass: ':hover', ':active', ':focus' + * A pseudoclass: ':hover', ':active', ':focus', ':focus-within' * @param options * Options object: * `parents`: True if the pseudo-class should be added * to parent nodes. * `enabled`: False if the pseudo-class should be locked @@ -1245,11 +1245,11 @@ /** * Remove a pseudo-class lock from a node. * * @param NodeActor node * @param string pseudo - * A pseudoclass: ':hover', ':active', ':focus' + * A pseudoclass: ':hover', ':active', ':focus', ':focus-within' * @param options * Options object: * `parents`: True if the pseudo-class should be removed * from parent nodes. * diff --git a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html --- a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html +++ b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html @@ -11,11 +11,11 @@