Skip to content

Commit 375743d

Browse files
committed
Bug 2005189 - [devtools] Display @Custom-Media rules in Style Editor at-rules sidebar. r=devtools-reviewers,ochameau
Differential Revision: https://phabricator.services.mozilla.com/D275939
1 parent 0543162 commit 375743d

File tree

5 files changed

+184
-7
lines changed

5 files changed

+184
-7
lines changed

devtools/client/styleeditor/StyleEditorUI.sys.mjs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1446,6 +1446,23 @@ export class StyleEditorUI extends EventEmitter {
14461446
type.append(
14471447
this.#panelDoc.createTextNode(`${rule.positionTryName}\u00A0`)
14481448
);
1449+
} else if (rule.type === "custom-media") {
1450+
const parts = [];
1451+
const { customMediaName, customMediaQuery } = rule;
1452+
for (let i = 0, len = customMediaQuery.length; i < len; i++) {
1453+
const media = customMediaQuery[i];
1454+
const queryEl = this.#panelDoc.createElementNS(HTML_NS, "span");
1455+
queryEl.textContent = media.text;
1456+
if (!media.matches) {
1457+
queryEl.classList.add("media-condition-unmatched");
1458+
}
1459+
parts.push(queryEl);
1460+
if (len > 1 && i !== len - 1) {
1461+
parts.push(", ");
1462+
}
1463+
}
1464+
1465+
type.append(`${customMediaName} `, ...parts);
14491466
}
14501467

14511468
const cond = this.#panelDoc.createElementNS(HTML_NS, "span");

devtools/client/styleeditor/test/browser_styleeditor_at_rules_sidebar.js

Lines changed: 121 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ add_task(async function () {
4242
await pushPref("layout.css.properties-and-values.enabled", true);
4343
// Enable anchor positioning
4444
await pushPref("layout.css.anchor-positioning.enabled", true);
45+
// Enable @custom-media
46+
await pushPref("layout.css.custom-media.enabled", true);
4547

4648
const { ui } = await openStyleEditorForURL(TESTCASE_URI);
4749

@@ -91,7 +93,7 @@ async function testInlineAtRulesEditor(ui, editor) {
9193
is(sidebar.hidden, false, "sidebar is showing on editor with @media");
9294

9395
const entries = sidebar.querySelectorAll(".at-rule-label");
94-
is(entries.length, 8, "8 at-rules displayed in sidebar");
96+
is(entries.length, 14, "14 at-rules displayed in sidebar");
9597

9698
await testRule({
9799
ui,
@@ -166,6 +168,70 @@ async function testInlineAtRulesEditor(ui, editor) {
166168
type: "position-try",
167169
positionTryName: "--pt-custom-bottom",
168170
});
171+
172+
await testRule({
173+
ui,
174+
editor,
175+
rule: entries[8],
176+
line: 42,
177+
type: "custom-media",
178+
customMediaName: "--mobile-breakpoint",
179+
customMediaQuery: [
180+
{ text: "(width < 320px) and (height < 1420px)" },
181+
{ text: ", " },
182+
{ text: "not print" },
183+
],
184+
});
185+
186+
await testRule({
187+
ui,
188+
editor,
189+
rule: entries[9],
190+
line: 43,
191+
type: "custom-media",
192+
customMediaName: "--enabled",
193+
customMediaQuery: [{ text: "true" }],
194+
});
195+
196+
await testRule({
197+
ui,
198+
editor,
199+
rule: entries[10],
200+
line: 44,
201+
type: "custom-media",
202+
customMediaName: "--disabled",
203+
customMediaQuery: [{ text: "false", matches: false }],
204+
});
205+
206+
await testRule({
207+
ui,
208+
editor,
209+
rule: entries[11],
210+
line: 49,
211+
type: "media",
212+
conditionText: "(--mobile-breakpoint)",
213+
matches: false,
214+
});
215+
216+
await testRule({
217+
ui,
218+
editor,
219+
rule: entries[12],
220+
line: 53,
221+
type: "media",
222+
conditionText: "(--enabled)",
223+
matches: false,
224+
});
225+
226+
await testRule({
227+
ui,
228+
editor,
229+
rule: entries[13],
230+
line: 57,
231+
type: "media",
232+
conditionText: "(--disabled)",
233+
matches: false,
234+
});
169235
}
170236

171237
async function testMediaEditor(ui, editor) {
@@ -298,6 +364,12 @@ async function testMediaRuleAdded(ui, editor) {
298364
* @param {string} options.layerName: Optional name of the @layer
299365
* @param {string} options.positionTryName: Name of the @position-try if type is "position-try"
300366
* @param {string} options.propertyName: Name of the @property if type is "property"
367+
* @param {string} options.customMediaName: Name of the @custom-media if type is "custom-media"
368+
* @param {Array<object>} options.customMediaQuery: query parts of the @custom-media if type is "custom-media"
369+
* @param {string} options.customMediaQuery[].text: the query string of the part of the @custom-media
370+
* if type is "custom-media"
371+
* @param {boolean} options.customMediaQuery[].matches: whether or not this part is style as matching,
372+
* if type is "custom-media". Defaults to true.
301373
* @param {number} options.line: Line of the rule
302374
* @param {string} options.type: The type of the rule (container, layer, media, support, property ).
303375
* Defaults to "media".
@@ -311,6 +383,8 @@ async function testRule({
311383
layerName,
312384
positionTryName,
313385
propertyName,
386+
customMediaName,
387+
customMediaQuery,
314388
line,
315389
type = "media",
316390
}) {
@@ -323,11 +397,52 @@ async function testRule({
323397
} else if (type === "position-try") {
324398
name = positionTryName;
325399
}
326-
is(
327-
atTypeEl.textContent,
328-
`@${type}\u00A0${name ? `${name}\u00A0` : ""}`,
329-
"label for at-rule type is correct"
330-
);
400+
401+
if (type === "custom-media") {
402+
const atTypeChilNodes = Array.from(atTypeEl.childNodes);
403+
is(
404+
atTypeChilNodes.shift().textContent,
405+
`@custom-media\u00A0`,
406+
"label for @custom-media is correct"
407+
);
408+
is(
409+
atTypeChilNodes.shift().textContent,
410+
`${customMediaName} `,
411+
"name for @custom-media is correct"
412+
);
413+
is(
414+
atTypeChilNodes.length,
415+
customMediaQuery.length,
416+
`Got expected number of children of @custom-media (got ${JSON.stringify(atTypeChilNodes.map(n => n.textContent))})`
417+
);
418+
for (let i = 0; i < atTypeChilNodes.length; i++) {
419+
const node = atTypeChilNodes[i];
420+
is(
421+
node.textContent,
422+
customMediaQuery[i].text,
423+
`Got expected text for part #${i} of @custom-media`
424+
);
425+
if (customMediaQuery[i].matches ?? true) {
426+
ok(
427+
// handle TextNode
428+
!node.classList ||
429+
!node.classList.contains("media-condition-unmatched"),
430+
`Text for part #${i} of @custom-media ("${node.textContent}") does not have unmatching class`
431+
);
432+
} else {
433+
ok(
434+
node.classList.contains("media-condition-unmatched"),
435+
`Text for part #${i} of @custom-media ("${node.textContent}") has expected unmatching class`
436+
);
437+
}
438+
}
439+
} else {
440+
is(
441+
atTypeEl.textContent,
442+
`@${type}\u00A0${name ? `${name}\u00A0` : ""}`,
443+
"label for at-rule type is correct"
444+
);
445+
}
331446

332447
const cond = rule.querySelector(".at-rule-condition");
333448
is(

devtools/client/styleeditor/test/media-rules.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@
5151
bottom: unset;
5252
margin-top: 10px;
5353
}
54+
55+
@custom-media --mobile-breakpoint (width < 320px) and (height < 1420px), not print;
56+
@custom-media --enabled true;
57+
@custom-media --disabled false;
58+
59+
div {
60+
color: chocolate;
61+
62+
@media (--mobile-breakpoint) {
63+
color: peachpuff;
64+
}
65+
66+
@media (--enabled) {
67+
color: green;
68+
}
69+
70+
@media (--disabled) {
71+
color: red;
72+
}
73+
}
5474
</style>
5575
</head>
5676
<body>

devtools/server/actors/utils/stylesheets-manager.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,31 @@ class StyleSheetsManager extends EventEmitter {
728728
line: InspectorUtils.getRelativeRuleLine(rule),
729729
column: InspectorUtils.getRuleColumn(rule),
730730
});
731+
} else if (className === "CSSCustomMediaRule") {
732+
const customMediaQuery = [];
733+
if (typeof rule.query === "boolean") {
734+
customMediaQuery.push({
735+
text: rule.query.toString(),
736+
matches: rule.query === true,
737+
});
738+
} else {
739+
// if query is not a boolean, it's a MediaList
740+
for (let i = 0, len = rule.query.length; i < len; i++) {
741+
customMediaQuery.push({
742+
text: rule.query[i],
743+
// For now always consider the media query as matching.
744+
// This should be changed as part of Bug 2006379
745+
matches: true,
746+
});
747+
}
748+
}
749+
atRules.push({
750+
type: "custom-media",
751+
customMediaName: rule.name,
752+
customMediaQuery,
753+
line: InspectorUtils.getRelativeRuleLine(rule),
754+
column: InspectorUtils.getRuleColumn(rule),
755+
});
731756
}
732757
}
733758
return {

layout/inspector/InspectorUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,7 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
568568
// so the DevTools team gets notified and can decide if it should be
569569
// displayed.
570570
switch (rule->Type()) {
571+
case StyleCssRuleType::CustomMedia:
571572
case StyleCssRuleType::Media:
572573
case StyleCssRuleType::Supports:
573574
case StyleCssRuleType::LayerBlock:
@@ -577,7 +578,6 @@ static uint32_t CollectAtRules(ServoCSSRuleList& aRuleList,
577578
(void)aResult.AppendElement(OwningNonNull(*rule), fallible);
578579
break;
579580
}
580-
case StyleCssRuleType::CustomMedia:
581581
case StyleCssRuleType::Style:
582582
case StyleCssRuleType::Import:
583583
case StyleCssRuleType::Document:

0 commit comments

Comments
 (0)