forked from mirrors/gecko-dev
Bug 1893593 - [devtools] Display @scope rules in the Rules view. r=devtools-reviewers,bomsy.
Depends on D208028 Differential Revision: https://phabricator.services.mozilla.com/D208726
This commit is contained in:
parent
ebaa9c434c
commit
18e2ff7bbc
7 changed files with 232 additions and 2 deletions
|
|
@ -2,6 +2,7 @@
|
|||
tags = "devtools"
|
||||
subsuite = "devtools"
|
||||
support-files = [
|
||||
"doc_at_scope.css",
|
||||
"doc_blob_stylesheet.html",
|
||||
"doc_copystyles.css",
|
||||
"doc_copystyles.html",
|
||||
|
|
@ -69,6 +70,8 @@ skip-if = ["verify && debug && os == 'win'"]
|
|||
|
||||
["browser_rules_add-rule.js"]
|
||||
|
||||
["browser_rules_at_scope.js"]
|
||||
|
||||
["browser_rules_authored.js"]
|
||||
|
||||
["browser_rules_authored_color.js"]
|
||||
|
|
|
|||
179
devtools/client/inspector/rules/test/browser_rules_at_scope.js
Normal file
179
devtools/client/inspector/rules/test/browser_rules_at_scope.js
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Test that the rule-view properly handles @scope rules.
|
||||
|
||||
const TEST_URI = `
|
||||
<link href="${URL_ROOT_COM_SSL}doc_at_scope.css" rel="stylesheet">
|
||||
<h1>Hello @scope!</h1>
|
||||
<main>
|
||||
<style>
|
||||
@scope {
|
||||
:scope, [data-test="scoped-inline-style"] {
|
||||
border: 1px solid aqua;
|
||||
}
|
||||
|
||||
div, [data-test="scoped-inline-style"] {
|
||||
background: tomato;
|
||||
}
|
||||
|
||||
/* test nested @scope */
|
||||
@scope (:scope section) {
|
||||
:scope, [data-test="nested-scoped-inline-style"] {
|
||||
background: gold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div id=a>
|
||||
inline-style scope target
|
||||
<section id="a-child">inline-style nested scope target</section>
|
||||
</div>
|
||||
</main>
|
||||
<aside>
|
||||
<div id=b>
|
||||
<span>Dough</span>
|
||||
<div id=c class="limit">
|
||||
<span>Donut hole</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
`;
|
||||
|
||||
add_task(async function () {
|
||||
await pushPref("layout.css.at-scope.enabled", true);
|
||||
await addTab(
|
||||
"https://example.com/document-builder.sjs?html=" +
|
||||
encodeURIComponent(TEST_URI)
|
||||
);
|
||||
const { inspector, view } = await openRuleView();
|
||||
await assertRules("main", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `:scope, [data-test="scoped-inline-style"]`,
|
||||
ancestorRulesData: ["@scope {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("main #a", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `div, [data-test="scoped-inline-style"]`,
|
||||
ancestorRulesData: ["@scope {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("main #a #a-child", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `:scope, [data-test="nested-scoped-inline-style"]`,
|
||||
ancestorRulesData: ["@scope {", " @scope (:scope section) {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("aside #b", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `div, [data-test="start-and-end-inherit"]`,
|
||||
ancestorRulesData: ["@scope (aside) to (.limit) {"],
|
||||
},
|
||||
{
|
||||
selector: `div, [data-test="start-and-end"]`,
|
||||
ancestorRulesData: ["@scope (aside) to (.limit) {"],
|
||||
},
|
||||
{
|
||||
selector: `div, [data-test="start-no-end"]`,
|
||||
ancestorRulesData: ["@scope (aside) {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("aside #b > span", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `& span`,
|
||||
ancestorRulesData: [
|
||||
"@scope (aside) to (.limit) {",
|
||||
` div, [data-test="start-and-end"] {`,
|
||||
],
|
||||
},
|
||||
{
|
||||
selector: `div, [data-test="start-and-end-inherit"]`,
|
||||
inherited: true,
|
||||
ancestorRulesData: ["@scope (aside) to (.limit) {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("aside #c", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `div, [data-test="start-no-end"]`,
|
||||
ancestorRulesData: ["@scope (aside) {"],
|
||||
},
|
||||
{
|
||||
selector: `div, [data-test="start-and-end-inherit"]`,
|
||||
inherited: true,
|
||||
ancestorRulesData: ["@scope (aside) to (.limit) {"],
|
||||
},
|
||||
]);
|
||||
|
||||
await assertRules("aside #c > span", [
|
||||
{ selector: `element`, ancestorRulesData: null },
|
||||
{
|
||||
selector: `div, [data-test="start-and-end-inherit"]`,
|
||||
inherited: true,
|
||||
ancestorRulesData: ["@scope (aside) to (.limit) {"],
|
||||
},
|
||||
]);
|
||||
|
||||
async function assertRules(nodeSelector, expectedRules) {
|
||||
await selectNode(nodeSelector, inspector);
|
||||
const rulesInView = Array.from(
|
||||
view.element.querySelectorAll(".ruleview-rule")
|
||||
);
|
||||
is(
|
||||
rulesInView.length,
|
||||
expectedRules.length,
|
||||
`[${nodeSelector}] All expected rules are displayed`
|
||||
);
|
||||
|
||||
for (let i = 0; i < expectedRules.length; i++) {
|
||||
const expectedRule = expectedRules[i];
|
||||
info(`[${nodeSelector}] Checking rule #${i}: ${expectedRule.selector}`);
|
||||
|
||||
const selector = rulesInView[i].querySelector(
|
||||
".ruleview-selectors-container"
|
||||
)?.innerText;
|
||||
|
||||
is(
|
||||
selector,
|
||||
expectedRule.selector,
|
||||
`[${nodeSelector}] Expected selector for rule #${i}`
|
||||
);
|
||||
|
||||
const isInherited = rulesInView[i].matches(
|
||||
".ruleview-header-inherited + .ruleview-rule"
|
||||
);
|
||||
if (expectedRule.inherited) {
|
||||
ok(isInherited, `[${nodeSelector}] rule #${i} is inherited`);
|
||||
} else {
|
||||
ok(!isInherited, `[${nodeSelector}] rule #${i} is not inherited`);
|
||||
}
|
||||
|
||||
if (expectedRule.ancestorRulesData == null) {
|
||||
is(
|
||||
getRuleViewAncestorRulesDataElementByIndex(view, i),
|
||||
null,
|
||||
`[${nodeSelector}] No ancestor rules data displayed for ${selector}`
|
||||
);
|
||||
} else {
|
||||
is(
|
||||
getRuleViewAncestorRulesDataTextByIndex(view, i),
|
||||
expectedRule.ancestorRulesData.join("\n"),
|
||||
`[${nodeSelector}] Expected ancestor rules data displayed for ${selector}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
// Test that the rule-view content is correct when the page defines nested at-rules (@media, @layer, @supports, …)
|
||||
const TEST_URI = `
|
||||
<body>
|
||||
<style type="text/css">
|
||||
body {
|
||||
container: mycontainer / inline-size;
|
||||
|
|
@ -15,8 +16,10 @@ const TEST_URI = `
|
|||
@container mycontainer (min-width: 1px) {
|
||||
@media screen {
|
||||
@container mycontainer (min-width: 2rem) {
|
||||
h1, [test-hint="nested"] {
|
||||
background: gold;
|
||||
@scope (:scope) to (:scope > h1) {
|
||||
h1, [test-hint="nested"] {
|
||||
background: gold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,9 +28,11 @@ const TEST_URI = `
|
|||
}
|
||||
</style>
|
||||
<h1>Hello nested at-rules!</h1>
|
||||
</body>
|
||||
`;
|
||||
|
||||
add_task(async function () {
|
||||
await pushPref("layout.css.at-scope.enabled", true);
|
||||
await addTab(
|
||||
"https://example.com/document-builder.sjs?html=" +
|
||||
encodeURIComponent(TEST_URI)
|
||||
|
|
@ -46,6 +51,7 @@ add_task(async function () {
|
|||
` @container mycontainer (min-width: 1px) {`,
|
||||
` @media screen {`,
|
||||
` @container mycontainer (min-width: 2rem) {`,
|
||||
` @scope (:scope) to (:scope > h1) {`,
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
|||
25
devtools/client/inspector/rules/test/doc_at_scope.css
Normal file
25
devtools/client/inspector/rules/test/doc_at_scope.css
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@scope (aside) {
|
||||
div, [data-test="start-no-end"] {
|
||||
box-shadow: 60px -16px teal;
|
||||
}
|
||||
}
|
||||
|
||||
@scope (aside) to (.limit) {
|
||||
div, [data-test="start-and-end"] {
|
||||
outline: 2px solid gold;
|
||||
|
||||
span {
|
||||
color: cornflowerblue;
|
||||
}
|
||||
}
|
||||
|
||||
/* This should show up as inherited rule for spans that are in .limit subtree, even
|
||||
if the rule doesn't directly apply */
|
||||
div, [data-test="start-and-end-inherit"] {
|
||||
color: salmon;
|
||||
}
|
||||
}
|
||||
|
|
@ -253,6 +253,16 @@ RuleEditor.prototype = {
|
|||
selectorContainer.append(
|
||||
this.doc.createTextNode(`@import ${ancestorData.value}`)
|
||||
);
|
||||
} else if (ancestorData.type == "scope") {
|
||||
let text = `@scope`;
|
||||
if (ancestorData.start) {
|
||||
text += ` (${ancestorData.start})`;
|
||||
|
||||
if (ancestorData.end) {
|
||||
text += ` to (${ancestorData.end})`;
|
||||
}
|
||||
}
|
||||
selectorContainer.append(this.doc.createTextNode(text));
|
||||
} else if (ancestorData.selectors) {
|
||||
ancestorData.selectors.forEach((selector, i) => {
|
||||
if (i !== 0) {
|
||||
|
|
|
|||
|
|
@ -544,6 +544,12 @@ class StyleRuleActor extends Actor {
|
|||
type,
|
||||
conditionText: rawRule.conditionText,
|
||||
});
|
||||
} else if (ruleClassName === "CSSScopeRule") {
|
||||
ancestorData.push({
|
||||
type,
|
||||
start: rawRule.start,
|
||||
end: rawRule.end,
|
||||
});
|
||||
} else if (rawRule.selectorText) {
|
||||
// All the previous cases where about at-rules; this one is for regular rule
|
||||
// that are ancestors because CSS nesting was used.
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ exports.CSSAtRuleClassNameType = {
|
|||
CSSMediaRule: "media",
|
||||
CSSNamespaceRule: "namespace",
|
||||
CSSPageRule: "page",
|
||||
CSSScopeRule: "scope",
|
||||
CSSSupportsRule: "supports",
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue