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 @@