summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/css-contain/container-queries
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/css/css-contain/container-queries
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/css-contain/container-queries')
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/animation-container-size.html43
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/animation-container-type-dynamic.html68
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/animation-nested-animation.html47
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/animation-nested-transition.html44
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/aspect-ratio-feature-evaluation.html49
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/at-container-parsing.html195
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/at-container-serialization.html74
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/at-container-style-parsing.html75
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/at-container-style-serialization.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/auto-scrollbars.html53
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/backdrop-invalidation.html51
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/calc-evaluation.html32
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-001.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-002.html30
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-003.html32
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-004.html33
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-005.html36
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-006.html37
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container.html27
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/chrome-legacy-skip-recalc.html20
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/column-spanner-in-container.html41
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/conditional-container-status.html28
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-computed.html28
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-for-cue-ref.html23
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-for-cue.html27
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-for-shadow-dom.html345
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-inheritance.html18
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-inner-at-rules.html196
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-inside-multicol-with-table.html41
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-longhand-animation-type.html45
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-name-computed.html23
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-name-invalidation.html74
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-name-parsing.html45
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-name-tree-scoped.html73
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-nested.html239
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-parsing.html63
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-selection.html183
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation-after-load.html39
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation.html39
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-size-nested-invalidation.html62
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-size-shadow-invalidation.html55
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-type-computed.html18
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-type-containment.html85
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-type-invalidation.html70
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-type-layout-invalidation.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-type-parsing.html44
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-animation.html70
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-basic.html54
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-computational-independence.html20
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-content-box.html58
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-invalidation.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-ref.html21
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient.html25
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-dynamic.html37
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-fallback.html68
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container.html111
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-ineligible-container.html44
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-invalidation.html119
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-media-queries.html61
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache-ref.html18
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache.html24
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-selection.html101
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-shadow.html65
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-small-viewport-fallback.html70
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-svglength.html95
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/container-units-typed-om.html59
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/counters-flex-circular.html76
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/counters-in-container-dynamic.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/counters-in-container.html25
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/counters-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/br-crash.html6
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/canvas-as-container-crash.html10
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-000-crash.html6
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-001-crash.html6
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1346969-crash.html14
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1362391-crash.html8
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html5
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-layout-root-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-quotes-crash.html11
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-remove-insert-evaluator-crash.html15
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-001-crash.html10
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-002-crash.html15
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-in-canvas-crash.html11
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-type-change-chrome-legacy-crash.html16
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/dialog-backdrop-crash.html25
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/dirty-rowgroup-crash.html18
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-000-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-001-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-002-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-003-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/focus-inside-content-visibility-crash.html42
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/force-sibling-style-crash.html16
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-000-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-001-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-002-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-003-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/iframe-init-crash.html3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-multicol-inside-container-crash.html21
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-000-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-001-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-column-group-container-crash.html14
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-placeholder-inline-size-crash.html12
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/marker-gcs-after-disconnect-crash.html26
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/math-block-container-child-crash.html14
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/orthogonal-replaced-crash.html11
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/pseudo-container-crash.html14
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/reversed-ol-crash.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-layout-root-crash.html22
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-text-crash.html8
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-000-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-001-crash.html17
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-002-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-003-crash.html19
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-004-crash.html16
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-005-crash.html8
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/custom-layout-container-001.https.html66
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-queries.html436
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-query-change.html89
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/deep-nested-inline-size-containers.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-create.html30
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-remove.html30
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/display-contents-dynamic-style-queries.html35
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/display-contents.html93
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/display-in-container-ref.html47
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/display-in-container.html69
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/display-none.html393
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change.html26
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/font-relative-calc-dynamic.html35
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/font-relative-units-dynamic.html280
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/font-relative-units.html100
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/fragmented-container-001.html48
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/get-animations.html34
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/grid-container.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/grid-item-container.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/idlharness.html29
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/iframe-in-container-invalidation.html58
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/iframe-invalidation.html43
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/ineligible-containment.html51
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inline-size-and-min-width.html26
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats-ref.html9
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats.html47
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment-vertical-rl.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment.html37
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching-ref.html3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching.html13
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/layout-dependent-focus.html39
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/multicol-container-001.html31
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/multicol-inside-container.html28
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/nested-query-containers.html125
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/never-match-container.html44
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/orthogonal-wm-container-query.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/percentage-padding-orthogonal.html66
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-001.html59
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002-ref.html6
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002.html28
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b-ref.html12
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b.html58
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-003.html69
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-004.html52
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-005.html58
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-006.html66
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-007.html49
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-008.html59
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/query-content-box.html80
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/query-evaluation-style.html80
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/query-evaluation.html87
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/reattach-container-with-dirty-child.html37
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden-ref.html3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden.html53
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/sibling-layout-dependency.html134
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/size-container-no-principal-box.html63
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes-ref.html3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes.html11
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/size-feature-evaluation.html91
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/style-change-in-container.html30
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/style-container-for-shadow-dom.html265
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/style-not-sharing-float.html40
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/support/cq-testcommon.js3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/support/test.vtt4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-child-container.html38
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container.html24
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container.html23
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/svg-root-size-container.html36
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display-ref.html4
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display.html26
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop-ref.html3
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop.html20
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-container.html33
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog.html45
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/top-layer-nested-dialog.html45
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/transition-scrollbars.html59
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event-002.html45
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event.html58
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/unsupported-axis.html228
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/viewport-units-dynamic.html59
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/viewport-units.html33
-rw-r--r--testing/web-platform/tests/css/css-contain/container-queries/whitespace-update-after-removal.html26
201 files changed, 9899 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/animation-container-size.html b/testing/web-platform/tests/css/css-contain/container-queries/animation-container-size.html
new file mode 100644
index 0000000000..fefb721cbc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/animation-container-size.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>Container Queries - Animating container size</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from { width: 200px; }
+ to { width: 100px; }
+ }
+ #container {
+ container-type: inline-size;
+ animation: anim 1s linear paused;
+ }
+ #target {
+ background-color: green;
+ }
+
+ @container (width: 200px) {
+ #target {
+ background-color: blue;
+ }
+ }
+</style>
+<div id=container>
+ <div id=target>
+ Test
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 0, 255)');
+
+ assert_equals(container.getAnimations().length, 1);
+ let animation = container.getAnimations()[0];
+ animation.currentTime = 500;
+
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 128, 0)');
+ }, 'Animation affects container query evaluation');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/animation-container-type-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/animation-container-type-dynamic.html
new file mode 100644
index 0000000000..835e8e1be1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/animation-container-type-dynamic.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<title>Container Queries - Animated container creating new containers</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from { width: 200px; }
+ to { width: 300px; }
+ }
+ #container {
+ container-type: inline-size;
+ animation: anim 1s linear paused;
+ }
+ #target {
+ background-color: red;
+ }
+
+ #intermediate {
+ width: 100px;
+ }
+
+ @container (min-width: 250px) {
+ #intermediate {
+ container-type: inline-size;
+ }
+ }
+
+ @container (width: 200px) {
+ #target {
+ background-color: blue;
+ }
+ }
+
+ @container (width: 100px) {
+ /* Initially queries #container, but later queries #intermediate, when
+ the other container query starts matching. */
+ #target {
+ background-color: green;
+ }
+ }
+</style>
+<div id=container>
+ <div id=intermediate>
+ <div id=target>
+ Test
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 0, 255)');
+
+ assert_equals(container.getAnimations().length, 1);
+ let animation = container.getAnimations()[0];
+
+ animation.currentTime = 600;
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 128, 0)');
+
+ // Verify that #intermediate is queried by changing its width. The container
+ // query will stop matching if #intermediate is the queried container.
+ intermediate.style.width = '110px';
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(255, 0, 0)');
+ }, 'Animated container creating new container');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-animation.html b/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-animation.html
new file mode 100644
index 0000000000..7f1ae854ae
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-animation.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<title>Container Queries - Animations within animating container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ @keyframes outer {
+ from { width: 100px; }
+ to { width: 300px; }
+ }
+ @keyframes inner {
+ from { background-color: blue; }
+ to { background-color: yellow; }
+ }
+ #container {
+ container-type: inline-size;
+ animation: outer 1s linear paused;
+ }
+ #target {
+ background-color: green;
+ }
+
+ @container (min-width: 200px) {
+ #target {
+ animation: inner 1s linear paused;
+ }
+ }
+</style>
+<div id=container>
+ <div id=target>
+ Test
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 128, 0)');
+
+ assert_equals(container.getAnimations().length, 1);
+ let animation = container.getAnimations()[0];
+ animation.currentTime = 600;
+
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(0, 0, 255)');
+ }, 'Animated container can create inner animation');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-transition.html b/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-transition.html
new file mode 100644
index 0000000000..934f995a97
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/animation-nested-transition.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>Container Queries - Animated container with inner transition</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ @keyframes outer {
+ from { width: 100px; }
+ to { width: 300px; }
+ }
+ #container {
+ container-type: inline-size;
+ animation: outer 1s linear paused;
+ }
+ #target {
+ background-color: rgb(100, 100, 100);
+ }
+
+ @container (min-width: 200px) {
+ #target {
+ transition: background-color 100s steps(2, start);
+ background-color: rgb(200, 200, 200);
+ }
+ }
+</style>
+<div id=container>
+ <div id=target>
+ Test
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(100, 100, 100)');
+
+ assert_equals(container.getAnimations().length, 1);
+ let animation = container.getAnimations()[0];
+ animation.currentTime = 600;
+
+ assert_equals(getComputedStyle(target).backgroundColor, 'rgb(150, 150, 150)');
+ }, 'Animated container size triggers transition');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/aspect-ratio-feature-evaluation.html b/testing/web-platform/tests/css/css-contain/container-queries/aspect-ratio-feature-evaluation.html
new file mode 100644
index 0000000000..843f34732b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/aspect-ratio-feature-evaluation.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>@container queries with aspect-ratio</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#aspect-ratio">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container {
+ width: 100px;
+ height: 100px;
+ }
+ #inline-size { container-type: inline-size; }
+ #size { container-type: size; }
+ span { color: red }
+ @container (min-aspect-ratio: 1 / 1000) {
+ span { color: green; }
+ }
+ @container (min-aspect-ratio: 2 / 1) {
+ span { background-color: lime; }
+ }
+</style>
+<div id="inline-size" class="container"><span></span></div>
+<div id="size" class="container"><span></span></div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const red = "rgb(255, 0, 0)";
+ const green = "rgb(0, 128, 0)";
+ const lime = "rgb(0, 255, 0)";
+ const transparent = "rgba(0, 0, 0, 0)";
+
+ const inline_span = document.querySelector("#inline-size > span");
+ const size_span = document.querySelector("#size > span");
+
+ test(() => {
+ assert_equals(getComputedStyle(inline_span).color, red,
+ "Should not match for inline-size containment");
+ assert_equals(getComputedStyle(size_span).color, green,
+ "Should match for block-size containment");
+ assert_equals(getComputedStyle(size_span).backgroundColor, transparent,
+ "Square should not match 2/1 min-ratio");
+ }, "@container queries with aspect-ratio and size containment");
+
+ test(() => {
+ document.querySelector("#size").style.width = "200px";
+ assert_equals(getComputedStyle(size_span).backgroundColor, lime,
+ "Should match 2/1 min-ratio");
+ }, "@container query with aspect-ratio change after resize");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/at-container-parsing.html b/testing/web-platform/tests/css/css-contain/container-queries/at-container-parsing.html
new file mode 100644
index 0000000000..b0196b5e5a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/at-container-parsing.html
@@ -0,0 +1,195 @@
+<!doctype html>
+<title>@container: parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div style="container-name:name;container-type:size; width:100px; height:100px">
+ <main id=main></main>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function cleanup_main() {
+ while (main.firstChild)
+ main.firstChild.remove();
+ }
+
+ function set_style(text) {
+ let style = document.createElement('style');
+ style.innerText = text;
+ main.append(style);
+ return style;
+ }
+
+ function test_rule_valid(query) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container ${query} {}`);
+ assert_equals(style.sheet.rules.length, 1);
+ }, query);
+ }
+
+ function test_condition_invalid(condition) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container name ${condition} {}`);
+ assert_equals(style.sheet.rules.length, 0);
+ }, condition);
+ }
+
+ // Tests that 1) the condition parses, and 2) is either "unknown" or not, as
+ // specified.
+ function test_condition_valid(condition, unknown) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`
+ @container name ${condition} {}
+ @container name (${condition}) or (not (${condition})) { main { --match:true; } }
+ `);
+ assert_equals(style.sheet.rules.length, 2);
+ const expected = unknown ? '' : 'true';
+ assert_equals(getComputedStyle(main).getPropertyValue('--match'), expected);
+ }, condition);
+ }
+
+ function test_condition_known(condition) {
+ test_condition_valid(condition, false /* unknown */);
+ }
+
+ function test_condition_unknown(condition) {
+ test_condition_valid(condition, true /* unknown */);
+ }
+
+ function test_container_name_invalid(container_name) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container ${container_name} not (width) {}`);
+ assert_equals(style.sheet.rules.length, 0);
+ }, `Container name: ${container_name}`);
+ }
+
+ function test_container_name_valid(container_name) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container ${container_name} not (width) {}`);
+ assert_equals(style.sheet.rules.length, 1);
+ }, `Container name: ${container_name}`);
+ }
+
+ test_condition_known('(width)');
+ test_condition_known('(min-width: 0px)');
+ test_condition_known('(max-width: 0px)');
+ test_condition_known('(height)');
+ test_condition_known('(min-height: 0px)');
+ test_condition_known('(max-height: 0px)');
+ test_condition_known('(aspect-ratio)');
+ test_condition_known('(min-aspect-ratio: 1/2)');
+ test_condition_known('(max-aspect-ratio: 1/2)');
+ test_condition_known('(orientation: portrait)');
+ test_condition_known('(inline-size)');
+ test_condition_known('(min-inline-size: 0px)');
+ test_condition_known('(max-inline-size: 0px)');
+ test_condition_known('(block-size)');
+ test_condition_known('(min-block-size: 0px)');
+ test_condition_known('(max-block-size: 0px)');
+
+ test_condition_known('(width: 100px)');
+ test_condition_known('((width: 100px))');
+ test_condition_known('(not (width: 100px))');
+ test_condition_known('((width: 100px) and (height: 100px))');
+ test_condition_known('(((width: 40px) or (width: 50px)) and (height: 100px))');
+ test_condition_known('((width: 100px) and ((height: 40px) or (height: 50px)))');
+ test_condition_known('(((width: 40px) and (height: 50px)) or (height: 100px))');
+ test_condition_known('((width: 50px) or ((width: 40px) and (height: 50px)))');
+ test_condition_known('((width: 100px) and (not (height: 100px)))');
+ test_condition_known('(width < 100px)');
+ test_condition_known('(width <= 100px)');
+ test_condition_known('(width = 100px)');
+ test_condition_known('(width > 100px)');
+ test_condition_known('(width >= 100px)');
+ test_condition_known('(100px < width)');
+ test_condition_known('(100px <= width)');
+ test_condition_known('(100px = width)');
+ test_condition_known('(100px > width)');
+ test_condition_known('(100px >= width)');
+ test_condition_known('(100px < width < 200px)');
+ test_condition_known('(100px < width <= 200px)');
+ test_condition_known('(100px <= width < 200px)');
+ test_condition_known('(100px > width > 200px)');
+ test_condition_known('(100px > width >= 200px)');
+ test_condition_known('(100px >= width > 200px)');
+
+ test_condition_known('(width: calc(10px))');
+ test_condition_known('(width: calc(10em))');
+ test_condition_known('(width: calc(10px + 10em))');
+ test_condition_known('(width < calc(10px + 10em))');
+ test_condition_known('(width < max(10px, 10em))');
+ test_condition_known('(calc(10px + 10em) < width)');
+ test_condition_known('(calc(10px + 10em) < width < max(30px, 30em))');
+ test_condition_known('(width: 100px) and (height: 100px)');
+ test_condition_known('(width: 100px) or (height: 100px)');
+ test_condition_known('not (width: 100px)');
+
+ test_condition_unknown('foo(width)');
+ test_condition_unknown('size(width)');
+ test_condition_unknown('(asdf)');
+ test_condition_unknown('(resolution > 100dpi)');
+ test_condition_unknown('(resolution: 150dpi)');
+ test_condition_unknown('(resolution: calc(2x))');
+ test_condition_unknown('(color)');
+ test_condition_unknown('(min-color: 1)');
+ test_condition_unknown('(color-index >= 1)');
+ test_condition_unknown('size(grid)');
+ test_condition_unknown('(grid)');
+ test_condition_unknown('(width == 100px)');
+ test_condition_unknown('(100px == width)');
+ test_condition_unknown('(100px = width = 200px)');
+ test_condition_unknown('(100px < width > 200px)');
+ test_condition_unknown('(100px <= width >= 200px)');
+ test_condition_unknown('(100px <= width > 200px)');
+ test_condition_unknown('(100px < width >= 200px)');
+ test_condition_unknown('(100px : width : 200px)');
+
+ test_condition_invalid('screen');
+ test_condition_invalid('print');
+ test_condition_invalid('not print');
+ test_condition_invalid('only print');
+ test_condition_invalid('screen and (width: 100px)');
+ test_condition_invalid('screen or (width: 100px)');
+ test_condition_invalid('not screen and (width: 100px)');
+ test_condition_invalid('not screen or (width: 100px)');
+ test_condition_invalid('(width: 100px), (height: 100px)');
+ test_condition_invalid('foo (width: 100px)');
+
+ test_rule_valid('name not (width <= 500px)');
+ test_rule_valid('not (width <= 500px)');
+
+ test_container_name_valid('foo');
+ test_container_name_valid(' foo');
+ test_container_name_valid(' foo ');
+ test_container_name_valid('normal');
+ test_container_name_valid('Normal');
+ test_container_name_valid('auto');
+ test_container_name_valid('Auto');
+
+ test_container_name_invalid('foo foo');
+ test_container_name_invalid('1px');
+ test_container_name_invalid('50gil');
+ test_container_name_invalid('name(foo)');
+ test_container_name_invalid('type(inline-size)');
+ test_container_name_invalid('"foo"');
+ test_container_name_invalid('"inherit"');
+ test_container_name_invalid('inherit');
+ test_container_name_invalid('INITIAL');
+ test_container_name_invalid('Unset');
+ test_container_name_invalid('deFAULT');
+ test_container_name_invalid('none');
+ test_container_name_invalid('None');
+ test_container_name_invalid('and');
+ test_container_name_invalid('or');
+ test_container_name_invalid('not');
+ test_container_name_invalid('And');
+ test_container_name_invalid('oR');
+ test_container_name_invalid('nOt');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/at-container-serialization.html b/testing/web-platform/tests/css/css-contain/container-queries/at-container-serialization.html
new file mode 100644
index 0000000000..141062a8d4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/at-container-serialization.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<title>CSS Container Queries: @container serialization</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style id="testSheet">
+ @container (width=100px) {
+ @container \!-name (inline-size > 200px ) {
+ #id { color: lime }
+ }
+ #id { color: green }
+ }
+ @container ( wiDTh ) { }
+ @container (width:100px) { }
+ @container (min-width: 100px) { }
+ @container ( MAX-WIDTH:100px ) { }
+ @container (width > 100px) { }
+ @container (width < 100px) { }
+ @container (widTH >= 100px) { }
+ @container (width <= 100px) { }
+ @container (10px < width < 100px) { }
+ @container (10px <= width <= 100px) { }
+ @container (100px>WIDTH>10px) { }
+ @container ( 100px >= width >= 10px ) { }
+ @container (calc(1em + 1px) >= width >= max(10em, 10px)) { }
+</style>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ let rules = testSheet.sheet.cssRules;
+
+ test(() => {
+ assert_equals(rules.length, 14);
+ assert_equals(rules[0].cssRules.length, 2);
+
+ assert_equals(rules[0].conditionText, "(width = 100px)");
+ assert_equals(rules[0].cssRules[0].conditionText, "\\!-name (inline-size > 200px)");
+ }, "Serialization of conditionText");
+
+ test(() => {
+ assert_equals(rules[0].cssRules[0].cssText, "@container \\!-name (inline-size > 200px) {\n #id { color: lime; }\n}");
+ }, "Serialization of inner @container rule");
+
+ test(() => {
+ assert_equals(rules[0].cssText, "@container (width = 100px) {\n @container \\!-name (inline-size > 200px) {\n #id { color: lime; }\n}\n #id { color: green; }\n}");
+ }, "Serialization of nested @container rule");
+
+ test(() => {
+ assert_equals(rules[1].conditionText, "(width)");
+ }, "Serialization of boolean condition syntax");
+
+ test(() => {
+ assert_equals(rules[2].conditionText, "(width: 100px)");
+ assert_equals(rules[3].conditionText, "(min-width: 100px)");
+ assert_equals(rules[4].conditionText, "(max-width: 100px)");
+ }, "Serialization of colon condition syntax");
+
+ test(() => {
+ assert_equals(rules[5].conditionText, "(width > 100px)");
+ assert_equals(rules[6].conditionText, "(width < 100px)");
+ assert_equals(rules[7].conditionText, "(width >= 100px)");
+ assert_equals(rules[8].conditionText, "(width <= 100px)");
+ assert_equals(rules[9].conditionText, "(10px < width < 100px)");
+ assert_equals(rules[10].conditionText, "(10px <= width <= 100px)");
+ assert_equals(rules[11].conditionText, "(100px > width > 10px)");
+ assert_equals(rules[12].conditionText, "(100px >= width >= 10px)");
+ }, "Serialization of range condition syntax");
+
+ test(() => {
+ assert_equals(rules[13].conditionText, "(calc(1em + 1px) >= width >= max(10em, 10px))");
+ }, "Serialization of calc()");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-parsing.html b/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-parsing.html
new file mode 100644
index 0000000000..36fda2e366
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-parsing.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<title>@container: style queries parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div style="container-name:name">
+ <main id="main"></main>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function cleanup_main() {
+ while (main.firstChild)
+ main.firstChild.remove();
+ }
+
+ function set_style(text) {
+ let style = document.createElement('style');
+ style.innerText = text;
+ main.append(style);
+ return style;
+ }
+
+ function test_rule_valid(query) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container ${query} {}`);
+ assert_equals(style.sheet.rules.length, 1);
+ }, query);
+ }
+
+ function test_condition_invalid(condition) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`@container name ${condition} {}`);
+ assert_equals(style.sheet.rules.length, 0);
+ }, condition);
+ }
+
+ // Tests that 1) the condition parses, and 2) is either "unknown" or not, as
+ // specified.
+ function test_condition_valid(condition, unknown) {
+ test(t => {
+ t.add_cleanup(cleanup_main);
+ let style = set_style(`
+ @container name ${condition} {}
+ @container name (${condition}) or (not (${condition})) { main { --match:true; } }
+ `);
+ assert_equals(style.sheet.rules.length, 2);
+ const expected = unknown ? '' : 'true';
+ assert_equals(getComputedStyle(main).getPropertyValue('--match'), expected);
+ }, condition);
+ }
+
+ function test_condition_known(condition) {
+ test_condition_valid(condition, false /* unknown */);
+ }
+
+ function test_condition_unknown(condition) {
+ test_condition_valid(condition, true /* unknown */);
+ }
+
+ test_condition_known('style(--my-prop: foo)');
+ test_condition_known('style(--my-prop: foo - bar ())');
+ test_condition_known('style(not ((--foo: calc(10px + 2em)) and ((--foo: url(x)))))');
+ test_condition_known('style((--foo: bar) or (--bar: 10px))');
+ test_condition_known('style(--my-prop:)');
+ test_condition_known('style(--my-prop: )');
+ test_condition_known('style(--foo: bar !important)');
+ test_condition_known('style(--foo)');
+
+ test_condition_unknown('style(--foo: bar;)');
+ test_condition_unknown('style(style(--foo: bar))');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-serialization.html b/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-serialization.html
new file mode 100644
index 0000000000..a4fb25378c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/at-container-style-serialization.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries: style() conditionText serialization</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://drafts.csswg.org/cssom/#serialize-a-css-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style id="testSheet">
+ @container style( --foo:bar) { }
+ @container STyle(--foo: ) { }
+ @container STyle(--foo:) { }
+ @container STyle(--foo) { }
+ @container style( ( --FOO: BAR) OR ( prop: val ) ) { }
+ @container style (--foo: bar) { }
+ @container style(--foo: bar baz) { }
+ @container style(--foo:2.100 ) { }
+</style>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ assert_equals(testSheet.sheet.cssRules.length, 8);
+ });
+
+ const tests = [
+ ["style(--foo: bar)", "Normalize spaces"],
+ ["style(--foo: )", "Empty declaration value - spaces"],
+ ["style(--foo: )", "Empty declaration value"],
+ ["style(--foo)", "No declaration value"],
+ ["style((--FOO: BAR) or ( prop: val ))", "Unknown CSS property after 'or'"],
+ ["style (--foo: bar)", "Not a style function with space before '('"],
+ ["style(--foo: bar baz)", "Spaces preserved in custom property value"],
+ ["style(--foo: 2.100)", "Original string number in custom property value"]
+ ].map((e, i) => [testSheet.sheet.cssRules[i], ...e]);
+
+ tests.forEach((t) => {
+ test(() => assert_equals(t[0].conditionText, t[1]), t[2]);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/auto-scrollbars.html b/testing/web-platform/tests/css/css-contain/container-queries/auto-scrollbars.html
new file mode 100644
index 0000000000..9cf797186c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/auto-scrollbars.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<title>CSS Container Queries Test: scrollbar stability for @container queries and overflow:auto</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#scrollbar-layout">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #scroller {
+ height: 100px;
+ width: 100px;
+ overflow-y: auto;
+ }
+ #container {
+ container-type: inline-size;
+ }
+ #inner {
+ height: 100px;
+ border-bottom: 1px solid red;
+ }
+ @container (max-width: 99px) {
+ #inner {
+ height: 50px;
+ }
+ }
+</style>
+<div id="precondition" style="width:100px;height:100px;overflow:scroll"></div>
+<div id="scroller">
+ <div id="container">
+ <div id="inner"></div>
+ </div>
+</div>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ assert_implements_optional(precondition.clientWidth < 100,
+ "Tests do not work with overlay scrollbars");
+ });
+
+ test(() => {
+ assert_less_than(scroller.clientWidth, 100, "Expects a vertical scrollbar");
+ assert_equals(getComputedStyle(inner).height, "50px",
+ "Layout with a scrollbar means the container query applies");
+ }, "Initial layout - expecting a scrollbar without overflowing content instead of overflowing content without a scrollbar");
+
+ test(() => {
+ inner.style.borderBottomWidth = "2px";
+ assert_less_than(scroller.clientWidth, 100, "Expects a vertical scrollbar");
+ assert_equals(getComputedStyle(inner).height, "50px",
+ "Layout with a scrollbar means the container query applies");
+ }, "Same result after a reflow");
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/backdrop-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/backdrop-invalidation.html
new file mode 100644
index 0000000000..6a59a2f894
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/backdrop-invalidation.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>Test that ::backdrop responds to container size changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ :root {
+ color: black;
+ }
+
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 40px;
+ }
+
+ ::backdrop {
+ background-color: black;
+ }
+
+ @container (min-width: 300px) {
+ ::backdrop {
+ background-color: green;
+ }
+ }
+</style>
+<main id=container>
+ <dialog>test</dialog>
+</main>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ let dialog = document.querySelector('dialog');
+
+ test(function() {
+ try {
+ dialog.showModal();
+
+ assert_equals(getComputedStyle(dialog, '::backdrop').backgroundColor, 'rgb(0, 0, 0)');
+
+ container.style.width = '300px';
+ assert_equals(getComputedStyle(dialog, '::backdrop').backgroundColor, 'rgb(0, 128, 0)');
+
+ container.style = '';
+ assert_equals(getComputedStyle(dialog, '::backdrop').backgroundColor, 'rgb(0, 0, 0)');
+ } finally {
+ dialog.close();
+ }
+ }, 'Pseudo-element ::backdrop responds to container size changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/calc-evaluation.html b/testing/web-platform/tests/css/css-contain/container-queries/calc-evaluation.html
new file mode 100644
index 0000000000..71a5e23764
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/calc-evaluation.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: calc()</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/mediaqueries-4/#units">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ :root { font-size: 10px; }
+
+ /* To make output more readable */
+ :root > * { font-size: initial; }
+
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 50px;
+ }
+ @container (width = calc(100px + 10rem)) {
+ #target { color: green; }
+ }
+</style>
+<div id=container>
+ <div id=target></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).color, 'rgb(0, 128, 0)');
+ }, 'em relative inline-size');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-001.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-001.html
new file mode 100644
index 0000000000..f904d1fe24
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-001.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Container Queries Test: Canvas as size container for focusable child</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<style>
+ @supports (container-type: size) {
+ canvas:focus-within {
+ border: 50px solid green;
+ }
+ canvas {
+ display: block;
+ width: 100px;
+ height: 100px;
+ box-sizing: border-box;
+ container-type: size;
+ }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas>
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ target.focus();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-002.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-002.html
new file mode 100644
index 0000000000..689feeb5ff
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-002.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>CSS Container Queries Test: Absolute positioned canvas as size container for focusable child</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<style>
+ @supports (container-type: size) {
+ canvas:focus-within {
+ border: 50px solid green;
+ }
+ canvas {
+ display: block;
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ box-sizing: border-box;
+ container-type: size;
+ }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas>
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ target.focus();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-003.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-003.html
new file mode 100644
index 0000000000..74199cc72b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-003.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>CSS Container Queries Test: Canvas as size container for focusable child with display</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<style>
+ @supports (container-type: size) {
+ canvas:focus-within {
+ background-color: green;
+ }
+ canvas {
+ display: block;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ #target { display: none; }
+ @container (width = 100px) {
+ #target { display: block; }
+ }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas>
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ target.focus();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-004.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-004.html
new file mode 100644
index 0000000000..b23846382b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-004.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>CSS Container Queries Test: Absolute positioned canvas as size container for focusable child with display</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<style>
+ @supports (container-type: size) {
+ canvas:focus-within {
+ background-color: green;
+ }
+ canvas {
+ display: block;
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ #target { display: none; }
+ @container (width = 100px) {
+ #target { display: block; }
+ }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas>
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ requestAnimationFrame(()=> {
+ requestAnimationFrame(()=> {
+ target.focus();
+ });
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-005.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-005.html
new file mode 100644
index 0000000000..9132592434
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-005.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>CSS Container Queries Test: Canvas as dynamic size container for focusable child with display</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ canvas {
+ display: block;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ #target { display: none; }
+ @container (width = 200px) {
+ #target { display: block; }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas id="canvas">
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ target.focus();
+ assert_not_equals(document.activeElement, target);
+ }, "Initially display:none, not focusable");
+
+ test(() => {
+ canvas.style.width = "200px";
+ target.focus();
+ assert_equals(document.activeElement, target);
+ }, "Focusable after container size change");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-006.html b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-006.html
new file mode 100644
index 0000000000..02dbce6275
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/canvas-as-container-006.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>CSS Container Queries Test: Absolute positioned canvas as dynamic size container for focusable child with display</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ canvas {
+ display: block;
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ #target { display: none; }
+ @container (width = 200px) {
+ #target { display: block; }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<canvas id="canvas">
+ <div id="target" tabIndex="1"></div>
+</canvas>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ target.focus();
+ assert_not_equals(document.activeElement, target);
+ }, "Initially display:none, not focusable");
+
+ test(() => {
+ canvas.style.width = "200px";
+ target.focus();
+ assert_equals(document.activeElement, target);
+ }, "Focusable after container size change");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container-ref.html
new file mode 100644
index 0000000000..8dd3b8b2c1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>You should see the word PASS below.</p>
+PASS
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container.html b/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container.html
new file mode 100644
index 0000000000..8c58d22b7d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/change-display-in-container.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>CSS Container Queries Test: Change display and box inside a container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="change-display-in-container-ref.html">
+<style>
+ .fail { display: inline; }
+ .pass { display: none; }
+ #container { container-type: size; width: 100px; }
+ @container (min-width: 200px) {
+ .fail { display: none; }
+ .pass { display: inline; }
+ }
+</style>
+<p>You should see the word PASS below.</p>
+<div id="container">
+ <span>
+ <span class="fail">FAIL</span>
+ </span>
+ <span>
+ <span class="pass">PASS</span>
+ <span class="fail">FAIL</span>
+ </span>
+</div>
+<script>
+ container.offsetTop;
+ container.style.width = "auto";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/chrome-legacy-skip-recalc.html b/testing/web-platform/tests/css/css-contain/container-queries/chrome-legacy-skip-recalc.html
new file mode 100644
index 0000000000..b86a4091a2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/chrome-legacy-skip-recalc.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>CSS Container Queries Test: Chrome legacy layout skipping style recalc</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1288879">
+<link rel="match" href="/css/reference/pass_if_pass_below.html">
+<style>
+ #container {
+ container-type: inline-size;
+ }
+ #multicol {
+ column-count: 1;
+ }
+
+ @supports not (container-type: inline-size) {
+ #container { display: none }
+ }
+</style>
+<p>Test passes if there is the word "PASS" below.</p>
+<div id="container"><span>PASS</span></div>
+<span id="multicol"><table></table></span>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/column-spanner-in-container.html b/testing/web-platform/tests/css/css-contain/container-queries/column-spanner-in-container.html
new file mode 100644
index 0000000000..d494e28504
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/column-spanner-in-container.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<title>CSS Container Queries Test: Column-spanner depending on container in column</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #multicol {
+ container-type: inline-size;
+ width: 600px;
+ columns: 2;
+ column-gap: 0;
+ height: 200px;
+ }
+ #spanner { height: 100px; }
+ @container (width = 600px) {
+ #spanner {
+ column-span: all;
+ }
+ }
+</style>
+<div id="multicol">
+ <div id="spanner"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(spanner).width, "600px");
+ }, "#spanner matching container with column-width 300px, getting column-span:all");
+
+ test(() => {
+ multicol.style.width = "500px";
+ assert_equals(getComputedStyle(spanner).width, "250px");
+ }, "Reducing #multicol width means #spanner no longer gets column-span:all");
+
+ test(() => {
+ multicol.style.width = "";
+ assert_equals(getComputedStyle(spanner).width, "600px");
+ }, "Back to matching 300px and column-span:all");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/conditional-container-status.html b/testing/web-platform/tests/css/css-contain/container-queries/conditional-container-status.html
new file mode 100644
index 0000000000..e9762f9323
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/conditional-container-status.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Conditionally removing container status</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .parent { width: 300px; }
+ .child { width: 100px; }
+ .parent, .child { container-type: inline-size; }
+ @container (min-width: 200px) {
+ .child { container-type: initial; }
+ .grandchild { border: 3px solid green }
+ }
+</style>
+<div class="parent">
+ <div class="child">
+ <div class="grandchild">You should see a green border around this text</div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function() {
+ let s = getComputedStyle(document.querySelector('.grandchild'));
+ assert_equals(s.getPropertyValue('border-color'), 'rgb(0, 128, 0)');
+ }, 'Conditionally applying container-type:initial');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-computed.html b/testing/web-platform/tests/css/css-contain/container-queries/container-computed.html
new file mode 100644
index 0000000000..2be304481b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-computed.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Computed values of container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_computed_value('container', 'initial', 'none');
+test_computed_value('container', 'inherit', 'none');
+test_computed_value('container', 'unset', 'none');
+test_computed_value('container', 'none / inline-size');
+test_computed_value('container', 'none / size');
+test_computed_value('container', 'inline-size / inline-size');
+test_computed_value('container', 'block-size / size');
+test_computed_value('container', 'foo / inline-size');
+test_computed_value('container', 'foo /inline-size', 'foo / inline-size');
+test_computed_value('container', 'foo/ inline-size', 'foo / inline-size');
+test_computed_value('container', 'foo/inline-size', 'foo / inline-size');
+test_computed_value('container', 'FoO / size');
+test_computed_value('container', 'foo bar / size', 'foo bar / size');
+test_computed_value('container', 'foo / normal', 'foo');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue-ref.html
new file mode 100644
index 0000000000..48b2622363
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue-ref.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+ <title>CSS Test Reference</title>
+ <script src="/common/reftest-wait.js"></script>
+ <style>
+ video {
+ contain: size;
+ width: 200px;
+ height: 200px;
+ }
+ video::cue { background-color: green }
+ video::cue(b) { color: lime }
+ </style>
+</head>
+<body>
+ <video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <track default src="support/test.vtt">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ </video>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue.html b/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue.html
new file mode 100644
index 0000000000..f53d161db0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-for-cue.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+ <title>CSS Container Queries Test: Container for pseudo elements</title>
+ <link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+ <link rel="match" href="container-for-cue-ref.html">
+ <script src="/common/reftest-wait.js"></script>
+ <style>
+ video {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+ @container (width = 200px) {
+ video::cue { background-color: green }
+ video::cue(b) { color: lime }
+ }
+ </style>
+</head>
+<body>
+ <video autoplay onplaying="this.onplaying = null; this.pause(); takeScreenshot();">
+ <source src="/media/white.webm" type="video/webm">
+ <source src="/media/white.mp4" type="video/mp4">
+ <track default src="support/test.vtt">
+ </video>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-for-shadow-dom.html b/testing/web-platform/tests/css/css-contain/container-queries/container-for-shadow-dom.html
new file mode 100644
index 0000000000..d1a0c3b568
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-for-shadow-dom.html
@@ -0,0 +1,345 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Container Queries Test: query container for Shadow DOM</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #inclusive-ancestor-across-root,
+ #inclusive-ancestor-skip-slotting,
+ #inclusive-ancestor-slotted,
+ #inclusive-ancestor-host,
+ #inclusive-ancestor-part,
+ #inclusive-ancestor-slotted-before,
+ #inclusive-ancestor-host-before,
+ #inclusive-ancestor-part-before,
+ #inclusive-ancestor-inner-part,
+ #inclusive-ancestor-slot-fallback,
+ #inner-scope-host-part {
+ width: 400px;
+ container-type: inline-size;
+ }
+</style>
+
+<div id="inclusive-ancestor-across-root">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ @container (width = 400px) {
+ #t1 { color: green; }
+ }
+ </style>
+ <div id="t1"></div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-skip-slotting">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ </style>
+ <div>
+ <slot></slot>
+ </div>
+ </template>
+ <style>
+ @container (width = 400px) {
+ #t2 { color: green; }
+ }
+ </style>
+ <div id="t2"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slotted">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ slot {
+ display: block;
+ width: 200px;
+ container-type: inline-size;
+ }
+ @container (width = 200px) {
+ ::slotted(#t3) { color: green; }
+ }
+ </style>
+ <slot></slot>
+ </template>
+ <div id="t3"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-host">
+ <div id="t4">
+ <template shadowrootmode="open">
+ <style>
+ @container (width = 400px) {
+ :host(#t4) { color: green; }
+ }
+ </style>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-part">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ </style>
+ <div>
+ <span id="t5" part="part"></span>
+ </div>
+ </template>
+ <style>
+ @container (width = 400px) {
+ #inclusive-ancestor-part > div::part(part) { color: green; }
+ }
+ </style>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slotted-before">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ slot {
+ display: block;
+ width: 200px;
+ container-type: inline-size;
+ }
+ @container (width = 200px) {
+ ::slotted(#t6)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ <slot></slot>
+ </template>
+ <style>
+ #t6 {
+ width: 400px;
+ container-type: inline-size;
+ }
+ </style>
+ <div id="t6"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-host-before">
+ <div id="t7">
+ <template shadowrootmode="open">
+ <style>
+ :host {
+ width: 200px;
+ container-type: inline-size;
+ }
+ @container (width = 200px) {
+ :host(#t7)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-part-before">
+ <style>
+ @container (width = 400px) {
+ #inclusive-ancestor-part-before > div::part(part)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ </style>
+ <div>
+ <span id="t8" part="part"></span>
+ </div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-inner-part">
+ <style>
+ @container (width = 400px) {
+ #inclusive-ancestor-inner-part > div::part(inner-part) { color: green; }
+ }
+ </style>
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ </style>
+ <div exportparts="inner-part">
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ </style>
+ <div>
+ <span id="t9" part="inner-part"></span>
+ </div>
+ </template>
+ </div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slot-fallback">
+ <div><template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ @container (width = 200px) {
+ #t10 { color: green; }
+ }
+ </style>
+ <div>
+ <slot><span id="t10"></span></slot>
+ </div>
+ </template></div>
+</div>
+
+<div id="no-container-for-part">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ #t11 { color: green; }
+ </style>
+ <div>
+ <span id="t11" part="part"></span>
+ </div>
+ </template>
+ <style>
+ @container (width = 200px) {
+ #no-container-for-part > div::part(part) { color: red; }
+ }
+ </style>
+ </div>
+</div>
+
+<div id="inner-scope-host-part">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ div {
+ width: 200px;
+ container-type: inline-size;
+ }
+ @container (width = 400px) {
+ :host::part(part) { color: green; }
+ }
+ </style>
+ <div>
+ <span id="t12" part="part"></span>
+ </div>
+ </template>
+ <style>
+ </style>
+ </div>
+</div>
+
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ polyfill_declarative_shadow_dom(document);
+ });
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ const t1 = document.querySelector("#inclusive-ancestor-across-root > div").shadowRoot.querySelector("#t1");
+ assert_equals(getComputedStyle(t1).color, green);
+ }, "Match container in outer tree");
+
+ test(() => {
+ const t2 = document.querySelector("#t2");
+ assert_equals(getComputedStyle(t2).color, green);
+ }, "Match container in same tree, not walking flat tree ancestors");
+
+ test(() => {
+ const t3 = document.querySelector("#t3");
+ assert_equals(getComputedStyle(t3).color, green);
+ }, "Match container in ::slotted selector's originating element tree");
+
+ test(() => {
+ const t4 = document.querySelector("#t4");
+ assert_equals(getComputedStyle(t4).color, green);
+ }, "Match container in outer tree for :host");
+
+ test(() => {
+ const t5 = document.querySelector("#inclusive-ancestor-part > div").shadowRoot.querySelector("#t5");
+ assert_equals(getComputedStyle(t5).color, green);
+ }, "Match container in ::part selector's originating element tree");
+
+ test(() => {
+ const t6 = document.querySelector("#t6");
+ assert_equals(getComputedStyle(t6, "::before").color, green);
+ }, "Match container for ::before in ::slotted selector's originating element tree");
+
+ test(() => {
+ const t7 = document.querySelector("#t7");
+ assert_equals(getComputedStyle(t7, "::before").color, green);
+ }, "Match container in outer tree for :host::before");
+
+ test(() => {
+ const t8 = document.querySelector("#inclusive-ancestor-part-before > div").shadowRoot.querySelector("#t8");
+ assert_equals(getComputedStyle(t8, "::before").color, green);
+ }, "Match container for ::before in ::part selector's originating element tree");
+
+ test(() => {
+ const outerhost = document.querySelector("#inclusive-ancestor-inner-part > div");
+ const innerhost = outerhost.shadowRoot.querySelector("div");
+ const t9 = innerhost.shadowRoot.querySelector("#t9");
+ assert_equals(getComputedStyle(t9).color, green);
+ }, "Match container for ::part selector's originating element tree for exportparts");
+
+ test(() => {
+ const t10 = document.querySelector("#inclusive-ancestor-slot-fallback > div").shadowRoot.querySelector("#t10");
+ assert_equals(getComputedStyle(t10).color, green);
+ }, "Match container for slot light tree child fallback");
+
+ test(() => {
+ const t11 = document.querySelector("#no-container-for-part > div").shadowRoot.querySelector("#t11");
+ assert_equals(getComputedStyle(t11).color, green);
+ }, "Should not match container inside shadow tree for ::part()");
+
+ test(() => {
+ const t12 = document.querySelector("#inner-scope-host-part > div").shadowRoot.querySelector("#t12");
+ assert_equals(getComputedStyle(t12).color, green);
+ }, "A :host::part rule should match containers in the originating element tree");
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-inheritance.html b/testing/web-platform/tests/css/css-contain/container-queries/container-inheritance.html
new file mode 100644
index 0000000000..b333b691fd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-inheritance.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Inheritance of container-*</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+setup(() => assert_implements_container_queries());
+
+assert_not_inherited('container-name', 'none', 'foo');
+assert_not_inherited('container-type', 'normal', 'inline-size');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-inner-at-rules.html b/testing/web-platform/tests/css/css-contain/container-queries/container-inner-at-rules.html
new file mode 100644
index 0000000000..ac18002929
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-inner-at-rules.html
@@ -0,0 +1,196 @@
+<!doctype html>
+<title>@container: inner at-rules</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 100px;
+ height: 100px;
+ }
+
+</style>
+<div id=container>
+ <div id=child></div>
+</div>
+
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+
+<style>
+ @container (width: 100px) {
+ @keyframes anim1 {
+ from { --anim1:true; }
+ to { --anim1:true; }
+ }
+ }
+
+ @container (width: 200px) {
+ @keyframes anim2 {
+ from { --anim2:true; }
+ to { --anim2:true; }
+ }
+ }
+
+ #child { animation: anim1 10s paused, anim2 10s paused; }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--anim1'), 'true');
+ assert_equals(getComputedStyle(child).getPropertyValue('--anim2'), 'true');
+ }, '@keyframes is defined regardless of evaluation');
+</script>
+
+
+<style>
+ @container (width: 100px) {
+ @property --prop1 {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 0px;
+ }
+ }
+
+ @container (width: 200px) {
+ @property --prop2 {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 0px;
+ }
+ }
+
+ #child {
+ font-size: 20px;
+ --prop1:1em;
+ --prop2:2em;
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--prop1'), '20px');
+ assert_equals(getComputedStyle(child).getPropertyValue('--prop2'), '40px');
+ }, '@property is defined regardless of evaluation');
+</script>
+
+
+<style>
+ @container (width: 100px) {
+ @layer a;
+ }
+
+ @container (width: 200px) {
+ @layer b;
+ }
+
+ @layer b {
+ #child { --layer:b; }
+ }
+
+ @layer a {
+ #child { --layer:a; }
+ }
+
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--layer'), 'b');
+ }, '@layer order respected regardless of evaluation');
+</script>
+
+
+<style>
+ @container (width: 100px) {
+ @font-face {
+ font-family: Font1;
+ font-stretch: 50% 200%;
+ src: url(/fonts/Ahem.ttf);
+ }
+ }
+
+ @container (width: 200px) {
+ @font-face {
+ font-family: Font2;
+ font-stretch: 40% 190%;
+ src: url(/fonts/Ahem.ttf);
+ }
+ }
+
+</style>
+<script>
+ promise_test(async (t) => {
+ const fonts1 = await document.fonts.load("20px Font1");
+ assert_not_equals(fonts1[0], undefined);
+ assert_equals(fonts1[0].stretch, "50% 200%");
+
+ const fonts2 = await document.fonts.load("20px Font2");
+ assert_not_equals(fonts2[0], undefined);
+ assert_equals(fonts2[0].stretch, "40% 190%");
+ }, '@font-face is defined regardless of evaluation');
+</script>
+
+
+<style>
+ @container (width: 100px) {
+ /* Assumed to be false */
+ @media (width: 0px) {
+ #child { --media1:true; }
+ }
+ /* Assumed to be true */
+ @media (min-width: 0px) {
+ #child { --media2:true; }
+ }
+ }
+
+ /* Same again, but with failing container query. */
+ @container (width: 200px) {
+ @media (width: 0px) {
+ #child { --media3:true; }
+ }
+ @media (min-width: 0px) {
+ #child { --media4:true; }
+ }
+ }
+
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--media1'), '');
+ assert_equals(getComputedStyle(child).getPropertyValue('--media2'), 'true');
+ assert_equals(getComputedStyle(child).getPropertyValue('--media3'), '');
+ assert_equals(getComputedStyle(child).getPropertyValue('--media4'), '');
+ }, '@media works inside @container');
+</script>
+
+
+<style>
+ @container (width: 100px) {
+ @supports (width: 500kg) {
+ #child { --supports1:true; }
+ }
+ @supports (width: 500px) {
+ #child { --supports2:true; }
+ }
+ }
+
+ /* Same again, but with failing container query. */
+ @container (width: 200px) {
+ @supports (width: 500kg) {
+ #child { --supports3:true; }
+ }
+ @supports (width: 500px) {
+ #child { --supports4:true; }
+ }
+ }
+
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--supports1'), '');
+ assert_equals(getComputedStyle(child).getPropertyValue('--supports2'), 'true');
+ assert_equals(getComputedStyle(child).getPropertyValue('--supports3'), '');
+ assert_equals(getComputedStyle(child).getPropertyValue('--supports4'), '');
+ }, '@supports works inside @container');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-inside-multicol-with-table.html b/testing/web-platform/tests/css/css-contain/container-queries/container-inside-multicol-with-table.html
new file mode 100644
index 0000000000..8688159da3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-inside-multicol-with-table.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<title>CSS Container Queries Test: container inside multicol with table</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #multicol {
+ columns: 2;
+ }
+ .container {
+ container-type: inline-size;
+ width: 100px;
+ }
+ @container (width = 100px) {
+ #t1, #t2 { color: green; }
+ }
+</style>
+<div id="multicol">
+ <div class="container">
+ <div id="t1"></div>
+ </div>
+ <table>
+ <div class="container">
+ <div id="t2"></div>
+ </div>
+ </table>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(t1).color, green);
+ }, "Matching size container inside table inside multicol");
+
+ test(() => {
+ assert_equals(getComputedStyle(t2).color, green);
+ }, "Matching size container inside multicol with table sibling");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-longhand-animation-type.html b/testing/web-platform/tests/css/css-contain/container-queries/container-longhand-animation-type.html
new file mode 100644
index 0000000000..aded2a3ec8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-longhand-animation-type.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<title>Container Queries - The container longhands are not animatable</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#propdef-container-name">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#propdef-container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from {
+ --ref:PASS;
+ container-name: FAIL;
+ container-type: size;
+ }
+ to {
+ --ref:PASS;
+ container-name: FAIL;
+ container-type: size;
+ }
+ }
+ #container {
+ --ref:FAIL;
+ container-name: PASS;
+ container-type: inline-size;
+ animation: anim 1s linear paused;
+ }
+</style>
+<div id=container>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(container).getPropertyValue('--ref'), 'PASS');
+ }, 'Reference variable is applied');
+
+ test(() => {
+ assert_equals(getComputedStyle(container).getPropertyValue('container-name'), 'PASS');
+ }, 'container-name is not animatable');
+
+ test(() => {
+ assert_equals(getComputedStyle(container).getPropertyValue('container-type'), 'inline-size');
+ }, 'container-type is not animatable');
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-name-computed.html b/testing/web-platform/tests/css/css-contain/container-queries/container-name-computed.html
new file mode 100644
index 0000000000..f58d54744e
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-name-computed.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Computed values of container-name</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_computed_value('container-name', 'initial', 'none');
+test_computed_value('container-name', 'unset', 'none');
+test_computed_value('container-name', 'foo');
+test_computed_value('container-name', 'FoO');
+test_computed_value('container-name', 'foo bar');
+test_computed_value('container-name', 'bar foo');
+test_computed_value('container-name', 'foo foo bar');
+test_computed_value('container-name', 'foo bar foo');
+test_computed_value('container-name', 'bar foo foo');
+test_computed_value('container-name', '\\!escaped');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-name-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-name-invalidation.html
new file mode 100644
index 0000000000..aad225def9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-name-invalidation.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<title>container-name invalidation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ div {
+ color: black;
+ }
+ #outer {
+ container-name: c1;
+ container-type: inline-size;
+ width: 300px;
+ }
+
+ #inner {
+ container-name: c2;
+ container-type: inline-size;
+ width: 200px;
+ }
+
+ #intermediate {
+ width: 250px;
+ }
+
+ @container c1 (width: 250px) {
+ #child {
+ color: green;
+ }
+ }
+</style>
+<div id=outer>
+ <div id=intermediate>
+ <div id=inner>
+ <div id=child>Test</div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function(t) {
+ t.add_cleanup(() => { outer.style = ''; });
+
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+
+ outer.style.width = '250px';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+
+ outer.style.width = '251px';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+ }, 'Changing a named container invalidates relevant descendants');
+
+ test(function(t) {
+ t.add_cleanup(() => {
+ outer.style = '';
+ intermediate.style = '';
+ });
+
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+
+ // #intermediate becomes the new container.
+ intermediate.style = 'container-name:c1; container-type:inline-size';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+
+ // #outer becomes the container again.
+ intermediate.style = '';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+
+ outer.style.width = '250px';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+ }, 'Changing container-name invalidates relevant descendants');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-name-parsing.html b/testing/web-platform/tests/css/css-contain/container-queries/container-name-parsing.html
new file mode 100644
index 0000000000..4f385cca52
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-name-parsing.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Parsing of container-name</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_valid_value('container-name', 'initial');
+test_valid_value('container-name', 'inherit');
+test_valid_value('container-name', 'unset');
+test_valid_value('container-name', 'revert');
+test_valid_value('container-name', 'none');
+test_valid_value('container-name', 'foo');
+test_valid_value('container-name', 'BAR');
+test_valid_value('container-name', 'foo bar');
+test_valid_value('container-name', 'foo foo');
+test_valid_value('container-name', '\\!escaped');
+test_valid_value('container-name', 'auto');
+test_valid_value('container-name', 'normal');
+
+test_invalid_value('container-name', 'none none');
+test_invalid_value('container-name', 'foo, bar');
+test_invalid_value('container-name', '#fff');
+test_invalid_value('container-name', '1px');
+test_invalid_value('container-name', 'default'); /* reserved */
+
+test_invalid_value('container-name', '"initial"');
+test_invalid_value('container-name', '"inherit"');
+test_invalid_value('container-name', '"unset"');
+test_invalid_value('container-name', '"revert"');
+test_invalid_value('container-name', '"none"');
+test_invalid_value('container-name', '"foo"');
+
+test_invalid_value('container-name', 'not');
+test_invalid_value('container-name', 'and');
+test_invalid_value('container-name', 'or');
+test_invalid_value('container-name', 'Not');
+test_invalid_value('container-name', 'aNd');
+test_invalid_value('container-name', 'oR');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-name-tree-scoped.html b/testing/web-platform/tests/css/css-contain/container-queries/container-name-tree-scoped.html
new file mode 100644
index 0000000000..e66f51d035
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-name-tree-scoped.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Container Queries Test: Tree scoped container-name</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<link rel="help" href="https://drafts.csswg.org/css-scoping-1/#shadow-names">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
+<script src="support/cq-testcommon.js"></script>
+
+<div id="container-name-host">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ :host { container-name: foo; }
+ </style>
+ <slot></slot>
+ </template>
+ <div id="t1"></div>
+ </div>
+ <style>
+ #container-name-host > div {
+ container-type: inline-size;
+ }
+ #t1 { color: green; }
+ @container foo (width > 0px) {
+ #t1 { color: red; }
+ }
+ </style>
+</div>
+
+<div id="container-name-slotted">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ ::slotted(div) {
+ container-name: foo;
+ }
+ </style>
+ <slot></slot>
+ </template>
+ <div>
+ <div id="t2"></div>
+ </div>
+ </div>
+ <style>
+ #container-name-slotted > div > div {
+ container-type: inline-size;
+ }
+ #t2 { color: green; }
+ @container foo (width > 0px) {
+ #t2 { color: red; }
+ }
+ </style>
+</div>
+
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ polyfill_declarative_shadow_dom(document);
+ });
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(t1).color, green);
+ }, "Outer scope query should not match container-name set by :host rule in shadow tree");
+
+ test(() => {
+ assert_equals(getComputedStyle(t2).color, green);
+ }, "Outer scope query should not match container-name set by ::slotted rule in shadow tree");
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-nested.html b/testing/web-platform/tests/css/css-contain/container-queries/container-nested.html
new file mode 100644
index 0000000000..3ad35bd2be
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-nested.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<title>@container (nested)</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+
+#outer {
+ container-name: outer;
+ container-type: size;
+ width: 100px;
+ height: 100px;
+}
+
+#inner {
+ container-name: inner;
+ container-type: size;
+ width: 50px;
+ height: 50px;
+}
+</style>
+<div id=outer>
+ <div id=inner>
+ <div id=child></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+
+<!--
+ "Implicit" refers to implicit container selection, i.e. understanding which
+ container to evaluate against by looking at the features used.
+-->
+
+<style>
+ @container (width: 50px) {
+ @container (height: 50px) {
+ #child { --implicit:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--implicit'), 'true');
+ }, 'Implicit');
+</script>
+
+
+<style>
+ @container (width: 70px) {
+ @container (height: 50px) {
+ #child { --implicit-outer-fail:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--implicit-outer-fail'), '');
+ }, 'Implicit, outer failing');
+</script>
+
+
+<style>
+ @container (width: 50px) {
+ @container (height: 70px) {
+ #child { --implicit-inner-fail:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--implicit-inner-fail'), '');
+ }, 'Implicit, inner failing');
+</script>
+
+
+<style>
+ @container outer (width: 100px) {
+ @container inner (height: 50px) {
+ #child { --named-outer-inner:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--named-outer-inner'), 'true');
+ }, 'Outer named, inner named');
+</script>
+
+
+<style>
+ @container inner (width: 50px) {
+ @container outer (height: 100px) {
+ #child { --named-outer-inner-reverse:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--named-outer-inner-reverse'), 'true');
+ }, 'Outer named, inner named (reverse)');
+</script>
+
+
+<style>
+ @container unknown (width: 100px) {
+ @container inner (height: 50px) {
+ #child { --named-failing-outer:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--named-failing-outer'), '');
+ }, 'Failing outer name');
+</script>
+
+<style>
+ @container outer (width: 100px) {
+ @container unknown (height: 50px) {
+ #child { --named-failing-inner:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--named-failing-inner'), '');
+ }, 'Failing inner name');
+</script>
+
+
+<style>
+ @container outer (width: 100px) {
+ @container (height: 50px) {
+ #child { --named-outer-inner-implicit:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--named-outer-inner-implicit'), 'true');
+ }, 'Outer named, inner implicit');
+</script>
+
+
+<style>
+ @container (width: 50px) {
+ @container inner (height: 50px) {
+ #child { --implicit-outer-inner-named:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--implicit-outer-inner-named'), 'true');
+ }, 'Inner named, outer implicit');
+</script>
+
+
+<style>
+ @container (width: 50px) {
+ @container outer (height: 100px) {
+ #child { --implicit-outer-inner-named-reverse:true; }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--implicit-outer-inner-named-reverse'), 'true');
+ }, 'Inner named, outer implicit (reverse)');
+</script>
+
+
+<style>
+ @container (width > 1px) {
+ @container (width > 2px) {
+ @container (width > 3px) {
+ #child { --three-levels:true; }
+ }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--three-levels'), 'true');
+ }, 'Three levels');
+</script>
+
+
+<style>
+ @container (width > 1px) {
+ @container (width > 2000px) {
+ @container (width > 3px) {
+ #child { --three-levels-middle-fail:true; }
+ }
+ }
+ }
+</style>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--three-levels-middle-fail'), '');
+ }, 'Three levels, middle fail');
+</script>
+
+
+<style>
+ @container (width: 50px) {
+ @container outer (height: 100px) {
+ #child { --inner-named-invalidation:true; }
+ }
+ }
+</style>
+<script>
+ test((t) => {
+ t.add_cleanup(() => { outer.style = ''; });
+ assert_equals(getComputedStyle(child).getPropertyValue('--inner-named-invalidation'), 'true');
+ outer.style.height = '200px';
+ assert_equals(getComputedStyle(child).getPropertyValue('--inner-named-invalidation'), '');
+ }, 'Named inner invalidation');
+</script>
+
+
+<style>
+ @container (width: 50px) {
+ @container outer (height: 100px) {
+ #child { --outer-implicit-invalidation:true; }
+ }
+ }
+</style>
+<script>
+ test((t) => {
+ t.add_cleanup(() => { inner.style = ''; });
+ assert_equals(getComputedStyle(child).getPropertyValue('--outer-implicit-invalidation'), 'true');
+ inner.style.width = '200px';
+ assert_equals(getComputedStyle(child).getPropertyValue('--outer-implicit-invalidation'), '');
+ }, 'Implicit outer invalidation');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-parsing.html b/testing/web-platform/tests/css/css-contain/container-queries/container-parsing.html
new file mode 100644
index 0000000000..87b3bdd48c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-parsing.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Parsing of container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-name">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_valid_value('container', 'initial');
+test_valid_value('container', 'inherit');
+test_valid_value('container', 'unset');
+test_valid_value('container', 'revert');
+test_valid_value('container', 'none');
+test_valid_value('container', 'none / normal', 'none');
+test_valid_value('container', 'inline-size');
+test_valid_value('container', 'none / inline-size', 'none / inline-size');
+test_valid_value('container', 'size');
+test_valid_value('container', 'block-size / size');
+test_valid_value('container', 'inline-size / inline-size');
+test_valid_value('container', 'size / size');
+test_valid_value('container', 'foo');
+test_valid_value('container', 'foo / normal', 'foo');
+test_valid_value('container', 'foo bar / size');
+test_valid_value('container', 'foo bar / normal', 'foo bar');
+test_valid_value('container', 'FOO / size');
+test_valid_value('container', 'FOO/size', 'FOO / size');
+test_valid_value('container', ' FOO /size', 'FOO / size');
+test_valid_value('container', 'normal / size');
+test_valid_value('container', 'auto / size');
+
+test_invalid_value('container', 'none none');
+test_invalid_value('container', 'none inline-size');
+test_invalid_value('container', 'none / inline-size none');
+test_invalid_value('container', 'none / inline-size normal');
+test_invalid_value('container', 'none / inline-size inline-size');
+test_invalid_value('container', 'none / inline-size block-size unknown');
+test_invalid_value('container', 'none / inline-size block-size');
+test_invalid_value('container', 'none / size block-size');
+test_invalid_value('container', 'none, none');
+test_invalid_value('container', 'none, normal');
+test_invalid_value('container', 'none / none');
+test_invalid_value('container', 'none / auto');
+test_invalid_value('container', 'none / foo');
+test_invalid_value('container', 'none / foo, bar');
+test_invalid_value('container', '#fff');
+test_invalid_value('container', '1px');
+test_invalid_value('container', 'default');
+test_invalid_value('container', '10px / inline-size');
+test_invalid_value('container', '#fefefe / inline-size');
+test_invalid_value('container', 'calc(3px) / inline-size');
+test_invalid_value('container', 'size 1 / name');
+test_invalid_value('container', 'none / block-size');
+test_invalid_value('container', 'name / block-size');
+test_invalid_value('container', ' NAME / block-size', 'NAME / block-size');
+test_invalid_value('container', 'NAME/block-size','NAME / block-size');
+test_invalid_value('container', 'block-size / block-size');
+test_invalid_value('container', 'none / size style');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-selection.html b/testing/web-platform/tests/css/css-contain/container-queries/container-selection.html
new file mode 100644
index 0000000000..cef20f85a2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-selection.html
@@ -0,0 +1,183 @@
+<!doctype html>
+<title>@container: selection using name and implicit selection</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+
+ main { background-color: lightgray; }
+ main > div { background-color: skyblue; }
+ main > div > div { background-color: seagreen; }
+ main > div > div > div { background-color: tomato; }
+
+ main {
+ width: 64px;
+ height: 64px;
+ }
+
+ main div {
+ width: 50%;
+ height: 50%;
+ }
+
+ .inline { container-type: inline-size; }
+ .size { container-type: size; }
+
+ .a-inline { container: a / inline-size; }
+ .a-size { container: a / size; }
+
+ .b-size { container: inline- b / size; }
+ .b-size { container: b / size; }
+
+ .ab-size { container: a b / size; }
+
+ .a { container-name: a; contain: strict; }
+
+</style>
+
+<main>
+ <div class="inline">
+ <div class="size">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="size">
+ <div class="inline">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="inline">
+ <div class="inline">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="a-size">
+ <div class="b-size">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="a-size">
+ <div class="a-size">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="a-size">
+ <div class="a">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="a-size">
+ <div class="b-size">
+ <div class="a-inline">
+ <span></span>
+ </div>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="a-inline">
+ <div class="b-size">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<main>
+ <div class="ab-size">
+ <div class="size">
+ <span></span>
+ </div>
+ </div>
+</main>
+
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function test_query(prelude, selector, expected) {
+ test(t => {
+ let elements = document.querySelectorAll(selector);
+ assert_equals(elements.length, 1);
+ let element = elements[0];
+
+ let style = document.createElement('style');
+ t.add_cleanup(() => { style.remove(); });
+ style.innerText = `@container ${prelude} { span { --match:true; } } `;
+ document.body.append(style);
+
+ assert_equals(getComputedStyle(element).getPropertyValue('--match'), expected);
+ }, `${prelude} for ${selector}`);
+ }
+
+ // Test that a given container query applies to the specified element.
+ // The provided selector must unique identify the element.
+ function test_applied(prelude, selector) {
+ test_query(prelude, selector, 'true');
+ }
+
+ function test_rejected(prelude, selector) {
+ test_query(prelude, selector, '');
+ }
+
+ // For the following tests, the inner container has a size of 16x16px,
+ // and the outer container has a size of 32x32px.
+
+ // Implicit selection:
+ test_applied('(width: 16px)', '.size > .inline > span');
+ test_applied('(height: 16px)', '.inline > .size > span');
+ test_applied('(width: 16px)', '.inline > .size > span');
+ test_applied('(height: 32px)', '.size > .inline > span');
+ test_rejected('(height: 16px)', '.size > .inline > span');
+
+ // Name selection:
+ test_applied('a (width: 32px)', '.a-size > .b-size > span');
+ test_applied('b (width: 16px)', '.a-size > .b-size > span');
+ test_rejected('c (width)', '.a-size > .b-size > span');
+ test_applied('a (width: 16px)', '.a-size > .a-size > span');
+
+ // container-name alone does not establish a container:
+ test_applied('a (width: 32px)', '.a-size > .a > span');
+
+ // Can query container with multiple names:
+ test_applied('a (width: 32px)', '.ab-size > .size > span');
+ test_applied('b (width: 32px)', '.ab-size > .size > span');
+ test_rejected('c (width)', '.ab-size > .size > span');
+
+ // The following tests have three containers:
+ //
+ // outer -> 32x32px
+ // middle -> 16x16px
+ // inner -> 8x8px
+
+ // Combinations of name and implicit selection:
+ test_applied('a (width: 8px)', '.a-size > .b-size > .a-inline > span');
+ test_applied('b (width: 16px)', '.a-size > .b-size > .a-inline > span');
+ test_applied('a (height: 32px)', '.a-size > .b-size > .a-inline > span');
+ test_rejected('a (height)', '.a-inline > .b-size');
+
+ // Same tests as above, but logical versions:
+ test_applied('a (inline-size: 8px)', '.a-size > .b-size > .a-inline > span');
+ test_applied('b (inline-size: 16px)', '.a-size > .b-size > .a-inline > span');
+ test_applied('a (block-size: 32px)', '.a-size > .b-size > .a-inline > span');
+ test_rejected('a (block-size)', '.a-inline > .b-size');
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation-after-load.html b/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation-after-load.html
new file mode 100644
index 0000000000..cf5687aa39
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation-after-load.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>@container: invalidation of container size after load event</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 4em;
+ border: 1px solid black;
+ }
+ @container (width > 300px) {
+ #child { color: green; }
+ }
+</style>
+<div id=container>
+ <div id=child>
+ Green
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ container.offsetTop;
+
+ container.style.width = '400px';
+ container.style.setProperty('--x', 'x'); // crbug.com/1321010
+
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation.html
new file mode 100644
index 0000000000..ab26971749
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-size-invalidation.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>@container-dependent elements respond to container size changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 50px;
+ }
+ div { color: red; }
+ @container (min-width: 300px) {
+ div { color: green; }
+ }
+</style>
+<main id=container>
+ <div id=child>
+ Test
+ <p><span id=descendant>Deep</span></p>
+ </div>
+</main>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function() {
+ assert_equals(getComputedStyle(child).color, 'rgb(255, 0, 0)');
+ container.style.width = '300px';
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+ }, 'Children respond to changes in container size');
+
+ test(function() {
+ container.style = '';
+ assert_equals(getComputedStyle(descendant).color, 'rgb(255, 0, 0)');
+ container.style.width = '300px';
+ assert_equals(getComputedStyle(descendant).color, 'rgb(0, 128, 0)');
+ }, 'Descendants respond to changes in container size');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-size-nested-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-size-nested-invalidation.html
new file mode 100644
index 0000000000..a549f6d848
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-size-nested-invalidation.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<title>Nested @container-dependent elements respond to outer container size changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer {
+ container-type: size;
+ container-name: outer;
+ width: 100px;
+ height: 100px;
+ }
+
+ /* Note that it's intentional that nothing queries this container. */
+ #inner {
+ container-type: size;
+ width: 42px;
+ height: 42px;
+ }
+
+ @container (width > 90px) {
+ #outer_child {
+ --outer:true;
+ }
+ }
+
+ @container outer (width > 70px) {
+ #inner_child {
+ --inner:true;
+ }
+ }
+</style>
+<div id=outer>
+ <div id=outer_child></div>
+ <div id=inner>
+ <div id=inner_child></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function() {
+ assert_equals(getComputedStyle(outer_child).getPropertyValue('--outer'), 'true');
+ assert_equals(getComputedStyle(inner_child).getPropertyValue('--inner'), 'true');
+
+ outer.style.width = '80px';
+
+ assert_equals(getComputedStyle(outer_child).getPropertyValue('--outer'), '');
+ assert_equals(getComputedStyle(inner_child).getPropertyValue('--inner'), 'true');
+
+ outer.style.width = '60px';
+
+ assert_equals(getComputedStyle(outer_child).getPropertyValue('--outer'), '');
+ assert_equals(getComputedStyle(inner_child).getPropertyValue('--inner'), '');
+
+ outer.style.width = '100px';
+
+ assert_equals(getComputedStyle(outer_child).getPropertyValue('--outer'), 'true');
+ assert_equals(getComputedStyle(inner_child).getPropertyValue('--inner'), 'true');
+ }, 'Queries that skip a container are invalidated correctly');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-size-shadow-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-size-shadow-invalidation.html
new file mode 100644
index 0000000000..35773b3006
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-size-shadow-invalidation.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Container Queries Test: Invalidate size container query for Shadow DOM</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container {
+ container-type: inline-size;
+ width: 100px;
+ }
+ @container (width = 200px) {
+ .target { color: green; }
+ }
+</style>
+<div id="host_container" class="container">
+ <template shadowrootmode="open">
+ <div class="container">
+ <slot></slot>
+ </div>
+ </template>
+ <div class="target">Green</div>
+</div>
+<div id="non_host_container" class="container">
+ <div>
+ <template shadowrootmode="open">
+ <div class="container">
+ <slot></slot>
+ </div>
+ </template>
+ <div class="target">Green</div>
+ </div>
+</div>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ polyfill_declarative_shadow_dom(document);
+ });
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ document.body.offsetTop;
+ host_container.style.width = "200px";
+ assert_equals(getComputedStyle(document.querySelector("#host_container .target")).color, green);
+ }, "Host container child invalidated with container in shadow tree");
+
+ test(() => {
+ document.body.offsetTop;
+ non_host_container.style.width = "200px";
+ assert_equals(getComputedStyle(document.querySelector("#non_host_container .target")).color, green);
+ }, "Non-host container child invalidated with container in shadow tree");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-type-computed.html b/testing/web-platform/tests/css/css-contain/container-queries/container-type-computed.html
new file mode 100644
index 0000000000..0b5e033a0f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-type-computed.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Computed values of container-type</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_computed_value('container-type', 'initial', 'normal');
+test_computed_value('container-type', 'unset', 'normal');
+test_computed_value('container-type', 'inline-size');
+test_computed_value('container-type', 'size');
+test_computed_value('container-type', 'normal');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-type-containment.html b/testing/web-platform/tests/css/css-contain/container-queries/container-type-containment.html
new file mode 100644
index 0000000000..66500a6a71
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-type-containment.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<title>CSS Container Queries Test: applied containment for container-type</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+
+<style>
+ /* Note: background colors have no impact on the test result. They are
+ present to make it easier to visually verify that the test
+ does the right thing. */
+ .square {
+ width: 50px;
+ height: 50px;
+ background: tomato;
+ }
+ .half {
+ width: 25px;
+ height: 50px;
+ background: red;
+ }
+ div > div:nth-of-type(1) { background: skyblue; }
+ div > div:nth-of-type(2) { background: hotpink; }
+</style>
+
+<div id=test1 class=square>
+ <div id=float1 class=half style="float:left"></div>
+ <div id=child1 class=half style="container-type:inline-size"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(child1.offsetLeft, test1.offsetLeft + float1.offsetWidth);
+ }, 'container-type:inline-size turns on layout containment');
+</script>
+
+<hr>
+
+<div id=test2 class=square>
+ <div id=ref2 style="float:left">A</div>
+ <div id=child2 style="float:left; container-type:inline-size">A</div>
+</div>
+<script>
+ test(() => {
+ assert_equals(child2.offsetWidth, 0);
+ assert_equals(child2.offsetHeight, ref2.offsetHeight);
+ }, 'container-type:inline-size turns on inline-size containment');
+</script>
+
+<hr>
+
+<div id=test3 class=square>
+ <div id=child3 style="float:left; container-type:size">A</div>
+</div>
+<script>
+ test(() => {
+ assert_equals(child3.offsetHeight, 0);
+ assert_equals(child3.offsetWidth, 0);
+ }, 'container-type:size turns on full size containment');
+</script>
+
+<hr>
+
+<style>
+ #ref4::before, #child4::before {
+ content: counter(foo);
+ }
+</style>
+<div id=test4 class=square style="counter-set: foo 5">
+ <div id=ref4 style="float:left"></div>
+ <section style="container-type:inline-size">
+ <span style="counter-increment: foo 1000;"></span>
+ </section>
+ <section style="container-type:size">
+ <span style="counter-increment: foo 1000;"></span>
+ </section>
+ <div id=child4 style="float:left"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(child4.offsetWidth, ref4.offsetWidth);
+ }, 'container-type:inline/size turns on style containment');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-type-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-type-invalidation.html
new file mode 100644
index 0000000000..90e0b4acd1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-type-invalidation.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<title>container-type invalidation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ div {
+ color: black;
+ }
+ #outer {
+ width: 300px;
+ }
+
+ #intermediate {
+ width: 250px;
+ }
+
+ #inner {
+ width: 200px;
+ }
+
+ .container {
+ container-type: inline-size;
+ }
+
+ @container ((max-width: 200px) or (min-width: 300px)) {
+ #child { color: green; }
+ }
+
+</style>
+<div id=outer>
+ <div id=intermediate>
+ <div id=inner>
+ <div id=child>Test</div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function(t) {
+ t.add_cleanup(() => {
+ for (let e of [outer, intermediate, inner])
+ e.classList.remove('container');
+ });
+
+ // No container.
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+
+ outer.classList.add('container');
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+
+ // The container query does not match widths in the range [201, 299],
+ // and #intermediate has width:250px.
+ intermediate.classList.add('container');
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+
+ inner.classList.add('container');
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+
+ // Should have no effect, #inner is the container.
+ outer.classList.remove('container');
+ intermediate.classList.remove('container');
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 128, 0)');
+
+ inner.classList.remove('container');
+ assert_equals(getComputedStyle(child).color, 'rgb(0, 0, 0)');
+ }, 'Changing the container type invalidates relevant descendants');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-type-layout-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-type-layout-invalidation.html
new file mode 100644
index 0000000000..3103de81f7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-type-layout-invalidation.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>container-type layout invalidation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #div {
+ width: fit-content;
+ }
+</style>
+<div id=div>
+ content
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function(t) {
+ t.add_cleanup(() => { div.style = ''; });
+
+ assert_greater_than(div.offsetWidth, 0);
+ assert_greater_than(div.offsetHeight, 0);
+
+ div.style.containerType = 'size';
+
+ assert_equals(div.offsetWidth, 0);
+ assert_equals(div.offsetHeight, 0);
+ }, 'Changing container-type invalidates layout');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-type-parsing.html b/testing/web-platform/tests/css/css-contain/container-queries/container-type-parsing.html
new file mode 100644
index 0000000000..5805a927b3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-type-parsing.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Containment Test: Parsing of container-type</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id="target"></div>
+<script>
+setup(() => assert_implements_container_queries());
+
+test_valid_value('container-type', 'initial');
+test_valid_value('container-type', 'inherit');
+test_valid_value('container-type', 'unset');
+test_valid_value('container-type', 'revert');
+test_valid_value('container-type', 'normal');
+test_valid_value('container-type', 'size');
+test_valid_value('container-type', 'inline-size');
+
+test_invalid_value('container-type', 'none');
+test_invalid_value('container-type', 'auto');
+test_invalid_value('container-type', 'block-size');
+test_invalid_value('container-type', 'normal normal');
+test_invalid_value('container-type', 'normal inline-size');
+test_invalid_value('container-type', 'inline-size normal');
+test_invalid_value('container-type', 'inline-size inline-size');
+test_invalid_value('container-type', 'inline-size block-size');
+test_invalid_value('container-type', 'block-size inline-size');
+test_invalid_value('container-type', 'size inline-size');
+test_invalid_value('container-type', 'inline-size size');
+test_invalid_value('container-type', 'normal, normal');
+test_invalid_value('container-type', 'foo');
+test_invalid_value('container-type', '"foo"');
+test_invalid_value('container-type', 'foo, bar');
+test_invalid_value('container-type', '#fff');
+test_invalid_value('container-type', '1px');
+test_invalid_value('container-type', 'default');
+test_invalid_value('container-type', 'size nonsense');
+test_invalid_value('container-type', 'style');
+test_invalid_value('container-type', 'inline-size style', 'style inline-size');
+test_invalid_value('container-type', 'style inline-size');
+test_invalid_value('container-type', 'style size');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-animation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-animation.html
new file mode 100644
index 0000000000..cf1b9a8f34
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-animation.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<title>Container Relative Units: Animation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+
+ @keyframes anim_cqw { from { top: 20cqw; } to { top: 40cqw; } }
+ @keyframes anim_cqh { from { top: 20cqh; } to { top: 40cqh; } }
+ @keyframes anim_cqi { from { top: 20cqi; } to { top: 40cqi; } }
+ @keyframes anim_cqb { from { top: 20cqb; } to { top: 40cqb; } }
+ @keyframes anim_cqmin { from { top: 20cqmin; } to { top: 40cqmin; } }
+ @keyframes anim_cqmax { from { top: 20cqmax; } to { top: 40cqmax; } }
+
+ #container > div {
+ animation-delay: -5s;
+ animation-play-state: paused;
+ animation-duration: 10s;
+ animation-timing-function: linear;
+ }
+
+ #element_cqw { animation-name: anim_cqw; }
+ #element_cqh { animation-name: anim_cqh; }
+ #element_cqi { animation-name: anim_cqi; }
+ #element_cqb { animation-name: anim_cqb; }
+ #element_cqmin { animation-name: anim_cqmin; }
+ #element_cqmax { animation-name: anim_cqmax; }
+
+</style>
+<div id=container>
+ <div id=element_cqw></div>
+ <div id=element_cqh></div>
+ <div id=element_cqi></div>
+ <div id=element_cqb></div>
+ <div id=element_cqmin></div>
+ <div id=element_cqmax></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const units = ['cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax'];
+
+ for (let unit of units) {
+ test(() => {
+ let element = document.getElementById(`element_${unit}`)
+ assert_equals(getComputedStyle(element).top, '60px');
+ }, `Animation using ${unit} unit`);
+
+ test(() => {
+ let element = document.getElementById(`element_${unit}`)
+ assert_equals(getComputedStyle(element).top, '60px');
+ try {
+ container.style.width = '300px';
+ container.style.height = '300px';
+ assert_equals(getComputedStyle(element).top, '90px');
+ } finally {
+ container.style = '';
+ }
+
+ assert_equals(getComputedStyle(element).top, '60px');
+ }, `Animation using ${unit} unit responds to changing container size`);
+ }
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-basic.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-basic.html
new file mode 100644
index 0000000000..166a003a29
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-basic.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<title>Container Relative Units: cqi, cqb, etc</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .inline { container-type: inline-size; }
+ .size { container-type: size; }
+ .inline.outer { width: 500px; }
+ .size.outer { height: 400px; }
+ .inline.inner { width: 300px; }
+</style>
+<div id=ref></div>
+<div class="inline outer">
+ <div class="size outer">
+ <div class="inline inner">
+ <div id=child>Test</div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function assert_unit_equals(element, actual, expected) {
+ try {
+ element.style.padding = actual;
+ ref.style.padding = expected;
+ assert_equals(getComputedStyle(element).paddingLeft,
+ getComputedStyle(ref).paddingLeft);
+ } finally {
+ element.style = '';
+ ref.style = '';
+ }
+ }
+
+ test(function() {
+ assert_unit_equals(child, '0cqi', '0px');
+ assert_unit_equals(child, '1cqi', '3px');
+ assert_unit_equals(child, '10cqi', '30px');
+ assert_unit_equals(child, '10cqw', '30px');
+ assert_unit_equals(child, '10cqb', '40px');
+ assert_unit_equals(child, '10cqh', '40px');
+ assert_unit_equals(child, '10cqmin', '30px');
+ assert_unit_equals(child, '10cqmax', '40px');
+ }, 'Container relative units');
+
+ test(function() {
+ assert_unit_equals(child, '10cqi', '30px');
+ assert_unit_equals(child, '10cqb', '40px');
+ assert_unit_equals(child, 'calc(10cqi + 10cqb)', '70px');
+ assert_unit_equals(child, 'max(10cqi, 10cqb)', '40px');
+ }, 'Container relative units in math functions');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-computational-independence.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-computational-independence.html
new file mode 100644
index 0000000000..694b665c79
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-computational-independence.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Container Relative Units: Computationally independent</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const units = ['cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax'];
+
+ for (let unit of units) {
+ test(function() {
+ assert_throws_dom('SyntaxError', () => {
+ CSS.registerProperty({ name: '--x', inherits: false, syntax: '<length>', initialValue: `1${unit}` });
+ });
+ }, `Container relative unit ${unit} is not computationally independent`);
+ }
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-content-box.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-content-box.html
new file mode 100644
index 0000000000..89a76e868c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-content-box.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<title>Container Relative Units: evaluate against the content box</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+.size {
+ container-type: size;
+ width:100px;
+ height:50px;
+ border: 10px solid green;
+ padding: 10px;
+}
+.border-box {
+ box-sizing: border-box;
+}
+</style>
+<div id=ref></div>
+<div class="size">
+ <div id=child></div>
+</div>
+<div class="size border-box">
+ <div id=child2></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function assert_unit_equals(element, actual, expected) {
+ try {
+ element.style.padding = actual;
+ ref.style.padding = expected;
+ assert_equals(getComputedStyle(element).paddingLeft,
+ getComputedStyle(ref).paddingLeft);
+ } finally {
+ element.style = '';
+ ref.style = '';
+ }
+ }
+
+ test(function() {
+ assert_unit_equals(child, '10cqi', '10px');
+ assert_unit_equals(child, '10cqw', '10px');
+ assert_unit_equals(child, '10cqb', '5px');
+ assert_unit_equals(child, '10cqh', '5px');
+ assert_unit_equals(child, '10cqmin', '5px');
+ assert_unit_equals(child, '10cqmax', '10px');
+ }, 'Container units are relative to the content box of the container');
+
+ test(function() {
+ assert_unit_equals(child2, '10cqi', '6px');
+ assert_unit_equals(child2, '10cqw', '6px');
+ assert_unit_equals(child2, '10cqb', '1px');
+ assert_unit_equals(child2, '10cqh', '1px');
+ assert_unit_equals(child2, '10cqmin', '1px');
+ assert_unit_equals(child2, '10cqmax', '6px');
+ }, 'Container units are relative to the content box of the container (box-sizing: border-box)');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-invalidation.html
new file mode 100644
index 0000000000..665a14dcaa
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-invalidation.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>Container Relative Units in gradients</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="match" href="container-units-gradient-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ .container {
+ container-type: size;
+ width: 500px;
+ height: 400px;
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .smaller {
+ width: 400px;
+ height: 300px;
+ }
+ .box {
+ width: 100px;
+ height: 100px;
+ margin: 5px;
+ }
+</style>
+<div class=container>
+ <div class=box style="background:linear-gradient(green 5cqw, blue 10cqh)"></div>
+ <div class=box style="background:linear-gradient(green 5cqi, blue 10cqb)"></div>
+ <div class=box style="background:linear-gradient(green 5cqmin, blue 10cqmax)"></div>
+ <div class=box style="background:radial-gradient(green 5cqw, blue 10cqh)"></div>
+ <div class=box style="background:conic-gradient(from 180deg at 10cqh, green, blue)"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ document.querySelector('.container').classList.add('smaller');
+ waitForAtLeastOneFrame().then(takeScreenshot);
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-ref.html
new file mode 100644
index 0000000000..dbdabd9bc4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<style>
+ .container {
+ width: 400px;
+ height: 300px;
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .box {
+ width: 100px;
+ height: 100px;
+ margin: 5px;
+ }
+</style>
+<div class=container>
+ <div class=box style="background:linear-gradient(green 20px, blue 30px)"></div>
+ <div class=box style="background:linear-gradient(green 20px, blue 30px)"></div>
+ <div class=box style="background:linear-gradient(green 15px, blue 40px)"></div>
+ <div class=box style="background:radial-gradient(green 20px, blue 30px)"></div>
+ <div class=box style="background:conic-gradient(from 180deg at 30px, green, blue);"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient.html
new file mode 100644
index 0000000000..3d6f5378c2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-gradient.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Container Relative Units in gradients</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="match" href="container-units-gradient-ref.html">
+<style>
+ .container {
+ container-type: size;
+ width: 400px;
+ height: 300px;
+ display: flex;
+ flex-wrap: wrap;
+ }
+ .box {
+ width: 100px;
+ height: 100px;
+ margin: 5px;
+ }
+</style>
+<div class=container>
+ <div class=box style="background:linear-gradient(green 5cqw, blue 10cqh)"></div>
+ <div class=box style="background:linear-gradient(green 5cqi, blue 10cqb)"></div>
+ <div class=box style="background:linear-gradient(green 5cqmin, blue 10cqmax)"></div>
+ <div class=box style="background:radial-gradient(green 5cqw, blue 10cqh)"></div>
+ <div class=box style="background:conic-gradient(from 180deg at 10cqh, green, blue);"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-dynamic.html
new file mode 100644
index 0000000000..c1f929241c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-dynamic.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>Container Relative Units: in @container prelude (dynamic)</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer {
+ container-type: size;
+ width: 40px;
+ height: 40px;
+ }
+ #container {
+ container-type: size;
+ width: 16px;
+ height: 16px;
+ }
+
+ @container ((width = 16px) and (width = 50cqw)) { #child { --cqw:true; } }
+
+</style>
+
+<div id=outer>
+ <div id=container>
+ <div id=child>Test</div>
+ </div>
+</div>
+
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(child).getPropertyValue('--cqw'), '');
+ outer.style.width = '32px';
+ assert_equals(getComputedStyle(child).getPropertyValue('--cqw'), 'true');
+ }, 'Query with container-relative units are responsive to changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-fallback.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-fallback.html
new file mode 100644
index 0000000000..3784499c38
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container-fallback.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>Container Relative Units: container relative units fall back to small viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ iframe {
+ width: 200px;
+ height: 320px;
+ }
+</style>
+<iframe id=iframe srcdoc="
+ <style>
+ #parent {
+ container-type: inline-size;
+ width: 64px;
+ height: 50px;
+ }
+ #container {
+ container-type: size;
+ width: 32px;
+ height: 32px;
+ }
+
+ #target1, #target2 { color: green; }
+
+ /* Unit should evaluate against width of #parent */
+ @container ((height = 32px) and (height = 50cqw)) {
+ #target1 { color: blue; }
+ }
+
+ /* Unit should evaluate against height of iframe */
+ @container ((height = 32px) and (height = 10cqh)) {
+ #target2 { color: blue; }
+ }
+
+ </style>
+ <div id=parent>
+ <div id=container>
+ <div id=target1></div>
+ <div id=target2></div>
+ </div>
+ </div>
+"></iframe>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ let inner_target1 = iframe.contentDocument.querySelector('#target1');
+ let inner_target2 = iframe.contentDocument.querySelector('#target2');
+ assert_equals(getComputedStyle(inner_target1).color, 'rgb(0, 0, 255)');
+ assert_equals(getComputedStyle(inner_target2).color, 'rgb(0, 0, 255)');
+
+ iframe.style = 'height:400px';
+
+ // #target1 is not affected since it evaluated against another container.
+ // #target2 *is* affected, because it evaluated against the iframe size
+ // which just changed.
+ assert_equals(getComputedStyle(inner_target1).color, 'rgb(0, 0, 255)');
+ assert_equals(getComputedStyle(inner_target2).color, 'rgb(0, 128, 0)');
+ }, 'Use small viewport size as fallback');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container.html
new file mode 100644
index 0000000000..9ddca55ec1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-in-at-container.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<title>Container Relative Units: in @container prelude</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .size { container-type: size; }
+ .inline { container-type: inline-size; }
+ .ancestor {
+ container-type: size;
+ width: 64px;
+ height: 160px;
+ }
+ .parent {
+ container-type: inline-size;
+ width: 32px;
+ height: 77px;
+ }
+ .container {
+ container-type: size;
+ width: 16px;
+ height: 16px;
+ }
+
+ /* Unit should resolve against .parent width. */
+ @container ((width = 16px) and (width = 50cqw)) { #child1 { --cqw:true; } }
+
+ /* Unit should resolve against .ancestor height. */
+ @container ((width = 16px) and (width = 10cqh)) { #child1 { --cqh:true; } }
+
+ /* Unit should resolve against .parent width. */
+ @container ((width = 16px) and (width = 50cqi)) { #child1 { --cqi:true; } }
+
+ /* Unit should resolve against .ancestor height. */
+ @container ((width = 16px) and (width = 10cqb)) { #child1 { --cqb:true; } }
+
+ /* Unit should resolve against biggest of w/h. */
+ @container ((width = 16px) and (width = 10cqmax)) { #child1 { --cqmax:true; } }
+
+ /* Unit should resolve against smallest of w/h. */
+ @container ((width = 16px) and (width = 50cqmin)) { #child1 { --cqmin:true; } }
+
+ /* Flipped writing mode: */
+
+ /* Non-logical units are the same as above */
+ @container ((width = 16px) and (width = 50cqw)) { #child2 { --cqw:true; } }
+ @container ((width = 16px) and (width = 10cqh)) { #child2 { --cqh:true; } }
+ @container ((width = 16px) and (width = 10cqmax)) { #child2 { --cqmax:true; } }
+ @container ((width = 16px) and (width = 50cqmin)) { #child2 { --cqmin:true; } }
+
+ /* Unit should resolve against .ancestor height. */
+ @container ((width = 16px) and (width = 50cqb)) { #child2 { --cqi:true; } }
+
+ /* Unit should resolve against .parent width. */
+ @container ((width = 16px) and (width = 10cqi)) { #child2 { --cqb:true; } }
+</style>
+
+<div class=ancestor>
+ <div class=parent>
+ <div class=container>
+ <div id=child1>Test1</div>
+ </div>
+ </div>
+</div>
+
+<div class=ancestor>
+ <div class=parent>
+ <div class=container style="writing-mode:vertical-rl;">
+ <div id=child2>Test1</div>
+ </div>
+ </div>
+</div>
+
+<script>
+ setup(() => assert_implements_container_queries());
+
+ let units = [
+ 'cqw',
+ 'cqh',
+ 'cqi',
+ 'cqb',
+ 'cqmin',
+ 'cqmax',
+ ];
+
+ for (let unit of units) {
+ test(() => {
+ assert_equals(getComputedStyle(child1).getPropertyValue(`--${unit}`), 'true');
+ }, `${unit} unit resolves against appropriate container`);
+ }
+
+ // Ensure that the writing mode of the subject element is not relevant for
+ // container-relative units in the @container prelude.
+ for (let unit of units) {
+ test((t) => {
+ t.add_cleanup(() => {
+ child1.style = '';
+ });
+ child1.style.writingMode = 'vertical-rl';
+ assert_equals(getComputedStyle(child1).getPropertyValue(`--${unit}`), 'true');
+ }, `${unit} unit resolves against appropriate container (vertical writing-mode on subject)`);
+ }
+
+ for (let unit of units) {
+ test(() => {
+ assert_equals(getComputedStyle(child2).getPropertyValue(`--${unit}`), 'true');
+ }, `${unit} unit resolves against appropriate container (vertical writing-mode on container)`);
+ }
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-ineligible-container.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-ineligible-container.html
new file mode 100644
index 0000000000..8882d4a38b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-ineligible-container.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Container Relative Units: ineligible container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<style>
+ #grandparent, #parent { container-type: size; }
+ #grandparent { width: 300px; height: 250px; }
+ #parent { width: 200px; height: 150px; }
+ #target { width: 10cqw; height: 10cqh; }
+</style>
+<div id="log"></div>
+<div id="grandparent">
+ <div id="parent">
+ <div id="target"></div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+ const cases = {
+ "/* basic */": [20, 15],
+ "display: table": [30, 25],
+ "display: table-cell": [30, 25],
+ "display: inline": [30, 25],
+ "display: contents": [30, 25],
+ "display: none": [30, 25],
+ "container-type: normal": [30, 25],
+ "container-type: inline-size": [20, 25],
+ "container-type: inline-size; writing-mode: vertical-lr": [30, 15],
+ };
+ const parent = document.getElementById("parent");
+ const target = document.getElementById("target");
+ const cs = getComputedStyle(target);
+ for (let [style, [width, height]] of Object.entries(cases)) {
+ test(() => {
+ parent.style.cssText = style;
+ assert_equals(cs.width, width + "px", "width");
+ assert_equals(cs.height, height + "px", "height");
+ }, style);
+ }
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-invalidation.html
new file mode 100644
index 0000000000..abb766cd0c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-invalidation.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<title>Container Relative Units: Invalidation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #inline { container-type: inline-size; }
+ #size, #outer { container-type: size; }
+ .h600 { height: 600px; }
+ .w500 { width: 500px; }
+ .h400 { height: 400px; }
+ .w300 { width: 300px; }
+ .child {
+ padding-left: 10cqi;
+ padding-right: 10cqb;
+ }
+</style>
+<div id=ref></div>
+<div id=outer class="h600">
+ <div id=size class="w500 h400">
+ <div id=inline class="w300">
+ <div id=child class="child">Test</div>
+ <div><div id=deeper class="child">Test</div></div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function assert_cqi_equals(element, expected) {
+ assert_equals(getComputedStyle(element).paddingLeft, expected);
+ }
+
+ function assert_cqb_equals(element, expected) {
+ assert_equals(getComputedStyle(element).paddingRight, expected);
+ }
+
+ test(function(t) {
+ assert_cqi_equals(child, '30px');
+ assert_cqi_equals(deeper, '30px');
+
+ try {
+ inline.style.containerType = 'normal';
+ assert_cqi_equals(child, '50px');
+ assert_cqi_equals(deeper, '50px');
+ } finally {
+ inline.style = '';
+ }
+
+ assert_cqi_equals(child, '30px');
+ assert_cqi_equals(deeper, '30px');
+ }, `cqi respond when selected container changes type (inline-size -> normal)`);
+
+ test(function() {
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+
+ try {
+ size.style.containerType = 'normal';
+ assert_cqb_equals(child, '60px');
+ assert_cqb_equals(deeper, '60px');
+ } finally {
+ size.style = '';
+ }
+
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+ }, `cqb respond when selected container changes type (size -> normal)`);
+
+ test(function() {
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+
+ try {
+ inline.style.containerType = 'size';
+ inline.style.height = '200px';
+ assert_cqb_equals(child, '20px');
+ assert_cqb_equals(deeper, '20px');
+ } finally {
+ inline.style = '';
+ }
+
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+ }, `cqb respond when intermediate container changes type (inline-size -> size)`);
+
+ test(function() {
+ assert_cqi_equals(child, '30px');
+ assert_cqi_equals(deeper, '30px');
+
+ try {
+ inline.style.width = '50px';
+ assert_cqi_equals(child, '5px');
+ assert_cqi_equals(deeper, '5px');
+ } finally {
+ inline.style = '';
+ }
+
+ assert_cqi_equals(child, '30px');
+ assert_cqi_equals(deeper, '30px');
+ }, 'cqi respond when selected container changes inline-size');
+
+ test(function() {
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+
+ try {
+ size.style.height = '50px';
+ assert_cqb_equals(child, '5px');
+ assert_cqb_equals(deeper, '5px');
+ } finally {
+ size.style = '';
+ }
+
+ assert_cqb_equals(child, '40px');
+ assert_cqb_equals(deeper, '40px');
+ }, 'cqb respond when selected container changes block-size');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-media-queries.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-media-queries.html
new file mode 100644
index 0000000000..7b76f654e5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-media-queries.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Container-relative units in @media</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+
+<style>
+ iframe {
+ width: 200px;
+ height: 100px;
+ }
+</style>
+
+<iframe id=iframe></iframe>
+
+<script>
+setup(() => assert_implements_container_queries());
+
+const doc = iframe.contentDocument;
+const win = iframe.contentWindow;
+
+function test_media_query(feature, result, description) {
+ test((t) => {
+ t.add_cleanup(() => { doc.body.innerHTML = ''; })
+ doc.body.innerHTML = `
+ <style>
+ body {
+ color: red;
+ }
+ @media (${feature}) {
+ body {
+ color: green;
+ }
+ }
+ </style>
+ `;
+ assert_equals(win.getComputedStyle(doc.body).color, result);
+ }, description);
+}
+
+function test_media_query_applies(feature) {
+ test_media_query(feature, 'rgb(0, 128, 0)', `@media(${feature}) applies`);
+}
+
+function test_media_query_does_not_apply(feature) {
+ test_media_query(feature, 'rgb(255, 0, 0)', `@media(${feature}) does not apply`);
+}
+
+// Container-relative units resolve against the "small viewport size" for
+// media queries.
+test_media_query_applies('width:100cqw');
+test_media_query_applies('width:100cqi');
+test_media_query_applies('width:100cqmax');
+test_media_query_applies('height:100cqh');
+test_media_query_applies('height:100cqb');
+test_media_query_applies('height:100cqmin');
+test_media_query_does_not_apply('width:90cqw');
+test_media_query_does_not_apply('height:90cqh');
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache-ref.html
new file mode 100644
index 0000000000..6c8261959f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache-ref.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<meta charset="utf-8">
+<style>
+ .container {
+ width: 100px;
+ }
+
+ .half {
+ height: 50%;
+ background-color: green;
+ }
+</style>
+<div class="container" style="height: 100px">
+ <div class="half"></div>
+</div>
+<div class="container" style="height: 200px">
+ <div class="half"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache.html
new file mode 100644
index 0000000000..cc93f7793a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-rule-cache.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset="utf-8">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1832481">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="match" href="container-units-rule-cache-ref.html">
+<style>
+ .container {
+ width: 100px;
+ container-type: size;
+ }
+
+ .half {
+ height: 50cqh;
+ background-color: green;
+ }
+</style>
+<div class="container" style="height: 100px">
+ <div class="half"></div>
+</div>
+<div class="container" style="height: 200px">
+ <div class="half"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-selection.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-selection.html
new file mode 100644
index 0000000000..16a44cd176
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-selection.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<title>Container Relative Units: Advanced Container Selection</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ * { writing-mode: initial; }
+ .inline { container-type: inline-size; }
+ .size { container-type: size; }
+ .vertical { writing-mode: vertical-rl; }
+ .w500 { width: 500px; }
+ .h400 { height: 400px; }
+ .w300 { width: 300px; }
+ .h200 { height: 200px; }
+ .w100 { width: 100px; }
+</style>
+<div id=ref></div>
+<div id=c1>
+ <div id=c2>
+ <div id=c3>
+ <div id=c4>
+ <div id=child>Test</div>
+ </div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function assert_unit_equals(element, actual, expected) {
+ try {
+ element.style.padding = actual;
+ ref.style.padding = expected;
+ assert_equals(getComputedStyle(element).paddingLeft,
+ getComputedStyle(ref).paddingLeft);
+ } finally {
+ element.style = '';
+ ref.style = '';
+ }
+ }
+
+ test(() => {
+ try {
+ c1.className = 'inline w500'; // Selected by nothing.
+ c2.className = 'size h400 w300'; // Selected by cqh, cqb.
+ c3.className = 'inline w100'; // Selected by cqw, cqi.
+ assert_unit_equals(child, '10cqw', '10px');
+ assert_unit_equals(child, '10cqi', '10px');
+ assert_unit_equals(child, '10cqh', '40px');
+ assert_unit_equals(child, '10cqb', '40px');
+ assert_unit_equals(child, '10cqmin', '10px');
+ assert_unit_equals(child, '10cqmax', '40px');
+
+ c3.className = ''; // cqw, cqi now selects c2 instead.
+ assert_unit_equals(child, '10cqw', '30px');
+ assert_unit_equals(child, '10cqi', '30px');
+ assert_unit_equals(child, '10cqh', '40px');
+ assert_unit_equals(child, '10cqb', '40px');
+ assert_unit_equals(child, '10cqmin', '30px');
+ assert_unit_equals(child, '10cqmax', '40px');
+
+ } finally {
+ for (let c of [c1, c2, c3, c4, child])
+ c.className = '';
+ }
+ }, 'Container units select the proper container');
+
+ test(() => {
+ try {
+ c1.className = 'size w500 h400';
+ c2.className = 'inline w300 h200';
+
+ // [cqi, cqb] corresponds to [cqw, cqh].
+ assert_unit_equals(child, '10cqw', '30px');
+ assert_unit_equals(child, '10cqi', '30px');
+ assert_unit_equals(child, '10cqh', '40px');
+ assert_unit_equals(child, '10cqb', '40px');
+
+ child.className = 'vertical';
+ // [cqi, cqb] now corresponds to [cqh, cqw].
+ assert_unit_equals(child, '10cqw', '30px');
+ assert_unit_equals(child, '10cqi', '40px');
+ assert_unit_equals(child, '10cqh', '40px');
+ assert_unit_equals(child, '10cqb', '30px');
+
+ c2.classList.add('vertical');
+ // The inline containment on #c2 now applies to the vertical axis.
+ // [cqi, cqb] still corresponds to [cqh, cqw], but we now expect
+ // cqh to resolve against #c2, and cqw to resolve against #c1.
+ assert_unit_equals(child, '10cqw', '50px');
+ assert_unit_equals(child, '10cqi', '20px');
+ assert_unit_equals(child, '10cqh', '20px');
+ assert_unit_equals(child, '10cqb', '50px');
+
+ } finally {
+ for (let c of [c1, c2, c3, c4, child])
+ c.className = '';
+ }
+ }, 'Units respond to the writing-mode of the element');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-shadow.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-shadow.html
new file mode 100644
index 0000000000..c7fd0500f2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-shadow.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<title>Container Relative Units: Shadow DOM</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+ #direct {
+ container-type: inline-size;
+ width: 50cqw;
+ height: 50cqh;
+ }
+ #nondirect {
+ width: 10cqw;
+ height: 10cqh;
+ background: green;
+ }
+</style>
+<div id=outer>
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ #inner {
+ container-type: size;
+ width: 30px;
+ height: 30px;
+ }
+ </style>
+ <div id=inner>
+ <slot></slot>
+ </div>
+ </template>
+ <div>
+ <div id=direct>
+ <div id=nondirect>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ polyfill_declarative_shadow_dom(document);
+ });
+
+ test(() => {
+ let cs = getComputedStyle(direct);
+ assert_equals(cs.width, '100px');
+ assert_equals(cs.height, '100px');
+ }, 'Direct slotted child queries shadow-including ancestors');
+
+ test(() => {
+ let cs = getComputedStyle(nondirect);
+ assert_equals(cs.width, '10px'); // #direct
+ assert_equals(cs.height, '20px'); // #outer
+ }, 'Nondirect slotted child queries shadow-including ancestors');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-small-viewport-fallback.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-small-viewport-fallback.html
new file mode 100644
index 0000000000..6c8851681f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-small-viewport-fallback.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<title>Container Relative Units: fall back to small viewport</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ iframe {
+ width: 200px;
+ height: 40px;
+ }
+</style>
+<iframe id=iframe srcdoc="
+ <style>
+ #container {
+ container-type: inline-size;
+ width: 70px;
+ height: 30px;
+ }
+ #target {
+ left: 10cqw;
+ top: 10cqh;
+ right: 10cqi;
+ bottom: 10cqb;
+ margin-left: 10cqmax;
+ margin-right: 10cqmin;
+ }
+ </style>
+ <div id=container>
+ <div id=target></div>
+ </div>
+"></iframe>
+
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ let inner_target = iframe.contentDocument.querySelector('#target');
+
+ // Since there's an inline-size container, cqw/cqi should evaluate against
+ // that.
+ assert_equals(getComputedStyle(inner_target).left, '7px'); // 10cqw
+ assert_equals(getComputedStyle(inner_target).right, '7px'); // 10cqi
+
+ // However, there is no size container, so cqh/cqb should evaluate against
+ // the small viewport size.
+ assert_equals(getComputedStyle(inner_target).top, '4px'); // 10cqh
+ assert_equals(getComputedStyle(inner_target).bottom, '4px'); // 10cqb
+
+ assert_equals(getComputedStyle(inner_target).marginLeft, '7px'); // 10cqmax
+ assert_equals(getComputedStyle(inner_target).marginRight, '4px'); // 10cqmin
+
+ iframe.style = 'width:400px;height:80px';
+
+ // Not affected by resize:
+ assert_equals(getComputedStyle(inner_target).left, '7px'); // 10cqw
+ assert_equals(getComputedStyle(inner_target).right, '7px'); // 10cqi
+
+ // Affected:
+ assert_equals(getComputedStyle(inner_target).top, '8px'); // 10cqh
+ assert_equals(getComputedStyle(inner_target).bottom, '8px'); // 10cqb
+ assert_equals(getComputedStyle(inner_target).marginLeft, '8px'); // 10cqmax
+ assert_equals(getComputedStyle(inner_target).marginRight, '7px'); // 10cqmin
+ }, 'Use small viewport size as fallback');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-svglength.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-svglength.html
new file mode 100644
index 0000000000..8bb227c049
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-svglength.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: container-relative units in SVGLength</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="help" href="https://svgwg.org/svg2-draft/types.html#InterfaceSVGLength">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 150px;
+ }
+</style>
+<div id=container>
+ <svg id=rootSVGElement>
+ <rect id="rect1" width="10cqw" height="10cqh"/>
+ <rect id="rect2" width="10cqi" height="10cqb"/>
+ <rect id="rect3" width="10cqmin" height="10cqmax"/>
+ <rect id="rect4" width="calc(10cqmin + 10cqmax)" height="calc(10cqw + 3px)"/>
+ <rect id="rect_dynamic"/>
+ <rect id="rect_animated" width="42px" height="42px" fill="green">
+ <animate id=animation attributeName=width from="5cqw" to="10cqw" begin="0s" dur="4s"/>
+ </rect>
+ </svg>
+</div>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ container.offsetTop;
+ });
+
+ function cleanup() {
+ rect_dynamic.removeAttribute('width');
+ rect_dynamic.removeAttribute('height');
+ }
+
+ test(() => {
+ assert_equals(rect1.width.baseVal.unitType, SVGLength.SVG_LENGTHTYPE_UNKNOWN);
+ }, 'unitType with container-relative units');
+
+ test(() => {
+ assert_equals(rect1.width.baseVal.value, 20);
+ assert_equals(rect1.height.baseVal.value, 15);
+ }, 'cqw,cqh can be resolved');
+
+ test(() => {
+ assert_equals(rect2.width.baseVal.value, 20);
+ assert_equals(rect2.height.baseVal.value, 15);
+ }, 'cqi,cqb can be resolved');
+
+ test(() => {
+ assert_equals(rect3.width.baseVal.value, 15);
+ assert_equals(rect3.height.baseVal.value, 20);
+ }, 'cqmin,cqmax can be resolved');
+
+ test(() => {
+ assert_equals(rect4.width.baseVal.value, 35);
+ assert_equals(rect4.height.baseVal.value, 23);
+ }, 'calc() with container-relative units can be resolved');
+
+ test((t) => {
+ t.add_cleanup(cleanup);
+ rect_dynamic.setAttribute('width', '20cqw');
+ rect_dynamic.setAttribute('height', '20cqh');
+ assert_equals(rect_dynamic.width.baseVal.value, 40);
+ assert_equals(rect_dynamic.height.baseVal.value, 30);
+
+ rect_dynamic.width.baseVal.value = 80;
+ rect_dynamic.height.baseVal.value = 45;
+ assert_equals(rect_dynamic.getAttribute('width'), '80');
+ assert_equals(rect_dynamic.getAttribute('height'), '45');
+ }, 'Can modify value with container-relative units');
+
+ smil_async_test((t) => {
+ t.add_cleanup(cleanup);
+ let assert_width = (expected) => {
+ let epsilon = 1.0;
+ return () => {
+ assert_approx_equals(rect_animated.width.animVal.value, expected, epsilon);
+ };
+ };
+ const expectedValues = [
+ // [animationId, time, sampleCallback]
+ ["animation", 0.0, assert_width(10)],
+ ["animation", 2.0, assert_width(15)],
+ ["animation", 3.999, assert_width(20)],
+ ["animation", 4, assert_width(42)],
+ ];
+
+ runAnimationTest(t, expectedValues);
+ });
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/container-units-typed-om.html b/testing/web-platform/tests/css/css-contain/container-queries/container-units-typed-om.html
new file mode 100644
index 0000000000..6da3306fdf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/container-units-typed-om.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Container Relative Units: CSS Typed OM</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-lengths">
+<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#stylepropertymap">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<div id=element></div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const units = ['cqw', 'cqh', 'cqi', 'cqb', 'cqmin', 'cqmax'];
+ const functions = {
+ cqw: CSS.cqw,
+ cqh: CSS.cqh,
+ cqi: CSS.cqi,
+ cqb: CSS.cqb,
+ cqmin: CSS.cqmin,
+ cqmax: CSS.cqmax,
+ };
+
+ for (let unit of units) {
+ let func = functions[unit];
+
+ test(() => {
+ assert_equals(`${func(10)}`, `10${unit}`);
+ }, `CSS.${unit} function`);
+
+ test(() => {
+ try {
+ element.style.top = `10${unit}`;
+ let value = element.attributeStyleMap.get('top');
+ assert_equals(value.value, 10);
+ assert_equals(value.unit, unit);
+ } finally {
+ element.style = '';
+ }
+ }, `Reify value with ${unit} unit`);
+
+ test(() => {
+ try {
+ element.attributeStyleMap.set('top', `10${unit}`);
+ assert_equals(element.style.top, `10${unit}`);
+ } finally {
+ element.style = '';
+ }
+ }, `Set value with ${unit} unit (string)`);
+
+ test(() => {
+ try {
+ element.attributeStyleMap.set('top', func(10));
+ assert_equals(element.style.top, `10${unit}`);
+ } finally {
+ element.style = '';
+ }
+ }, `Set value with ${unit} unit (CSS.${unit})`);
+ }
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/counters-flex-circular.html b/testing/web-platform/tests/css/css-contain/container-queries/counters-flex-circular.html
new file mode 100644
index 0000000000..d60049e26c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/counters-flex-circular.html
@@ -0,0 +1,76 @@
+<!doctype html>
+<title>CSS Container Queries Test: counters inside container should not affect container size via flex layout</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<link rel="stylesheet" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #flex {
+ width: 200px;
+ display: flex;
+ flex-direction: row;
+ counter-reset: my-count 0;
+ }
+ /* #item1 grows to use remaining space given #item2's content */
+ #item1 {
+ flex-grow: 1;
+ height: 100px;
+ }
+ #container {
+ container-type: size;
+ }
+ #item2 {
+ flex-grow: 0;
+ font: 50px/1 Ahem;
+ }
+ /* #item2 size depends on generated content which depends on my-count
+ counter. */
+ #item2::before {
+ display: inline-block;
+ content: counter(my-count);
+ }
+ /* The counter-increment inside the container should not affect the size of
+ #item2 because of style containment. Otherwise we would have a
+ circularity. */
+ @container (min-width: 125px) {
+ #inner {
+ counter-increment: my-count 10;
+ background-color: green;
+ }
+ }
+</style>
+<div id="flex">
+ <div id="item1">
+ <div id="container">
+ <div id="inner"></div>
+ </div>
+ </div>
+ <div id="item2"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const item1_width = parseInt(getComputedStyle(item1).width);
+ const item2_width = parseInt(getComputedStyle(item2).width);
+ const container_width = parseInt(getComputedStyle(container).width);
+ const inner_width = parseInt(getComputedStyle(inner).width);
+
+ test(() => {
+ assert_equals(item1_width, container_width);
+ assert_equals(item1_width, inner_width);
+ }, "#item1, #container, and #inner should all have the same width: " + item1_width);
+
+ test(() => {
+ let expected_background = container_width >= 125 ? "rgb(0, 128, 0)" : "rgba(0, 0, 0, 0)";
+ assert_equals(getComputedStyle(inner).backgroundColor, expected_background);
+ }, "The container query should match the layed out width");
+
+ test(() => {
+ assert_equals(item1_width + item2_width, 200);
+ }, "The sum of the item widths should match the flexbox width");
+
+ test(() => {
+ assert_equals(parseInt(getComputedStyle(item2, "::before").width), item2_width);
+ }, "The size of the flex item #2 should be given by its contents");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container-dynamic.html
new file mode 100644
index 0000000000..d85ab6cb42
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container-dynamic.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Container Queries Test: counter updates</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<link rel="match" href="counters-ref.html">
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+
+ #counter::before {
+ content: counter(my-counter);
+ }
+
+ @container (min-width: 300px) {
+ #counter {
+ counter-reset: my-counter 100;
+ }
+ }
+</style>
+<p>Pass if you see the number 100 below.</p>
+<div id="container">
+ <div id="counter"></div>
+</div>
+<script>
+ container.offsetTop;
+ container.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container.html b/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container.html
new file mode 100644
index 0000000000..376f52ea7c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/counters-in-container.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>CSS Container Queries Test: counters depending on container queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-type">
+<link rel="match" href="counters-ref.html">
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+
+ #counter::before {
+ content: counter(my-counter);
+ }
+
+ @container (min-width: 200px) {
+ #counter {
+ counter-reset: my-counter 100;
+ }
+ }
+</style>
+<p>Pass if you see the number 100 below.</p>
+<div id="container">
+ <div id="counter"></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/counters-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/counters-ref.html
new file mode 100644
index 0000000000..303c1e89bd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/counters-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>Pass if you see the number 100 below.</p>
+<div>100</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/br-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/br-crash.html
new file mode 100644
index 0000000000..6631ba2fd5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/br-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>Don't crash for blocky &lt;br> (etc) with inline-size containment</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#contain-property">
+<link rel="help" href="https://crbug.com/1313444">
+<br style="container-type:inline-size; display:block;">
+<wbr style="container-type:inline-size; display:block;">
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/canvas-as-container-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/canvas-as-container-crash.html
new file mode 100644
index 0000000000..ae7fe8dc16
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/canvas-as-container-crash.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>CSS Container Queries Test: Absolute positioned canvas container crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1289850">
+<p>Pass if there is no crash.</p>
+<canvas id="canv" style="display:block;position:absolute;container-type:inline-size"></canvas>
+<script>
+ canv.offsetTop;
+ canv.appendChild(document.createElement("span"));
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-000-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-000-crash.html
new file mode 100644
index 0000000000..f30461919a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-000-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1289718">
+<div style="container-type:inline-size;">
+ <span style="columns:2;"></span>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-001-crash.html
new file mode 100644
index 0000000000..ce530fb2c8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1289718-001-crash.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1289718">
+<div style="container-type:inline-size;">
+ <video style="columns:2;"></video>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1346969-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1346969-crash.html
new file mode 100644
index 0000000000..37c74cf8cb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1346969-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Chrome crash bug 1346969</title>
+<link rel="help" href="https://crbug.com/1346969">
+<table id="table"> </table>
+<script>
+ document.body.offsetTop;
+ table.style.containerType = "inline-size";
+ table.style.appearance = "auto";
+ table.style.columnCount = 2;
+ table.createTBody();
+ table.createCaption();
+ document.body.offsetTop;
+ table.style.whiteSpace = "pre-wrap";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1362391-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1362391-crash.html
new file mode 100644
index 0000000000..d4e1fec400
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1362391-crash.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>DCHECK failure for style recalc from layout tree rebuild</title>
+<link rel="help" href="https://crbug.com/1362391">
+<div style="display:table-column-group">
+ <div style="container-type:size">
+ <image title="crash"></image>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html
new file mode 100644
index 0000000000..bdf4002753
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1429955.html">
+<div id="mc" style="display:list-item; width:0; columns:2; container-type:size;">
+ <div id="abs" style="position:absolute; container-type:inline-size;">line</div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-layout-root-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-layout-root-crash.html
new file mode 100644
index 0000000000..e3e709a240
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-layout-root-crash.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html class="reftest-wait">
+<link rel="help" href="https://crbug.com/1371820">
+<style>
+ body, div, img { container-type: size; }
+</style>
+<p>Pass if no crash.</p>
+<div id="div"><img id="img" alt="a"></div>
+<script>
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ // Adds a layout root inside the div size container.
+ img.alt = img.src = "b";
+ // Marks div size container for layout which skips style recalc for the sub-tree.
+ div.style.width = "500px";
+ document.documentElement.classList.remove("reftest-wait");
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-quotes-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-quotes-crash.html
new file mode 100644
index 0000000000..363f96fd02
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-quotes-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Container Queries Test: Quotes update outside container being laid out causes crash</title>
+<link rel="help" href="https://crbug.com/1313003">
+<style>
+ div { container-type: size }
+</style>
+<div style="float: right">
+ <span></span>
+ <div style="position:absolute"><q></q></div>
+ <q></q>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-remove-insert-evaluator-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-remove-insert-evaluator-crash.html
new file mode 100644
index 0000000000..986f6b0bf6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/chrome-remove-insert-evaluator-crash.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>Removing and re-inserting a container should crash</title>
+<link rel="help" href="https://crbug.com/1342750">
+<style>
+ #container { container-type: inline-size }
+</style>
+<div id="outer">
+ <div id="container"></div>
+</div>
+<script>
+ container.offsetTop;
+ let removed = container;
+ container.remove();
+ outer.appendChild(removed);
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-001-crash.html
new file mode 100644
index 0000000000..fe421500da
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-001-crash.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<title>CSS Container Queries Test: TR container with multicol TD crashes Chrome</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1291471">
+<p>Pass if test does not crash.</p>
+<table>
+ <tr style="container-type:size;">
+ <td style="columns:2"></td>
+ </tr>
+</table>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-002-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-002-crash.html
new file mode 100644
index 0000000000..24b9f1aab2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/columns-in-table-002-crash.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<title>CSS Container Queries Test: container with multicol table-header-group crashes Chrome</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1307656">
+<p>Pass if test does not crash.</p>
+<div id="container" style="container-type:inline-size">
+ <span style="display:table-header-group;columns:1"></span>
+ <span style="display:table-header-group;"></span>
+</div>
+<script>
+ // This originally caused a crash.
+ document.body.offsetTop;
+ // Additionally make sure we don't crash when the container is re-attached.
+ container.style.display = "inline-block";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-in-canvas-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-in-canvas-crash.html
new file mode 100644
index 0000000000..215c6a04db
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-in-canvas-crash.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Don't crash with a container query container inside canvas</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3">
+<link rel="help" href="https://crbug.com/1321471">
+<canvas>
+ <div>
+ <div style="container-type: size">
+ <div>Test</div>
+ </div>
+ </div>
+</canvas>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-type-change-chrome-legacy-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-type-change-chrome-legacy-crash.html
new file mode 100644
index 0000000000..609142a2c5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/container-type-change-chrome-legacy-crash.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>CSS Container Queries Test: Changing container-type in Chrome legacy layout</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1286773">
+<p>Pass if there is no crash.</p>
+<span style="column-count: 1"><table></table></span>
+<video id="video"></video>
+<input id="input"></input>
+<script>
+ document.body.offsetTop;
+ video.style.containerType = "inline-size";
+ document.body.offsetLeft;
+ video.style.columnCount = "1";
+ input.setAttribute("type", "button");
+ document.body.offsetTop;
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dialog-backdrop-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dialog-backdrop-crash.html
new file mode 100644
index 0000000000..3bbdf160cf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dialog-backdrop-crash.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<style>
+ html {
+ overflow: hidden;
+ }
+
+ dialog {
+ container-type: size;
+ width: 100px;
+ height: 100px;
+ }
+
+ @container (width > 1px) {
+ dialog::backdrop {
+ margin: 10px;
+ background-color: green;
+ }
+ }
+</style>
+<dialog id=dialog>
+ Hello World
+</dialog>
+<script>
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dirty-rowgroup-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dirty-rowgroup-crash.html
new file mode 100644
index 0000000000..2a66cd452a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/dirty-rowgroup-crash.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<div style='container-type: size;'></div>
+<table>
+ <tbody id='id_0'></tbody>
+ <th id='id_1'>
+ <tr>
+ <th></th>
+ </tr>
+ </th>
+</table>
+<script>
+ const tbody = document.getElementById('id_0')
+ tbody.getBoundingClientRect();
+ const theader = document.getElementById('id_1')
+ tbody.outerText = 'foo';
+ theader.setAttribute('rowspan', 100)
+ tbody.getBoundingClientRect();
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-000-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-000-crash.html
new file mode 100644
index 0000000000..e7b789345c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-000-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: flex;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:600px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-001-crash.html
new file mode 100644
index 0000000000..0c0648c15b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-001-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: flex;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:400px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-002-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-002-crash.html
new file mode 100644
index 0000000000..ef3052d2c9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-002-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: flex;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:600px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-003-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-003-crash.html
new file mode 100644
index 0000000000..a86f25a773
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/flex-in-columns-003-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: flex;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:400px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/focus-inside-content-visibility-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/focus-inside-content-visibility-crash.html
new file mode 100644
index 0000000000..1bf68d6686
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/focus-inside-content-visibility-crash.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<title>Container Queries Test: size change detected while focusing inside content-visibility: auto container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#using-cv-auto">
+<link rel="help" href="https://crbug.com/1270848">
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+
+<style>
+.spacer { height: 3000px; }
+.auto { content-visibility: auto; }
+#container {
+ border: 1px solid black;
+ width: 100px;
+ height: 100px;
+
+ container-type: size;
+}
+#input {
+ width: 100%;
+ visibility: hidden;
+}
+@container (min-width: 150px) {
+ #input { visibility: visible; }
+}
+
+</style>
+
+<div class=spacer></div>
+<div class=auto>
+ <div id=container>
+ <input id=input type=text></input>
+ </div>
+</div>
+
+<script>
+function focus() {
+ container.style.width = "200px";
+ input.focus();
+}
+
+onload = () => requestAnimationFrame(() => requestAnimationFrame(focus));
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/force-sibling-style-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/force-sibling-style-crash.html
new file mode 100644
index 0000000000..093a01b809
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/force-sibling-style-crash.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>getComputedStyle on sibling of style-dirty container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1306385">
+<div id=ancestor style="--x:foo">
+ <div id=container style="container-type:size;width:100px;">
+ <span>Test</span>
+ </div>
+ <div id=target></div>
+</div>
+<script>
+ ancestor.offsetTop;
+ ancestor.style.setProperty('--x', 'bar');
+ container.style.width = '200px';
+ getComputedStyle(target).getPropertyValue('--x');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-000-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-000-crash.html
new file mode 100644
index 0000000000..56cf6cfdbb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-000-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: grid;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:600px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-001-crash.html
new file mode 100644
index 0000000000..b9cf100533
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-001-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: grid;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:400px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-002-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-002-crash.html
new file mode 100644
index 0000000000..762ad44f24
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-002-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: grid;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:600px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-003-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-003-crash.html
new file mode 100644
index 0000000000..11089e6902
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/grid-in-columns-003-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: grid;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:400px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/iframe-init-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/iframe-init-crash.html
new file mode 100644
index 0000000000..e915c2479f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/iframe-init-crash.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<audio controls style='container-type: size'></audio>
+<iframe>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-multicol-inside-container-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-multicol-inside-container-crash.html
new file mode 100644
index 0000000000..7e209f7ffd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-multicol-inside-container-crash.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>CSS Container Queries Test: Inline multicol inside size container - crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/829028">
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 100px;
+ }
+ @container (width <= 200px) {
+ #multicol {
+ column-count: 2;
+ column-gap: 0;
+ }
+ }
+</style>
+<p>Test passes if it doesn't crash.</p>
+<div id="container">
+ <span id="multicol"><div></div></span>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-000-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-000-crash.html
new file mode 100644
index 0000000000..733b2c4ee9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-000-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ columns: 2;
+ }
+ }
+</style>
+<div id="container" style="container-type:inline-size; width:600px;">
+ <span id="target"></span>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-001-crash.html
new file mode 100644
index 0000000000..3b9bdf32bd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/inline-with-columns-001-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ columns: 2;
+ }
+ }
+</style>
+<div id="container" style="container-type:inline-size; width:400px;">
+ <span id="target"></span>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-column-group-container-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-column-group-container-crash.html
new file mode 100644
index 0000000000..5e520a45cf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-column-group-container-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>CSS Container Queries Test: </title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://crbug.com/1282782">
+<p>Pass if this test does not crash</p>
+<span style="column-count: 1">
+ <span style="display:table-column-group"></span>
+ <input id="inp">
+</span>
+<script>
+ document.body.offsetTop;
+ document.body.style.setProperty("container", "inline-size");
+ inp.setAttribute("type", "image");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-placeholder-inline-size-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-placeholder-inline-size-crash.html
new file mode 100644
index 0000000000..4b1284e5cb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/input-placeholder-inline-size-crash.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<title>CSS Container Queries Test: Crash: input inline-size container with placeholder</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1288692">
+<p>Pass if this test does not crash</p>
+<input id="input" style="container-type:size">
+<script>
+ document.body.offsetTop;
+ input.style.position = "absolute";
+ input.setAttribute("placeholder", "placeholder");
+ document.body.offsetTop;
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/marker-gcs-after-disconnect-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/marker-gcs-after-disconnect-crash.html
new file mode 100644
index 0000000000..3680c79512
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/marker-gcs-after-disconnect-crash.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<title>Don't crash during getComputedStyle which removes ::marker</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3">
+<link rel="help" href="https://crbug.com/1349732">
+<style>
+#container {
+ width: 100px;
+ height: 100px;
+ container-type: size;
+}
+@container (width) {
+ span { color: green; }
+}
+</style>
+<ul>
+ <li id="target"></li>
+</ul>
+<div id=container>
+ <span>PASS if no crash</span>
+</div>
+<script>
+let li = document.querySelector('li');
+getComputedStyle(target, '::marker').width;
+li.style.listStyleType = 'none';
+getComputedStyle(target, '::marker').width;
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/math-block-container-child-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/math-block-container-child-crash.html
new file mode 100644
index 0000000000..00b6836655
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/math-block-container-child-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>CSS Container Queries Test: Math block container child crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://crbug.com/1294268">
+<p>Pass if there is no crash.</p>
+<math id="m" style="display:block math"></math>
+<script>
+ let div = document.createElement("div");
+ div.style.containerType = "size";
+ m.appendChild(div);
+ div.appendChild(document.createElement("span"));
+ document.body.offsetTop;
+ div.style.color = "green";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/orthogonal-replaced-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/orthogonal-replaced-crash.html
new file mode 100644
index 0000000000..10474fd984
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/orthogonal-replaced-crash.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Chrome crash for replaced in orthogonal flow query container</title>
+<link rel="help" href="https://crbug.com/1325673">
+<p>Pass if no crash.</p>
+<iframe></iframe>
+<style>
+ body {
+ container-type: size;
+ writing-mode: vertical-rl;
+ }
+</style>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/pseudo-container-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/pseudo-container-crash.html
new file mode 100644
index 0000000000..f998c3a446
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/pseudo-container-crash.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>CSS Container Queries Test: No crash when ::after is a container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<link rel="help" href="https://crbug.com/1225381">
+<style>
+ div::after {
+ container-type: size;
+ content: '';
+ display: block;
+ }
+</style>
+<div>
+ PASS if no crash
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/reversed-ol-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/reversed-ol-crash.html
new file mode 100644
index 0000000000..fa4d35380c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/reversed-ol-crash.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>Don't crash with intermediate container in reversed list</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-style">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-style">
+<link rel="help" href="https://crbug.com/1377644">
+<style>
+.container {
+ container-type: size;
+}
+
+/* Prevent double layout due to scrollbar speculation */
+html {
+ overflow: hidden;
+}
+
+@container (width > 1px) {
+ .item {
+ display: list-item;
+ }
+}
+</style>
+<ol reversed>
+ <li>A</li>
+ <div class=container>
+ <div class=item>
+ B
+ </div>
+ </div>
+</ol>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-layout-root-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-layout-root-crash.html
new file mode 100644
index 0000000000..75a3839add
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-layout-root-crash.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<title>@container changing display of SVG element should not crash</title>
+<link rel="help" href="https://crbug.com/1245689">
+<iframe id="frame" style="width: 200px"></iframe>
+<script>
+ frame.srcdoc = `
+ <style>
+ #container {
+ container-type: inline-size;
+ }
+ @container (min-width: 300px) {
+ .hide { display: none; }
+ }
+ </style>
+ <div id="container">
+ <div class="hide"><svg></svg></div>
+ </div>`;
+
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() =>
+ requestAnimationFrame(() => frame.style.width = "400px")));
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-text-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-text-crash.html
new file mode 100644
index 0000000000..aadba08679
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/svg-text-crash.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>SVG text element with size container-type should not crash</title>
+<link rel="help" href="https://crbug.com/1298319">
+<style>
+ text { container-type: inline-size; }
+</style>
+<p>Pass if no crash.</p>
+<svg><text></text></svg>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-000-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-000-crash.html
new file mode 100644
index 0000000000..566a4eb1eb
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-000-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: table;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:600px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-001-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-001-crash.html
new file mode 100644
index 0000000000..4fab9de88f
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-001-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 500px) {
+ #target {
+ display: table;
+ }
+ }
+</style>
+<div id="container" style="columns:2; container-type:inline-size; width:400px;">
+ <div id="target"></div>
+</div>
+<script>
+ document.body.offsetTop;
+ container.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-002-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-002-crash.html
new file mode 100644
index 0000000000..4f0cdc0740
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-002-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: table;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:600px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-003-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-003-crash.html
new file mode 100644
index 0000000000..436da592d9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-003-crash.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1276898">
+<style>
+ @container (max-width: 250px) {
+ #target {
+ display: table;
+ }
+ }
+</style>
+<div id="ancestor" style="columns:2; column-gap:0; width:400px;">
+ <div style="container-type:inline-size;">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ ancestor.style.width = "600px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-004-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-004-crash.html
new file mode 100644
index 0000000000..daed42f009
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-004-crash.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>CSS Container Queries Crash Test</title>
+<link rel="help" href="https://crbug.com/1338055">
+<p>Pass if no crash</p>
+<div style="container-type:inline-size">
+ <span style="columns: 1">
+ <canvas>
+ <script>
+ document.body.offsetTop;
+ </script>
+ <div style="container-type:inline-size">
+ <span style="display:table-column-group"></span>
+ </div>
+ </canvas>
+ </span>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-005-crash.html b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-005-crash.html
new file mode 100644
index 0000000000..60d6f9d3a1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/crashtests/table-in-columns-005-crash.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<title>CSS Container Queries Crash Test</title>
+<link rel="help" href="https://crbug.com/1336334">
+<li style="container-type:inline-size">
+ <span style="columns:2">
+ <table></table>
+ </span>
+</li>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/custom-layout-container-001.https.html b/testing/web-platform/tests/css/css-contain/container-queries/custom-layout-container-001.https.html
new file mode 100644
index 0000000000..8f301e8ebf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/custom-layout-container-001.https.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html class=reftest-wait>
+<title>CSS Container Queries Test: Size queries on CSS Layout API containers</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.css-houdini.org/css-layout-api/">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/worklet-reftest.js"></script>
+<style>
+ #test1 {
+ width: 400px;
+ height: 100px;
+ }
+ #outer {
+ display: inline; /* Shouldn't pass without layout api support */
+ display: layout(half);
+ height: 100px;
+ container-type: inline-size;
+ }
+ @container (width = 400px) {
+ #inner {
+ display: inline; /* Shouldn't pass without layout api support */
+ display: layout(half);
+ height: 100px;
+ container-type: inline-size;
+ }
+ }
+ @container (width = 200px) {
+ #green {
+ background-color: green;
+ height: 100px;
+ }
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="test1">
+ <div id="outer">
+ <div id="inner">
+ <div id="green"></div>
+ </div>
+ </div>
+</div>
+
+<script id="code" type="text/worklet">
+ registerLayout("half", class {
+ async intrinsicSizes() {}
+ async layout(children, edges, constraints, styleMap) {
+ const childInlineSize = constraints.fixedInlineSize / 2;
+ const childFragments = await Promise.all(children.map((child) => {
+ return child.layoutNextFragment({fixedInlineSize: childInlineSize});
+ }));
+
+ for (let childFragment of childFragments) {
+ childFragment.inlineOffset = 0;
+ childFragment.blockOffset = 0;
+ }
+ const autoBlockSize = 100;
+ return {autoBlockSize, childFragments};
+ }
+ });
+</script>
+
+<script>
+ importWorkletAndTerminateTestAfterAsyncPaint(CSS.layoutWorklet, document.getElementById("code").textContent);
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-queries.html b/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-queries.html
new file mode 100644
index 0000000000..29b5004af0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-queries.html
@@ -0,0 +1,436 @@
+<!doctype html>
+<title>CSS Container Queries Test: custom property style queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#style-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer {
+ container-name: outer;
+ --inner: false;
+ --outer: true;
+ --inner-no-space:false;
+ --outer-no-space:true;
+ --inner-space-after:false ;
+ --outer-space-after:true ;
+ }
+ #inner {
+ --inner: true;
+ --outer: false;
+ --inner-no-space:true;
+ --outer-no-space:false;
+ --inner-space-after:true ;
+ --outer-space-after:false ;
+ }
+</style>
+<div id="outer">
+ <div id="inner">
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ const green = "rgb(0, 128, 0)";
+
+ function test_evaluation(query, expected) {
+ test((t) => {
+ let style_node = document.createElement('style');
+ t.add_cleanup(() => {
+ style_node.remove();
+ });
+ style_node.innerText = `@container ${query} { #target { --applied:true; } }`;
+
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ document.head.append(style_node);
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), expected ? 'true' : '');
+ }, `${query}`);
+ }
+
+ setup(() => assert_implements_container_queries());
+
+ test_evaluation('style(--inner: true)', true);
+ test_evaluation('style(--inner:true)', true);
+ test_evaluation('style(--inner:true )', true);
+ test_evaluation('style(--inner: true )', true);
+ test_evaluation('style(--inner-no-space: true)', true);
+ test_evaluation('style(--inner-no-space:true)', true);
+ test_evaluation('style(--inner-no-space:true )', true);
+ test_evaluation('style(--inner-no-space: true )', true);
+ test_evaluation('style(--inner-space-after: true)', true);
+ test_evaluation('style(--inner-space-after:true)', true);
+ test_evaluation('style(--inner-space-after:true )', true);
+ test_evaluation('style(--inner-space-after: true )', true);
+ test_evaluation('style(--outer: true)', false);
+ test_evaluation('style(--outer:true)', false);
+ test_evaluation('style(--outer:true )', false);
+ test_evaluation('style(--outer: true )', false);
+ test_evaluation('style(--outer-no-space: true)', false);
+ test_evaluation('style(--outer-no-space:true)', false);
+ test_evaluation('style(--outer-no-space:true )', false);
+ test_evaluation('style(--outer-no-space: true )', false);
+ test_evaluation('style(--outer-space-after: true)', false);
+ test_evaluation('style(--outer-space-after:true)', false);
+ test_evaluation('style(--outer-space-after:true )', false);
+ test_evaluation('style(--outer-space-after: true )', false);
+ test_evaluation('outer style(--inner: true)', false);
+ test_evaluation('outer style(--inner:true)', false);
+ test_evaluation('outer style(--inner:true )', false);
+ test_evaluation('outer style(--inner: true )', false);
+ test_evaluation('outer style(--inner-no-space: true)', false);
+ test_evaluation('outer style(--inner-no-space:true)', false);
+ test_evaluation('outer style(--inner-no-space:true )', false);
+ test_evaluation('outer style(--inner-no-space: true )', false);
+ test_evaluation('outer style(--inner-space-after: true)', false);
+ test_evaluation('outer style(--inner-space-after:true)', false);
+ test_evaluation('outer style(--inner-space-after:true )', false);
+ test_evaluation('outer style(--inner-space-after: true )', false);
+ test_evaluation('outer style(--outer: true)', true);
+ test_evaluation('outer style(--outer:true)', true);
+ test_evaluation('outer style(--outer:true )', true);
+ test_evaluation('outer style(--outer: true )', true);
+ test_evaluation('outer style(--outer-no-space: true)', true);
+ test_evaluation('outer style(--outer-no-space:true)', true);
+ test_evaluation('outer style(--outer-no-space:true )', true);
+ test_evaluation('outer style(--outer-no-space: true )', true);
+ test_evaluation('outer style(--outer-space-after: true)', true);
+ test_evaluation('outer style(--outer-space-after:true)', true);
+ test_evaluation('outer style(--outer-space-after:true )', true);
+ test_evaluation('outer style(--outer-space-after: true )', true);
+</script>
+
+<style>
+ #important {
+ --foo: bar;
+ }
+ @container style(--foo: bar !important) {
+ #important-child { color: green; }
+ }
+</style>
+<div id="important">
+ <div id="important-child"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#important-child")).color, green);
+ }, "Query custom property with !important declaration");
+</script>
+
+<style>
+ #var-query {
+ --foo: baz;
+ --bar: baz;
+ }
+ @container style(--foo: var(--bar)) {
+ #var-subst { color: green; }
+ }
+ @container not style(--foo: var(--unknown)) {
+ #var-subst-unknown { color: green; }
+ }
+ @container not style(--foo: var(--unknown, nomatch)) {
+ #var-subst-unknown-fallback { color: green; }
+ }
+ @container style(--foo: var(--unknown, baz)) {
+ #var-subst-matching-fallback { color: green; }
+ }
+ @container style(--baz: var(--unknown)) {
+ #var-subst-unknown-matching { color: green; }
+ }
+</style>
+<div id="var-query">
+ <div id="var-subst"></div>
+ <div id="var-subst-unknown"></div>
+ <div id="var-subst-unknown-fallback"></div>
+ <div id="var-subst-matching-fallback"></div>
+ <div id="var-subst-unknown-matching"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#var-subst")).color, green);
+ }, "Query custom property using var()");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#var-subst-unknown")).color, green);
+ }, "Query custom property including unknown var() reference");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#var-subst-unknown-fallback")).color, green);
+ }, "Query custom property including unknown var() reference with non-matching fallback");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#var-subst-matching-fallback")).color, green);
+ }, "Query custom property including unknown var() reference with matching fallback");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#var-subst-unknown-matching")).color, green);
+ }, "Query custom property matching guaranteed-invalid values");
+</script>
+
+<style>
+ #revert {
+ --foo: revert;
+ }
+ #revert-layer {
+ --foo: revert-layer;
+ }
+ #revert-child, #revert-layer-child {
+ color: green;
+ }
+ @container style(--foo: revert) {
+ #revert-child { color: red; }
+ }
+ @container style(--foo: revert-layer) {
+ #revert-layer-child { color: red; }
+ }
+</style>
+<div id="revert">
+ <div id="revert-child"></div>
+</div>
+<div id="revert-layer">
+ <div id="revert-layer-child"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#revert-child")).color, green);
+ }, "Style query with revert keyword is false");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#revert-layer-child")).color, green);
+ }, "Style query with revert-layer keyword is false");
+</script>
+
+<style>
+ #defaulting {
+ --inherit: baz;
+ --inherit-no: baz;
+ }
+ #defaulting-container {
+ --inherit-no: bar;
+ --unset-no: baz;
+ --initial-no: baz;
+ }
+ @container style(--initial: initial) {
+ #initial { color: green; }
+ }
+ @container not style(--initial) {
+ #initial-implicit { color: green; }
+ }
+ @container not style(--initial-no: initial) {
+ #initial-no { color: green; }
+ }
+ @container style(--initial-no) {
+ #initial-no-implicit { color: green; }
+ }
+ @container style(--inherit: inherit) {
+ #inherit { color: green; }
+ }
+ @container not style(--inherit-no: inherit) {
+ #inherit-no { color: green; }
+ }
+ @container style(--unset: unset) {
+ #unset { color: green; }
+ }
+ @container not style(--unset-no: unset) {
+ #unset-no { color: green; }
+ }
+</style>
+<div id="defaulting">
+ <div id="defaulting-container">
+ <div id="initial"></div>
+ <div id="initial-implicit"></div>
+ <div id="initial-no"></div>
+ <div id="initial-no-implicit"></div>
+ <div id="inherit"></div>
+ <div id="inherit-no"></div>
+ <div id="unset"></div>
+ <div id="unset-no"></div>
+ </div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#initial")).color, green);
+ }, "Style query 'initial' matching");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#initial-implicit")).color, green);
+ }, "Style query matching negated value-less query against initial value");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#initial-no")).color, green);
+ }, "Style query 'initial' not matching");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#initial-no-implicit")).color, green);
+ }, "Style query matching value-less query against non-initial value");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#inherit")).color, green);
+ }, "Style query 'inherit' matching");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#inherit-no")).color, green);
+ }, "Style query 'inherit' not matching");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#unset")).color, green);
+ }, "Style query 'unset' matching");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#unset-no")).color, green);
+ }, "Style query 'unset' not matching");
+</script>
+
+<style>
+ @property --reg-length {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 10px;
+ }
+
+ #registered {
+ container-type: inline-size;
+ width: 200px;
+ font-size: 20px;
+ }
+
+ #reg-container-px {
+ --reg-length: 10px;
+ }
+ @container style(--reg-length: 10px) {
+ #reg-px { color: green; }
+ }
+ @container style(--reg-length: initial) {
+ #reg-px-initial { color: green; }
+ }
+ @container not style(--reg-length) {
+ #reg-px-initial-implicit { color: green; }
+ }
+
+ #reg-container-font-relative {
+ --reg-length: 10px;
+ }
+ @container style(--reg-length: 0.5em) {
+ #reg-font-relative { color: green; }
+ }
+
+ #reg-container-font-relative-2 {
+ --reg-length: 0.5em;
+ }
+ @container style(--reg-length: 10px) {
+ #reg-font-relative-2 { color: green; }
+ }
+
+ #reg-container-container-relative {
+ width: 100px;
+ --reg-length: 100px;
+ }
+ @container style(--reg-length: 50cqi) {
+ #reg-container-relative { color: green; }
+ }
+
+ #reg-container-initial {
+ --reg-length: 10px;
+ }
+ @container style(--reg-length: 10px) {
+ #reg-initial-value { color: green; }
+ }
+ @container style(--reg-length: initial) {
+ #reg-initial-keyword { color: green; }
+ }
+ @container not style(--reg-length) {
+ #reg-initial-implicit { color: green; }
+ }
+</style>
+<div id="registered">
+ <div id="reg-container-px">
+ <div id="reg-px"></div>
+ <div id="reg-px-initial"></div>
+ <div id="reg-px-initial-implicit"></div>
+ </div>
+ <div id="reg-container-font-relative">
+ <div id="reg-font-relative"></div>
+ </div>
+ <div id="reg-container-font-relative-2">
+ <div id="reg-font-relative-2"></div>
+ </div>
+ <div id="reg-container-container-relative">
+ <div id="reg-container-relative"></div>
+ </div>
+ <div id="reg-container-initial">
+ <div id="reg-initial-value"></div>
+ <div id="reg-initial-keyword"></div>
+ <div id="reg-initial-implicit"></div>
+ </div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-px")).color, green);
+ }, "Match registered <length> custom property with px.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-px-initial")).color, green);
+ }, "Match registered <length> custom property with px via initial keyword.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-font-relative")).color, green);
+ }, "Match registered <length> custom property with em in query.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-font-relative-2")).color, green);
+ }, "Match registered <length> custom property with em in computed value.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-container-relative")).color, green);
+ }, "Match registered <length> custom property with cqi unit.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-initial-value")).color, green);
+ }, "Match registered <length> custom property with initial value.");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#reg-initial-keyword")).color, green);
+ }, "Match registered <length> custom property with initial value via initial keyword.");
+</script>
+
+<style>
+ #original-text {
+ --number: 100.00;
+ --spaces: a b;
+ }
+ @container style(--number: 100.00) {
+ #original-text-number {
+ color: green;
+ }
+ }
+ @container style(--number: 100.0) {
+ #original-text-number {
+ color: red;
+ }
+ }
+ @container style(--number: 100) {
+ #original-text-number {
+ color: red;
+ }
+ }
+ @container style(--spaces: a b) {
+ #original-text-spaces {
+ color: green;
+ }
+ }
+ @container style(--number: a b) {
+ #original-text-spaces {
+ color: red;
+ }
+ }
+</style>
+<div id="original-text">
+ <div id="original-text-number"></div>
+ <div id="original-text-spaces"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#original-text-number")).color, green);
+ }, "Should only match exact string for numbers in non-registered custom properties");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#original-text-spaces")).color, green);
+ }, "Spaces should not collapse in non-registered custom properties");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-query-change.html b/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-query-change.html
new file mode 100644
index 0000000000..6669ede31d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/custom-property-style-query-change.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<title>CSS Container Queries Test: custom property style query changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#style-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container { container-name: my-container; }
+ #child, #grandchild { color: red; }
+ @container style(--target: child) {
+ #child { color: green; }
+ }
+ @container my-container style(--target: grandchild) {
+ #grandchild { color: green; }
+ }
+</style>
+<div id="container">
+ <div id="child"></div>
+ <div>
+ <div id="grandchild"></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+ const red = "rgb(255, 0, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(child).color, red);
+ assert_equals(getComputedStyle(grandchild).color, red);
+ }, "Initially no queries match.");
+
+ test(() => {
+ container.style.setProperty("--target", "child");
+ assert_equals(getComputedStyle(child).color, green);
+ assert_equals(getComputedStyle(grandchild).color, red);
+ }, "Target child");
+
+ test(() => {
+ container.style.setProperty("--target", "grandchild");
+ assert_equals(getComputedStyle(child).color, red);
+ assert_equals(getComputedStyle(grandchild).color, green);
+ }, "Target grandchild");
+</script>
+
+<style>
+ @property --length {
+ syntax: "<length>";
+ inherits: false;
+ initial-value: 0px;
+ }
+
+ #reg_container {
+ container-name: my-reg-container;
+ font-size: 50px;
+ }
+ #reg_child, #reg_grandchild { color: red; }
+ @container style(--length: 100px) {
+ #reg_child { color: green; }
+ }
+ @container my-reg-container style(--length: 200px) {
+ #reg_grandchild { color: green; }
+ }
+</style>
+<div id="reg_container">
+ <div id="reg_child"></div>
+ <div>
+ <div id="reg_grandchild"></div>
+ </div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(reg_child).color, red);
+ assert_equals(getComputedStyle(reg_grandchild).color, red);
+ }, "Initially no queries for registered property match.");
+
+ test(() => {
+ reg_container.style.setProperty("--length", "2em");
+ assert_equals(getComputedStyle(reg_child).color, green);
+ assert_equals(getComputedStyle(reg_grandchild).color, red);
+ }, "Registered property query child");
+
+ test(() => {
+ reg_container.style.setProperty("--length", "200px");
+ assert_equals(getComputedStyle(reg_child).color, red);
+ assert_equals(getComputedStyle(reg_grandchild).color, green);
+ }, "Registered property query grandchild");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/deep-nested-inline-size-containers.html b/testing/web-platform/tests/css/css-contain/container-queries/deep-nested-inline-size-containers.html
new file mode 100644
index 0000000000..00bc8b0a6b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/deep-nested-inline-size-containers.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries Test: Deeply nested inline-size container queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style id="test_style">
+ .container { container-type: inline-size; }
+ #outer { width: 200px; }
+</style>
+<div id="outer" class="container"></div>
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+
+ // Create 50 nested inline-size containers where a child container is 1px
+ // narrower than its parent
+ let sheet = test_style.sheet;
+ let container = outer;
+ for (let width = 200; width > 150; --width) {
+ sheet.insertRule(`
+ @container (width = ${width}px) {
+ .container { max-width: ${width-1}px; }
+ }
+ `);
+ let child = document.createElement("div");
+ child.className = "container";
+ container.appendChild(child);
+ container = child;
+ }
+ });
+
+ test(() => {
+ let expected_width = 200;
+ for (let container of document.querySelectorAll(".container"))
+ assert_equals(container.offsetWidth, expected_width--);
+ }, "Test that all container widths match");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-create.html b/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-create.html
new file mode 100644
index 0000000000..f72bb69f68
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-create.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>CSS Container Queries Test: ::backdrop appearing conditionally on dialog container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="top-layer-dialog-backdrop-ref.html">
+<style>
+ html {
+ /* Prevent multiple layout passes due to scrollbars */
+ overflow: hidden;
+ background: red;
+ }
+ dialog {
+ container-type: size;
+ width: 100px;
+ height: 100px;
+ visibility: hidden;
+ }
+ dialog::backdrop {
+ display: none;
+ background-color: green;
+ }
+ @container (width > 1px) {
+ dialog::backdrop {
+ display: block;
+ }
+ }
+</style>
+<dialog id="dialog"></dialog>
+<script>
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-remove.html b/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-remove.html
new file mode 100644
index 0000000000..47b4030492
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/dialog-backdrop-remove.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>CSS Container Queries Test: ::backdrop disappearing conditionally on dialog container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="top-layer-dialog-backdrop-ref.html">
+<style>
+ html {
+ /* Prevent multiple layout passes due to scrollbars */
+ overflow: hidden;
+ background: green;
+ }
+ dialog {
+ container-type: size;
+ width: 100px;
+ height: 100px;
+ visibility: hidden;
+ }
+ dialog::backdrop {
+ display: block;
+ background-color: red;
+ }
+ @container (width > 1px) {
+ dialog::backdrop {
+ display: none;
+ }
+ }
+</style>
+<dialog id="dialog"></dialog>
+<script>
+ dialog.showModal();
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/display-contents-dynamic-style-queries.html b/testing/web-platform/tests/css/css-contain/container-queries/display-contents-dynamic-style-queries.html
new file mode 100644
index 0000000000..782cf56655
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/display-contents-dynamic-style-queries.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>CSS Container Queries Test: Invalidate style queries and display:contents</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#style-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container.contents {
+ --foo: bar;
+ display: contents;
+ }
+ #target {
+ color: red;
+ }
+ @container style(--foo: bar) {
+ #target {
+ color: green;
+ }
+ }
+</style>
+<div id="container">
+ <div id="target">This should be green</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).color, "rgb(255, 0, 0)");
+ }, "Initially the color is red");
+
+ test(() => {
+ container.className = "contents";
+ assert_equals(getComputedStyle(target).color, "rgb(0, 128, 0)");
+ }, "After display and --foo changes, style() query causes the color to be green");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/display-contents.html b/testing/web-platform/tests/css/css-contain/container-queries/display-contents.html
new file mode 100644
index 0000000000..d96a46d06a
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/display-contents.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<title>@container and display:contents</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-size">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+<style>
+ .container {
+ container-type: inline-size;
+ width: 30px;
+ height: 30px;
+ background: tomato;
+ }
+ .big {
+ width: 50px;
+ height: 50px;
+ background: skyblue;
+ }
+ .contents {
+ display: contents;
+ }
+
+ @container (width: 30px) {
+ .target { --x:30; }
+ }
+
+ @container (width: 50px) {
+ .target { --x:50; }
+ }
+
+ main {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+</style>
+
+<main>
+ <!-- Container is display:contents -->
+ <div class="container contents">
+ <div>
+ <div class="target" id=target1></div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target1);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when container is display:contents');
+ </script>
+
+ <!-- Container becomes display:contents -->
+ <div id=container2 class="container">
+ <div>
+ <div class="target" id=target2></div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target2);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ container2.classList.add('contents');
+ assert_equals(s.getPropertyValue('--x'), '');
+ container2.classList.remove('contents');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when container becomes display:contents');
+ </script>
+
+ <!-- Intermediate container becomes display:contents -->
+ <div class="container">
+ <div>
+ <div id=container3 class="container">
+ <div>
+ <div class="target" id=target3></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target3);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ container3.classList.add('contents');
+ assert_equals(s.getPropertyValue('--x'), '');
+ container3.classList.remove('contents');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when intermediate container becomes display:contents');
+ </script>
+</main>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/display-in-container-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/display-in-container-ref.html
new file mode 100644
index 0000000000..fd8e9ef0c1
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/display-in-container-ref.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<style>
+ .container, .not_a_container {
+ width: auto;
+ height: 100px;
+ border: 1px solid black;
+ margin-bottom: 10px;
+ }
+ span {
+ border: 1px solid green;
+ margin: 2px;
+ }
+</style>
+<div style="width:150px">
+ <div class=container>
+ <main>
+ <div style="display:flex">
+ <span style="flex:1">Test1</span>
+ <span style="flex:1">Test2</span>
+ <span style="flex:1">Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
+<div style="width:200px">
+ <div class=container>
+ <main>
+ <div>
+ <span style="display:block">Test1</span>
+ <span style="display:block">Test2</span>
+ <span style="display:block">Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
+<div style="width:150px">
+ <div class=not_a_container>
+ <main>
+ <div>
+ <span>Test1</span>
+ <span>Test2</span>
+ <span>Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/display-in-container.html b/testing/web-platform/tests/css/css-contain/container-queries/display-in-container.html
new file mode 100644
index 0000000000..a2a4cd731c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/display-in-container.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container queries affecting display type</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="display-in-container-ref.html">
+<style>
+ .container, .not_a_container {
+ width: auto;
+ height: 100px;
+ border: 1px solid black;
+ margin-bottom: 10px;
+ }
+ .container {
+ container-type: size;
+ }
+ span {
+ border: 1px solid green;
+ margin: 2px;
+ }
+
+ /* Note: 150px - 2px, since .container has a 1px border */
+ @container (min-width: 148px) {
+ div { display: flex; }
+ span { flex: 1; }
+ }
+
+ /* Note: 200px - 2px, since .container has a 1px border */
+ @container (min-width: 198px) {
+ div { display: revert; }
+ span { display: block; }
+ }
+
+ /* Should not apply: */
+ @container (min-width: 199px) {
+ * { color: red; background-color: red; }
+ }
+</style>
+<div style="width:150px">
+ <div class=container>
+ <main>
+ <div>
+ <span>Test1</span>
+ <span>Test2</span>
+ <span>Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
+<div style="width:200px">
+ <div class=container>
+ <main>
+ <div>
+ <span>Test1</span>
+ <span>Test2</span>
+ <span>Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
+<div style="width:150px">
+ <div class=not_a_container>
+ <main>
+ <div>
+ <span>Test1</span>
+ <span>Test2</span>
+ <span>Test3</span>
+ </div>
+ </main>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/display-none.html b/testing/web-platform/tests/css/css-contain/container-queries/display-none.html
new file mode 100644
index 0000000000..8d07ec09dd
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/display-none.html
@@ -0,0 +1,393 @@
+<!doctype html>
+<title>@container in display:none</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-size">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+<style>
+ .container {
+ container-type: size;
+ width: 30px;
+ height: 30px;
+ background: tomato;
+ }
+ .small {
+ width: 10px;
+ height: 10px;
+ }
+ .big {
+ width: 50px;
+ height: 50px;
+ background: skyblue;
+ }
+ .auto {
+ width: auto;
+ }
+ .none {
+ display: none;
+ }
+ .pseudo::before {
+ content: "foo";
+ }
+ .pseudo_none::before {
+ content: "foo";
+ display: none;
+ }
+
+ @container (width: 30px) {
+ .target { --x:30; }
+ }
+
+ @container (width: 50px) {
+ .target { --x:50; }
+ }
+
+ main {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+</style>
+
+<main>
+ <!-- Target element is display:none -->
+ <div class="container">
+ <div>
+ <div>
+ <div class="target none" id=target1></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target1);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when element is display:none');
+ </script>
+
+ <!-- Parent is display:none -->
+ <div class="container">
+ <div>
+ <div class="none">
+ <div class="target" id=target2></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target2);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when parent is display:none');
+ </script>
+
+ <!-- Ancestor is display:none -->
+ <div class="container">
+ <div class="none">
+ <div>
+ <div class="target" id=target3></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target3);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when ancestor is display:none');
+ </script>
+
+ <!-- Container is display:none -->
+ <div class="container none">
+ <div>
+ <div>
+ <div class="target" id=target4></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target4);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when container is display:none');
+ </script>
+
+ <!-- Target element is display:none in nested container -->
+ <div class="container big">
+ <div>
+ <div>
+ <div class="container">
+ <div>
+ <div>
+ <div class="target none" id=target5></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target5);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when element in nested container is display:none');
+ </script>
+
+ <!-- Inner container is display:none -->
+ <div class="container big">
+ <div>
+ <div>
+ <div class="container none">
+ <div>
+ <div>
+ <div class="target" id=target6></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target6);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when inner container is display:none');
+ </script>
+
+ <!-- Intermediate ancestor is display:none -->
+ <div class="container big">
+ <div class="none">
+ <div>
+ <div class="container">
+ <div>
+ <div>
+ <div class="target" id=target7></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target7);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when intermediate ancestor is display:none');
+ </script>
+
+ <!-- Outer container is display:none -->
+ <div class="container big none">
+ <div>
+ <div>
+ <div class="container">
+ <div>
+ <div>
+ <div class="target" id=target8></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target8);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when outer container is display:none');
+ </script>
+
+ <!-- Nothing is display:none initially, but target becomes display:none -->
+ <div class="container">
+ <div>
+ <div>
+ <div class="target" id=target9></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target9);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ target9.classList.add('none');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when element becomes display:none');
+ </script>
+
+ <!-- Nothing is display:none initially, but parent becomes display:none -->
+ <div class="container">
+ <div>
+ <div id=parent10>
+ <div class="target" id=target10></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target10);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ parent10.classList.add('none');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when parent becomes display:none');
+ </script>
+
+ <!-- Nothing is display:none initially, but ancestor becomes display:none -->
+ <div class="container">
+ <div id=ancestor11>
+ <div>
+ <div class="target" id=target11></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target11);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ ancestor11.classList.add('none');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when ancestor becomes display:none');
+ </script>
+
+ <!-- Nothing is display:none initially, but container becomes display:none -->
+ <div class="container" id=container12>
+ <div>
+ <div>
+ <div class="target" id=target12></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target12);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ container12.classList.add('none');
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when container becomes display:none');
+ </script>
+
+ <!-- Intermediate container becomes display:none -->
+ <div class="container big">
+ <div>
+ <div>
+ <div class="container" id=container13>
+ <div>
+ <div>
+ <div class="target" id=target13></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target13);
+ assert_equals(s.getPropertyValue('--x'), '30');
+ container13.classList.add('none');
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when intermediate container becomes display:none');
+ </script>
+
+ <!-- Pseudo-element is display:none -->
+ <div class="container">
+ <div>
+ <div>
+ <div class="target pseudo_none" id=target14></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target14, '::before');
+ assert_equals(s.getPropertyValue('content'), '"foo"');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when ::before is display:none');
+ </script>
+
+ <!-- Pseudo-element with display:none originating element -->
+ <div class="container">
+ <div>
+ <div>
+ <div class="target pseudo none" id=target15></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target15, '::before');
+ assert_equals(s.getPropertyValue('content'), '"foo"');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle when originating element is display:none');
+ </script>
+
+ <!-- Pseudo-element with display:none ancestor -->
+ <div class="container">
+ <div class="none">
+ <div>
+ <div class="target pseudo" id=target16></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target16, '::before');
+ assert_equals(s.getPropertyValue('content'), '"foo"');
+ assert_equals(s.getPropertyValue('--x'), '30');
+ }, 'getComputedStyle on ::before when ancestor element is display:none');
+ </script>
+
+ <!-- Pseudo-element with in display:none container -->
+ <div class="container none">
+ <div>
+ <div>
+ <div class="target pseudo" id=target17></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ let s = getComputedStyle(target17, '::before');
+ assert_equals(s.getPropertyValue('content'), '"foo"');
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle on ::before when container is display:none');
+ </script>
+
+ <!-- Target in display:none with layout dirty outer element -->
+ <div class=small id="outer18">
+ <div class="container auto">
+ <div class="none">
+ <div>
+ <div class="target" id=target18></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ target18.offsetTop;
+ let s = getComputedStyle(target18);
+ assert_equals(s.getPropertyValue('--x'), '');
+
+ outer18.classList.remove('small');
+ outer18.classList.add('big');
+ assert_equals(s.getPropertyValue('--x'), '50');
+ }, 'getComputedStyle when in display:none with layout dirty outer element');
+ </script>
+
+ <!-- Intermediate container has forced style -->
+ <div class="container">
+ <div class="none">
+ <div id="inner19" class="container">
+ <div id="target19" class="target"></div>
+ </div>
+ </div>
+ </div>
+ <script>
+ test(function() {
+ getComputedStyle(inner19).getPropertyValue('--x');
+ let s = getComputedStyle(target19);
+ assert_equals(s.getPropertyValue('--x'), '');
+ }, 'getComputedStyle when display:none inner container has forced style');
+ </script>
+</main>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change-ref.html
new file mode 100644
index 0000000000..b6e8dc6038
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>Pass if the rendered legend below is "PASS"</p>
+<fieldset style="width:400px"><legend>PASS</legend></fieldset>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change.html b/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change.html
new file mode 100644
index 0000000000..15b44a0e52
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/fieldset-legend-change.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>CSS Container Queries Test: inline-size query changes rendered legend in fieldset</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="fieldset-legend-change-ref.html">
+<p>Pass if the rendered legend below is "PASS"</p>
+<style>
+ fieldset {
+ width: 200px;
+ container-type: inline-size;
+ }
+ .wide { width: 400px; }
+
+ @container (min-width: 300px) {
+ #fail {
+ display: none;
+ }
+ }
+</style>
+<fieldset id="fieldset">
+ <legend id="fail">FAIL</legend>
+ <legend>PASS</legend>
+</fieldset>
+<script>
+ fieldset.offsetTop;
+ fieldset.className = "wide";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/font-relative-calc-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-calc-dynamic.html
new file mode 100644
index 0000000000..54f01d45a3
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-calc-dynamic.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>CSS Container Queries Test: font-relative calc - dynamic</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ body { font-size: 10px; }
+ body.larger { font-size: 20px; }
+ #container {
+ container-type: inline-size;
+ width: 100px;
+ color: red;
+ }
+ #intermediate {
+ font-size: 8px;
+ }
+ @container (width: calc(1em + 80px)) {
+ #target { color: green; }
+ }
+</style>
+<div id="container">
+ <div id=intermediate>
+ <div id="target"></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target).color, 'rgb(255, 0, 0)');
+ document.body.className = 'larger';
+ assert_equals(getComputedStyle(target).color, 'rgb(0, 128, 0)');
+ }, 'font-relative calc() is responsive to container font-size changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units-dynamic.html
new file mode 100644
index 0000000000..63a07c61db
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units-dynamic.html
@@ -0,0 +1,280 @@
+<!doctype html>
+<title>CSS Container Queries Test: font-relative units - dynamic</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+
+setup(() => assert_implements_container_queries());
+
+// Inflate a <template> subtree into #main, run the test function,
+// then clean up.
+function test_template(template_element, test_fn, description) {
+ test((t) => {
+ assert_equals(template_element.tagName, "TEMPLATE");
+ t.add_cleanup(() => main.replaceChildren());
+ main.append(template_element.content.cloneNode(true));
+ test_fn(t);
+ }, description);
+}
+
+const green = "rgb(0, 128, 0)";
+const red = "rgb(255, 0, 0)";
+
+</script>
+
+<style>
+ main {
+ color: red;
+ }
+ #container {
+ container-type: inline-size;
+ width: 100px;
+ }
+ #container > div {
+ font-size: 16px;
+ }
+</style>
+
+<main id=main>
+</main>
+
+<template>
+ <style>
+ main { font-size: 10px; }
+ main.larger { font-size: 20px; }
+ @container (width: 5em) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => main.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ main.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'em units respond to changes');
+</script>
+
+<template>
+ <style>
+ :root { font-size: 10px; }
+ :root.larger { font-size: 50px; }
+ @container (width: 2rem) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => document.documentElement.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ document.documentElement.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'rem units respond to changes');
+</script>
+
+<template>
+ <style>
+ main { font-size: 10px; }
+ main.larger { font-size: 20px; }
+ @container (width <= 15ex) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => main.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ main.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'ex units respond to changes');
+</script>
+
+<template>
+ <style>
+ :root { font-size: 10px; }
+ :root.larger { font-size: 20px; }
+ @container (width <= 12rex) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => document.documentElement.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ document.documentElement.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'rex units respond to changes');
+</script>
+
+<template>
+ <style>
+ main { font-size: 10px; }
+ main.larger { font-size: 20px; }
+ @container (width <= 15ch) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => main.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ main.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'ch units respond to changes');
+</script>
+
+<template>
+ <style>
+ :root { font-size: 10px; }
+ :root.larger { font-size: 20px; }
+ @container (width <= 15rch) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => document.documentElement.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ document.documentElement.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'rch units respond to changes');
+</script>
+
+<template>
+ <style>
+ main {
+ font-size: 10px;
+ line-height: 5;
+ }
+ main.larger { font-size: 20px; }
+ @container (width <= 1lh) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => main.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ main.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'lh units respond to changes');
+</script>
+
+<template>
+ <style>
+ :root {
+ font-size: 10px;
+ line-height: 5;
+ }
+ :root.larger {
+ font-size: 20px;
+ }
+ @container (width <= 1rlh) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => document.documentElement.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ document.documentElement.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'rlh units respond to changes');
+</script>
+
+<template>
+ <style>
+ main { font-size: 10px; }
+ main.larger { font-size: 20px; }
+ @container (width <= 8ic) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => main.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ main.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'ic units respond to changes');
+</script>
+
+
+<template>
+ <style>
+ :root { font-size: 10px; }
+ :root.larger { font-size: 20px; }
+ @container (width <= 8ric) {
+ #test { color: green }
+ }
+ </style>
+ <div id="container">
+ <div>
+ <div id="test"></div>
+ </div>
+ </div>
+</template>
+<script>
+test_template(document.currentScript.previousElementSibling, (t) => {
+ t.add_cleanup(() => document.documentElement.classList.remove("larger"));
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, red);
+ document.documentElement.classList.add("larger");
+ assert_equals(getComputedStyle(main.querySelector("#test")).color, green);
+}, 'ric units respond to changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units.html b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units.html
new file mode 100644
index 0000000000..7f711ebf96
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/font-relative-units.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<title>CSS Container Queries Test: font-relative units</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ :root { font-size: 10px; line-height: 10px; }
+ #em_container {
+ container-type: inline-size;
+ width: 100px;
+ font-size: 100px;
+ }
+ #ex_container {
+ container-type: inline-size;
+ font-size: 50px;
+ width: 10ex;
+ height: 50rex;
+ }
+ #ch_container {
+ container-type: inline-size;
+ font-size: 50px;
+ width: 10ch;
+ }
+ #ic_container {
+ container-type: inline-size;
+ font-size: 50px;
+ width: 10ic;
+ }
+ #lh_container {
+ container-type: inline-size;
+ line-height: 50px;
+ width: 10lh;
+ }
+ @container (width: 1em) {
+ #em_test { color: green }
+ }
+ @container (width: 10rem) {
+ #rem_test { color: green }
+ }
+ @container (width: 10ex) {
+ #ex_test { color: green }
+ }
+ @container (49rex <= width <= 100rex) {
+ #rex_test { color: green }
+ }
+ @container (width: 10ch) {
+ #ch_test { color: green }
+ }
+ @container (width: 50rch) {
+ #rch_test { color: green }
+ }
+ @container (width: 10ic) {
+ #ic_test { color: green }
+ }
+ @container (width: 50ric) {
+ #ric_test { color: green }
+ }
+ @container (width: 10lh) {
+ #lh_test { color: green }
+ }
+ @container (width: 50rlh) {
+ #rlh_test { color: green }
+ }
+</style>
+<div id="em_container">
+ <div id="em_test"></div>
+ <div id="rem_test"></div>
+</div>
+<div id="ex_container">
+ <div id="ex_test"></div>
+ <div id="rex_test"></div>
+</div>
+<div id="ch_container">
+ <div id="ch_test"></div>
+ <div id="rch_test"></div>
+</div>
+<div id="ic_container">
+ <div id="ic_test"></div>
+ <div id="ric_test"></div>
+</div>
+<div id="lh_container">
+ <div id="lh_test"></div>
+ <div id="rlh_test"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+ test(() => assert_equals(getComputedStyle(em_test).color, green), "em relative inline-size");
+ test(() => assert_equals(getComputedStyle(rem_test).color, green), "rem relative inline-size");
+ test(() => assert_equals(getComputedStyle(ex_test).color, green), "ex relative inline-size");
+ test(() => assert_equals(getComputedStyle(rex_test).color, green), "rex relative inline-size");
+ test(() => assert_equals(getComputedStyle(ch_test).color, green), "ch relative inline-size");
+ test(() => assert_equals(getComputedStyle(rch_test).color, green), "rch relative inline-size");
+ test(() => assert_equals(getComputedStyle(ic_test).color, green), "ic relative inline-size");
+ test(() => assert_equals(getComputedStyle(ric_test).color, green), "ric relative inline-size");
+ test(() => assert_equals(getComputedStyle(lh_test).color, green), "lh relative inline-size");
+ test(() => assert_equals(getComputedStyle(rlh_test).color, green), "rlh relative inline-size");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/fragmented-container-001.html b/testing/web-platform/tests/css/css-contain/container-queries/fragmented-container-001.html
new file mode 100644
index 0000000000..886f179054
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/fragmented-container-001.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<title>CSS Container Queries Test: Query fragmented inline-size container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #multicol {
+ width: 400px;
+ column-count: 2;
+ column-fill: auto;
+ column-gap: 0;
+ height: 100px;
+ }
+ #float {
+ float: left;
+ width: 100px;
+ height: 50px;
+ }
+ #container {
+ container-type: inline-size;
+ display: flow-root;
+ height: 200px;
+ }
+ #first-child {
+ break-after: column;
+ }
+ @container (width = 100px) {
+ #first-child { color: green; }
+ #second-child { color: green; }
+ }
+</style>
+<div id="multicol">
+ <div id="float"></div>
+ <div id="container">
+ <div id="first-child"></div>
+ <div id="second-child"></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ const green = "rgb(0, 128, 0)";
+ assert_equals(getComputedStyle(document.querySelector("#first-child")).color, green);
+ assert_equals(getComputedStyle(document.querySelector("#second-child")).color, green);
+ }, "Children of fragmented inline-size container should match inline-size of first fragment");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/get-animations.html b/testing/web-platform/tests/css/css-contain/container-queries/get-animations.html
new file mode 100644
index 0000000000..dca41c6ada
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/get-animations.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>getAnimations depending on container query</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: inline-size;
+ width: 100px;
+ }
+ #div { color: red; }
+ @keyframes test {
+ from { color: green; }
+ to { color: green; }
+ }
+ @container (min-width: 200px) {
+ #div { animation: test 1s linear forwards; }
+ }
+</style>
+<div id=container>
+ <div id=div>Green</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(div).color, 'rgb(255, 0, 0)');
+
+ container.style = 'width:300px';
+ assert_equals(div.getAnimations().length, 1);
+ assert_equals(getComputedStyle(div).color, 'rgb(0, 128, 0)');
+ }, 'Calling getAnimations updates layout of parent frame if needed');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/grid-container.html b/testing/web-platform/tests/css/css-contain/container-queries/grid-container.html
new file mode 100644
index 0000000000..60278e09c6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/grid-container.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Container Queries Test: Grid container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #grid {
+ display: grid;
+ container-type: inline-size;
+ width: 400px;
+ grid-template-columns: 1fr 1fr;
+ }
+ @container (width = 400px) {
+ #grid div { color: green }
+ }
+</style>
+<div id="grid">
+ <div id="item1"></div>
+ <div id="item2"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(item1).color, "rgb(0, 128, 0)");
+ assert_equals(getComputedStyle(item2).color, "rgb(0, 128, 0)");
+ }, "Check that grid items can query grid container");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/grid-item-container.html b/testing/web-platform/tests/css/css-contain/container-queries/grid-item-container.html
new file mode 100644
index 0000000000..f1c66efc26
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/grid-item-container.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries Test: Grid item container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #grid {
+ width: 300px;
+ display: grid;
+ grid-template-columns: 2fr 1fr;
+ }
+ .item {
+ container-type: inline-size;
+ }
+ @container (width > 50px) {
+ .item div { color: lime; }
+ }
+ @container (width > 150px) {
+ .item div { color: green; }
+ }
+</style>
+<div id="grid">
+ <div class="item">
+ <div id="target1"></div>
+ </div>
+ <div class="item">
+ <div id="target2"></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target1).color, "rgb(0, 128, 0)", "First item container should be 200px wide");
+ assert_equals(getComputedStyle(target2).color, "rgb(0, 255, 0)", "Second item container should be 100px wide");
+ }, "Check that children can query grid item containers");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/idlharness.html b/testing/web-platform/tests/css/css-contain/container-queries/idlharness.html
new file mode 100644
index 0000000000..ac1a677bb9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/idlharness.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>CSS Container Queries: CSSContainer Rule IDL tests</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+<!-- used to provide objects -->
+<style>
+ @container cont (width = 100px) {
+ @container (inline-size > 200em) {
+ #id { color: lime }
+ }
+ #id { color: green }
+ }
+</style>
+<script>
+ idl_test(
+ ['css-contain-3'],
+ ['css-conditional', 'cssom', 'dom'],
+ idl_array => {
+ idl_array.add_objects({
+ CSSContainerRule: ['sheet.cssRules[0]',
+ 'sheet.cssRules[0].cssRules[0]'],
+ });
+ self.sheet = document.styleSheets[0];
+ }
+ );
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/iframe-in-container-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/iframe-in-container-invalidation.html
new file mode 100644
index 0000000000..f43d1ce789
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/iframe-in-container-invalidation.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>@container-dependent elements respond to size changes of an @container-dependent iframe</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 200px;
+ }
+ iframe {
+ width: 200px;
+ height: 40px;
+ }
+ @container (width > 300px) {
+ iframe { width: 400px; }
+ }
+</style>
+<div id=container>
+ <iframe id=iframe srcdoc="
+ <style>
+ div#container {
+ container-type: size;
+ height: 20px;
+ }
+ div#child { color: red; }
+ @container (width > 300px) {
+ div#child { color: green; }
+ }
+ </style>
+ <div id=container>
+ <div id=child>Test</div>
+ </div>
+ "></iframe>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ let inner_div = iframe.contentDocument.querySelector('div#child');
+ assert_equals(getComputedStyle(inner_div).color, 'rgb(255, 0, 0)');
+
+ // Changing the size of the outer container changes the size of the iframe,
+ // which in turn should change the size of the inner container (inside that
+ // iframe).
+ container.style.width = '400px';
+ container.style.setProperty('--x', 'x'); // crbug.com/1312940
+
+ assert_equals(getComputedStyle(inner_div).color, 'rgb(0, 128, 0)');
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/iframe-invalidation.html b/testing/web-platform/tests/css/css-contain/container-queries/iframe-invalidation.html
new file mode 100644
index 0000000000..51f2be9cfa
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/iframe-invalidation.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<title>@container-dependent elements respond to iframe size changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ iframe {
+ width: 200px;
+ height: 40px;
+ }
+</style>
+<iframe id=iframe srcdoc="
+ <style>
+ div#container {
+ container-type: size;
+ height: 20px;
+ }
+ div#child { color: red; }
+ @container (min-width: 300px) {
+ div#child { color: green; }
+ }
+ </style>
+ <div id=container>
+ <div id=child>Test</div>
+ </div>
+"></iframe>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ let inner_div = iframe.contentDocument.querySelector('div#child');
+ assert_equals(getComputedStyle(inner_div).color, 'rgb(255, 0, 0)');
+
+ iframe.style = 'width:400px';
+ assert_equals(getComputedStyle(inner_div).color, 'rgb(0, 128, 0)');
+ })
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/ineligible-containment.html b/testing/web-platform/tests/css/css-contain/container-queries/ineligible-containment.html
new file mode 100644
index 0000000000..36ce68d864
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/ineligible-containment.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<title>Containers ineligible for containment</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-size">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer, #inner1, #inner2 {
+ width: 200px;
+ container-type: inline-size;
+ }
+ #inner1 {
+ display: table;
+ }
+ p {
+ color: green;
+ }
+ @container (min-width: 1px) {
+ p { color: red; }
+ }
+</style>
+<div id=outer>
+ <div id=inner1>
+ <p id=p1>Test1</p>
+ </div>
+ <div id=inner2>
+ <p id=p2>Test1</p>
+ </div>
+</main>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function(t) {
+ // #inner1 is the container, but it does not satisfy the containment
+ // requirements, hence the query should fail.
+ assert_equals(getComputedStyle(p1).color, 'rgb(0, 128, 0)');
+ }, 'Container ineligible for containment');
+
+ test(function(t) {
+ t.add_cleanup(() => { inner2.style = ''; });
+
+ assert_equals(getComputedStyle(p2).color, 'rgb(255, 0, 0)');
+
+ inner2.style = 'display:table';
+
+ // #inner2 is still the container, but it no longer satisfies the
+ // containment requirements.
+ assert_equals(getComputedStyle(p2).color, 'rgb(0, 128, 0)');
+ }, 'Changing containment eligibility invalidates style');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inline-size-and-min-width.html b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-and-min-width.html
new file mode 100644
index 0000000000..8ddcbc614c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-and-min-width.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>CSS Container Queries Test: query of inline-size container is affected by min-width property</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: inline-size;
+ min-width: 200px;
+ width: fit-content;
+ }
+ @container (min-width: 200px) {
+ #child { color: green }
+ }
+</style>
+<div id="container">
+ <div id="child">Green</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(child).color, "rgb(0, 128, 0)");
+ }, "min-width of inline-size container affects container size");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats-ref.html
new file mode 100644
index 0000000000..ecd72b7516
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats-ref.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>You should see the text "no red" to the left of the third float and no red.</p>
+<div style="width:400px">
+ <div style="float:right;width:200px;height:150px;background:blue"></div>
+ <div style="float:left;width:250px;height:100px;background:blue"></div>
+ <div style="float:right;width:300px;height:100px;background:blue"></div>
+ <div style="height:200px;display:flow-root">No red</div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats.html b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats.html
new file mode 100644
index 0000000000..88b81c6759
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-bfc-floats.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>CSS Container Queries Test: inline-size constrained by floats - layout moving forwards</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#containment-inline-size">
+<link rel="match" href="inline-size-bfc-floats-ref.html">
+<style>
+ .float { float: left; background-color: blue; }
+ .right { float: right; }
+
+ #outer { width: 400px; }
+ #float1 { width: 200px; height: 150px; }
+ #float2 { width: 250px; height: 100px; }
+ #float3 { width: 300px; height: 100px; }
+
+ #container { container-type: inline-size; }
+
+ /* Initially, text + 200px of red content (#content1 + #content2) is too tall
+ to make #container fit by #float1 */
+ .content { height: 100px; background-color: red; }
+
+ /* Trying to fit #container beside #float2 causes the width to remove
+ #content1. text + 100px of red content (#content2) is too tall to fit
+ beside #float2. It would at this point fit beside #float1, but that would
+ cause the width to increase again, and the spec says layout always moves
+ forward. */
+ @container (width < 200px) {
+ #content1 { display: none }
+ }
+
+ /* Trying to fit #container beside #float3 causes the rest of the red content
+ (#content2) to disappear. */
+ @container (width < 150px) {
+ #content2 { display: none }
+ }
+</style>
+<p>You should see the text "no red" to the left of the third float and no red.</p>
+<div id="outer">
+ <div id="float1" class="float right"></div>
+ <div id="float2" class="float left"></div>
+ <div id="float3" class="float right"></div>
+
+ <div id="container">
+ No red
+ <div id="content1" class="content"></div>
+ <div id="content2" class="content"></div>
+ </div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment-vertical-rl.html b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment-vertical-rl.html
new file mode 100644
index 0000000000..38c88f2df7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment-vertical-rl.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries Test: query of inline-size container in vertical-rl</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+ #ancestry { writing-mode: vertical-rl; }
+ #keg { container-type: inline-size; }
+ @container (max-height: 200px) {
+ #target { width: 400px; }
+ }
+ @container (min-height: 400px) {
+ #target { width: 20px; }
+ }
+</style>
+<div id="ancestry">
+ <div id="keg">
+ <div id="target">
+ <div style="width:50px;"></div>
+ </div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(()=> {
+ ancestry.style.height = "100px";
+ assert_equals(keg.offsetWidth, 400);
+
+ ancestry.style.height = "300px";
+ assert_equals(keg.offsetWidth, 50);
+
+ ancestry.style.height = "500px";
+ assert_equals(keg.offsetWidth, 20);
+ }, "inline-size containment only");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment.html b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment.html
new file mode 100644
index 0000000000..d519322bd2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inline-size-containment.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>CSS Container Queries Test: query of inline-size container is affected by min-width property</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+ #keg { container-type: inline-size; }
+ @container (max-width: 200px) {
+ #target { height: 400px; }
+ }
+ @container (min-width: 400px) {
+ #target { height: 20px; }
+ }
+</style>
+<div id="ancestry">
+ <div id="keg">
+ <div id="target">
+ <div style="height:50px;"></div>
+ </div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(()=> {
+ ancestry.style.width = "100px";
+ assert_equals(keg.offsetHeight, 400);
+
+ ancestry.style.width = "300px";
+ assert_equals(keg.offsetHeight, 50);
+
+ ancestry.style.width = "500px";
+ assert_equals(keg.offsetHeight, 20);
+ }, "inline-size containment only");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching-ref.html
new file mode 100644
index 0000000000..99e9c334bf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<div style="color:green">This text should be green.</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching.html b/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching.html
new file mode 100644
index 0000000000..5f39124e51
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/inner-first-line-non-matching.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<title>CSS Container Queries Test: Non-matching ::first-line in @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="inner-first-line-non-matching-ref.html">
+<style>
+ #outer::first-line { color: green }
+ @container (width > 99999px) {
+ #inner::first-line { color: red }
+ }
+</style>
+<div id="outer">
+ <div id="inner">This text should be green.</div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/layout-dependent-focus.html b/testing/web-platform/tests/css/css-contain/container-queries/layout-dependent-focus.html
new file mode 100644
index 0000000000..a16370ac56
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/layout-dependent-focus.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<title>CSS Container Queries: Input losing focus as a result of a size query</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: inline-size;
+ width: 200px;
+ }
+ #container.narrow {
+ width: 100px;
+ }
+ @container (width = 100px) {
+ #inner.hide { visibility: hidden; }
+ }
+</style>
+<div id="outer">
+ <div id="container">
+ <input type="text" id="inner">
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ let hide_test = async_test("Verify that onblur is called on hidden input");
+ onload = () => {
+ inner.addEventListener("blur", () => hide_test.done());
+ inner.focus();
+ inner.className = "hide";
+ container.className = "narrow";
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ hide_test.step(() => assert_unreached("Event listener for 'blur' not called"));
+ });
+ });
+ };
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/multicol-container-001.html b/testing/web-platform/tests/css/css-contain/container-queries/multicol-container-001.html
new file mode 100644
index 0000000000..3032170ac6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/multicol-container-001.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>CSS Container Queries Test: Query multicol container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #multicol {
+ container-type: inline-size;
+ width: 400px;
+ column-count: 2;
+ column-gap: 0;
+ }
+ @container (width = 400px) {
+ #first-child { color: green; }
+ #second-child { color: green; }
+ }
+</style>
+<div id="multicol">
+ <div id="first-child">First</div>
+ <div id="second-child">Second</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ const green = "rgb(0, 128, 0)";
+ assert_equals(getComputedStyle(document.querySelector("#first-child")).color, green);
+ assert_equals(getComputedStyle(document.querySelector("#second-child")).color, green);
+ }, "Children of multicol inline-size container should match inline-size of the container");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/multicol-inside-container.html b/testing/web-platform/tests/css/css-contain/container-queries/multicol-inside-container.html
new file mode 100644
index 0000000000..9fc8393a51
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/multicol-inside-container.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>CSS Container Queries Test: Multicol inside size container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<style>
+ #container {
+ container-type: size;
+ width: 200px;
+ height: 100px;
+ }
+ @container (width <= 200px) {
+ #multicol {
+ column-count: 2;
+ column-gap: 0;
+ }
+ }
+ #green {
+ display: inline-block;
+ width: 100%;
+ height: 100px;
+ background-color: green;
+ vertical-align: bottom;
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="container">
+ <div id="multicol"><div id="green"></div></div>
+</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/nested-query-containers.html b/testing/web-platform/tests/css/css-contain/container-queries/nested-query-containers.html
new file mode 100644
index 0000000000..83cc3c2fec
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/nested-query-containers.html
@@ -0,0 +1,125 @@
+<!DOCTYPE html>
+<title>Nested query containers affecting each other</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-size">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ body > section {
+ contain: strict;
+ width: 500px;
+ }
+</style>
+<body>
+<script>
+promise_setup(() => {
+ assert_implements_container_queries();
+ return new Promise(resolve => {
+ addEventListener("load", () => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.body.className = "run";
+ resolve();
+ });
+ });
+ }, {once: true});
+ });
+});
+
+function booleanTuples(n) {
+ const tuple = new Array(n);
+ function* recursion(i) {
+ if (i == n) {
+ yield tuple.slice();
+ return;
+ }
+ tuple[i] = false;
+ yield* recursion(i + 1);
+ tuple[i] = true;
+ yield* recursion(i + 1);
+ }
+ return recursion(0);
+}
+
+// The following display values evaluate container queries to unknown.
+const testCases = [
+ {
+ display: "inline",
+ expected: {
+ width: depth => depth % 2 ? 0 : 500 - depth,
+ height: depth => 0,
+ },
+ },
+ {
+ display: "contents",
+ expected: {
+ width: depth => depth % 2 ? 0 : 500 - depth,
+ height: depth => 0,
+ },
+ },
+ {
+ display: "table-cell",
+ expected: {
+ width: depth => depth % 2 ? 2 : 0,
+ height: depth => depth % 2 ? 2 : 0,
+ },
+ },
+ {
+ display: "table",
+ expected: {
+ width: depth => depth % 2 ? 4 : 0,
+ height: depth => depth % 2 ? 4 : 0,
+ },
+ },
+];
+
+let testNum = 1;
+for (let testCase of testCases) {
+ for (let tuple of booleanTuples(3)) {
+ const section = document.createElement("section");
+ const id = "test" + testNum;
+ section.id = id;
+ const style = document.createElement("style");
+ style.textContent = `
+ :where(body${tuple[0] ? ".run" : ""}) > #${id} {
+ container-type: size;
+ container-name: name;
+ }
+ :where(body${tuple[1] ? ".run" : ""}) > #${id} div {
+ container-type: size;
+ container-name: name;
+ border: solid;
+ border-width: 1px;
+ }
+ @container name (width >= 0) {
+ :where(body${tuple[2] ? ".run" : ""}) > #${id} div {
+ display: ${testCase.display};
+ border-style: dotted;
+ }
+ }
+ `;
+ section.appendChild(style);
+ section.insertAdjacentHTML(
+ "beforeend",
+ "<div><div><div><div><div><div></div></div></div></div></div></div>"
+ );
+ document.body.appendChild(section);
+ promise_test(async function() {
+ let div = section.querySelector("div");
+ let depth = 1;
+ while (div) {
+ const cs = getComputedStyle(div);
+ assert_equals(cs.display, depth % 2 ? testCase.display : "block");
+ assert_equals(cs.borderStyle, depth % 2 ? "dotted" : "solid", "borderStyle");
+ assert_equals(div.clientWidth, testCase.expected.width(depth), "clientWidth");
+ assert_equals(div.clientHeight, testCase.expected.height(depth), "clientHeight");
+ div = div.firstElementChild;
+ depth += 1;
+ }
+ }, id + " - " + testCase.display + " - 0b" + tuple.map(Number).join(""));
+ testNum += 1;
+ }
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/never-match-container.html b/testing/web-platform/tests/css/css-contain/container-queries/never-match-container.html
new file mode 100644
index 0000000000..9d5ff6d227
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/never-match-container.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container querying size of elements without layout containment</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+<style>
+ #outer-container {
+ width: 100px;
+ container-type: inline-size;
+ }
+ #container-inline, #svg-container {
+ width: 100px;
+ container-type: inline-size;
+ }
+ @container (width >= 0px) {
+ #inner { color: red; }
+ #svg-inner { fill: red; }
+ }
+</style>
+<div id="outer-container">
+ <span id="container-inline">
+ <span id="inner">Not red</span>
+ </span>
+ <svg>
+ <g id="svg-container">
+ <text x="0" y="20" id="svg-inner">Not red</text>
+ </g>
+ </svg>
+</div>
+<script>
+ const red = "rgb(255, 0, 0)";
+
+ test(() => {
+ assert_not_equals(getComputedStyle(inner).color, red);
+ }, "Size @container query against inline box never matches");
+
+ test(() => {
+ assert_not_equals(getComputedStyle(document.querySelector("#svg-inner")).fill, red);
+ }, "Size @container query against svg element never matches");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/orthogonal-wm-container-query.html b/testing/web-platform/tests/css/css-contain/container-queries/orthogonal-wm-container-query.html
new file mode 100644
index 0000000000..1ad52bf499
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/orthogonal-wm-container-query.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries Test: Orthogonal writing-mode change in @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<link rel="stylesheet" href="/fonts/ahem.css">
+<style>
+ #container {
+ container-type: size;
+ width: 50vw;
+ height: 50vh;
+ }
+ #orthogonal {
+ font: 50px/1 Ahem;
+ }
+ @container (max-width: 100px) {
+ #orthogonal {
+ writing-mode: vertical-lr;
+ }
+ }
+</style>
+<div id="container">
+ <div id="orthogonal">XX</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(orthogonal.offsetWidth, container.offsetWidth);
+ }, "Initial non-orthogonal width");
+
+ test(() => {
+ container.style.width = "100px";
+ assert_equals(orthogonal.offsetWidth, 50);
+ assert_not_equals(orthogonal.offsetWidth, container.offsetWidth);
+ }, "Orthogonal width");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/percentage-padding-orthogonal.html b/testing/web-platform/tests/css/css-contain/container-queries/percentage-padding-orthogonal.html
new file mode 100644
index 0000000000..0c419d18a9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/percentage-padding-orthogonal.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container queries affecting height affecting percentage padding</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #vertical {
+ width: 500px;
+ writing-mode: vertical-lr;
+ }
+
+ #padded {
+ width: 100%;
+ box-sizing: border-box;
+ padding-right: 100%;
+ background-color: orange;
+ }
+
+ #horizontal {
+ writing-mode: horizontal-tb;
+ width: 100%;
+ }
+
+ #container {
+ width: 100%;
+ container-type: inline-size;
+ background-color: green;
+ }
+
+ #first, #second { height: 50px; }
+
+ @container (width <= 400px) {
+ #second { display: none; }
+ }
+</style>
+<div id="vertical">
+ <div id="padded">
+ <div id="horizontal">
+ <div id="container">
+ <div id="first"></div>
+ <div id="second"></div>
+ </div>
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => assert_equals(padded.offsetHeight, 100),
+ "#container height measured with 500px width. Both container children visible");
+ test(() => assert_equals(container.offsetWidth, 400),
+ "#container width 400px after padding is applied.");
+ test(() => assert_equals(container.offsetHeight, 50),
+ "#container width 400px after padding is applied. #second is removed from the rendering");
+
+ // Reduce width by 1px to test that a re-layout is not stateful.
+ vertical.style.width = "499px";
+
+ test(() => assert_equals(padded.offsetHeight, 100),
+ "#container height measured with 499px width. Both container children visible");
+ test(() => assert_equals(container.offsetWidth, 399),
+ "#container width 399px after padding is applied. #second is removed from the rendering");
+ test(() => assert_equals(container.offsetHeight, 50),
+ "#container width 399x after padding is applied. #second is removed from the rendering");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-001.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-001.html
new file mode 100644
index 0000000000..0baef0bfc2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-001.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: Container for elements with pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ :root {
+ color: black;
+ }
+
+ .container {
+ container-type: size;
+ width: 200px;
+ height: 40px;
+ }
+
+ @container (min-width: 300px) {
+ #container1 div::before { content: "before"; }
+ #container1 div::after { content: "after"; }
+ #container2 li::marker { color: green; }
+ }
+</style>
+<main id=container1 class=container>
+ <div>test</div>
+</main>
+<main id=container2 class=container>
+ <ol>
+ <li>One</li>
+ </ol>
+</main>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(function() {
+ let div = document.querySelector('#container1 > div');
+ assert_equals(getComputedStyle(div, '::before').content, 'none');
+ assert_equals(getComputedStyle(div, '::after').content, 'none');
+
+ container1.style.width = '300px';
+ assert_equals(getComputedStyle(div, '::before').content, '"before"');
+ assert_equals(getComputedStyle(div, '::after').content, '"after"');
+
+ container1.style = '';
+ assert_equals(getComputedStyle(div, '::before').content, 'none');
+ assert_equals(getComputedStyle(div, '::after').content, 'none');
+ }, 'Pseudo-elements ::before and ::after respond to container size changes');
+
+ test(function() {
+ let li = document.querySelector('#container2 li');
+ assert_equals(getComputedStyle(li, '::marker').color, 'rgb(0, 0, 0)');
+
+ container2.style.width = '300px';
+ assert_equals(getComputedStyle(li, '::marker').color, 'rgb(0, 128, 0)');
+
+ container2.style = '';
+ assert_equals(getComputedStyle(li, '::marker').color, 'rgb(0, 0, 0)');
+ }, 'Pseudo-element ::marker responds to container size changes');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002-ref.html
new file mode 100644
index 0000000000..da96a826cf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<div>PASS</div>
+<div>PASS</div>
+<div>PASS</div>
+<div>PASS</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002.html
new file mode 100644
index 0000000000..a44c64c700
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: Container for ::before/::after pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="pseudo-elements-002-ref.html">
+<style>
+ .container { container-type: inline-size; }
+
+ @container (max-width: 100px) { #c1::before { content: "PASS" } }
+ @container (min-width: 150px) { #c1::before { content: "FAIL" } }
+
+ @container (max-width: 100px) { #c2::before { content: "PASS" } }
+ @container (min-width: 150px) { #c2::before { content: "FAIL" } }
+
+ @container (max-width: 100px) { #c3::after { content: "PASS" } }
+ @container (min-width: 150px) { #c3::after { content: "FAIL" } }
+
+ @container (max-width: 100px) { #c4::after { content: "PASS" } }
+ @container (min-width: 150px) { #c4::after { content: "FAIL" } }
+</style>
+<div id="c1" class="container" style="width:100px"></div>
+<div id="c2" class="container" style="width:200px"></div>
+<div id="c3" class="container" style="width:100px"></div>
+<div id="c4" class="container" style="width:200px"></div>
+<script>
+ document.body.offsetTop;
+ c2.style.width = "100px";
+ c4.style.width = "100px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b-ref.html
new file mode 100644
index 0000000000..5783b77fec
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<div><span style="color:green">P</span>ASS if P is green.</div>
+<div><span style="color:green">P</span>ASS if P is green.</div>
+<div><span style="color:green">P</span>ASS if P is green.</div>
+<div><span style="color:green">P</span>ASS if P is green.</div>
+<div style="color:green">PASS if text is green.</div>
+<div style="color:green">PASS if text is green.</div>
+<div style="color:green">PASS if text is green.</div>
+<div style="color:green">PASS if text is green.</div>
+<div style="color:green">PASS if text is green.</div>
+<div style="color:green">PASS if text is green.</div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b.html
new file mode 100644
index 0000000000..e1874e035b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-002b.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: Container for ::first-letter/::first-line pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="pseudo-elements-002b-ref.html">
+<style>
+ .container { container-type: inline-size; }
+
+ @container (max-width: 300px) { #c5::first-letter { color: green } }
+ @container (max-width: 300px) { #c6::first-letter { color: green } }
+
+ @container (min-width: 400px) { #c7::first-letter { color: green } }
+ @container (min-width: 400px) { #c8::first-letter { color: green } }
+
+ @container (max-width: 300px) { #c9::first-line { color: green } }
+ @container (max-width: 300px) { #c10::first-line { color: green } }
+
+ @container (min-width: 400px) { #c11::first-line { color: green } }
+ @container (min-width: 400px) { #c12::first-line { color: green } }
+
+ #c13::first-line { color: red }
+ @container (min-width: 400px) { #c13::first-line { color: green } }
+ @container (min-width: 400px) { #c14::first-line { color: green } }
+</style>
+<div class="container" style="width:400px">
+ <div id="c5" class="container" style="width:300px">PASS if P is green.</div>
+</div>
+<div class="container" style="width:400px">
+ <div id="c6" class="container" style="width:400px">PASS if P is green.</div>
+</div>
+<div id="c7" class="container" style="width:400px">
+ <div class="container" style="width:300px">PASS if P is green.</div>
+</div>
+<div id="c8" class="container" style="width:300px">
+ <div class="container" style="width:300px">PASS if P is green.</div>
+</div>
+<div class="container" style="width:400px">
+ <div id="c9" class="container" style="width:300px">PASS if text is green.</div>
+</div>
+<div class="container" style="width:400px">
+ <div id="c10" class="container" style="width:400px">PASS if text is green.</div>
+</div>
+<div id="c11" class="container" style="width:400px">
+ <div class="container" style="width:300px">PASS if text is green.</div>
+</div>
+<div id="c12" class="container" style="width:300px">
+ <div class="container" style="width:300px">PASS if text is green.</div>
+</div>
+<div id="c13" class="container" style="width:300px">PASS if text is green.</div>
+<div id="c14" class="container" style="width:300px">PASS if text is green.</div>
+<script>
+ document.body.offsetTop;
+ c6.style.width = "300px";
+ c8.style.width = "400px";
+ c10.style.width = "300px";
+ c12.style.width = "400px";
+ c13.style.width = "400px";
+ c14.style.width = "400px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-003.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-003.html
new file mode 100644
index 0000000000..2d7647f710
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-003.html
@@ -0,0 +1,69 @@
+<!doctype html>
+<title>@container: originating element container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container { container-type: inline-size; }
+ #target { display: list-item; }
+ @container (max-width: 200px) {
+ #target::before { content: "PASS"; color: green; }
+ #target::after { color: green; }
+ #target::marker { color: green; }
+ #target::first-line { color: green; }
+ #target::first-letter { color: green; }
+ }
+ @container ((min-width: 300px) and (max-width: 350px)) {
+ #outer::first-line { color: green; }
+ #outer::first-letter { color: green; }
+ }
+ dialog::backdrop { background-color: lime; }
+ @container (max-width: 100px) {
+ dialog::backdrop { background-color: green; }
+ }
+</style>
+<div style="width: 400px" class="container">
+ <div style="width: 300px" class="container">
+ <div id="target" class="container" style="width: 200px">First-line</div>
+ <dialog id="dialog" class="container" style="width: 100px"></dialog>
+ </div>
+ <div style="width: 400px" class="container">
+ <div id="outer" style="width: 300px" class="container">
+ <div class="container" style="width: 200px">First-line</div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+ const lime = "rgb(0, 255, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(target, "::before").color, green);
+ }, "Originating element container for ::before");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::after").color, green);
+ }, "Originating element container for ::after");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::marker").color, green);
+ }, "Originating element container for ::marker");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-line").color, green);
+ }, "Originating element container for ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-letter").color, green);
+ }, "Originating element container for ::first-letter");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-line").color, green);
+ }, "Originating element container for outer ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-letter").color, green);
+ }, "Originating element container for outer ::first-letter");
+ test((t) => {
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog, "::backdrop").backgroundColor, lime, "::backdrop not rendered");
+ dialog.showModal();
+ assert_equals(getComputedStyle(dialog, "::backdrop").backgroundColor, green, "::backdrop rendered");
+ }, "Originating element container for ::backdrop");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-004.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-004.html
new file mode 100644
index 0000000000..db199f2205
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-004.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>@container: originating element container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #target { container-type: inline-size; }
+ #target::before,
+ #target::after,
+ #target::marker,
+ #target::first-line,
+ #target::first-letter,
+ #target::backdrop {
+ color: red;
+ }
+ @container (width >= 300px) {
+ #target::before,
+ #target::after,
+ #target::marker,
+ #target::first-line,
+ #target::first-letter,
+ #target::backdrop {
+ color: green;
+ }
+ }
+</style>
+<div id="outer" style="width: 200px">
+ <div id="target"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+ const red = "rgb(255, 0, 0)";
+
+ const pseudo_elements = ["::before", "::after", "::marker", "::first-line", "::first-letter", "::backdrop"];
+
+ pseudo_elements.forEach((pseudo_element) => {
+ test(() => {
+ assert_equals(getComputedStyle(target, pseudo_element).color, red);
+ }, `Initial color for ${pseudo_element}`);
+ });
+
+ outer.style.width = "300px";
+
+ pseudo_elements.forEach((pseudo_element) => {
+ test(() => {
+ assert_equals(getComputedStyle(target, pseudo_element).color, green);
+ }, `Color for ${pseudo_element} depending on container`);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-005.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-005.html
new file mode 100644
index 0000000000..575b66d6f8
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-005.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<title>CSS Container Queries Test: Style container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #c1 {
+ --theme: green;
+ }
+ @container style(--theme: green) {
+ #c1::before {
+ content: "";
+ color: green;
+ display: block;
+ height: 100px;
+ }
+ }
+</style>
+<div id="c1"></div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ let style = getComputedStyle(c1, "::before");
+ assert_equals(style.color, "rgb(0, 128, 0)");
+ assert_equals(style.height, "100px");
+ }, "::before pseudo element querying style() of originating element");
+</script>
+
+<style>
+ #c2 {
+ --theme: red;
+ }
+ #c2::before { color: red }
+ #c2.green {
+ --theme: green;
+ }
+ @container style(--theme: green) {
+ #c2::before {
+ content: "";
+ color: green;
+ }
+ }
+</style>
+<div id="c2"></div>
+<script>
+ test(() => {
+ let style = getComputedStyle(c2, "::before");
+ assert_equals(style.color, "rgb(255, 0, 0)");
+ }, "::before pseudo element not matching style()");
+
+ test(() => {
+ c2.className = "green";
+ let style = getComputedStyle(c2, "::before");
+ assert_equals(style.color, "rgb(0, 128, 0)");
+ }, "::before pseudo element matching style() query after class change");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-006.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-006.html
new file mode 100644
index 0000000000..65aee97f75
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-006.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<title>@container: originating element container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container { container-type: inline-size; }
+ #target { display: list-item; }
+ @container (max-width: 200px) {
+ #target::before { content: "PASS"; font-size: 10cqw; }
+ #target::after { font-size: 10cqw; }
+ #target::marker { font-size: 10cqw; }
+ #target::first-line { font-size: 10cqw; }
+ #target::first-letter { font-size: 10cqw; }
+ }
+ @container ((min-width: 300px) and (max-width: 350px)) {
+ #outer::first-line { font-size: 10cqw; }
+ #outer::first-letter { font-size: 10cqw; }
+ }
+ dialog::backdrop { font-size: 0px; }
+ @container (max-width: 100px) {
+ dialog::backdrop { font-size: 10cqw; }
+ }
+</style>
+<div style="width: 400px" class="container">
+ <div style="width: 300px" class="container">
+ <div id="target" class="container" style="width: 200px">First-line</div>
+ <dialog id="dialog" class="container" style="width: 100px"></dialog>
+ </div>
+ <div style="width: 400px" class="container">
+ <div id="outer" style="width: 300px" class="container">
+ <div class="container" style="width: 200px">First-line</div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target, "::before").fontSize, "20px");
+ }, "Originating element container for ::before");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::after").fontSize, "20px");
+ }, "Originating element container for ::after");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::marker").fontSize, "20px");
+ }, "Originating element container for ::marker");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-line").fontSize, "20px");
+ }, "Originating element container for ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-letter").fontSize, "20px");
+ }, "Originating element container for ::first-letter");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-line").fontSize, "30px");
+ }, "Originating element container for outer ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-letter").fontSize, "30px");
+ }, "Originating element container for outer ::first-letter");
+ test((t) => {
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog, "::backdrop").fontSize, "0px", "::backdrop not rendered");
+ dialog.showModal();
+ assert_equals(getComputedStyle(dialog, "::backdrop").fontSize, "10px", "::backdrop rendered");
+ }, "Originating element container for ::backdrop");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-007.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-007.html
new file mode 100644
index 0000000000..951f4226f4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-007.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<title>@container: originating element container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #target { container-type: inline-size; }
+ #target::before,
+ #target::after,
+ #target::marker,
+ #target::first-line,
+ #target::first-letter,
+ #target::backdrop {
+ font-size: 0px;
+ }
+ @container (width >= 300px) {
+ #target::before,
+ #target::after,
+ #target::marker,
+ #target::first-line,
+ #target::first-letter,
+ #target::backdrop {
+ font-size: 10cqw;
+ }
+ }
+</style>
+<div id="outer" style="width: 200px">
+ <div id="target"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const pseudo_elements = ["::before", "::after", "::marker", "::first-line", "::first-letter", "::backdrop"];
+
+ pseudo_elements.forEach((pseudo_element) => {
+ test(() => {
+ assert_equals(getComputedStyle(target, pseudo_element).fontSize, "0px");
+ }, `Initial font-size for ${pseudo_element}`);
+ });
+
+ outer.style.width = "300px";
+
+ pseudo_elements.forEach((pseudo_element) => {
+ test(() => {
+ assert_equals(getComputedStyle(target, pseudo_element).fontSize, "30px");
+ }, `font-size for ${pseudo_element} depending on container`);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-008.html b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-008.html
new file mode 100644
index 0000000000..1d722a46b6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/pseudo-elements-008.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>@container: originating element container for pseudo elements</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container { container-type: inline-size; }
+ #target { display: list-item; }
+ #target::before { content: "PASS"; font-size: 10cqw; }
+ #target::after { font-size: 10cqw; }
+ #target::marker { font-size: 10cqw; }
+ #target::first-line { font-size: 10cqw; }
+ #target::first-letter { font-size: 10cqw; }
+ #outer::first-line { font-size: 10cqw; }
+ #outer::first-letter { font-size: 10cqw; }
+ dialog::backdrop { font-size: 10cqw; }
+</style>
+<div style="width: 400px" class="container">
+ <div style="width: 300px" class="container">
+ <div id="target" class="container" style="width: 200px">First-line</div>
+ <dialog id="dialog" class="container" style="width: 100px"></dialog>
+ </div>
+ <div style="width: 400px" class="container">
+ <div id="outer" style="width: 300px" class="container">
+ <div class="container" style="width: 200px">First-line</div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target, "::before").fontSize, "20px");
+ }, "Originating element container for ::before");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::after").fontSize, "20px");
+ }, "Originating element container for ::after");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::marker").fontSize, "20px");
+ }, "Originating element container for ::marker");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-line").fontSize, "20px");
+ }, "Originating element container for ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(target, "::first-letter").fontSize, "20px");
+ }, "Originating element container for ::first-letter");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-line").fontSize, "30px");
+ }, "Originating element container for outer ::first-line");
+ test(() => {
+ assert_equals(getComputedStyle(outer, "::first-letter").fontSize, "30px");
+ }, "Originating element container for outer ::first-letter");
+ test((t) => {
+ t.add_cleanup(() => dialog.close());
+ assert_equals(getComputedStyle(dialog, "::backdrop").fontSize, "30px", "::backdrop not rendered");
+ dialog.showModal();
+ assert_equals(getComputedStyle(dialog, "::backdrop").fontSize, "10px", "::backdrop rendered");
+ }, "Originating element container for ::backdrop");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/query-content-box.html b/testing/web-platform/tests/css/css-contain/container-queries/query-content-box.html
new file mode 100644
index 0000000000..5f6cc9fdbe
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/query-content-box.html
@@ -0,0 +1,80 @@
+<!doctype html>
+<title>CSS Container Queries Test: Size queries match content-box</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container {
+ container-type: size;
+ border: 10px solid black;
+ padding: 40px;
+ margin: 20px;
+ }
+
+ #container1 {
+ box-sizing: content-box;
+ width: 100px;
+ height: 100px;
+ }
+
+ #container2 {
+ box-sizing: border-box;
+ width: 200px;
+ height: 200px;
+ }
+
+ #container3 {
+ box-sizing: content-box;
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ }
+
+ #container4 {
+ box-sizing: border-box;
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ }
+
+ @container ((width = 100px) and (height = 100px)) {
+ .target {
+ background-color: green;
+ height: 100%;
+ }
+ }
+</style>
+<div id="container1" class="container">
+ <div class="target"></div>
+</div>
+<div id="container2" class="container">
+ <div class="target"></div>
+</div>
+<div id="container3" class="container">
+ <div class="target"></div>
+</div>
+<div id="container4" class="container">
+ <div class="target"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#container1 > .target")).backgroundColor, green);
+ }, "Size queries with content-box sizing");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#container2 > .target")).backgroundColor, green);
+ }, "Size queries with border-box sizing");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#container3 > .target")).backgroundColor, green);
+ }, "Size queries with content-box sizing and overflow:scroll");
+
+ test(() => {
+ assert_equals(getComputedStyle(document.querySelector("#container4 > .target")).backgroundColor, green);
+ }, "Size queries with border-box sizing and overflow:scroll");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation-style.html b/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation-style.html
new file mode 100644
index 0000000000..4262f8bb0c
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation-style.html
@@ -0,0 +1,80 @@
+<!doctype html>
+<title>Evaluation of style queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ --applied: false;
+ --foo: bar;
+ }
+</style>
+<div id=container>
+ <div id=inner></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function test_query(query, expected) {
+ test((t) => {
+ let style = document.createElement('style');
+ t.add_cleanup(() => { style.remove(); });
+ style.innerText = `@container ${query} { #inner { --applied:true; } }`;
+ let cs = getComputedStyle(inner);
+ assert_equals(cs.getPropertyValue('--applied'), 'false');
+ document.head.append(style);
+ assert_equals(cs.getPropertyValue('--applied'), expected.toString());
+ }, query);
+ };
+
+ // Note that the following assumes that elements are style containers by
+ // default [1], and that:
+ //
+ // - style(--foo: bar) is a query that returns 'true', and
+ // - style(--baz: qux) is a query that returns 'false'.
+ //
+ // [1] https://github.com/w3c/csswg-drafts/issues/7066
+
+ // Nesting in <style-query>:
+ test_query('style((--foo: bar))', true);
+ test_query('style((--baz: qux))', false);
+ test_query('style((unknown))', false);
+ test_query('unknown((--foo: bar))', false);
+
+ // "not" in <style-query>:
+ test_query('style(not (--foo: bar))', false);
+ test_query('style(not (--baz: qux))', true);
+ test_query('style(not (unknown))', false);
+
+ // "and" in <style-query>:
+ test_query('style((--foo: bar) and (--foo: bar))', true);
+ test_query('style((--foo: bar) and (--foo: bar) and (--foo: bar))', true);
+ test_query('style((--baz: qux) and (--baz: qux))', false);
+ test_query('style((--baz: qux) and (--foo: bar) and (--foo: bar))', false);
+ test_query('style((--foo: bar) and (--baz: qux) and (--foo: bar))', false);
+ test_query('style((--foo: bar) and (--foo: bar) and (--baz: qux))', false);
+ test_query('style((unknown) and (--foo: bar) and (--foo: bar))', false);
+ test_query('style((--foo: bar) and (unknown) and (--foo: bar))', false);
+ test_query('style((--foo: bar) and (--foo: bar) and (unknown))', false);
+
+ // "or" in <style-query>:
+ test_query('style((--foo: bar) or (--foo: bar))', true);
+ test_query('style((--foo: bar) or (--foo: bar) or (--foo: bar))', true);
+ test_query('style((--baz: qux) or (--baz: qux))', false);
+ test_query('style((--baz: qux) or (--foo: bar) or (--foo: bar))', true);
+ test_query('style((--foo: bar) or (--baz: qux) or (--foo: bar))', true);
+ test_query('style((--foo: bar) or (--foo: bar) or (--baz: qux))', true);
+ test_query('style((unknown) or (--foo: bar) or (--foo: bar))', true);
+ test_query('style((--foo: bar) or (unknown) or (--foo: bar))', true);
+ test_query('style((--foo: bar) or (--foo: bar) or (unknown))', true);
+ test_query('style((unknown) or (--baz: qux) or (--foo: bar))', true);
+
+ // Combinations, <style-query>:
+ test_query('style(not ((--foo: bar) and (--foo: bar)))', false);
+ test_query('style(not ((--foo: bar) and (--baz: qux)))', true);
+ test_query('style((--foo: bar) and (not ((--baz: qux) or (--foo: bar))))', false);
+ test_query('style((--baz: qux) or (not ((--baz: qux) and (--foo: bar))))', true);
+ test_query('style((--baz: qux) or ((--baz: qux) and (--foo: bar)))', false);
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation.html b/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation.html
new file mode 100644
index 0000000000..5b301f2d1d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/query-evaluation.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<title>Evaluation of queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ width: 1px;
+ height: 0px;
+ container-type: size;
+ --applied:false;
+ }
+</style>
+<div id=container>
+ <div id=inner></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function test_query(query, expected) {
+ test((t) => {
+ let style = document.createElement('style');
+ t.add_cleanup(() => { style.remove(); });
+ style.innerText = `@container ${query} { #inner { --applied:true; } }`;
+ let cs = getComputedStyle(inner);
+ assert_equals(cs.getPropertyValue('--applied'), 'false');
+ document.head.append(style);
+ assert_equals(cs.getPropertyValue('--applied'), expected.toString());
+ }, query);
+ };
+
+ // We don't care about specific features in this file, only higher level
+ // evaluation like "and", "or", and so forth. The features "width", "height"
+ // and "unknown" are arbitrarily chosen to represent true, false, and
+ // unknown values, respectively.
+
+ test_query('(width)', true);
+ test_query('(height)', false);
+ test_query('(unknown)', false);
+ test_query('unknown(width)', false);
+
+ // Nesting in <container-query>:
+ test_query('((width))', true);
+ test_query('((height))', false);
+ test_query('((unknown))', false);
+ test_query('((((width))))', true);
+ test_query('((((height))))', false);
+ test_query('((((unknown))))', false);
+
+ // "not" in <container-query>:
+ test_query('(not (width))', false);
+ test_query('(not (height))', true);
+ test_query('(not (unknown))', false);
+ test_query('(not unknown(width))', false);
+
+ // "and" in <container-query>:
+ test_query('((width) and (width))', true);
+ test_query('((width) and (width) and (width))', true);
+ test_query('((height) and (height))', false);
+ test_query('((height) and (width) and (width))', false);
+ test_query('((width) and (height) and (width))', false);
+ test_query('((width) and (width) and (height))', false);
+ test_query('((unknown) and (width) and (width))', false);
+ test_query('((width) and (unknown) and (width))', false);
+ test_query('((width) and (width) and (unknown))', false);
+
+ // "or" in <container-query>:
+ test_query('((width) or (width))', true);
+ test_query('((width) or (width) or (width))', true);
+ test_query('((height) or (height))', false);
+ test_query('((height) or (width) or (width))', true);
+ test_query('((width) or (height) or (width))', true);
+ test_query('((width) or (width) or (height))', true);
+ test_query('((unknown) or (width) or (width))', true);
+ test_query('((width) or (unknown) or (width))', true);
+ test_query('((width) or (width) or (unknown))', true);
+ test_query('((unknown) or (height) or (width))', true);
+
+ // Combinations, <container-query>:
+ test_query('(not ((width) and (width)))', false);
+ test_query('(not ((width) and (height)))', true);
+ test_query('((width) and (not ((height) or (width))))', false);
+ test_query('((height) or (not ((height) and (width))))', true);
+ test_query('((height) or ((height) and (width)))', false);
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/reattach-container-with-dirty-child.html b/testing/web-platform/tests/css/css-contain/container-queries/reattach-container-with-dirty-child.html
new file mode 100644
index 0000000000..680d9caa84
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/reattach-container-with-dirty-child.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container changing display type while descendant styles change</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: inline-size;
+ }
+ @container (min-width: 200px) {
+ div { color: red }
+ }
+ @container (max-width: 150px) {
+ div { color: lime }
+ }
+</style>
+<div id="container">
+ <div id="child"><span id="inner">XXX</span></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ container.offsetTop;
+ assert_equals(getComputedStyle(child).color, "rgb(255, 0, 0)");
+ }, "Initially wider than 200px");
+
+ test(() => {
+ container.style.width = "100px";
+ container.style.display = "inline-block";
+ inner.style.color = "green";
+ container.offsetTop;
+ assert_equals(getComputedStyle(child).color, "rgb(0, 255, 0)");
+ assert_equals(getComputedStyle(inner).color, "rgb(0, 128, 0)");
+ }, "Container query changed and inner.style applied");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden-ref.html
new file mode 100644
index 0000000000..2a87df85b7
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<div style="width:200px;height:200px;background:green"></div>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden.html b/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden.html
new file mode 100644
index 0000000000..a3658f7f34
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/resize-while-content-visibility-hidden.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>CSS Container Queries Test: condition change while content-visibility: hidden</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#content-visibility">
+<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org">
+<link rel="match" href="resize-while-content-visibility-hidden-ref.html">
+<link rel="assert" content="Container query applies even if container is content-visibility: hidden">
+
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+#container {
+ container-name: container;
+ container-type: size;
+ width: 300px;
+ height: 300px;
+}
+
+#child {
+ width: 200px;
+ height: 200px;
+ background: red;
+}
+
+#container.wide { width: 500px; }
+.locked { content-visibility: hidden; }
+
+@container container (min-width: 400px) { #child { background: green; } }
+</style>
+
+<div id=container>
+ <div id=child></div>
+</div>
+
+<script>
+async function runTest() {
+ await new Promise(requestAnimationFrame);
+ container.classList.add("locked");
+
+ await new Promise(requestAnimationFrame);
+ container.classList.add("wide");
+
+ await new Promise(requestAnimationFrame);
+ container.classList.remove("locked");
+
+ await new Promise(requestAnimationFrame);
+ takeScreenshot();
+}
+
+requestAnimationFrame(() => { requestAnimationFrame(() => runTest()) });
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/sibling-layout-dependency.html b/testing/web-platform/tests/css/css-contain/container-queries/sibling-layout-dependency.html
new file mode 100644
index 0000000000..5e30a998d2
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/sibling-layout-dependency.html
@@ -0,0 +1,134 @@
+<!doctype html>
+<title>@container-dependent styles respond to layout changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="help" href="https://drafts.csswg.org/css-contain-2/#containment-size">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+<style>
+
+ @container (width: 10px) { .affected { --x:10; } }
+ @container (width: 20px) { .affected { --x:20; } }
+
+ .flex {
+ display: flex;
+ height: 30px;
+ width: 30px;
+ }
+
+ .container {
+ container-type: size;
+ flex: 1;
+ background: tomato;
+ }
+
+ .sibling {
+ background-color: skyblue;
+ }
+ .w10 {
+ width: 10px;
+ }
+ .ahem { font: 5px Ahem; }
+
+ /* The following is just to make the results more human-readable. */
+ main {
+ display: flex;
+ flex-wrap: wrap;
+ }
+
+</style>
+
+<main>
+ <!-- A sibling of the container gets a layout-affecting style change -->
+ <div class=flex>
+ <div class=container>
+ <div>
+ <div>
+ <div class=affected id=target1></div>
+ </div>
+ </div>
+ </div>
+ <div class="sibling w10" id=sibling1></div>
+ </div>
+ <script>
+ test(function() {
+ let cs = getComputedStyle(target1);
+ assert_equals(cs.getPropertyValue('--x'), '20');
+
+ sibling1.style.width = '20px';
+ assert_equals(cs.getPropertyValue('--x'), '10');
+ }, 'Sibling style mutation');
+ </script>
+
+ <!-- A sibling of the container gets a layout-affecting style change
+ affecting the parent of the gCS target -->
+ <div class=flex>
+ <div class=container>
+ <div>
+ <div class=affected id=parent2>
+ <div id=target2></div>
+ </div>
+ </div>
+ </div>
+ <div class="sibling w10" id=sibling2></div>
+ </div>
+ <script>
+ test(function() {
+ let cs = getComputedStyle(target2);
+ assert_equals(cs.getPropertyValue('--x'), '20');
+
+ sibling2.style.width = '20px';
+ assert_equals(cs.getPropertyValue('--x'), '10');
+ }, 'Sibling style mutation, parent is affected');
+ </script>
+
+<!-- A sibling of the container gets a layout-affecting style change
+ affecting an ancestor of the gCS target -->
+ <div class=flex>
+ <div class=container>
+ <div class=affected id=ancestor3>
+ <div>
+ <div id=target3></div>
+ </div>
+ </div>
+ </div>
+ <div class="sibling w10" id=sibling3></div>
+ </div>
+ <script>
+ test(function() {
+ let cs = getComputedStyle(target3);
+ assert_equals(cs.getPropertyValue('--x'), '20');
+
+ sibling3.style.width = '20px';
+ assert_equals(cs.getPropertyValue('--x'), '10');
+ }, 'Sibling style mutation, ancestor is affected');
+ </script>
+
+ <!-- A sibling of the container needs layout via text mutation -->
+ <div class=flex>
+ <div class=container>
+ <div>
+ <div>
+ <div class=affected id=target4></div>
+ </div>
+ </div>
+ </div>
+ <div class="sibling ahem" id=sibling4>XX</div>
+ </div>
+ <script>
+ promise_test(async function() {
+ await document.fonts.ready;
+
+ let cs = getComputedStyle(target4);
+ assert_equals(cs.getPropertyValue('--x'), '20');
+
+ sibling4.textContent = 'XXXX';
+ assert_equals(cs.getPropertyValue('--x'), '10');
+ }, 'Sibling text mutation');
+ </script>
+
+</main>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/size-container-no-principal-box.html b/testing/web-platform/tests/css/css-contain/container-queries/size-container-no-principal-box.html
new file mode 100644
index 0000000000..4bff0681ab
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/size-container-no-principal-box.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<title>CSS Container Queries Test: size container types apply to elements without a principal box</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #outer {
+ container-type: inline-size;
+ }
+ #inner_none {
+ display: none;
+ container-type: inline-size;
+ }
+ #inner_contents {
+ display: contents;
+ container-type: inline-size;
+ }
+ @container (min-width: 0) {
+ span { color: red; }
+ }
+ @container (min-width: 0) {
+ #ref { color: green; }
+ }
+ @container not (max-width: 0) {
+ span { background-color: red; }
+ }
+ @container not (max-width: 0) {
+ #ref { background-color: green; }
+ }
+</style>
+<div id="outer">
+ <div id="ref"></div>
+ <div id="inner_none"><span></span></div>
+ <div id="inner_contents"><span></span></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(ref).color, "rgb(0, 128, 0)");
+ }, "(min-width: 0) can match a container with a principal box");
+
+ test(() => {
+ assert_equals(getComputedStyle(inner_none.firstChild).color, "rgb(0, 0, 0)");
+ }, "(min-width: 0) does not match a container without a principal box (display:none)");
+
+ test(() => {
+ assert_equals(getComputedStyle(inner_contents.firstChild).color, "rgb(0, 0, 0)");
+ }, "(min-width: 0) does not match a container without a principal box (display:contents)");
+
+ test(() => {
+ assert_equals(getComputedStyle(ref).backgroundColor, "rgb(0, 128, 0)");
+ }, "not (max-width: 0) can match a container with a principal box");
+
+ test(() => {
+ assert_equals(getComputedStyle(inner_none.firstChild).backgroundColor, "rgba(0, 0, 0, 0)");
+ }, "not (max-width: 0) does not match a container without a principal box (display:none)");
+
+ test(() => {
+ assert_equals(getComputedStyle(inner_contents.firstChild).backgroundColor, "rgba(0, 0, 0, 0)");
+ }, "not (max-width: 0) does not match a container without a principal box (display:contents)");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes-ref.html
new file mode 100644
index 0000000000..e1822fa544
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<q style="display:block">This text must be quoted.</q>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes.html b/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes.html
new file mode 100644
index 0000000000..b88f882cd0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/size-container-with-quotes.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>CSS Container Queries Test: &lt;q&gt; element as a size container must generate quotes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="size-container-with-quotes-ref.html">
+<style>
+ q {
+ container-type: inline-size;
+ display: block;
+ }
+</style>
+<q>This text must be quoted.</q>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/size-feature-evaluation.html b/testing/web-platform/tests/css/css-contain/container-queries/size-feature-evaluation.html
new file mode 100644
index 0000000000..600a266fce
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/size-feature-evaluation.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<title>Evaluation of size features</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+
+<div id=container>
+ <div id=target>
+ Test
+ </div>
+</div>
+
+<script>
+setup(() => assert_implements_container_queries());
+
+function test_evaluation(container_class, query, expected) {
+ test(function(t) {
+ let style_node = document.createElement('style');
+ t.add_cleanup(() => {
+ container.classList.remove(container_class);
+ style_node.remove();
+ });
+ style_node.innerText = `@container ${query} { #target { --applied:true; } }`;
+
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), '');
+ container.classList.add(container_class);
+ document.head.append(style_node);
+ assert_equals(getComputedStyle(target).getPropertyValue('--applied'), expected ? 'true' : '');
+ }, `${query} (.${container_class})`);
+}
+
+</script>
+
+<style>
+ .horizontal {
+ width: 100px;
+ height: 200px;
+ container-type: size;
+ }
+
+ .vertical {
+ width: 100px;
+ height: 200px;
+ container-type: size;
+ writing-mode: vertical-rl;
+ }
+</style>
+<script>
+
+ for (let cls of ['horizontal', 'vertical']) {
+
+ let logical_width = (cls == 'horizontal') ? 'inline' : 'block';
+ let logical_height = (cls == 'horizontal') ? 'block' : 'inline';
+
+ test_evaluation(cls, '(width < 100px)', false);
+ test_evaluation(cls, '(width >= 100px)', true);
+ test_evaluation(cls, '(min-width: 100px)', true);
+ test_evaluation(cls, '(min-width: 101px)', false);
+ test_evaluation(cls, '(max-width: 100px)', true);
+ test_evaluation(cls, '(max-width: 99px)', false);
+
+ test_evaluation(cls, '(height < 200px)', false);
+ test_evaluation(cls, '(height >= 200px)', true);
+ test_evaluation(cls, '(min-height: 200px)', true);
+ test_evaluation(cls, '(min-height: 201px)', false);
+ test_evaluation(cls, '(max-height: 200px)', true);
+ test_evaluation(cls, '(max-height: 199px)', false);
+
+ test_evaluation(cls, `(${logical_width}-size < 100px)`, false);
+ test_evaluation(cls, `(${logical_width}-size >= 100px)`, true);
+ test_evaluation(cls, `(min-${logical_width}-size: 100px)`, true);
+ test_evaluation(cls, `(min-${logical_width}-size: 101px)`, false);
+ test_evaluation(cls, `(max-${logical_width}-size: 100px)`, true);
+ test_evaluation(cls, `(max-${logical_width}-size: 99px)`, false);
+
+ test_evaluation(cls, `(${logical_height}-size < 200px)`, false);
+ test_evaluation(cls, `(${logical_height}-size >= 200px)`, true);
+ test_evaluation(cls, `(min-${logical_height}-size: 200px)`, true);
+ test_evaluation(cls, `(min-${logical_height}-size: 201px)`, false);
+ test_evaluation(cls, `(max-${logical_height}-size: 200px)`, true);
+ test_evaluation(cls, `(max-${logical_height}-size: 199px)`, false);
+
+ test_evaluation(cls, '(orientation: landscape)', false);
+ test_evaluation(cls, '(orientation: portrait)', true);
+
+ test_evaluation(cls, '(aspect-ratio: 1/2)', true);
+ test_evaluation(cls, '(aspect-ratio: 2/1)', false);
+ }
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/style-change-in-container.html b/testing/web-platform/tests/css/css-contain/container-queries/style-change-in-container.html
new file mode 100644
index 0000000000..ed4baa7e8b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/style-change-in-container.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>CSS Container Queries Test: recompute style inside a @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container { container-type: size; }
+ @container (min-width: 1px) {
+ #content { color: green; }
+ }
+</style>
+<div id="container">
+ <div id="content"></div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ let content = document.getElementById("content");
+
+ test(() => {
+ assert_equals(getComputedStyle(content).color, "rgb(0, 128, 0)");
+
+ // Dirty style of an element inside the container:
+ content.style.backgroundColor = "lime";
+
+ // The container query should still evaluate correctly:
+ assert_equals(getComputedStyle(content).color, 'rgb(0, 128, 0)');
+ }, "Basic test for container query evaluation stability");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/style-container-for-shadow-dom.html b/testing/web-platform/tests/css/css-contain/container-queries/style-container-for-shadow-dom.html
new file mode 100644
index 0000000000..b8bea09750
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/style-container-for-shadow-dom.html
@@ -0,0 +1,265 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Container Queries Test: style query container for Shadow DOM</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#query-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/declarative-shadow-dom-polyfill.js"></script>
+<script src="support/cq-testcommon.js"></script>
+
+<div id="inclusive-ancestor-across-root">
+ <div style="--foo: bar">
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ #t1 { color: green; }
+ }
+ </style>
+ <div id="t1"></div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-skip-slotting">
+ <div style="--foo: bar">
+ <template shadowrootmode="open">
+ <div style="--foo: baz">
+ <slot></slot>
+ </div>
+ </template>
+ <style>
+ @container style(--foo: bar) {
+ #t2 { color: green; }
+ }
+ </style>
+ <div id="t2"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slotted">
+ <div style="--foo: baz">
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ ::slotted(#t3) { color: green; }
+ }
+ </style>
+ <slot style="--foo: bar"></slot>
+ </template>
+ <div id="t3"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-host" style="--foo: bar">
+ <div id="t4">
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ :host(#t4) { color: green; }
+ }
+ </style>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-part">
+ <div style="--foo: bar">
+ <template shadowrootmode="open">
+ <div style="--foo: baz">
+ <span id="t5" part="part"></span>
+ </div>
+ </template>
+ <style>
+ @container style(--foo: bar) {
+ #inclusive-ancestor-part > div::part(part) { color: green; }
+ }
+ </style>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slotted-before">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ ::slotted(#t6)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ <slot style="--foo: bar"></slot>
+ </template>
+ <div id="t6" style="--foo: baz"></div>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-host-before">
+ <div id="t7" style="--foo: bar">
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ :host(#t7)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-part-before">
+ <style>
+ @container style(--foo: bar) {
+ #inclusive-ancestor-part-before > div::part(part)::before {
+ content: "X";
+ color: green;
+ }
+ }
+ </style>
+ <div style="--foo: bar">
+ <template shadowrootmode="open">
+ <div style="--foo: baz">
+ <span id="t8" part="part"></span>
+ </div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-inner-part">
+ <style>
+ @container style(--foo: bar) {
+ #inclusive-ancestor-inner-part > div::part(inner-part) { color: green; }
+ }
+ </style>
+ <div style="--foo: bar">
+ <template shadowrootmode="open">
+ <div exportparts="inner-part" style="-foo: baz">
+ <template shadowrootmode="open">
+ <div style="--foo: baz">
+ <span id="t9" part="inner-part"></span>
+ </div>
+ </template>
+ </div>
+ </template>
+ </div>
+</div>
+
+<div id="inclusive-ancestor-slot-fallback">
+ <div><template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ #t10 { color: green; }
+ }
+ </style>
+ <div>
+ <slot style="--foo: bar"><span id="t10"></span></slot>
+ </div>
+ </template></div>
+</div>
+
+<div id="no-container-for-part">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ #t11 { color: green; }
+ </style>
+ <div style="--foo: bar">
+ <span id="t11" part="part"></span>
+ </div>
+ </template>
+ <style>
+ @container style(--foo: bar) {
+ #no-container-for-part > div::part(part) { color: red; }
+ }
+ </style>
+ </div>
+</div>
+
+<div id="inner-scope-host-part" style="--foo: bar">
+ <div>
+ <template shadowrootmode="open">
+ <style>
+ @container style(--foo: bar) {
+ :host::part(part) { color: green; }
+ }
+ </style>
+ <div style="--foo: baz">
+ <span id="t12" part="part"></span>
+ </div>
+ </template>
+ </div>
+</div>
+
+<script>
+ setup(() => {
+ assert_implements_container_queries();
+ polyfill_declarative_shadow_dom(document);
+ });
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ const t1 = document.querySelector("#inclusive-ancestor-across-root > div").shadowRoot.querySelector("#t1");
+ assert_equals(getComputedStyle(t1).color, green);
+ }, "Match container in outer tree");
+
+ test(() => {
+ const t2 = document.querySelector("#t2");
+ assert_equals(getComputedStyle(t2).color, green);
+ }, "Match container in same tree, not walking flat tree ancestors");
+
+ test(() => {
+ const t3 = document.querySelector("#t3");
+ assert_equals(getComputedStyle(t3).color, green);
+ }, "Match container in ::slotted selector's originating element tree");
+
+ test(() => {
+ const t4 = document.querySelector("#t4");
+ assert_equals(getComputedStyle(t4).color, green);
+ }, "Match container in outer tree for :host");
+
+ test(() => {
+ const t5 = document.querySelector("#inclusive-ancestor-part > div").shadowRoot.querySelector("#t5");
+ assert_equals(getComputedStyle(t5).color, green);
+ }, "Match container in ::part selector's originating element tree");
+
+ test(() => {
+ const t6 = document.querySelector("#t6");
+ assert_equals(getComputedStyle(t6, "::before").color, green);
+ }, "Match container for ::before in ::slotted selector's originating element tree");
+
+ test(() => {
+ const t7 = document.querySelector("#t7");
+ assert_equals(getComputedStyle(t7, "::before").color, green);
+ }, "Match container in outer tree for :host::before");
+
+ test(() => {
+ const t8 = document.querySelector("#inclusive-ancestor-part-before > div").shadowRoot.querySelector("#t8");
+ assert_equals(getComputedStyle(t8, "::before").color, green);
+ }, "Match container for ::before in ::part selector's originating element tree");
+
+ test(() => {
+ const outerhost = document.querySelector("#inclusive-ancestor-inner-part > div");
+ const innerhost = outerhost.shadowRoot.querySelector("div");
+ const t9 = innerhost.shadowRoot.querySelector("#t9");
+ assert_equals(getComputedStyle(t9).color, green);
+ }, "Match container for ::part selector's originating element tree for exportparts");
+
+ test(() => {
+ const t10 = document.querySelector("#inclusive-ancestor-slot-fallback > div").shadowRoot.querySelector("#t10");
+ assert_equals(getComputedStyle(t10).color, green);
+ }, "Match container for slot light tree child fallback");
+
+ test(() => {
+ const t11 = document.querySelector("#no-container-for-part > div").shadowRoot.querySelector("#t11");
+ assert_equals(getComputedStyle(t11).color, green);
+ }, "Should not match container inside shadow tree for ::part()");
+
+ test(() => {
+ const t12 = document.querySelector("#inner-scope-host-part > div").shadowRoot.querySelector("#t12");
+ assert_equals(getComputedStyle(t12).color, green);
+ }, "A :host::part rule should match containers in the originating element tree");
+
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/style-not-sharing-float.html b/testing/web-platform/tests/css/css-contain/container-queries/style-not-sharing-float.html
new file mode 100644
index 0000000000..7c76bb32bf
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/style-not-sharing-float.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<title>CSS Container Queries Test: Check style is not sharing between cousins in the case of Container Queries</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .float {
+ float: left;
+ width: 25px;
+ height: 25px;
+ }
+ .item {
+ container-type: inline-size;
+ height: 25px;
+ }
+ @container (width >= 50px) {
+ .item div { color: lime; }
+ }
+ @container (width >= 150px) {
+ .item div { color: green; }
+ }
+</style>
+<div style="width: 150px">
+ <div class="float"></div>
+ <div class="item">
+ <div id="target1"></div>
+ </div>
+ <div class="item">
+ <div id="target2"></div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(target1).color, "rgb(0, 255, 0)", "Second item container should be 100px wide");
+ assert_equals(getComputedStyle(target2).color, "rgb(0, 128, 0)", "First item container should be 200px wide");
+ }, "Check that style is not sharing in the case of Container Queries");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/support/cq-testcommon.js b/testing/web-platform/tests/css/css-contain/container-queries/support/cq-testcommon.js
new file mode 100644
index 0000000000..2eaca1dd09
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/support/cq-testcommon.js
@@ -0,0 +1,3 @@
+function assert_implements_container_queries() {
+ assert_implements(CSS.supports("container-type:size"), "Basic support for container queries required");
+}
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/support/test.vtt b/testing/web-platform/tests/css/css-contain/container-queries/support/test.vtt
new file mode 100644
index 0000000000..ffd1d4ca44
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/support/test.vtt
@@ -0,0 +1,4 @@
+WEBVTT
+
+00:00:00.000 --> 00:00:10.000
+Sub-<b>title</b>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-child-container.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-child-container.html
new file mode 100644
index 0000000000..898fc22c2b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-child-container.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>CSS Container Queries Test: size query container inside foreignObject</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ svg {
+ display: block;
+ width: 200px;
+ height: 200px;
+ container-type: size;
+ }
+ #container {
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ @container (width = 100px) {
+ #inner { color: green; }
+ }
+</style>
+<svg>
+ <foreignObject>
+ <div id="container">
+ <div id="inner">Green</div>
+ </div>
+ </foreignObject>
+</svg>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(inner).color, green);
+ }, "#inner querying #container inside foreignObject");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container-ref.html
new file mode 100644
index 0000000000..abf1af122b
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>You should see the word PASS below and no red.</p>
+<svg><foreignObject style="width:100px;height:100px;"><div>PASS</div><foreignObject></svg>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container.html
new file mode 100644
index 0000000000..38fc493a16
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-foreignobject-no-size-container.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<title>CSS Container Queries Test: SVG &lt;foreignObject&gt; element not a size query container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="svg-foreignobject-no-size-container-ref.html">
+<style>
+ foreignObject {
+ display: block;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ @supports not (container-type: size) {
+ div { color: red; }
+ }
+ @container (width = 100px) {
+ div { color: red; }
+ }
+</style>
+<p>You should see the word PASS below and no red.</p>
+<svg>
+ <foreignObject>
+ <div id="div">PASS</div>
+ </foreignObject>
+</svg>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container-ref.html
new file mode 100644
index 0000000000..4f30c39939
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>You should see the word PASS below and no red.</p>
+<svg><text x="0" y="20" id="text">PASS</text></svg>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container.html
new file mode 100644
index 0000000000..ed9e853676
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-g-no-size-container.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>CSS Container Queries Test: SVG &lt;g&gt; element not a size query container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<link rel="match" href="svg-g-no-size-container-ref.html">
+<style>
+ g {
+ display: block;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+
+ @supports not (container-type: size) {
+ text { fill: red; }
+ }
+ @container (width = 100px) {
+ text { fill: red; }
+ }
+</style>
+<p>You should see the word PASS below and no red.</p>
+<svg>
+ <g><text x="0" y="20" id="text">PASS</text></g>
+</svg>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/svg-root-size-container.html b/testing/web-platform/tests/css/css-contain/container-queries/svg-root-size-container.html
new file mode 100644
index 0000000000..70ce40c0bc
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/svg-root-size-container.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>CSS Container Queries Test: SVG root as a size query container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ svg {
+ display: block;
+ width: 100px;
+ height: 100px;
+ container-type: size;
+ }
+ @container (width = 100px) {
+ #div, #text { color: green; }
+ }
+</style>
+<svg>
+ <text id="text">Green</text>
+ <foreignObject>
+ <div id="div">Green</div>
+ </foreignObject>
+</svg>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => {
+ assert_equals(getComputedStyle(text).color, green);
+ }, "SVG text querying SVG root size container");
+
+ test(() => {
+ assert_equals(getComputedStyle(div).color, green);
+ }, "div in foreignObject querying SVG root size container");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display-ref.html
new file mode 100644
index 0000000000..c0355d2f50
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<title>CSS Test Reference</title>
+<p>You should see the word PASS below.</p>
+<table><td>PASS</td></table>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display.html b/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display.html
new file mode 100644
index 0000000000..33a4f4fe72
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/table-inside-container-changing-display.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>CSS Container Queries Test: table inside @container changing display type</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="help" href="https://crbug.com/1284918">
+<link rel="match" href="table-inside-container-changing-display-ref.html">
+<style>
+ @supports not (container-type: inline-size) {
+ #container { display: none !important; }
+ }
+ #container {
+ width: 200px;
+ height: 200px;
+ container-type: inline-size;
+ }
+</style>
+<p>You should see the word PASS below.</p>
+<div id="container">
+ <div>
+ <table><td>PASS</td></table>
+ </div>
+</div>
+<script>
+ document.body.offsetTop;
+ document.querySelector("#container").style.display = "inline-block";
+ document.querySelector("table").style.color = "currentColor";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop-ref.html b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop-ref.html
new file mode 100644
index 0000000000..49c46974c9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop-ref.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<html style="background:green">
+<title>CSS Test Reference</title>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop.html b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop.html
new file mode 100644
index 0000000000..25635167a5
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-backdrop.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>CSS Container Queries Test: ::backdrop depending on @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="top-layer-dialog-backdrop-ref.html">
+<style>
+ html { background: green; }
+ #container { container-type: inline-size; }
+ @container (max-width: 200px) {
+ ::backdrop { display: none; }
+ #dialog { visibility: hidden; }
+ }
+</style>
+<div id="container">
+ <dialog id="dialog"></dialog>
+</div>
+<script>
+ dialog.showModal();
+ dialog.offsetTop;
+ container.style.width = "100px";
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-container.html b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-container.html
new file mode 100644
index 0000000000..5627a6cea0
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog-container.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>CSS Container Queries Test: Top layer element as a @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #parent { width: 100px; }
+ #dialog {
+ container-type: inline-size;
+ width: auto;
+ border: none;
+ }
+ #child { color: red; }
+ @container (min-width: 200px) {
+ #child { color: green; }
+ }
+</style>
+<div id="parent">
+ <dialog id="dialog"><span id="child"></span></dialog>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(child).color, "rgb(255, 0, 0)");
+ }, "#dialog initially sized by #containing-block");
+
+ test(() => {
+ dialog.showModal();
+ assert_equals(getComputedStyle(child).color, "rgb(0, 128, 0)");
+ }, "#dialog sized by viewport");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog.html b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog.html
new file mode 100644
index 0000000000..9d18b1862d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-dialog.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container with modal dialog child</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #container {
+ container-type: inline-size;
+ }
+ dialog {
+ color: red;
+ }
+ @container (max-width: 200px) {
+ dialog { color: green; }
+ }
+ @container (max-width: 100px) {
+ dialog { color: lime; }
+ }
+</style>
+<div id="container">
+ <dialog id="dialog"></dialog>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(dialog).color, "rgb(255, 0, 0)");
+ }, "#container initially wider than 200px");
+
+ test(() => {
+ container.style.width = "200px";
+ assert_equals(getComputedStyle(dialog).color, "rgb(0, 128, 0)");
+ }, "#container changed to 200px");
+
+ test(() => {
+ dialog.showModal();
+ assert_equals(getComputedStyle(dialog).color, "rgb(0, 128, 0)");
+ }, "Modal dialog still has parent as query container while in top layer");
+
+ test(() => {
+ container.style.width = "100px";
+ assert_equals(getComputedStyle(dialog).color, "rgb(0, 255, 0)");
+ }, "Container changes width while dialog is in top layer");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/top-layer-nested-dialog.html b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-nested-dialog.html
new file mode 100644
index 0000000000..1a6d573f24
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/top-layer-nested-dialog.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<title>CSS Container Queries Test: Nested top layer elements and @container</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ dialog { color: red; }
+ #container { width: 100px; }
+ #container, #outer { container-type: inline-size; }
+ @container (min-width: 200px) {
+ #outer { width: 400px; color: lime; }
+ }
+ @container (min-width: 400px) {
+ #inner { color: green; }
+ }
+</style>
+<div id="container">
+ <dialog id="outer">
+ <dialog id="inner"></dialog>
+ </dialog>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ assert_equals(getComputedStyle(outer).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(inner).color, "rgb(255, 0, 0)");
+ }, "Dialogs initially not matching for container queries");
+
+ test(() => {
+ container.offsetTop;
+ outer.showModal();
+ inner.showModal();
+ assert_equals(getComputedStyle(outer).color, "rgb(255, 0, 0)");
+ assert_equals(getComputedStyle(inner).color, "rgb(255, 0, 0)");
+ }, "Dialogs still not matching after showModal");
+
+ test(() => {
+ container.offsetTop;
+ container.style.width = "200px";
+ assert_equals(getComputedStyle(outer).color, "rgb(0, 255, 0)");
+ assert_equals(getComputedStyle(inner).color, "rgb(0, 128, 0)");
+ }, "@container queries start matching");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/transition-scrollbars.html b/testing/web-platform/tests/css/css-contain/container-queries/transition-scrollbars.html
new file mode 100644
index 0000000000..60d82d26b4
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/transition-scrollbars.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>Container Queries - Scrollbars do not cause transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions/#starting">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #scrollable {
+ overflow: auto;
+ width: 100px;
+ height: 100px;
+ }
+ #container {
+ container-type: inline-size;
+ }
+ #target {
+ background-color: black;
+ }
+
+ /* Matches with or without a scrollbar: */
+ @container (max-width: 100px) {
+ #target {
+ background-color: blue;
+ }
+ }
+
+ /* Matches only when there's a scrollbar: */
+ @container (max-width: 99px) {
+ #target {
+ background-color: green;
+ font-size: 10px;
+ transition: 2s steps(2, start) background-color;
+ }
+ }
+</style>
+<div id=scrollable>
+ <div id=container>
+ <div id=target>
+ Foo bar foo bar foo
+ Foo bar foo bar foo
+ Foo bar foo bar foo
+ Foo bar foo bar foo
+ Foo bar foo bar foo
+ </div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ // Whether or not a scrollbar appeared is out of scope for this test.
+ // The only thing we care about is that no transition was triggered.
+ // Therefore we allow both 'green' and 'blue', but not any other values.
+ let has_scrollbar = target.offsetWidth < 100;
+ let expected = has_scrollbar ? 'rgb(0, 128, 0)' : 'rgb(0, 0, 255)';
+ assert_equals(getComputedStyle(target).backgroundColor, expected);
+ }, 'Scrollbars do not cause a transition of background-color');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event-002.html b/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event-002.html
new file mode 100644
index 0000000000..dc9297004d
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event-002.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Container Queries - Style Change Event for transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions/#starting">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container {
+ container-type: inline-size;
+ }
+ #outer {
+ width: 100px;
+ color: green;
+ }
+ #target {
+ transition: color 100s step-end;
+ }
+
+ @container (min-width: 200px) {
+ #inner { color: red; }
+ }
+ @container (min-width: 200px) {
+ #target {
+ /* This rule exists just to have a container query dependency between
+ target and #inner */
+ background-color: orange;
+ }
+ }
+</style>
+<div id="outer" class="container">
+ <div id="inner" class="container">
+ <div id="target">Green</div>
+ </div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ test(() => {
+ outer.offsetTop;
+ outer.style.width = "200px";
+ assert_equals(getComputedStyle(target).color, "rgb(0, 128, 0)");
+ }, "#inner color change to red triggers a step transition starting at green");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event.html b/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event.html
new file mode 100644
index 0000000000..4cc1772979
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/transition-style-change-event.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Container Queries - Style Change Event for transitions</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions/#starting">
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#animated-containers">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ .container { container-type: size }
+ #outer {
+ width: 100px;
+ color: green;
+ }
+ @container (min-width: 200px) {
+ #inner { color: red }
+ }
+ @container (min-width: 400px) {
+ #target {
+ color: green;
+ transition: color 1s step-start;
+ }
+ }
+</style>
+<div id="outer" class="container">
+ <div id="inner" class="container">
+ <div id="target">Green</div>
+ </div>
+</div>
+</div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const t = async_test("");
+ const event_handler = t.step_func_done((e) => {
+ assert_unreached("Transition event incorrectly triggered: " + e.type);
+ });
+ for (let event_name of ["transitionrun",
+ "transitionstart",
+ "transitionend",
+ "transitioncancel"]) {
+ target.addEventListener(event_name, event_handler);
+ }
+
+ outer.offsetTop;
+ // #target is green. Making the #outer container 200px will turn #inner and
+ // #target red through inheritance.
+ outer.style.width = "200px";
+ // Making #inner 400px will make #target green.
+ inner.style.width = "400px";
+ // Both changes above should happen in one style change event and should not
+ // trigger any transition events. Run two rAFs to make sure any events have
+ // time to trigger.
+ requestAnimationFrame(() => requestAnimationFrame(t.step_func_done(() => {
+ assert_equals(getComputedStyle(inner).color, "rgb(255, 0, 0)",
+ "@container queries supported");
+ })));
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/unsupported-axis.html b/testing/web-platform/tests/css/css-contain/container-queries/unsupported-axis.html
new file mode 100644
index 0000000000..308de2f424
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/unsupported-axis.html
@@ -0,0 +1,228 @@
+<!doctype html>
+<title>Query against unsupported axis</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<script>
+ setup(() => assert_implements_container_queries());
+</script>
+
+<style>
+ #container {
+ width: 200px;
+ height: 100px;
+ container-type: inline-size;
+ }
+</style>
+
+<div id=container>
+ <div id=target>
+ Test
+ </div>
+</div>
+
+
+<style>
+ @container (width > 0px) {
+ #target { --width:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--width'), 'true');
+ }, '(width > 0px)');
+</script>
+
+
+<style>
+ @container (height > 0px) {
+ #target { --height:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // container-type:inline-size does not support queries along the block
+ // axis.
+ assert_equals(getComputedStyle(target).getPropertyValue('--height'), '');
+ }, '(height > 0px)');
+</script>
+
+
+<style>
+ @container ((height > 0px) or (width > 0px)) {
+ #target { --height-or-width:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // (height > 0px) requires container-type:size.
+ assert_equals(getComputedStyle(target).getPropertyValue('--height-or-width'), '');
+ }, '((height > 0px) or (width > 0px))');
+</script>
+
+
+<style>
+ @container ((width > 0px) or (height > 0px)) {
+ #target { --width-or-height:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // (height > 0px) requires container-type:size.
+ assert_equals(getComputedStyle(target).getPropertyValue('--width-or-height'), '');
+ }, '((width > 0px) or (height > 0px))');
+</script>
+
+<style>
+ @container ((orientation: landscape) or (width > 0px)) {
+ #target { --orientation-or-width:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // (orientation: landscape) requires container-type:size.
+ assert_equals(getComputedStyle(target).getPropertyValue('--orientation-or-width'), '');
+ }, '((orientation: landscape) or (width > 0px))');
+</script>
+
+
+<style>
+ @container ((width > 0px) or (orientation: landscape)) {
+ #target { --width-or-orientation:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // (orientation: landscape) requires container-type:size.
+ assert_equals(getComputedStyle(target).getPropertyValue('--width-or-orientation'), '');
+ }, '((width > 0px) or (orientation: landscape))');
+</script>
+
+
+<style>
+ @container ((height > 0px) or (orientation: landscape)) {
+ #target { --height-or-orientation:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--height-or-orientation'), '');
+ }, '((height > 0px) or (orientation: landscape))');
+</script>
+
+
+<style>
+ @container ((height > 0px) or (orientation: landscape)) {
+ #target { --height-or-orientation2:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // Adding full size containment via the 'contain' property does not
+ // make 'height' queryable. (Limited by container-type:inline-size).
+ t.add_cleanup(() => { target.style = ''; });
+ target.style.contain = 'size';
+ assert_equals(getComputedStyle(target).getPropertyValue('--height-or-orientation2'), '');
+ }, '((height > 0px) or (orientation: landscape)), with contain:size');
+</script>
+
+
+<style>
+ @container (inline-size > 0px) {
+ #target { --inline-size:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--inline-size'), 'true');
+ }, '(inline-size > 0px)');
+</script>
+
+
+<style>
+ @container (block-size > 0px) {
+ #target { --block-size:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // container-type:inline-size does not support queries along the block
+ // axis.
+ assert_equals(getComputedStyle(target).getPropertyValue('--block-size'), '');
+ }, '(block-size > 0px)');
+</script>
+
+
+<style>
+ @container (block-size > 0px) {
+ #target { --block-size2:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ // Changing the writing-mode does not affect the evaluation of block-size.
+ t.add_cleanup(() => { target.style = ''; });
+ target.style.writingMode = 'vertical-rl';
+ assert_equals(getComputedStyle(target).getPropertyValue('--block-size2'), '');
+ }, '(block-size > 0px), with writing-mode:vertical-rl');
+</script>
+
+
+<style>
+ @container not (width < 0px) {
+ #target { --not-width:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--not-width'), 'true');
+ }, 'not (width < 0px)');
+</script>
+
+
+<style>
+ @container not (height < 0px) {
+ #target { --not-height:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--not-height'), '');
+ }, 'not (height < 0px)');
+</script>
+
+
+<style>
+ @container not (inline-size < 0px) {
+ #target { --not-inline-size:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--not-inline-size'), 'true');
+ }, 'not (inline-size < 0px)');
+</script>
+
+
+<style>
+ @container not (block-size < 0px) {
+ #target { --not-block-size:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--not-block-size'), '');
+ }, 'not (block-size < 0px)');
+</script>
+
+<style>
+ @container not (orientation) {
+ #target { --not-orientation:true; }
+ }
+</style>
+<script>
+ test(function(t) {
+ assert_equals(getComputedStyle(target).getPropertyValue('--not-orientation'), '');
+ }, 'not (orientation)');
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/viewport-units-dynamic.html b/testing/web-platform/tests/css/css-contain/container-queries/viewport-units-dynamic.html
new file mode 100644
index 0000000000..2339533dee
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/viewport-units-dynamic.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<title>CSS Container Queries Test: @container-dependent elements respond to viewport unit changes</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ iframe {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<iframe id="iframe" srcdoc="
+ <style>
+ #vw, #vh {
+ container-type: inline-size;
+ width: 100px;
+ }
+
+ @container (min-width: 50vw) {
+ #vw span { color: green }
+ }
+ @container (min-width: 100vw) {
+ #vw span { color: red }
+ }
+ @container (min-width: 50vh) {
+ #vh span { color: green }
+ }
+ @container (min-width: 100vh) {
+ #vh span { color: red }
+ }
+ </style>
+ <div id=vw><span>Green</span></div>
+ <div id=vh><span>Green</span></div>
+"></iframe>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ function waitForLoad(w) {
+ return new Promise(resolve => w.addEventListener('load', resolve));
+ }
+
+ promise_test(async () => {
+ await waitForLoad(window);
+ const vw_child = iframe.contentDocument.querySelector("#vw > span");
+ const vh_child = iframe.contentDocument.querySelector("#vh > span");
+
+ assert_equals(getComputedStyle(vw_child).color, "rgb(255, 0, 0)", "vw before resize");
+ assert_equals(getComputedStyle(vh_child).color, "rgb(255, 0, 0)", "vh before resize");
+
+ iframe.style.width = "200px";
+ assert_equals(getComputedStyle(vw_child).color, "rgb(0, 128, 0)", "vw after width resize");
+ assert_equals(getComputedStyle(vh_child).color, "rgb(255, 0, 0)", "vh after width resize");
+
+ iframe.style.height = "200px";
+ assert_equals(getComputedStyle(vw_child).color, "rgb(0, 128, 0)", "vw after height resize");
+ assert_equals(getComputedStyle(vh_child).color, "rgb(0, 128, 0)", "vh after height resize");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/viewport-units.html b/testing/web-platform/tests/css/css-contain/container-queries/viewport-units.html
new file mode 100644
index 0000000000..9b8bb42c43
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/viewport-units.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>CSS Container Queries Test: viewport units</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#size-container">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/cq-testcommon.js"></script>
+<style>
+ #vw { container-type: inline-size; width: 10vw; }
+ #vh { container-type: inline-size; width: 10vh; }
+
+ @container (min-width: 10vw) {
+ #vw span { color: green }
+ }
+ @container (min-width: 11vw) {
+ #vw span { color: red }
+ }
+ @container (min-width: 10vh) {
+ #vh span { color: green }
+ }
+ @container (min-width: 11vh) {
+ #vh span { color: red }
+ }
+</style>
+<div id="vw"><span>Green</span></div>
+<div id="vh"><span>Green</span></div>
+<script>
+ setup(() => assert_implements_container_queries());
+
+ const green = "rgb(0, 128, 0)";
+
+ test(() => assert_equals(getComputedStyle(vw.firstChild).color, green), "Match width with vw");
+ test(() => assert_equals(getComputedStyle(vh.firstChild).color, green), "Match width with vh");
+</script>
diff --git a/testing/web-platform/tests/css/css-contain/container-queries/whitespace-update-after-removal.html b/testing/web-platform/tests/css/css-contain/container-queries/whitespace-update-after-removal.html
new file mode 100644
index 0000000000..a7df55efc6
--- /dev/null
+++ b/testing/web-platform/tests/css/css-contain/container-queries/whitespace-update-after-removal.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>CSS Container Queries Test: whitespace changes in container which changes evaluation</title>
+<link rel="help" href="https://drafts.csswg.org/css-contain-3/#container-queries">
+<link rel="match" href="change-display-in-container-ref.html">
+<style>
+ #container {
+ container-type: size;
+ width: 400px;
+ height: 200px;
+ }
+
+ @container (min-width: 400px) {
+ span { color: red; }
+ }
+</style>
+<p>You should see the word PASS below.</p>
+<div id="container"><span id="fail">FAIL</span> <span>PASS</span></div>
+<script>
+ if (CSS.supports("container-type:size")) {
+ container.offsetTop;
+ container.style.width = "200px";
+ // The space text node between the two spans no longer takes up space when the
+ // first span is removed.
+ fail.remove();
+ }
+</script>