Skip to content

Commit 22078b3

Browse files
andruudchromium-wpt-export-bot
authored andcommitted
[@scope] Support '@import scope(...)' behind a flag
This CL implements the ability to scope an entire stylesheet on import, via the new scope() function. The CSSWG has resolved to add the scope() function, but there is no spec yet. The WPTs are therefore marked as tentative for now. Note that this CL intentionally does not have parse-validity tests, such as scope() combined with other features of the @import prelude (e.g. layer(), media queries). This is because scope()'s "position" in the prelude grammar has not been defined, and the WG instead resolved allow full reordering of the conditions. This opens a new problem, tracked by Issue 10972 [1]. In other words, parsing tests are deferred until that issue is resolved. The WPTs in this only use @import scope(...)", which is assumed to be valid regardless of the outcome. Since regular top-level selectors are not relative selectors, they are not guaranteed to contain either '&' or ':scope' like selector parsing nested under CSSNestingType::kNesting or CSSNestingType::kScope (respectively). This means that selectors at the top-level of an imported stylesheet are not guaranteed to be scope-containing, so we need another way of enforcing the in-scope [2] requirement of scoped selectors. This is effectively done by always treating the last selector in a complex selector as scope-containing. Due to the same absence of '&' and ':scope' in the imported stylesheet, rules can also be incorrectly handled as "easy" or "covered by bucketing" even though they match in the context of a scope. Addressed this by disabling the optimization when context.style_scope is set. [1] w3c/csswg-drafts#10972 [2] https://drafts.csswg.org/css-cascade-6/#in-scope Bug: 369876911 Change-Id: I75bc3514d959c6762232bc769f844682ed4a50fd
1 parent 6d039d2 commit 22078b3

8 files changed

+219
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.x {
2+
--x: 1;
3+
}
4+
5+
:scope > .y {
6+
--y: 1;
7+
}
8+
9+
@scope (.inner-scope) {
10+
.z {
11+
--z: 1;
12+
}
13+
}
14+
15+
& > .w {
16+
--w: 1;
17+
}
18+
19+
& > & > .u {
20+
--u: 1;
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), implicit scope</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<main id=main>
7+
<div class=scope>
8+
<style>
9+
@import url("resources/scope-imported.css") scope();
10+
</style>
11+
<div class=x>Inside</div>
12+
</div>
13+
<div class=x>Outside</div>
14+
</main>
15+
<script>
16+
test(() => {
17+
let e = main.querySelector('.scope > .x');
18+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '1');
19+
}, 'Scope-imported rule applies within implicit scope');
20+
21+
test(() => {
22+
let e = main.querySelector('main > .x');
23+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '');
24+
}, 'Scope-imported rule does not apply outside of implicit scope');
25+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), :scope rules in imported stylesheet</title>
3+
<link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scope-atrule">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope));
8+
</style>
9+
<main id=main>
10+
<div class=scope>
11+
<div class=inner-scope>
12+
<div class=z>Inside</div>
13+
</div>
14+
</div>
15+
<div class=inner-scope>
16+
<div class=z>Outside</div>
17+
</div>
18+
</main>
19+
<script>
20+
test(() => {
21+
let e = main.querySelector('.scope > .inner-scope > .z');
22+
assert_equals(getComputedStyle(e).getPropertyValue('--z'), '1');
23+
}, '@scope within scope-imported stylesheet matches');
24+
25+
test(() => {
26+
let e = main.querySelector('#main > .inner-scope > .z');
27+
assert_equals(getComputedStyle(e).getPropertyValue('--z'), '');
28+
}, '@scope within scope-imported does not ignore import scope');
29+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), same stylesheet imported multiple times</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope1));
8+
@import url("resources/scope-imported.css") scope((.scope2));
9+
</style>
10+
<main id=main>
11+
<div class=scope1>
12+
<div class=x>Inside</div>
13+
</div>
14+
<div class=scope2>
15+
<div class=x>Inside</div>
16+
</div>
17+
<div class=x>Outside</div>
18+
</main>
19+
<script>
20+
test(() => {
21+
let e1 = main.querySelector('.scope1 > .x');
22+
let e2 = main.querySelector('.scope2 > .x');
23+
assert_equals(getComputedStyle(e1).getPropertyValue('--x'), '1');
24+
assert_equals(getComputedStyle(e2).getPropertyValue('--x'), '1');
25+
}, 'A stylesheet may be imported multiple times, and scoped differently');
26+
27+
test(() => {
28+
let e = main.querySelector('main > .x');
29+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '');
30+
}, 'Scope-imported rule does not apply outside of scope');
31+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), '&' selectors</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope));
8+
</style>
9+
<main id=main>
10+
<div class=scope>
11+
<div class=w>Inside</div>
12+
<div class=scope>
13+
<div class=w>Inner (W)</div>
14+
<div class=u>Inner (U)</div>
15+
</div>
16+
</div>
17+
</main>
18+
<script>
19+
test(() => {
20+
let e = main.querySelector('#main > .scope > .w');
21+
assert_equals(getComputedStyle(e).getPropertyValue('--w'), '1');
22+
}, 'The & selector matches the scoping root');
23+
24+
test(() => {
25+
let w = main.querySelector('#main > .scope > .scope > .w');
26+
assert_equals(getComputedStyle(w).getPropertyValue('--w'), '1');
27+
// The '& > & > .u' selector should behave like ':scope > :scope > .u'
28+
// and therefore never match.
29+
let u = main.querySelector('#main > .scope > .scope > .u');
30+
assert_equals(getComputedStyle(u).getPropertyValue('--u'), '');
31+
}, 'The & selector behaves like :scope');
32+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<!DOCTYPE html>
2+
<title>@import scope() with &lt;scope-end&gt;</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope) to (.limit));
8+
</style>
9+
<main id=main>
10+
<div class=scope>
11+
<div class=x>Inside</div>
12+
<div class=limit>
13+
<div class=x>Below limit</div>
14+
</div>
15+
</div>
16+
</main>
17+
<script>
18+
test(() => {
19+
let e = main.querySelector('.scope > .x');
20+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '1');
21+
}, 'Scope-imported rule applies within scope, above limit');
22+
23+
test(() => {
24+
let e = main.querySelector('.limit > .x');
25+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '');
26+
}, 'Scope-imported rule does not apply below limit');
27+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), :scope rules in imported stylesheet</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope));
8+
</style>
9+
<main id=main>
10+
<div class=scope>
11+
<div class=y>Inside</div>
12+
<div>
13+
<div class=y>Inside, but should not match</div>
14+
</div>
15+
</div>
16+
<div class=y>Outside</div>
17+
</main>
18+
<script>
19+
test(() => {
20+
let inside = main.querySelector('.scope > .y');
21+
assert_equals(getComputedStyle(inside).getPropertyValue('--y'), '1');
22+
23+
let inside_no_match = main.querySelector('.scope > div > .y');
24+
assert_equals(getComputedStyle(inside_no_match).getPropertyValue('--y'), '');
25+
26+
let outside = main.querySelector('#main > .y');
27+
assert_equals(getComputedStyle(outside).getPropertyValue('--y'), '');
28+
}, 'The :scope pseudo-class works in imported stylesheets');
29+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<title>@import scope(), &lt;scope-start&gt;</title>
3+
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7348">
4+
<script src="/resources/testharness.js"></script>
5+
<script src="/resources/testharnessreport.js"></script>
6+
<style>
7+
@import url("resources/scope-imported.css") scope((.scope));
8+
</style>
9+
<main id=main>
10+
<div class=scope>
11+
<div class=x>Inside</div>
12+
</div>
13+
<div class=x>Outside</div>
14+
</main>
15+
<script>
16+
test(() => {
17+
let e = main.querySelector('.scope > .x');
18+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '1');
19+
}, 'Scope-imported rule applies within scope');
20+
21+
test(() => {
22+
let e = main.querySelector('main > .x');
23+
assert_equals(getComputedStyle(e).getPropertyValue('--x'), '');
24+
}, 'Scope-imported rule does not apply outside of scope');
25+
</script>

0 commit comments

Comments
 (0)